From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/classpath/java/applet/Applet.java | 592 ++ libjava/classpath/java/applet/AppletContext.java | 154 + libjava/classpath/java/applet/AppletStub.java | 103 + libjava/classpath/java/applet/AudioClip.java | 68 + libjava/classpath/java/applet/package.html | 47 + libjava/classpath/java/awt/AWTError.java | 64 + libjava/classpath/java/awt/AWTEvent.java | 399 + .../classpath/java/awt/AWTEventMulticaster.java | 1210 ++++ libjava/classpath/java/awt/AWTException.java | 64 + libjava/classpath/java/awt/AWTKeyStroke.java | 676 ++ libjava/classpath/java/awt/AWTPermission.java | 121 + libjava/classpath/java/awt/ActiveEvent.java | 61 + libjava/classpath/java/awt/Adjustable.java | 171 + libjava/classpath/java/awt/AlphaComposite.java | 215 + libjava/classpath/java/awt/AttributeValue.java | 98 + libjava/classpath/java/awt/BasicStroke.java | 902 +++ libjava/classpath/java/awt/BorderLayout.java | 742 ++ libjava/classpath/java/awt/BufferCapabilities.java | 253 + libjava/classpath/java/awt/Button.java | 467 ++ libjava/classpath/java/awt/Canvas.java | 354 + libjava/classpath/java/awt/CardLayout.java | 501 ++ libjava/classpath/java/awt/Checkbox.java | 665 ++ libjava/classpath/java/awt/CheckboxGroup.java | 172 + libjava/classpath/java/awt/CheckboxMenuItem.java | 376 + libjava/classpath/java/awt/Choice.java | 569 ++ libjava/classpath/java/awt/Color.java | 1025 +++ libjava/classpath/java/awt/ColorPaintContext.java | 195 + libjava/classpath/java/awt/Component.java | 7099 ++++++++++++++++++ .../classpath/java/awt/ComponentOrientation.java | 215 + libjava/classpath/java/awt/Composite.java | 73 + libjava/classpath/java/awt/CompositeContext.java | 71 + libjava/classpath/java/awt/Container.java | 2370 ++++++ .../awt/ContainerOrderFocusTraversalPolicy.java | 482 ++ libjava/classpath/java/awt/Cursor.java | 239 + .../java/awt/DefaultFocusTraversalPolicy.java | 111 + .../java/awt/DefaultKeyboardFocusManager.java | 566 ++ libjava/classpath/java/awt/Desktop.java | 268 + libjava/classpath/java/awt/Dialog.java | 570 ++ libjava/classpath/java/awt/Dimension.java | 234 + libjava/classpath/java/awt/DisplayMode.java | 164 + libjava/classpath/java/awt/Event.java | 185 + .../classpath/java/awt/EventDispatchThread.java | 107 + libjava/classpath/java/awt/EventQueue.java | 658 ++ libjava/classpath/java/awt/FileDialog.java | 395 + libjava/classpath/java/awt/FlowLayout.java | 365 + .../classpath/java/awt/FocusTraversalPolicy.java | 103 + libjava/classpath/java/awt/Font.java | 1451 ++++ .../classpath/java/awt/FontFormatException.java | 65 + libjava/classpath/java/awt/FontMetrics.java | 448 ++ libjava/classpath/java/awt/Frame.java | 704 ++ libjava/classpath/java/awt/GradientPaint.java | 229 + libjava/classpath/java/awt/Graphics.java | 639 ++ libjava/classpath/java/awt/Graphics2D.java | 358 + .../classpath/java/awt/GraphicsConfigTemplate.java | 106 + .../classpath/java/awt/GraphicsConfiguration.java | 275 + libjava/classpath/java/awt/GraphicsDevice.java | 292 + .../classpath/java/awt/GraphicsEnvironment.java | 244 + libjava/classpath/java/awt/GridBagConstraints.java | 246 + libjava/classpath/java/awt/GridBagLayout.java | 1114 +++ libjava/classpath/java/awt/GridBagLayoutInfo.java | 70 + libjava/classpath/java/awt/GridLayout.java | 354 + libjava/classpath/java/awt/HeadlessException.java | 72 + .../java/awt/IllegalComponentStateException.java | 71 + libjava/classpath/java/awt/Image.java | 242 + libjava/classpath/java/awt/ImageCapabilities.java | 107 + libjava/classpath/java/awt/Insets.java | 177 + libjava/classpath/java/awt/ItemSelectable.java | 75 + libjava/classpath/java/awt/JobAttributes.java | 502 ++ libjava/classpath/java/awt/KeyEventDispatcher.java | 82 + .../classpath/java/awt/KeyEventPostProcessor.java | 81 + .../classpath/java/awt/KeyboardFocusManager.java | 1479 ++++ libjava/classpath/java/awt/Label.java | 294 + libjava/classpath/java/awt/LayoutManager.java | 92 + libjava/classpath/java/awt/LayoutManager2.java | 100 + .../classpath/java/awt/LightweightDispatcher.java | 362 + libjava/classpath/java/awt/List.java | 1222 ++++ libjava/classpath/java/awt/MediaTracker.java | 698 ++ libjava/classpath/java/awt/Menu.java | 444 ++ libjava/classpath/java/awt/MenuBar.java | 390 + libjava/classpath/java/awt/MenuComponent.java | 1302 ++++ libjava/classpath/java/awt/MenuContainer.java | 71 + libjava/classpath/java/awt/MenuItem.java | 623 ++ libjava/classpath/java/awt/MenuShortcut.java | 434 ++ libjava/classpath/java/awt/MouseInfo.java | 102 + libjava/classpath/java/awt/PageAttributes.java | 482 ++ libjava/classpath/java/awt/Paint.java | 79 + libjava/classpath/java/awt/PaintContext.java | 76 + libjava/classpath/java/awt/Panel.java | 173 + libjava/classpath/java/awt/Point.java | 250 + libjava/classpath/java/awt/PointerInfo.java | 84 + libjava/classpath/java/awt/Polygon.java | 611 ++ libjava/classpath/java/awt/PopupMenu.java | 190 + libjava/classpath/java/awt/PrintGraphics.java | 57 + libjava/classpath/java/awt/PrintJob.java | 104 + libjava/classpath/java/awt/Rectangle.java | 753 ++ libjava/classpath/java/awt/RenderingHints.java | 804 +++ libjava/classpath/java/awt/Robot.java | 423 ++ libjava/classpath/java/awt/ScrollPane.java | 680 ++ .../classpath/java/awt/ScrollPaneAdjustable.java | 241 + libjava/classpath/java/awt/Scrollbar.java | 815 +++ libjava/classpath/java/awt/Shape.java | 205 + libjava/classpath/java/awt/Stroke.java | 65 + libjava/classpath/java/awt/SystemColor.java | 462 ++ libjava/classpath/java/awt/TextArea.java | 631 ++ libjava/classpath/java/awt/TextComponent.java | 676 ++ libjava/classpath/java/awt/TextField.java | 484 ++ libjava/classpath/java/awt/TexturePaint.java | 118 + libjava/classpath/java/awt/Toolkit.java | 1429 ++++ libjava/classpath/java/awt/Transparency.java | 67 + libjava/classpath/java/awt/Window.java | 1286 ++++ libjava/classpath/java/awt/color/CMMException.java | 63 + libjava/classpath/java/awt/color/ColorSpace.java | 183 + .../classpath/java/awt/color/ICC_ColorSpace.java | 314 + libjava/classpath/java/awt/color/ICC_Profile.java | 1249 ++++ .../classpath/java/awt/color/ICC_ProfileGray.java | 133 + .../classpath/java/awt/color/ICC_ProfileRGB.java | 227 + .../java/awt/color/ProfileDataException.java | 64 + libjava/classpath/java/awt/color/package.html | 46 + .../classpath/java/awt/datatransfer/Clipboard.java | 213 + .../java/awt/datatransfer/ClipboardOwner.java | 56 + .../java/awt/datatransfer/DataFlavor.java | 1026 +++ .../java/awt/datatransfer/FlavorEvent.java | 57 + .../java/awt/datatransfer/FlavorListener.java | 54 + .../classpath/java/awt/datatransfer/FlavorMap.java | 75 + .../java/awt/datatransfer/FlavorTable.java | 73 + .../classpath/java/awt/datatransfer/MimeType.java | 283 + .../awt/datatransfer/MimeTypeParseException.java | 70 + .../java/awt/datatransfer/StringSelection.java | 157 + .../java/awt/datatransfer/SystemFlavorMap.java | 561 ++ .../java/awt/datatransfer/Transferable.java | 82 + .../datatransfer/UnsupportedFlavorException.java | 65 + .../classpath/java/awt/datatransfer/package.html | 47 + libjava/classpath/java/awt/dnd/Autoscroll.java | 69 + libjava/classpath/java/awt/dnd/DnDConstants.java | 77 + .../java/awt/dnd/DnDEventMulticaster.java | 74 + .../classpath/java/awt/dnd/DragGestureEvent.java | 219 + .../java/awt/dnd/DragGestureListener.java | 63 + .../java/awt/dnd/DragGestureRecognizer.java | 191 + libjava/classpath/java/awt/dnd/DragSource.java | 326 + .../classpath/java/awt/dnd/DragSourceAdapter.java | 126 + .../classpath/java/awt/dnd/DragSourceContext.java | 383 + .../java/awt/dnd/DragSourceDragEvent.java | 102 + .../java/awt/dnd/DragSourceDropEvent.java | 89 + .../classpath/java/awt/dnd/DragSourceEvent.java | 93 + .../classpath/java/awt/dnd/DragSourceListener.java | 97 + .../java/awt/dnd/DragSourceMotionListener.java | 64 + libjava/classpath/java/awt/dnd/DropTarget.java | 438 ++ .../classpath/java/awt/dnd/DropTargetAdapter.java | 100 + .../classpath/java/awt/dnd/DropTargetContext.java | 197 + .../java/awt/dnd/DropTargetDragEvent.java | 152 + .../java/awt/dnd/DropTargetDropEvent.java | 170 + .../classpath/java/awt/dnd/DropTargetEvent.java | 62 + .../classpath/java/awt/dnd/DropTargetListener.java | 89 + .../java/awt/dnd/InvalidDnDOperationException.java | 74 + .../java/awt/dnd/MouseDragGestureRecognizer.java | 131 + libjava/classpath/java/awt/dnd/package.html | 46 + .../java/awt/dnd/peer/DragSourceContextPeer.java | 57 + .../java/awt/dnd/peer/DropTargetContextPeer.java | 68 + .../java/awt/dnd/peer/DropTargetPeer.java | 48 + libjava/classpath/java/awt/dnd/peer/package.html | 46 + libjava/classpath/java/awt/doc-files/capjoin.png | Bin 0 -> 5325 bytes .../classpath/java/awt/event/AWTEventListener.java | 65 + .../java/awt/event/AWTEventListenerProxy.java | 95 + libjava/classpath/java/awt/event/ActionEvent.java | 228 + .../classpath/java/awt/event/ActionListener.java | 59 + .../classpath/java/awt/event/AdjustmentEvent.java | 222 + .../java/awt/event/AdjustmentListener.java | 58 + .../classpath/java/awt/event/ComponentAdapter.java | 97 + .../classpath/java/awt/event/ComponentEvent.java | 142 + .../java/awt/event/ComponentListener.java | 84 + .../classpath/java/awt/event/ContainerAdapter.java | 79 + .../classpath/java/awt/event/ContainerEvent.java | 135 + .../java/awt/event/ContainerListener.java | 70 + libjava/classpath/java/awt/event/FocusAdapter.java | 79 + libjava/classpath/java/awt/event/FocusEvent.java | 181 + .../classpath/java/awt/event/FocusListener.java | 69 + .../java/awt/event/HierarchyBoundsAdapter.java | 78 + .../java/awt/event/HierarchyBoundsListener.java | 70 + .../classpath/java/awt/event/HierarchyEvent.java | 255 + .../java/awt/event/HierarchyListener.java | 62 + libjava/classpath/java/awt/event/InputEvent.java | 399 + .../classpath/java/awt/event/InputMethodEvent.java | 305 + .../java/awt/event/InputMethodListener.java | 70 + .../classpath/java/awt/event/InvocationEvent.java | 258 + libjava/classpath/java/awt/event/ItemEvent.java | 155 + libjava/classpath/java/awt/event/ItemListener.java | 62 + libjava/classpath/java/awt/event/KeyAdapter.java | 88 + libjava/classpath/java/awt/event/KeyEvent.java | 1762 +++++ libjava/classpath/java/awt/event/KeyListener.java | 77 + libjava/classpath/java/awt/event/MouseAdapter.java | 106 + libjava/classpath/java/awt/event/MouseEvent.java | 502 ++ .../classpath/java/awt/event/MouseListener.java | 94 + .../java/awt/event/MouseMotionAdapter.java | 79 + .../java/awt/event/MouseMotionListener.java | 72 + .../classpath/java/awt/event/MouseWheelEvent.java | 232 + .../java/awt/event/MouseWheelListener.java | 60 + libjava/classpath/java/awt/event/PaintEvent.java | 127 + libjava/classpath/java/awt/event/TextEvent.java | 93 + libjava/classpath/java/awt/event/TextListener.java | 60 + .../classpath/java/awt/event/WindowAdapter.java | 156 + libjava/classpath/java/awt/event/WindowEvent.java | 314 + .../java/awt/event/WindowFocusListener.java | 68 + .../classpath/java/awt/event/WindowListener.java | 109 + .../java/awt/event/WindowStateListener.java | 62 + libjava/classpath/java/awt/event/package.html | 46 + .../classpath/java/awt/font/FontRenderContext.java | 137 + .../java/awt/font/GlyphJustificationInfo.java | 77 + libjava/classpath/java/awt/font/GlyphMetrics.java | 138 + libjava/classpath/java/awt/font/GlyphVector.java | 174 + .../classpath/java/awt/font/GraphicAttribute.java | 137 + .../java/awt/font/ImageGraphicAttribute.java | 187 + .../classpath/java/awt/font/LineBreakMeasurer.java | 148 + libjava/classpath/java/awt/font/LineMetrics.java | 67 + .../classpath/java/awt/font/MultipleMaster.java | 61 + libjava/classpath/java/awt/font/NumericShaper.java | 425 ++ libjava/classpath/java/awt/font/OpenType.java | 111 + .../java/awt/font/ShapeGraphicAttribute.java | 185 + libjava/classpath/java/awt/font/TextAttribute.java | 309 + libjava/classpath/java/awt/font/TextHitInfo.java | 128 + libjava/classpath/java/awt/font/TextLayout.java | 1420 ++++ libjava/classpath/java/awt/font/TextMeasurer.java | 190 + .../java/awt/font/TransformAttribute.java | 100 + libjava/classpath/java/awt/font/package.html | 46 + .../classpath/java/awt/geom/AffineTransform.java | 1489 ++++ libjava/classpath/java/awt/geom/Arc2D.java | 1413 ++++ libjava/classpath/java/awt/geom/Area.java | 3312 +++++++++ libjava/classpath/java/awt/geom/CubicCurve2D.java | 1724 +++++ libjava/classpath/java/awt/geom/Dimension2D.java | 118 + libjava/classpath/java/awt/geom/Ellipse2D.java | 413 ++ .../java/awt/geom/FlatteningPathIterator.java | 579 ++ libjava/classpath/java/awt/geom/GeneralPath.java | 992 +++ .../java/awt/geom/IllegalPathStateException.java | 71 + libjava/classpath/java/awt/geom/Line2D.java | 1182 +++ .../awt/geom/NoninvertibleTransformException.java | 65 + libjava/classpath/java/awt/geom/PathIterator.java | 189 + libjava/classpath/java/awt/geom/Point2D.java | 396 + libjava/classpath/java/awt/geom/QuadCurve2D.java | 1467 ++++ libjava/classpath/java/awt/geom/Rectangle2D.java | 992 +++ .../classpath/java/awt/geom/RectangularShape.java | 382 + .../classpath/java/awt/geom/RoundRectangle2D.java | 584 ++ .../classpath/java/awt/geom/doc-files/Area-1.png | Bin 0 -> 21447 bytes .../java/awt/geom/doc-files/CubicCurve2D-1.png | Bin 0 -> 6280 bytes .../java/awt/geom/doc-files/CubicCurve2D-2.png | Bin 0 -> 5791 bytes .../java/awt/geom/doc-files/CubicCurve2D-3.png | Bin 0 -> 13168 bytes .../java/awt/geom/doc-files/CubicCurve2D-4.png | Bin 0 -> 7839 bytes .../java/awt/geom/doc-files/CubicCurve2D-5.png | Bin 0 -> 5112 bytes .../java/awt/geom/doc-files/Ellipse-1.png | Bin 0 -> 19426 bytes .../geom/doc-files/FlatteningPathIterator-1.html | 481 ++ .../java/awt/geom/doc-files/GeneralPath-1.png | Bin 0 -> 13111 bytes .../java/awt/geom/doc-files/QuadCurve2D-1.png | Bin 0 -> 6363 bytes .../java/awt/geom/doc-files/QuadCurve2D-2.png | Bin 0 -> 5872 bytes .../java/awt/geom/doc-files/QuadCurve2D-3.png | Bin 0 -> 12334 bytes .../java/awt/geom/doc-files/QuadCurve2D-4.png | Bin 0 -> 7797 bytes .../java/awt/geom/doc-files/QuadCurve2D-5.png | Bin 0 -> 4757 bytes libjava/classpath/java/awt/geom/package.html | 46 + libjava/classpath/java/awt/im/InputContext.java | 438 ++ .../java/awt/im/InputMethodHighlight.java | 189 + .../classpath/java/awt/im/InputMethodRequests.java | 155 + libjava/classpath/java/awt/im/InputSubset.java | 129 + libjava/classpath/java/awt/im/package.html | 46 + libjava/classpath/java/awt/im/spi/InputMethod.java | 244 + .../java/awt/im/spi/InputMethodContext.java | 126 + .../java/awt/im/spi/InputMethodDescriptor.java | 113 + libjava/classpath/java/awt/im/spi/package.html | 46 + .../java/awt/image/AffineTransformOp.java | 608 ++ .../java/awt/image/AreaAveragingScaleFilter.java | 268 + .../classpath/java/awt/image/BandCombineOp.java | 218 + .../java/awt/image/BandedSampleModel.java | 759 ++ .../classpath/java/awt/image/BufferStrategy.java | 124 + .../classpath/java/awt/image/BufferedImage.java | 839 +++ .../java/awt/image/BufferedImageFilter.java | 110 + .../classpath/java/awt/image/BufferedImageOp.java | 107 + .../classpath/java/awt/image/ByteLookupTable.java | 175 + .../classpath/java/awt/image/ColorConvertOp.java | 537 ++ libjava/classpath/java/awt/image/ColorModel.java | 794 ++ .../java/awt/image/ComponentColorModel.java | 408 ++ .../java/awt/image/ComponentSampleModel.java | 755 ++ libjava/classpath/java/awt/image/ConvolveOp.java | 380 + .../classpath/java/awt/image/CropImageFilter.java | 184 + libjava/classpath/java/awt/image/DataBuffer.java | 437 ++ .../classpath/java/awt/image/DataBufferByte.java | 245 + .../classpath/java/awt/image/DataBufferDouble.java | 288 + .../classpath/java/awt/image/DataBufferFloat.java | 286 + .../classpath/java/awt/image/DataBufferInt.java | 244 + .../classpath/java/awt/image/DataBufferShort.java | 245 + .../classpath/java/awt/image/DataBufferUShort.java | 246 + .../classpath/java/awt/image/DirectColorModel.java | 435 ++ .../java/awt/image/FilteredImageSource.java | 124 + .../classpath/java/awt/image/ImageConsumer.java | 216 + libjava/classpath/java/awt/image/ImageFilter.java | 226 + .../classpath/java/awt/image/ImageObserver.java | 129 + .../classpath/java/awt/image/ImageProducer.java | 84 + .../java/awt/image/ImagingOpException.java | 66 + .../classpath/java/awt/image/IndexColorModel.java | 736 ++ libjava/classpath/java/awt/image/Kernel.java | 171 + libjava/classpath/java/awt/image/LookupOp.java | 307 + libjava/classpath/java/awt/image/LookupTable.java | 109 + .../java/awt/image/MemoryImageSource.java | 429 ++ .../awt/image/MultiPixelPackedSampleModel.java | 603 ++ .../classpath/java/awt/image/PackedColorModel.java | 188 + libjava/classpath/java/awt/image/PixelGrabber.java | 631 ++ .../awt/image/PixelInterleavedSampleModel.java | 123 + .../classpath/java/awt/image/RGBImageFilter.java | 265 + libjava/classpath/java/awt/image/Raster.java | 973 +++ .../java/awt/image/RasterFormatException.java | 65 + libjava/classpath/java/awt/image/RasterOp.java | 104 + .../classpath/java/awt/image/RenderedImage.java | 70 + .../java/awt/image/ReplicateScaleFilter.java | 257 + libjava/classpath/java/awt/image/RescaleOp.java | 385 + libjava/classpath/java/awt/image/SampleModel.java | 974 +++ .../classpath/java/awt/image/ShortLookupTable.java | 177 + .../awt/image/SinglePixelPackedSampleModel.java | 586 ++ libjava/classpath/java/awt/image/TileObserver.java | 47 + .../classpath/java/awt/image/VolatileImage.java | 253 + .../classpath/java/awt/image/WritableRaster.java | 448 ++ .../java/awt/image/WritableRenderedImage.java | 56 + libjava/classpath/java/awt/image/package.html | 46 + .../renderable/ContextualRenderedImageFactory.java | 56 + .../java/awt/image/renderable/ParameterBlock.java | 308 + .../java/awt/image/renderable/RenderContext.java | 141 + .../java/awt/image/renderable/RenderableImage.java | 61 + .../awt/image/renderable/RenderableImageOp.java | 157 + .../image/renderable/RenderableImageProducer.java | 166 + .../awt/image/renderable/RenderedImageFactory.java | 47 + .../java/awt/image/renderable/package.html | 46 + libjava/classpath/java/awt/package.html | 46 + libjava/classpath/java/awt/peer/ButtonPeer.java | 45 + libjava/classpath/java/awt/peer/CanvasPeer.java | 44 + .../java/awt/peer/CheckboxMenuItemPeer.java | 45 + libjava/classpath/java/awt/peer/CheckboxPeer.java | 51 + libjava/classpath/java/awt/peer/ChoicePeer.java | 53 + libjava/classpath/java/awt/peer/ComponentPeer.java | 532 ++ libjava/classpath/java/awt/peer/ContainerPeer.java | 83 + libjava/classpath/java/awt/peer/DesktopPeer.java | 64 + libjava/classpath/java/awt/peer/DialogPeer.java | 47 + .../classpath/java/awt/peer/FileDialogPeer.java | 51 + libjava/classpath/java/awt/peer/FontPeer.java | 44 + libjava/classpath/java/awt/peer/FramePeer.java | 77 + libjava/classpath/java/awt/peer/LabelPeer.java | 45 + .../classpath/java/awt/peer/LightweightPeer.java | 44 + libjava/classpath/java/awt/peer/ListPeer.java | 60 + libjava/classpath/java/awt/peer/MenuBarPeer.java | 48 + .../classpath/java/awt/peer/MenuComponentPeer.java | 54 + libjava/classpath/java/awt/peer/MenuItemPeer.java | 47 + libjava/classpath/java/awt/peer/MenuPeer.java | 48 + libjava/classpath/java/awt/peer/MouseInfoPeer.java | 61 + libjava/classpath/java/awt/peer/PanelPeer.java | 44 + libjava/classpath/java/awt/peer/PopupMenuPeer.java | 52 + libjava/classpath/java/awt/peer/RobotPeer.java | 55 + .../classpath/java/awt/peer/ScrollPanePeer.java | 51 + libjava/classpath/java/awt/peer/ScrollbarPeer.java | 46 + libjava/classpath/java/awt/peer/TextAreaPeer.java | 53 + .../classpath/java/awt/peer/TextComponentPeer.java | 65 + libjava/classpath/java/awt/peer/TextFieldPeer.java | 51 + libjava/classpath/java/awt/peer/WindowPeer.java | 69 + libjava/classpath/java/awt/peer/package.html | 46 + libjava/classpath/java/awt/print/Book.java | 159 + libjava/classpath/java/awt/print/NoPrinterJob.java | 124 + libjava/classpath/java/awt/print/PageFormat.java | 233 + libjava/classpath/java/awt/print/Pageable.java | 90 + libjava/classpath/java/awt/print/Paper.java | 197 + libjava/classpath/java/awt/print/Printable.java | 80 + .../java/awt/print/PrinterAbortException.java | 71 + .../classpath/java/awt/print/PrinterException.java | 71 + .../classpath/java/awt/print/PrinterGraphics.java | 58 + .../java/awt/print/PrinterIOException.java | 97 + libjava/classpath/java/awt/print/PrinterJob.java | 300 + libjava/classpath/java/awt/print/package.html | 46 + .../classpath/java/beans/AppletInitializer.java | 61 + libjava/classpath/java/beans/BeanDescriptor.java | 89 + libjava/classpath/java/beans/BeanInfo.java | 181 + libjava/classpath/java/beans/Beans.java | 368 + .../java/beans/ConstructorProperties.java | 72 + libjava/classpath/java/beans/Customizer.java | 86 + .../java/beans/DefaultPersistenceDelegate.java | 211 + libjava/classpath/java/beans/DesignMode.java | 95 + libjava/classpath/java/beans/Encoder.java | 433 ++ libjava/classpath/java/beans/EventHandler.java | 604 ++ .../classpath/java/beans/EventSetDescriptor.java | 763 ++ .../classpath/java/beans/ExceptionListener.java | 57 + libjava/classpath/java/beans/Expression.java | 138 + .../classpath/java/beans/FeatureDescriptor.java | 232 + .../java/beans/IndexedPropertyChangeEvent.java | 81 + .../java/beans/IndexedPropertyDescriptor.java | 421 ++ .../java/beans/IntrospectionException.java | 67 + libjava/classpath/java/beans/Introspector.java | 705 ++ libjava/classpath/java/beans/MethodDescriptor.java | 87 + .../classpath/java/beans/ParameterDescriptor.java | 52 + .../classpath/java/beans/PersistenceDelegate.java | 90 + .../classpath/java/beans/PropertyChangeEvent.java | 189 + .../java/beans/PropertyChangeListener.java | 61 + .../java/beans/PropertyChangeListenerProxy.java | 102 + .../java/beans/PropertyChangeSupport.java | 545 ++ .../classpath/java/beans/PropertyDescriptor.java | 665 ++ libjava/classpath/java/beans/PropertyEditor.java | 209 + .../java/beans/PropertyEditorManager.java | 216 + .../java/beans/PropertyEditorSupport.java | 265 + .../java/beans/PropertyVetoException.java | 85 + libjava/classpath/java/beans/SimpleBeanInfo.java | 145 + libjava/classpath/java/beans/Statement.java | 386 + libjava/classpath/java/beans/TODO | 4 + .../java/beans/VetoableChangeListener.java | 73 + .../java/beans/VetoableChangeListenerProxy.java | 102 + .../java/beans/VetoableChangeSupport.java | 532 ++ libjava/classpath/java/beans/Visibility.java | 85 + libjava/classpath/java/beans/XMLDecoder.java | 307 + libjava/classpath/java/beans/XMLEncoder.java | 267 + .../java/beans/beancontext/BeanContext.java | 272 + .../java/beans/beancontext/BeanContextChild.java | 174 + .../BeanContextChildComponentProxy.java | 60 + .../beans/beancontext/BeanContextChildSupport.java | 381 + .../beancontext/BeanContextContainerProxy.java | 63 + .../java/beans/beancontext/BeanContextEvent.java | 110 + .../beancontext/BeanContextMembershipEvent.java | 114 + .../beancontext/BeanContextMembershipListener.java | 70 + .../java/beans/beancontext/BeanContextProxy.java | 65 + .../BeanContextServiceAvailableEvent.java | 97 + .../beancontext/BeanContextServiceProvider.java | 138 + .../BeanContextServiceProviderBeanInfo.java | 60 + .../BeanContextServiceRevokedEvent.java | 112 + .../BeanContextServiceRevokedListener.java | 62 + .../beans/beancontext/BeanContextServices.java | 216 + .../beancontext/BeanContextServicesListener.java | 56 + .../beancontext/BeanContextServicesSupport.java | 896 +++ .../java/beans/beancontext/BeanContextSupport.java | 1079 +++ .../classpath/java/beans/beancontext/package.html | 46 + libjava/classpath/java/beans/package.html | 46 + libjava/classpath/java/io/BufferedInputStream.java | 379 + .../classpath/java/io/BufferedOutputStream.java | 191 + libjava/classpath/java/io/BufferedReader.java | 575 ++ libjava/classpath/java/io/BufferedWriter.java | 262 + .../classpath/java/io/ByteArrayInputStream.java | 251 + .../classpath/java/io/ByteArrayOutputStream.java | 284 + libjava/classpath/java/io/CharArrayReader.java | 305 + libjava/classpath/java/io/CharArrayWriter.java | 352 + .../classpath/java/io/CharConversionException.java | 73 + libjava/classpath/java/io/Closeable.java | 63 + libjava/classpath/java/io/DataInput.java | 456 ++ libjava/classpath/java/io/DataInputStream.java | 785 ++ libjava/classpath/java/io/DataOutput.java | 325 + libjava/classpath/java/io/DataOutputStream.java | 539 ++ libjava/classpath/java/io/DeleteFileHelper.java | 106 + libjava/classpath/java/io/EOFException.java | 76 + libjava/classpath/java/io/Externalizable.java | 107 + libjava/classpath/java/io/File.java | 1605 +++++ libjava/classpath/java/io/FileDescriptor.java | 140 + libjava/classpath/java/io/FileFilter.java | 65 + libjava/classpath/java/io/FileInputStream.java | 322 + .../classpath/java/io/FileNotFoundException.java | 73 + libjava/classpath/java/io/FileOutputStream.java | 309 + libjava/classpath/java/io/FilePermission.java | 293 + libjava/classpath/java/io/FileReader.java | 91 + libjava/classpath/java/io/FileWriter.java | 137 + libjava/classpath/java/io/FilenameFilter.java | 75 + libjava/classpath/java/io/FilterInputStream.java | 203 + libjava/classpath/java/io/FilterOutputStream.java | 149 + libjava/classpath/java/io/FilterReader.java | 184 + libjava/classpath/java/io/FilterWriter.java | 146 + libjava/classpath/java/io/Flushable.java | 62 + libjava/classpath/java/io/IOException.java | 74 + libjava/classpath/java/io/InputStream.java | 270 + libjava/classpath/java/io/InputStreamReader.java | 509 ++ .../classpath/java/io/InterruptedIOException.java | 94 + .../classpath/java/io/InvalidClassException.java | 110 + .../classpath/java/io/InvalidObjectException.java | 66 + .../classpath/java/io/LineNumberInputStream.java | 315 + libjava/classpath/java/io/LineNumberReader.java | 416 ++ libjava/classpath/java/io/NotActiveException.java | 72 + .../java/io/NotSerializableException.java | 74 + libjava/classpath/java/io/ObjectInput.java | 140 + libjava/classpath/java/io/ObjectInputStream.java | 2142 ++++++ .../classpath/java/io/ObjectInputValidation.java | 67 + libjava/classpath/java/io/ObjectOutput.java | 110 + libjava/classpath/java/io/ObjectOutputStream.java | 1490 ++++ libjava/classpath/java/io/ObjectStreamClass.java | 1161 +++ .../classpath/java/io/ObjectStreamConstants.java | 226 + .../classpath/java/io/ObjectStreamException.java | 74 + libjava/classpath/java/io/ObjectStreamField.java | 401 + .../classpath/java/io/OptionalDataException.java | 91 + libjava/classpath/java/io/OutputStream.java | 140 + libjava/classpath/java/io/OutputStreamWriter.java | 429 ++ libjava/classpath/java/io/PipedInputStream.java | 413 ++ libjava/classpath/java/io/PipedOutputStream.java | 181 + libjava/classpath/java/io/PipedReader.java | 364 + libjava/classpath/java/io/PipedWriter.java | 182 + libjava/classpath/java/io/PrintStream.java | 675 ++ libjava/classpath/java/io/PrintWriter.java | 689 ++ libjava/classpath/java/io/PushbackInputStream.java | 335 + libjava/classpath/java/io/PushbackReader.java | 383 + libjava/classpath/java/io/RandomAccessFile.java | 1049 +++ libjava/classpath/java/io/Reader.java | 286 + libjava/classpath/java/io/SequenceInputStream.java | 223 + libjava/classpath/java/io/Serializable.java | 54 + .../classpath/java/io/SerializablePermission.java | 112 + .../java/io/StreamCorruptedException.java | 73 + libjava/classpath/java/io/StreamTokenizer.java | 718 ++ .../classpath/java/io/StringBufferInputStream.java | 187 + libjava/classpath/java/io/StringReader.java | 208 + libjava/classpath/java/io/StringWriter.java | 212 + libjava/classpath/java/io/SyncFailedException.java | 66 + .../classpath/java/io/UTFDataFormatException.java | 74 + .../java/io/UnsupportedEncodingException.java | 73 + .../classpath/java/io/WriteAbortedException.java | 109 + libjava/classpath/java/io/Writer.java | 211 + libjava/classpath/java/io/package.html | 46 + .../classpath/java/lang/AbstractMethodError.java | 75 + .../classpath/java/lang/AbstractStringBuffer.java | 1037 +++ libjava/classpath/java/lang/Appendable.java | 122 + .../classpath/java/lang/ArithmeticException.java | 77 + .../java/lang/ArrayIndexOutOfBoundsException.java | 87 + .../classpath/java/lang/ArrayStoreException.java | 77 + libjava/classpath/java/lang/AssertionError.java | 148 + libjava/classpath/java/lang/Boolean.java | 251 + libjava/classpath/java/lang/Byte.java | 373 + libjava/classpath/java/lang/CharSequence.java | 99 + libjava/classpath/java/lang/Character.java | 4552 ++++++++++++ libjava/classpath/java/lang/Class.java | 1799 +++++ .../classpath/java/lang/ClassCastException.java | 76 + .../classpath/java/lang/ClassCircularityError.java | 73 + libjava/classpath/java/lang/ClassFormatError.java | 72 + libjava/classpath/java/lang/ClassLoader.java | 1151 +++ .../java/lang/ClassNotFoundException.java | 126 + .../java/lang/CloneNotSupportedException.java | 92 + libjava/classpath/java/lang/Cloneable.java | 78 + libjava/classpath/java/lang/Comparable.java | 98 + libjava/classpath/java/lang/Compiler.java | 127 + libjava/classpath/java/lang/Deprecated.java | 56 + libjava/classpath/java/lang/Double.java | 625 ++ libjava/classpath/java/lang/Enum.java | 237 + .../java/lang/EnumConstantNotPresentException.java | 96 + libjava/classpath/java/lang/Error.java | 107 + libjava/classpath/java/lang/Exception.java | 104 + .../java/lang/ExceptionInInitializerError.java | 123 + libjava/classpath/java/lang/Float.java | 633 ++ .../classpath/java/lang/IllegalAccessError.java | 76 + .../java/lang/IllegalAccessException.java | 99 + .../java/lang/IllegalArgumentException.java | 129 + .../java/lang/IllegalMonitorStateException.java | 78 + .../classpath/java/lang/IllegalStateException.java | 134 + .../java/lang/IllegalThreadStateException.java | 75 + .../java/lang/IncompatibleClassChangeError.java | 73 + .../java/lang/IndexOutOfBoundsException.java | 75 + .../java/lang/InheritableThreadLocal.java | 98 + .../classpath/java/lang/InstantiationError.java | 75 + .../java/lang/InstantiationException.java | 74 + libjava/classpath/java/lang/Integer.java | 841 +++ libjava/classpath/java/lang/InternalError.java | 72 + .../classpath/java/lang/InterruptedException.java | 80 + libjava/classpath/java/lang/Iterable.java | 60 + libjava/classpath/java/lang/LinkageError.java | 74 + libjava/classpath/java/lang/Long.java | 830 +++ libjava/classpath/java/lang/Math.java | 1052 +++ .../java/lang/NegativeArraySizeException.java | 77 + .../classpath/java/lang/NoClassDefFoundError.java | 76 + libjava/classpath/java/lang/NoSuchFieldError.java | 74 + .../classpath/java/lang/NoSuchFieldException.java | 73 + libjava/classpath/java/lang/NoSuchMethodError.java | 74 + .../classpath/java/lang/NoSuchMethodException.java | 72 + .../classpath/java/lang/NullPointerException.java | 82 + libjava/classpath/java/lang/Number.java | 131 + .../classpath/java/lang/NumberFormatException.java | 73 + libjava/classpath/java/lang/Object.java | 530 ++ libjava/classpath/java/lang/OutOfMemoryError.java | 73 + libjava/classpath/java/lang/Override.java | 56 + libjava/classpath/java/lang/Package.java | 414 ++ libjava/classpath/java/lang/Process.java | 130 + libjava/classpath/java/lang/ProcessBuilder.java | 337 + libjava/classpath/java/lang/Readable.java | 72 + libjava/classpath/java/lang/Runnable.java | 62 + libjava/classpath/java/lang/Runtime.java | 796 ++ libjava/classpath/java/lang/RuntimeException.java | 102 + libjava/classpath/java/lang/RuntimePermission.java | 209 + libjava/classpath/java/lang/SecurityException.java | 128 + libjava/classpath/java/lang/SecurityManager.java | 1087 +++ libjava/classpath/java/lang/Short.java | 383 + .../classpath/java/lang/StackOverflowError.java | 72 + libjava/classpath/java/lang/StackTraceElement.java | 279 + libjava/classpath/java/lang/StrictMath.java | 2575 +++++++ libjava/classpath/java/lang/String.java | 2200 ++++++ libjava/classpath/java/lang/StringBuffer.java | 976 +++ libjava/classpath/java/lang/StringBuilder.java | 706 ++ .../java/lang/StringIndexOutOfBoundsException.java | 85 + libjava/classpath/java/lang/SuppressWarnings.java | 69 + libjava/classpath/java/lang/System.java | 1120 +++ libjava/classpath/java/lang/Thread.java | 1379 ++++ libjava/classpath/java/lang/ThreadDeath.java | 68 + libjava/classpath/java/lang/ThreadGroup.java | 791 ++ libjava/classpath/java/lang/ThreadLocal.java | 182 + libjava/classpath/java/lang/ThreadLocalMap.java | 325 + libjava/classpath/java/lang/Throwable.java | 565 ++ .../java/lang/TypeNotPresentException.java | 98 + libjava/classpath/java/lang/UnknownError.java | 72 + .../classpath/java/lang/UnsatisfiedLinkError.java | 74 + .../java/lang/UnsupportedClassVersionError.java | 74 + .../java/lang/UnsupportedOperationException.java | 127 + libjava/classpath/java/lang/VerifyError.java | 72 + .../classpath/java/lang/VirtualMachineError.java | 73 + libjava/classpath/java/lang/Void.java | 68 + .../classpath/java/lang/annotation/Annotation.java | 135 + .../lang/annotation/AnnotationFormatError.java | 105 + .../AnnotationTypeMismatchException.java | 116 + .../classpath/java/lang/annotation/Documented.java | 50 + .../java/lang/annotation/ElementType.java | 59 + .../annotation/IncompleteAnnotationException.java | 107 + .../classpath/java/lang/annotation/Inherited.java | 51 + .../classpath/java/lang/annotation/Retention.java | 59 + .../java/lang/annotation/RetentionPolicy.java | 66 + libjava/classpath/java/lang/annotation/Target.java | 52 + .../classpath/java/lang/annotation/package.html | 46 + .../java/lang/instrument/ClassDefinition.java | 86 + .../java/lang/instrument/ClassFileTransformer.java | 84 + .../instrument/IllegalClassFormatException.java | 70 + .../java/lang/instrument/Instrumentation.java | 139 + .../instrument/UnmodifiableClassException.java | 69 + .../java/lang/management/ClassLoadingMXBean.java | 102 + .../java/lang/management/CompilationMXBean.java | 85 + .../lang/management/GarbageCollectorMXBean.java | 79 + .../classpath/java/lang/management/LockInfo.java | 114 + .../java/lang/management/ManagementFactory.java | 817 +++ .../java/lang/management/ManagementPermission.java | 131 + .../java/lang/management/MemoryMXBean.java | 172 + .../java/lang/management/MemoryManagerMXBean.java | 77 + .../lang/management/MemoryNotificationInfo.java | 214 + .../java/lang/management/MemoryPoolMXBean.java | 318 + .../classpath/java/lang/management/MemoryType.java | 50 + .../java/lang/management/MemoryUsage.java | 264 + .../java/lang/management/MonitorInfo.java | 179 + .../lang/management/OperatingSystemMXBean.java | 119 + .../java/lang/management/RuntimeMXBean.java | 278 + .../classpath/java/lang/management/ThreadInfo.java | 895 +++ .../java/lang/management/ThreadMXBean.java | 642 ++ .../classpath/java/lang/management/package.html | 64 + libjava/classpath/java/lang/package.html | 48 + .../classpath/java/lang/ref/PhantomReference.java | 73 + libjava/classpath/java/lang/ref/Reference.java | 176 + .../classpath/java/lang/ref/ReferenceQueue.java | 164 + libjava/classpath/java/lang/ref/SoftReference.java | 84 + libjava/classpath/java/lang/ref/WeakReference.java | 79 + libjava/classpath/java/lang/ref/package.html | 46 + .../java/lang/reflect/AccessibleObject.java | 233 + .../java/lang/reflect/AnnotatedElement.java | 115 + libjava/classpath/java/lang/reflect/Array.java | 655 ++ .../classpath/java/lang/reflect/Constructor.java | 453 ++ libjava/classpath/java/lang/reflect/Field.java | 735 ++ .../java/lang/reflect/GenericArrayType.java | 61 + .../java/lang/reflect/GenericDeclaration.java | 62 + .../lang/reflect/GenericSignatureFormatError.java | 63 + .../java/lang/reflect/InvocationHandler.java | 137 + .../lang/reflect/InvocationTargetException.java | 123 + .../MalformedParameterizedTypeException.java | 60 + libjava/classpath/java/lang/reflect/Member.java | 109 + libjava/classpath/java/lang/reflect/Method.java | 497 ++ libjava/classpath/java/lang/reflect/Modifier.java | 354 + .../java/lang/reflect/ParameterizedType.java | 122 + libjava/classpath/java/lang/reflect/Proxy.java | 1545 ++++ libjava/classpath/java/lang/reflect/README | 4 + .../java/lang/reflect/ReflectPermission.java | 102 + libjava/classpath/java/lang/reflect/TODO | 4 + libjava/classpath/java/lang/reflect/Type.java | 55 + .../classpath/java/lang/reflect/TypeVariable.java | 96 + .../lang/reflect/UndeclaredThrowableException.java | 128 + .../classpath/java/lang/reflect/WildcardType.java | 115 + libjava/classpath/java/lang/reflect/package.html | 47 + libjava/classpath/java/math/BigDecimal.java | 1559 ++++ libjava/classpath/java/math/BigInteger.java | 2676 +++++++ libjava/classpath/java/math/MathContext.java | 198 + libjava/classpath/java/math/RoundingMode.java | 89 + libjava/classpath/java/math/package.html | 46 + libjava/classpath/java/net/Authenticator.java | 313 + libjava/classpath/java/net/BindException.java | 74 + libjava/classpath/java/net/ConnectException.java | 75 + libjava/classpath/java/net/ContentHandler.java | 126 + .../classpath/java/net/ContentHandlerFactory.java | 65 + libjava/classpath/java/net/DatagramPacket.java | 391 + libjava/classpath/java/net/DatagramSocket.java | 968 +++ libjava/classpath/java/net/DatagramSocketImpl.java | 296 + .../java/net/DatagramSocketImplFactory.java | 60 + libjava/classpath/java/net/FileNameMap.java | 65 + libjava/classpath/java/net/HttpURLConnection.java | 589 ++ libjava/classpath/java/net/Inet4Address.java | 273 + libjava/classpath/java/net/Inet6Address.java | 429 ++ libjava/classpath/java/net/InetAddress.java | 655 ++ libjava/classpath/java/net/InetSocketAddress.java | 261 + libjava/classpath/java/net/JarURLConnection.java | 229 + .../classpath/java/net/MalformedURLException.java | 74 + libjava/classpath/java/net/MimeTypeMapper.java | 346 + libjava/classpath/java/net/MulticastSocket.java | 518 ++ libjava/classpath/java/net/NetPermission.java | 90 + libjava/classpath/java/net/NetworkInterface.java | 316 + .../classpath/java/net/NoRouteToHostException.java | 74 + .../classpath/java/net/PasswordAuthentication.java | 92 + .../java/net/PortUnreachableException.java | 72 + libjava/classpath/java/net/ProtocolException.java | 75 + libjava/classpath/java/net/Proxy.java | 139 + libjava/classpath/java/net/ProxySelector.java | 117 + libjava/classpath/java/net/ResolverCache.java | 269 + libjava/classpath/java/net/STATUS | 48 + libjava/classpath/java/net/ServerSocket.java | 627 ++ libjava/classpath/java/net/Socket.java | 1288 ++++ libjava/classpath/java/net/SocketAddress.java | 63 + libjava/classpath/java/net/SocketException.java | 75 + libjava/classpath/java/net/SocketImpl.java | 321 + libjava/classpath/java/net/SocketImplFactory.java | 59 + libjava/classpath/java/net/SocketOptions.java | 166 + libjava/classpath/java/net/SocketPermission.java | 636 ++ .../classpath/java/net/SocketTimeoutException.java | 73 + libjava/classpath/java/net/TODO | 24 + libjava/classpath/java/net/URI.java | 1450 ++++ libjava/classpath/java/net/URISyntaxException.java | 144 + libjava/classpath/java/net/URL.java | 1010 +++ libjava/classpath/java/net/URLClassLoader.java | 860 +++ libjava/classpath/java/net/URLConnection.java | 1150 +++ libjava/classpath/java/net/URLDecoder.java | 182 + libjava/classpath/java/net/URLEncoder.java | 186 + libjava/classpath/java/net/URLStreamHandler.java | 541 ++ .../java/net/URLStreamHandlerFactory.java | 65 + .../classpath/java/net/UnknownHostException.java | 77 + .../java/net/UnknownServiceException.java | 76 + libjava/classpath/java/net/package.html | 46 + libjava/classpath/java/nio/Buffer.java | 429 ++ .../java/nio/BufferOverflowException.java | 53 + .../java/nio/BufferUnderflowException.java | 53 + libjava/classpath/java/nio/ByteBuffer.java | 655 ++ libjava/classpath/java/nio/ByteBufferHelper.java | 343 + libjava/classpath/java/nio/ByteBufferImpl.java | 374 + libjava/classpath/java/nio/ByteOrder.java | 84 + libjava/classpath/java/nio/CharBuffer.java | 532 ++ libjava/classpath/java/nio/CharBufferImpl.java | 214 + libjava/classpath/java/nio/CharSequenceBuffer.java | 207 + libjava/classpath/java/nio/CharViewBufferImpl.java | 187 + .../classpath/java/nio/DirectByteBufferImpl.java | 441 ++ libjava/classpath/java/nio/DoubleBuffer.java | 386 + libjava/classpath/java/nio/DoubleBufferImpl.java | 173 + .../classpath/java/nio/DoubleViewBufferImpl.java | 170 + libjava/classpath/java/nio/FloatBuffer.java | 386 + libjava/classpath/java/nio/FloatBufferImpl.java | 170 + .../classpath/java/nio/FloatViewBufferImpl.java | 171 + libjava/classpath/java/nio/IntBuffer.java | 387 + libjava/classpath/java/nio/IntBufferImpl.java | 169 + libjava/classpath/java/nio/IntViewBufferImpl.java | 171 + .../classpath/java/nio/InvalidMarkException.java | 54 + libjava/classpath/java/nio/LongBuffer.java | 386 + libjava/classpath/java/nio/LongBufferImpl.java | 173 + libjava/classpath/java/nio/LongViewBufferImpl.java | 171 + libjava/classpath/java/nio/MappedByteBuffer.java | 97 + .../classpath/java/nio/MappedByteBufferImpl.java | 359 + .../java/nio/ReadOnlyBufferException.java | 54 + libjava/classpath/java/nio/ShortBuffer.java | 387 + libjava/classpath/java/nio/ShortBufferImpl.java | 173 + .../classpath/java/nio/ShortViewBufferImpl.java | 173 + .../nio/channels/AlreadyConnectedException.java | 50 + .../nio/channels/AsynchronousCloseException.java | 55 + .../classpath/java/nio/channels/ByteChannel.java | 43 + .../java/nio/channels/CancelledKeyException.java | 55 + libjava/classpath/java/nio/channels/Channel.java | 60 + libjava/classpath/java/nio/channels/Channels.java | 144 + .../nio/channels/ClosedByInterruptException.java | 55 + .../java/nio/channels/ClosedChannelException.java | 57 + .../java/nio/channels/ClosedSelectorException.java | 55 + .../nio/channels/ConnectionPendingException.java | 55 + .../java/nio/channels/DatagramChannel.java | 208 + .../classpath/java/nio/channels/FileChannel.java | 357 + libjava/classpath/java/nio/channels/FileLock.java | 151 + .../channels/FileLockInterruptionException.java | 57 + .../java/nio/channels/GatheringByteChannel.java | 79 + .../nio/channels/IllegalBlockingModeException.java | 59 + .../nio/channels/IllegalSelectorException.java | 55 + .../java/nio/channels/InterruptibleChannel.java | 51 + .../nio/channels/NoConnectionPendingException.java | 55 + .../nio/channels/NonReadableChannelException.java | 55 + .../nio/channels/NonWritableChannelException.java | 55 + .../java/nio/channels/NotYetBoundException.java | 55 + .../nio/channels/NotYetConnectedException.java | 55 + .../nio/channels/OverlappingFileLockException.java | 55 + libjava/classpath/java/nio/channels/Pipe.java | 121 + .../java/nio/channels/ReadableByteChannel.java | 64 + .../java/nio/channels/ScatteringByteChannel.java | 79 + .../java/nio/channels/SelectableChannel.java | 140 + .../classpath/java/nio/channels/SelectionKey.java | 164 + libjava/classpath/java/nio/channels/Selector.java | 134 + .../java/nio/channels/ServerSocketChannel.java | 98 + .../classpath/java/nio/channels/SocketChannel.java | 248 + .../nio/channels/UnresolvedAddressException.java | 55 + .../channels/UnsupportedAddressTypeException.java | 55 + .../java/nio/channels/WritableByteChannel.java | 60 + libjava/classpath/java/nio/channels/package.html | 47 + .../channels/spi/AbstractInterruptibleChannel.java | 119 + .../channels/spi/AbstractSelectableChannel.java | 271 + .../nio/channels/spi/AbstractSelectionKey.java | 78 + .../java/nio/channels/spi/AbstractSelector.java | 167 + .../java/nio/channels/spi/SelectorProvider.java | 178 + .../classpath/java/nio/channels/spi/package.html | 46 + .../java/nio/charset/CharacterCodingException.java | 55 + libjava/classpath/java/nio/charset/Charset.java | 402 ++ .../classpath/java/nio/charset/CharsetDecoder.java | 317 + .../classpath/java/nio/charset/CharsetEncoder.java | 369 + .../java/nio/charset/CoderMalfunctionError.java | 54 + .../classpath/java/nio/charset/CoderResult.java | 189 + .../java/nio/charset/CodingErrorAction.java | 66 + .../nio/charset/IllegalCharsetNameException.java | 73 + .../java/nio/charset/MalformedInputException.java | 79 + .../nio/charset/UnmappableCharacterException.java | 73 + .../nio/charset/UnsupportedCharsetException.java | 69 + libjava/classpath/java/nio/charset/package.html | 47 + .../java/nio/charset/spi/CharsetProvider.java | 95 + .../classpath/java/nio/charset/spi/package.html | 46 + libjava/classpath/java/nio/package.html | 46 + libjava/classpath/java/rmi/AccessException.java | 77 + .../classpath/java/rmi/AlreadyBoundException.java | 74 + libjava/classpath/java/rmi/ConnectException.java | 74 + libjava/classpath/java/rmi/ConnectIOException.java | 74 + libjava/classpath/java/rmi/MarshalException.java | 76 + libjava/classpath/java/rmi/MarshalledObject.java | 154 + libjava/classpath/java/rmi/Naming.java | 234 + .../classpath/java/rmi/NoSuchObjectException.java | 69 + libjava/classpath/java/rmi/NotBoundException.java | 76 + .../classpath/java/rmi/RMISecurityException.java | 79 + libjava/classpath/java/rmi/RMISecurityManager.java | 48 + libjava/classpath/java/rmi/Remote.java | 58 + libjava/classpath/java/rmi/RemoteException.java | 128 + libjava/classpath/java/rmi/ServerError.java | 64 + libjava/classpath/java/rmi/ServerException.java | 74 + .../classpath/java/rmi/ServerRuntimeException.java | 67 + .../classpath/java/rmi/StubNotFoundException.java | 78 + .../classpath/java/rmi/UnexpectedException.java | 75 + .../classpath/java/rmi/UnknownHostException.java | 75 + libjava/classpath/java/rmi/UnmarshalException.java | 88 + .../classpath/java/rmi/activation/Activatable.java | 531 ++ .../rmi/activation/ActivateFailedException.java | 76 + .../java/rmi/activation/ActivationDesc.java | 254 + .../java/rmi/activation/ActivationException.java | 122 + .../java/rmi/activation/ActivationGroup.java | 340 + .../java/rmi/activation/ActivationGroupDesc.java | 433 ++ .../java/rmi/activation/ActivationGroupID.java | 122 + .../java/rmi/activation/ActivationGroup_Stub.java | 110 + .../java/rmi/activation/ActivationID.java | 199 + .../rmi/activation/ActivationInstantiator.java | 73 + .../java/rmi/activation/ActivationMonitor.java | 87 + .../java/rmi/activation/ActivationSystem.java | 206 + .../classpath/java/rmi/activation/Activator.java | 72 + .../java/rmi/activation/UnknownGroupException.java | 69 + .../rmi/activation/UnknownObjectException.java | 69 + libjava/classpath/java/rmi/activation/package.html | 73 + libjava/classpath/java/rmi/dgc/DGC.java | 80 + libjava/classpath/java/rmi/dgc/Lease.java | 100 + libjava/classpath/java/rmi/dgc/VMID.java | 191 + libjava/classpath/java/rmi/dgc/package.html | 52 + libjava/classpath/java/rmi/package.html | 46 + .../java/rmi/registry/LocateRegistry.java | 87 + libjava/classpath/java/rmi/registry/Registry.java | 87 + .../java/rmi/registry/RegistryHandler.java | 58 + libjava/classpath/java/rmi/registry/package.html | 46 + .../classpath/java/rmi/server/ExportException.java | 78 + .../classpath/java/rmi/server/LoaderHandler.java | 71 + libjava/classpath/java/rmi/server/LogStream.java | 146 + libjava/classpath/java/rmi/server/ObjID.java | 197 + libjava/classpath/java/rmi/server/Operation.java | 78 + .../classpath/java/rmi/server/RMIClassLoader.java | 204 + .../java/rmi/server/RMIClassLoaderSpi.java | 64 + .../java/rmi/server/RMIClientSocketFactory.java | 47 + .../java/rmi/server/RMIFailureHandler.java | 46 + .../java/rmi/server/RMIServerSocketFactory.java | 47 + .../java/rmi/server/RMISocketFactory.java | 108 + libjava/classpath/java/rmi/server/RemoteCall.java | 86 + .../classpath/java/rmi/server/RemoteObject.java | 202 + .../rmi/server/RemoteObjectInvocationHandler.java | 229 + libjava/classpath/java/rmi/server/RemoteRef.java | 137 + .../classpath/java/rmi/server/RemoteServer.java | 115 + libjava/classpath/java/rmi/server/RemoteStub.java | 80 + .../java/rmi/server/ServerCloneException.java | 117 + .../java/rmi/server/ServerNotActiveException.java | 72 + libjava/classpath/java/rmi/server/ServerRef.java | 51 + libjava/classpath/java/rmi/server/Skeleton.java | 57 + .../java/rmi/server/SkeletonMismatchException.java | 68 + .../java/rmi/server/SkeletonNotFoundException.java | 79 + .../java/rmi/server/SocketSecurityException.java | 75 + libjava/classpath/java/rmi/server/UID.java | 225 + .../java/rmi/server/UnicastRemoteObject.java | 251 + .../classpath/java/rmi/server/Unreferenced.java | 43 + libjava/classpath/java/rmi/server/package.html | 46 + .../java/security/AccessControlContext.java | 218 + .../java/security/AccessControlException.java | 97 + .../classpath/java/security/AccessController.java | 229 + .../java/security/AlgorithmParameterGenerator.java | 277 + .../security/AlgorithmParameterGeneratorSpi.java | 94 + .../java/security/AlgorithmParameters.java | 317 + .../java/security/AlgorithmParametersSpi.java | 149 + libjava/classpath/java/security/AllPermission.java | 198 + .../classpath/java/security/BasicPermission.java | 308 + libjava/classpath/java/security/Certificate.java | 125 + libjava/classpath/java/security/CodeSource.java | 356 + .../classpath/java/security/DigestException.java | 92 + .../classpath/java/security/DigestInputStream.java | 167 + .../java/security/DigestOutputStream.java | 158 + .../classpath/java/security/DomainCombiner.java | 67 + .../java/security/DummyKeyPairGenerator.java | 75 + .../java/security/DummyMessageDigest.java | 90 + .../classpath/java/security/DummySignature.java | 102 + .../java/security/GeneralSecurityException.java | 97 + libjava/classpath/java/security/Guard.java | 60 + libjava/classpath/java/security/GuardedObject.java | 121 + libjava/classpath/java/security/Identity.java | 346 + libjava/classpath/java/security/IdentityScope.java | 216 + .../java/security/IntersectingDomainCombiner.java | 82 + .../InvalidAlgorithmParameterException.java | 95 + .../java/security/InvalidKeyException.java | 91 + .../java/security/InvalidParameterException.java | 70 + libjava/classpath/java/security/Key.java | 94 + libjava/classpath/java/security/KeyException.java | 94 + libjava/classpath/java/security/KeyFactory.java | 280 + libjava/classpath/java/security/KeyFactorySpi.java | 134 + .../java/security/KeyManagementException.java | 93 + libjava/classpath/java/security/KeyPair.java | 87 + .../classpath/java/security/KeyPairGenerator.java | 313 + .../java/security/KeyPairGeneratorSpi.java | 102 + libjava/classpath/java/security/KeyStore.java | 503 ++ .../classpath/java/security/KeyStoreException.java | 92 + libjava/classpath/java/security/KeyStoreSpi.java | 275 + libjava/classpath/java/security/MessageDigest.java | 382 + .../classpath/java/security/MessageDigestSpi.java | 174 + .../java/security/NoSuchAlgorithmException.java | 92 + .../java/security/NoSuchProviderException.java | 70 + libjava/classpath/java/security/Permission.java | 202 + .../java/security/PermissionCollection.java | 169 + libjava/classpath/java/security/Permissions.java | 254 + libjava/classpath/java/security/Policy.java | 297 + libjava/classpath/java/security/Principal.java | 85 + libjava/classpath/java/security/PrivateKey.java | 62 + .../classpath/java/security/PrivilegedAction.java | 64 + .../java/security/PrivilegedActionException.java | 109 + .../java/security/PrivilegedExceptionAction.java | 65 + .../classpath/java/security/ProtectionDomain.java | 252 + libjava/classpath/java/security/Provider.java | 218 + .../classpath/java/security/ProviderException.java | 92 + libjava/classpath/java/security/PublicKey.java | 60 + .../classpath/java/security/SecureClassLoader.java | 148 + libjava/classpath/java/security/SecureRandom.java | 420 ++ .../classpath/java/security/SecureRandomSpi.java | 85 + libjava/classpath/java/security/Security.java | 711 ++ .../java/security/SecurityPermission.java | 178 + libjava/classpath/java/security/Signature.java | 593 ++ .../java/security/SignatureException.java | 92 + libjava/classpath/java/security/SignatureSpi.java | 316 + libjava/classpath/java/security/SignedObject.java | 203 + libjava/classpath/java/security/Signer.java | 148 + .../java/security/UnrecoverableKeyException.java | 71 + .../java/security/UnresolvedPermission.java | 345 + libjava/classpath/java/security/acl/Acl.java | 153 + libjava/classpath/java/security/acl/AclEntry.java | 143 + .../java/security/acl/AclNotFoundException.java | 60 + libjava/classpath/java/security/acl/Group.java | 90 + .../java/security/acl/LastOwnerException.java | 62 + .../java/security/acl/NotOwnerException.java | 62 + libjava/classpath/java/security/acl/Owner.java | 95 + .../classpath/java/security/acl/Permission.java | 67 + libjava/classpath/java/security/acl/package.html | 46 + libjava/classpath/java/security/cert/CRL.java | 98 + .../classpath/java/security/cert/CRLException.java | 95 + .../classpath/java/security/cert/CRLSelector.java | 69 + libjava/classpath/java/security/cert/CertPath.java | 254 + .../java/security/cert/CertPathBuilder.java | 251 + .../security/cert/CertPathBuilderException.java | 159 + .../java/security/cert/CertPathBuilderResult.java | 63 + .../java/security/cert/CertPathBuilderSpi.java | 74 + .../java/security/cert/CertPathParameters.java | 58 + .../java/security/cert/CertPathValidator.java | 264 + .../security/cert/CertPathValidatorException.java | 226 + .../security/cert/CertPathValidatorResult.java | 63 + .../java/security/cert/CertPathValidatorSpi.java | 81 + .../classpath/java/security/cert/CertSelector.java | 58 + .../classpath/java/security/cert/CertStore.java | 305 + .../java/security/cert/CertStoreException.java | 159 + .../java/security/cert/CertStoreParameters.java | 60 + .../classpath/java/security/cert/CertStoreSpi.java | 103 + .../classpath/java/security/cert/Certificate.java | 306 + .../cert/CertificateEncodingException.java | 93 + .../java/security/cert/CertificateException.java | 96 + .../security/cert/CertificateExpiredException.java | 71 + .../java/security/cert/CertificateFactory.java | 355 + .../java/security/cert/CertificateFactorySpi.java | 224 + .../cert/CertificateNotYetValidException.java | 71 + .../security/cert/CertificateParsingException.java | 93 + .../cert/CollectionCertStoreParameters.java | 122 + .../security/cert/LDAPCertStoreParameters.java | 140 + .../java/security/cert/PKIXBuilderParameters.java | 149 + .../security/cert/PKIXCertPathBuilderResult.java | 104 + .../java/security/cert/PKIXCertPathChecker.java | 134 + .../security/cert/PKIXCertPathValidatorResult.java | 142 + .../java/security/cert/PKIXParameters.java | 547 ++ .../classpath/java/security/cert/PolicyNode.java | 108 + .../java/security/cert/PolicyQualifierInfo.java | 169 + .../classpath/java/security/cert/TrustAnchor.java | 185 + libjava/classpath/java/security/cert/X509CRL.java | 397 + .../classpath/java/security/cert/X509CRLEntry.java | 169 + .../java/security/cert/X509CRLSelector.java | 442 ++ .../java/security/cert/X509CertSelector.java | 1319 ++++ .../java/security/cert/X509Certificate.java | 589 ++ .../java/security/cert/X509Extension.java | 113 + libjava/classpath/java/security/cert/package.html | 46 + .../classpath/java/security/interfaces/DSAKey.java | 56 + .../security/interfaces/DSAKeyPairGenerator.java | 85 + .../java/security/interfaces/DSAParams.java | 72 + .../java/security/interfaces/DSAPrivateKey.java | 61 + .../java/security/interfaces/DSAPublicKey.java | 61 + .../classpath/java/security/interfaces/RSAKey.java | 57 + .../interfaces/RSAMultiPrimePrivateCrtKey.java | 112 + .../java/security/interfaces/RSAPrivateCrtKey.java | 95 + .../java/security/interfaces/RSAPrivateKey.java | 60 + .../java/security/interfaces/RSAPublicKey.java | 60 + .../java/security/interfaces/package.html | 46 + libjava/classpath/java/security/package.html | 46 + .../java/security/spec/AlgorithmParameterSpec.java | 52 + .../java/security/spec/DSAParameterSpec.java | 101 + .../java/security/spec/DSAPrivateKeySpec.java | 113 + .../java/security/spec/DSAPublicKeySpec.java | 113 + .../java/security/spec/EncodedKeySpec.java | 85 + .../security/spec/InvalidKeySpecException.java | 96 + .../spec/InvalidParameterSpecException.java | 76 + libjava/classpath/java/security/spec/KeySpec.java | 52 + .../java/security/spec/PKCS8EncodedKeySpec.java | 81 + .../java/security/spec/PSSParameterSpec.java | 87 + .../java/security/spec/RSAKeyGenParameterSpec.java | 97 + .../spec/RSAMultiPrimePrivateCrtKeySpec.java | 223 + .../java/security/spec/RSAOtherPrimeInfo.java | 126 + .../java/security/spec/RSAPrivateCrtKeySpec.java | 151 + .../java/security/spec/RSAPrivateKeySpec.java | 88 + .../java/security/spec/RSAPublicKeySpec.java | 88 + .../java/security/spec/X509EncodedKeySpec.java | 82 + libjava/classpath/java/security/spec/package.html | 46 + libjava/classpath/java/sql/Array.java | 186 + .../classpath/java/sql/BatchUpdateException.java | 141 + libjava/classpath/java/sql/Blob.java | 157 + libjava/classpath/java/sql/CallableStatement.java | 961 +++ libjava/classpath/java/sql/Clob.java | 187 + libjava/classpath/java/sql/Connection.java | 500 ++ libjava/classpath/java/sql/DataTruncation.java | 157 + libjava/classpath/java/sql/DatabaseMetaData.java | 2270 ++++++ libjava/classpath/java/sql/Date.java | 184 + libjava/classpath/java/sql/Driver.java | 123 + libjava/classpath/java/sql/DriverManager.java | 345 + libjava/classpath/java/sql/DriverPropertyInfo.java | 88 + libjava/classpath/java/sql/ParameterMetaData.java | 103 + libjava/classpath/java/sql/PreparedStatement.java | 458 ++ libjava/classpath/java/sql/Ref.java | 75 + libjava/classpath/java/sql/ResultSet.java | 1612 +++++ libjava/classpath/java/sql/ResultSetMetaData.java | 281 + libjava/classpath/java/sql/SQLData.java | 72 + libjava/classpath/java/sql/SQLException.java | 167 + libjava/classpath/java/sql/SQLInput.java | 258 + libjava/classpath/java/sql/SQLOutput.java | 267 + libjava/classpath/java/sql/SQLPermission.java | 57 + libjava/classpath/java/sql/SQLWarning.java | 120 + libjava/classpath/java/sql/Savepoint.java | 55 + libjava/classpath/java/sql/Statement.java | 375 + libjava/classpath/java/sql/Struct.java | 77 + libjava/classpath/java/sql/Time.java | 201 + libjava/classpath/java/sql/Timestamp.java | 319 + libjava/classpath/java/sql/Types.java | 85 + libjava/classpath/java/sql/package.html | 47 + libjava/classpath/java/text/Annotation.java | 112 + .../java/text/AttributedCharacterIterator.java | 277 + libjava/classpath/java/text/AttributedString.java | 378 + .../java/text/AttributedStringIterator.java | 389 + libjava/classpath/java/text/Bidi.java | 1001 +++ libjava/classpath/java/text/BreakIterator.java | 444 ++ libjava/classpath/java/text/CharacterIterator.java | 148 + libjava/classpath/java/text/ChoiceFormat.java | 506 ++ .../java/text/CollationElementIterator.java | 490 ++ libjava/classpath/java/text/CollationKey.java | 186 + libjava/classpath/java/text/Collator.java | 425 ++ libjava/classpath/java/text/DateFormat.java | 955 +++ libjava/classpath/java/text/DateFormatSymbols.java | 761 ++ libjava/classpath/java/text/DecimalFormat.java | 2278 ++++++ .../classpath/java/text/DecimalFormatSymbols.java | 766 ++ libjava/classpath/java/text/FieldPosition.java | 232 + libjava/classpath/java/text/Format.java | 181 + libjava/classpath/java/text/MessageFormat.java | 836 +++ libjava/classpath/java/text/NumberFormat.java | 903 +++ libjava/classpath/java/text/ParseException.java | 86 + libjava/classpath/java/text/ParsePosition.java | 160 + libjava/classpath/java/text/RuleBasedCollator.java | 1011 +++ libjava/classpath/java/text/SimpleDateFormat.java | 1323 ++++ .../java/text/StringCharacterIterator.java | 369 + libjava/classpath/java/text/package.html | 47 + .../java/text/spi/BreakIteratorProvider.java | 124 + .../classpath/java/text/spi/CollatorProvider.java | 79 + .../java/text/spi/DateFormatProvider.java | 129 + .../java/text/spi/DateFormatSymbolsProvider.java | 79 + .../text/spi/DecimalFormatSymbolsProvider.java | 79 + .../java/text/spi/NumberFormatProvider.java | 129 + libjava/classpath/java/text/spi/package.html | 50 + libjava/classpath/java/util/.cvsignore | 1 + .../classpath/java/util/AbstractCollection.java | 482 ++ libjava/classpath/java/util/AbstractList.java | 1204 ++++ libjava/classpath/java/util/AbstractMap.java | 819 +++ .../java/util/AbstractSequentialList.java | 235 + libjava/classpath/java/util/AbstractSet.java | 146 + libjava/classpath/java/util/ArrayList.java | 603 ++ libjava/classpath/java/util/Arrays.java | 4034 +++++++++++ libjava/classpath/java/util/BitSet.java | 758 ++ libjava/classpath/java/util/Calendar.java | 1620 +++++ libjava/classpath/java/util/Collection.java | 290 + libjava/classpath/java/util/Collections.java | 7623 ++++++++++++++++++++ libjava/classpath/java/util/Comparator.java | 119 + .../java/util/ConcurrentModificationException.java | 92 + libjava/classpath/java/util/Currency.java | 471 ++ libjava/classpath/java/util/Date.java | 1256 ++++ libjava/classpath/java/util/Dictionary.java | 136 + .../java/util/DuplicateFormatFlagsException.java | 88 + .../classpath/java/util/EmptyStackException.java | 69 + libjava/classpath/java/util/EnumMap.java | 405 ++ libjava/classpath/java/util/EnumSet.java | 518 ++ libjava/classpath/java/util/Enumeration.java | 82 + libjava/classpath/java/util/EventListener.java | 54 + .../classpath/java/util/EventListenerProxy.java | 75 + libjava/classpath/java/util/EventObject.java | 101 + .../FormatFlagsConversionMismatchException.java | 111 + libjava/classpath/java/util/Formattable.java | 92 + libjava/classpath/java/util/FormattableFlags.java | 123 + libjava/classpath/java/util/Formatter.java | 1498 ++++ .../java/util/FormatterClosedException.java | 60 + libjava/classpath/java/util/GregorianCalendar.java | 1365 ++++ libjava/classpath/java/util/HashMap.java | 907 +++ libjava/classpath/java/util/HashSet.java | 293 + libjava/classpath/java/util/Hashtable.java | 1397 ++++ libjava/classpath/java/util/IdentityHashMap.java | 990 +++ .../java/util/IllegalFormatCodePointException.java | 85 + .../util/IllegalFormatConversionException.java | 110 + .../java/util/IllegalFormatException.java | 75 + .../java/util/IllegalFormatFlagsException.java | 86 + .../java/util/IllegalFormatPrecisionException.java | 85 + .../java/util/IllegalFormatWidthException.java | 84 + .../java/util/InputMismatchException.java | 73 + .../util/InvalidPropertiesFormatException.java | 72 + libjava/classpath/java/util/Iterator.java | 87 + libjava/classpath/java/util/LinkedHashMap.java | 500 ++ libjava/classpath/java/util/LinkedHashSet.java | 159 + libjava/classpath/java/util/LinkedList.java | 1260 ++++ libjava/classpath/java/util/List.java | 451 ++ libjava/classpath/java/util/ListIterator.java | 170 + .../classpath/java/util/ListResourceBundle.java | 140 + libjava/classpath/java/util/Locale.java | 1029 +++ libjava/classpath/java/util/Map.java | 338 + .../java/util/MissingFormatArgumentException.java | 90 + .../java/util/MissingFormatWidthException.java | 88 + .../java/util/MissingResourceException.java | 105 + .../java/util/NoSuchElementException.java | 88 + libjava/classpath/java/util/Observable.java | 182 + libjava/classpath/java/util/Observer.java | 60 + libjava/classpath/java/util/PriorityQueue.java | 336 + libjava/classpath/java/util/Properties.java | 822 +++ .../classpath/java/util/PropertyPermission.java | 271 + .../java/util/PropertyPermissionCollection.java | 166 + .../java/util/PropertyResourceBundle.java | 171 + libjava/classpath/java/util/Random.java | 429 ++ libjava/classpath/java/util/RandomAccess.java | 64 + libjava/classpath/java/util/ResourceBundle.java | 625 ++ libjava/classpath/java/util/Scanner.java | 2223 ++++++ .../java/util/ServiceConfigurationError.java | 94 + libjava/classpath/java/util/ServiceLoader.java | 274 + libjava/classpath/java/util/Set.java | 265 + libjava/classpath/java/util/SimpleTimeZone.java | 1052 +++ libjava/classpath/java/util/SortedMap.java | 173 + libjava/classpath/java/util/SortedSet.java | 176 + libjava/classpath/java/util/Stack.java | 161 + libjava/classpath/java/util/StringTokenizer.java | 269 + libjava/classpath/java/util/TimeZone.java | 1781 +++++ libjava/classpath/java/util/Timer.java | 704 ++ libjava/classpath/java/util/TimerTask.java | 145 + .../java/util/TooManyListenersException.java | 78 + libjava/classpath/java/util/TreeMap.java | 3322 +++++++++ libjava/classpath/java/util/TreeSet.java | 641 ++ libjava/classpath/java/util/UUID.java | 372 + .../util/UnknownFormatConversionException.java | 86 + .../java/util/UnknownFormatFlagsException.java | 88 + libjava/classpath/java/util/Vector.java | 958 +++ libjava/classpath/java/util/WeakHashMap.java | 880 +++ .../java/util/concurrent/CopyOnWriteArrayList.java | 1463 ++++ libjava/classpath/java/util/jar/Attributes.java | 629 ++ libjava/classpath/java/util/jar/JarEntry.java | 172 + libjava/classpath/java/util/jar/JarException.java | 77 + libjava/classpath/java/util/jar/JarFile.java | 981 +++ .../classpath/java/util/jar/JarInputStream.java | 200 + .../classpath/java/util/jar/JarOutputStream.java | 113 + libjava/classpath/java/util/jar/Manifest.java | 213 + libjava/classpath/java/util/jar/package.html | 47 + .../java/util/logging/ConsoleHandler.java | 125 + .../classpath/java/util/logging/ErrorManager.java | 193 + .../classpath/java/util/logging/FileHandler.java | 665 ++ libjava/classpath/java/util/logging/Filter.java | 64 + libjava/classpath/java/util/logging/Formatter.java | 171 + libjava/classpath/java/util/logging/Handler.java | 386 + libjava/classpath/java/util/logging/Level.java | 416 ++ .../classpath/java/util/logging/LogManager.java | 986 +++ libjava/classpath/java/util/logging/LogRecord.java | 672 ++ libjava/classpath/java/util/logging/Logger.java | 1193 +++ .../classpath/java/util/logging/LoggingMXBean.java | 85 + .../java/util/logging/LoggingPermission.java | 75 + .../classpath/java/util/logging/MemoryHandler.java | 345 + .../java/util/logging/SimpleFormatter.java | 131 + .../classpath/java/util/logging/SocketHandler.java | 220 + .../classpath/java/util/logging/StreamHandler.java | 521 ++ .../classpath/java/util/logging/XMLFormatter.java | 389 + libjava/classpath/java/util/logging/package.html | 46 + libjava/classpath/java/util/package.html | 48 + .../java/util/prefs/AbstractPreferences.java | 1392 ++++ .../java/util/prefs/BackingStoreException.java | 104 + .../prefs/InvalidPreferencesFormatException.java | 116 + .../classpath/java/util/prefs/NodeChangeEvent.java | 111 + .../java/util/prefs/NodeChangeListener.java | 64 + .../java/util/prefs/PreferenceChangeEvent.java | 125 + .../java/util/prefs/PreferenceChangeListener.java | 60 + libjava/classpath/java/util/prefs/Preferences.java | 696 ++ .../java/util/prefs/PreferencesFactory.java | 65 + libjava/classpath/java/util/prefs/package.html | 46 + libjava/classpath/java/util/regex/MatchResult.java | 81 + libjava/classpath/java/util/regex/Matcher.java | 611 ++ libjava/classpath/java/util/regex/Pattern.java | 271 + .../java/util/regex/PatternSyntaxException.java | 135 + libjava/classpath/java/util/regex/package.html | 46 + .../java/util/spi/CurrencyNameProvider.java | 100 + .../java/util/spi/LocaleNameProvider.java | 135 + .../java/util/spi/LocaleServiceProvider.java | 125 + .../java/util/spi/TimeZoneNameProvider.java | 97 + libjava/classpath/java/util/spi/package.html | 50 + libjava/classpath/java/util/zip/Adler32.java | 205 + libjava/classpath/java/util/zip/CRC32.java | 132 + .../java/util/zip/CheckedInputStream.java | 135 + .../java/util/zip/CheckedOutputStream.java | 100 + libjava/classpath/java/util/zip/Checksum.java | 86 + .../java/util/zip/DataFormatException.java | 71 + libjava/classpath/java/util/zip/Deflater.java | 537 ++ .../classpath/java/util/zip/DeflaterConstants.java | 78 + .../classpath/java/util/zip/DeflaterEngine.java | 698 ++ .../classpath/java/util/zip/DeflaterHuffman.java | 776 ++ .../java/util/zip/DeflaterOutputStream.java | 198 + .../classpath/java/util/zip/DeflaterPending.java | 53 + .../classpath/java/util/zip/GZIPInputStream.java | 355 + .../classpath/java/util/zip/GZIPOutputStream.java | 151 + libjava/classpath/java/util/zip/Inflater.java | 726 ++ .../classpath/java/util/zip/InflaterDynHeader.java | 203 + .../java/util/zip/InflaterHuffmanTree.java | 217 + .../java/util/zip/InflaterInputStream.java | 265 + libjava/classpath/java/util/zip/OutputWindow.java | 175 + libjava/classpath/java/util/zip/PendingBuffer.java | 199 + .../classpath/java/util/zip/StreamManipulator.java | 215 + libjava/classpath/java/util/zip/ZipConstants.java | 93 + libjava/classpath/java/util/zip/ZipEntry.java | 457 ++ libjava/classpath/java/util/zip/ZipException.java | 72 + libjava/classpath/java/util/zip/ZipFile.java | 782 ++ .../classpath/java/util/zip/ZipInputStream.java | 381 + .../classpath/java/util/zip/ZipOutputStream.java | 440 ++ libjava/classpath/java/util/zip/package.html | 47 + 1253 files changed, 360222 insertions(+) create mode 100644 libjava/classpath/java/applet/Applet.java create mode 100644 libjava/classpath/java/applet/AppletContext.java create mode 100644 libjava/classpath/java/applet/AppletStub.java create mode 100644 libjava/classpath/java/applet/AudioClip.java create mode 100644 libjava/classpath/java/applet/package.html create mode 100644 libjava/classpath/java/awt/AWTError.java create mode 100644 libjava/classpath/java/awt/AWTEvent.java create mode 100644 libjava/classpath/java/awt/AWTEventMulticaster.java create mode 100644 libjava/classpath/java/awt/AWTException.java create mode 100644 libjava/classpath/java/awt/AWTKeyStroke.java create mode 100644 libjava/classpath/java/awt/AWTPermission.java create mode 100644 libjava/classpath/java/awt/ActiveEvent.java create mode 100644 libjava/classpath/java/awt/Adjustable.java create mode 100644 libjava/classpath/java/awt/AlphaComposite.java create mode 100644 libjava/classpath/java/awt/AttributeValue.java create mode 100644 libjava/classpath/java/awt/BasicStroke.java create mode 100644 libjava/classpath/java/awt/BorderLayout.java create mode 100644 libjava/classpath/java/awt/BufferCapabilities.java create mode 100644 libjava/classpath/java/awt/Button.java create mode 100644 libjava/classpath/java/awt/Canvas.java create mode 100644 libjava/classpath/java/awt/CardLayout.java create mode 100644 libjava/classpath/java/awt/Checkbox.java create mode 100644 libjava/classpath/java/awt/CheckboxGroup.java create mode 100644 libjava/classpath/java/awt/CheckboxMenuItem.java create mode 100644 libjava/classpath/java/awt/Choice.java create mode 100644 libjava/classpath/java/awt/Color.java create mode 100644 libjava/classpath/java/awt/ColorPaintContext.java create mode 100644 libjava/classpath/java/awt/Component.java create mode 100644 libjava/classpath/java/awt/ComponentOrientation.java create mode 100644 libjava/classpath/java/awt/Composite.java create mode 100644 libjava/classpath/java/awt/CompositeContext.java create mode 100644 libjava/classpath/java/awt/Container.java create mode 100644 libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java create mode 100644 libjava/classpath/java/awt/Cursor.java create mode 100644 libjava/classpath/java/awt/DefaultFocusTraversalPolicy.java create mode 100644 libjava/classpath/java/awt/DefaultKeyboardFocusManager.java create mode 100644 libjava/classpath/java/awt/Desktop.java create mode 100644 libjava/classpath/java/awt/Dialog.java create mode 100644 libjava/classpath/java/awt/Dimension.java create mode 100644 libjava/classpath/java/awt/DisplayMode.java create mode 100644 libjava/classpath/java/awt/Event.java create mode 100644 libjava/classpath/java/awt/EventDispatchThread.java create mode 100644 libjava/classpath/java/awt/EventQueue.java create mode 100644 libjava/classpath/java/awt/FileDialog.java create mode 100644 libjava/classpath/java/awt/FlowLayout.java create mode 100644 libjava/classpath/java/awt/FocusTraversalPolicy.java create mode 100644 libjava/classpath/java/awt/Font.java create mode 100644 libjava/classpath/java/awt/FontFormatException.java create mode 100644 libjava/classpath/java/awt/FontMetrics.java create mode 100644 libjava/classpath/java/awt/Frame.java create mode 100644 libjava/classpath/java/awt/GradientPaint.java create mode 100644 libjava/classpath/java/awt/Graphics.java create mode 100644 libjava/classpath/java/awt/Graphics2D.java create mode 100644 libjava/classpath/java/awt/GraphicsConfigTemplate.java create mode 100644 libjava/classpath/java/awt/GraphicsConfiguration.java create mode 100644 libjava/classpath/java/awt/GraphicsDevice.java create mode 100644 libjava/classpath/java/awt/GraphicsEnvironment.java create mode 100644 libjava/classpath/java/awt/GridBagConstraints.java create mode 100644 libjava/classpath/java/awt/GridBagLayout.java create mode 100644 libjava/classpath/java/awt/GridBagLayoutInfo.java create mode 100644 libjava/classpath/java/awt/GridLayout.java create mode 100644 libjava/classpath/java/awt/HeadlessException.java create mode 100644 libjava/classpath/java/awt/IllegalComponentStateException.java create mode 100644 libjava/classpath/java/awt/Image.java create mode 100644 libjava/classpath/java/awt/ImageCapabilities.java create mode 100644 libjava/classpath/java/awt/Insets.java create mode 100644 libjava/classpath/java/awt/ItemSelectable.java create mode 100644 libjava/classpath/java/awt/JobAttributes.java create mode 100644 libjava/classpath/java/awt/KeyEventDispatcher.java create mode 100644 libjava/classpath/java/awt/KeyEventPostProcessor.java create mode 100644 libjava/classpath/java/awt/KeyboardFocusManager.java create mode 100644 libjava/classpath/java/awt/Label.java create mode 100644 libjava/classpath/java/awt/LayoutManager.java create mode 100644 libjava/classpath/java/awt/LayoutManager2.java create mode 100644 libjava/classpath/java/awt/LightweightDispatcher.java create mode 100644 libjava/classpath/java/awt/List.java create mode 100644 libjava/classpath/java/awt/MediaTracker.java create mode 100644 libjava/classpath/java/awt/Menu.java create mode 100644 libjava/classpath/java/awt/MenuBar.java create mode 100644 libjava/classpath/java/awt/MenuComponent.java create mode 100644 libjava/classpath/java/awt/MenuContainer.java create mode 100644 libjava/classpath/java/awt/MenuItem.java create mode 100644 libjava/classpath/java/awt/MenuShortcut.java create mode 100644 libjava/classpath/java/awt/MouseInfo.java create mode 100644 libjava/classpath/java/awt/PageAttributes.java create mode 100644 libjava/classpath/java/awt/Paint.java create mode 100644 libjava/classpath/java/awt/PaintContext.java create mode 100644 libjava/classpath/java/awt/Panel.java create mode 100644 libjava/classpath/java/awt/Point.java create mode 100644 libjava/classpath/java/awt/PointerInfo.java create mode 100644 libjava/classpath/java/awt/Polygon.java create mode 100644 libjava/classpath/java/awt/PopupMenu.java create mode 100644 libjava/classpath/java/awt/PrintGraphics.java create mode 100644 libjava/classpath/java/awt/PrintJob.java create mode 100644 libjava/classpath/java/awt/Rectangle.java create mode 100644 libjava/classpath/java/awt/RenderingHints.java create mode 100644 libjava/classpath/java/awt/Robot.java create mode 100644 libjava/classpath/java/awt/ScrollPane.java create mode 100644 libjava/classpath/java/awt/ScrollPaneAdjustable.java create mode 100644 libjava/classpath/java/awt/Scrollbar.java create mode 100644 libjava/classpath/java/awt/Shape.java create mode 100644 libjava/classpath/java/awt/Stroke.java create mode 100644 libjava/classpath/java/awt/SystemColor.java create mode 100644 libjava/classpath/java/awt/TextArea.java create mode 100644 libjava/classpath/java/awt/TextComponent.java create mode 100644 libjava/classpath/java/awt/TextField.java create mode 100644 libjava/classpath/java/awt/TexturePaint.java create mode 100644 libjava/classpath/java/awt/Toolkit.java create mode 100644 libjava/classpath/java/awt/Transparency.java create mode 100644 libjava/classpath/java/awt/Window.java create mode 100644 libjava/classpath/java/awt/color/CMMException.java create mode 100644 libjava/classpath/java/awt/color/ColorSpace.java create mode 100644 libjava/classpath/java/awt/color/ICC_ColorSpace.java create mode 100644 libjava/classpath/java/awt/color/ICC_Profile.java create mode 100644 libjava/classpath/java/awt/color/ICC_ProfileGray.java create mode 100644 libjava/classpath/java/awt/color/ICC_ProfileRGB.java create mode 100644 libjava/classpath/java/awt/color/ProfileDataException.java create mode 100644 libjava/classpath/java/awt/color/package.html create mode 100644 libjava/classpath/java/awt/datatransfer/Clipboard.java create mode 100644 libjava/classpath/java/awt/datatransfer/ClipboardOwner.java create mode 100644 libjava/classpath/java/awt/datatransfer/DataFlavor.java create mode 100644 libjava/classpath/java/awt/datatransfer/FlavorEvent.java create mode 100644 libjava/classpath/java/awt/datatransfer/FlavorListener.java create mode 100644 libjava/classpath/java/awt/datatransfer/FlavorMap.java create mode 100644 libjava/classpath/java/awt/datatransfer/FlavorTable.java create mode 100644 libjava/classpath/java/awt/datatransfer/MimeType.java create mode 100644 libjava/classpath/java/awt/datatransfer/MimeTypeParseException.java create mode 100644 libjava/classpath/java/awt/datatransfer/StringSelection.java create mode 100644 libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java create mode 100644 libjava/classpath/java/awt/datatransfer/Transferable.java create mode 100644 libjava/classpath/java/awt/datatransfer/UnsupportedFlavorException.java create mode 100644 libjava/classpath/java/awt/datatransfer/package.html create mode 100644 libjava/classpath/java/awt/dnd/Autoscroll.java create mode 100644 libjava/classpath/java/awt/dnd/DnDConstants.java create mode 100644 libjava/classpath/java/awt/dnd/DnDEventMulticaster.java create mode 100644 libjava/classpath/java/awt/dnd/DragGestureEvent.java create mode 100644 libjava/classpath/java/awt/dnd/DragGestureListener.java create mode 100644 libjava/classpath/java/awt/dnd/DragGestureRecognizer.java create mode 100644 libjava/classpath/java/awt/dnd/DragSource.java create mode 100644 libjava/classpath/java/awt/dnd/DragSourceAdapter.java create mode 100644 libjava/classpath/java/awt/dnd/DragSourceContext.java create mode 100644 libjava/classpath/java/awt/dnd/DragSourceDragEvent.java create mode 100644 libjava/classpath/java/awt/dnd/DragSourceDropEvent.java create mode 100644 libjava/classpath/java/awt/dnd/DragSourceEvent.java create mode 100644 libjava/classpath/java/awt/dnd/DragSourceListener.java create mode 100644 libjava/classpath/java/awt/dnd/DragSourceMotionListener.java create mode 100644 libjava/classpath/java/awt/dnd/DropTarget.java create mode 100644 libjava/classpath/java/awt/dnd/DropTargetAdapter.java create mode 100644 libjava/classpath/java/awt/dnd/DropTargetContext.java create mode 100644 libjava/classpath/java/awt/dnd/DropTargetDragEvent.java create mode 100644 libjava/classpath/java/awt/dnd/DropTargetDropEvent.java create mode 100644 libjava/classpath/java/awt/dnd/DropTargetEvent.java create mode 100644 libjava/classpath/java/awt/dnd/DropTargetListener.java create mode 100644 libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java create mode 100644 libjava/classpath/java/awt/dnd/MouseDragGestureRecognizer.java create mode 100644 libjava/classpath/java/awt/dnd/package.html create mode 100644 libjava/classpath/java/awt/dnd/peer/DragSourceContextPeer.java create mode 100644 libjava/classpath/java/awt/dnd/peer/DropTargetContextPeer.java create mode 100644 libjava/classpath/java/awt/dnd/peer/DropTargetPeer.java create mode 100644 libjava/classpath/java/awt/dnd/peer/package.html create mode 100644 libjava/classpath/java/awt/doc-files/capjoin.png create mode 100644 libjava/classpath/java/awt/event/AWTEventListener.java create mode 100644 libjava/classpath/java/awt/event/AWTEventListenerProxy.java create mode 100644 libjava/classpath/java/awt/event/ActionEvent.java create mode 100644 libjava/classpath/java/awt/event/ActionListener.java create mode 100644 libjava/classpath/java/awt/event/AdjustmentEvent.java create mode 100644 libjava/classpath/java/awt/event/AdjustmentListener.java create mode 100644 libjava/classpath/java/awt/event/ComponentAdapter.java create mode 100644 libjava/classpath/java/awt/event/ComponentEvent.java create mode 100644 libjava/classpath/java/awt/event/ComponentListener.java create mode 100644 libjava/classpath/java/awt/event/ContainerAdapter.java create mode 100644 libjava/classpath/java/awt/event/ContainerEvent.java create mode 100644 libjava/classpath/java/awt/event/ContainerListener.java create mode 100644 libjava/classpath/java/awt/event/FocusAdapter.java create mode 100644 libjava/classpath/java/awt/event/FocusEvent.java create mode 100644 libjava/classpath/java/awt/event/FocusListener.java create mode 100644 libjava/classpath/java/awt/event/HierarchyBoundsAdapter.java create mode 100644 libjava/classpath/java/awt/event/HierarchyBoundsListener.java create mode 100644 libjava/classpath/java/awt/event/HierarchyEvent.java create mode 100644 libjava/classpath/java/awt/event/HierarchyListener.java create mode 100644 libjava/classpath/java/awt/event/InputEvent.java create mode 100644 libjava/classpath/java/awt/event/InputMethodEvent.java create mode 100644 libjava/classpath/java/awt/event/InputMethodListener.java create mode 100644 libjava/classpath/java/awt/event/InvocationEvent.java create mode 100644 libjava/classpath/java/awt/event/ItemEvent.java create mode 100644 libjava/classpath/java/awt/event/ItemListener.java create mode 100644 libjava/classpath/java/awt/event/KeyAdapter.java create mode 100644 libjava/classpath/java/awt/event/KeyEvent.java create mode 100644 libjava/classpath/java/awt/event/KeyListener.java create mode 100644 libjava/classpath/java/awt/event/MouseAdapter.java create mode 100644 libjava/classpath/java/awt/event/MouseEvent.java create mode 100644 libjava/classpath/java/awt/event/MouseListener.java create mode 100644 libjava/classpath/java/awt/event/MouseMotionAdapter.java create mode 100644 libjava/classpath/java/awt/event/MouseMotionListener.java create mode 100644 libjava/classpath/java/awt/event/MouseWheelEvent.java create mode 100644 libjava/classpath/java/awt/event/MouseWheelListener.java create mode 100644 libjava/classpath/java/awt/event/PaintEvent.java create mode 100644 libjava/classpath/java/awt/event/TextEvent.java create mode 100644 libjava/classpath/java/awt/event/TextListener.java create mode 100644 libjava/classpath/java/awt/event/WindowAdapter.java create mode 100644 libjava/classpath/java/awt/event/WindowEvent.java create mode 100644 libjava/classpath/java/awt/event/WindowFocusListener.java create mode 100644 libjava/classpath/java/awt/event/WindowListener.java create mode 100644 libjava/classpath/java/awt/event/WindowStateListener.java create mode 100644 libjava/classpath/java/awt/event/package.html create mode 100644 libjava/classpath/java/awt/font/FontRenderContext.java create mode 100644 libjava/classpath/java/awt/font/GlyphJustificationInfo.java create mode 100644 libjava/classpath/java/awt/font/GlyphMetrics.java create mode 100644 libjava/classpath/java/awt/font/GlyphVector.java create mode 100644 libjava/classpath/java/awt/font/GraphicAttribute.java create mode 100644 libjava/classpath/java/awt/font/ImageGraphicAttribute.java create mode 100644 libjava/classpath/java/awt/font/LineBreakMeasurer.java create mode 100644 libjava/classpath/java/awt/font/LineMetrics.java create mode 100644 libjava/classpath/java/awt/font/MultipleMaster.java create mode 100644 libjava/classpath/java/awt/font/NumericShaper.java create mode 100644 libjava/classpath/java/awt/font/OpenType.java create mode 100644 libjava/classpath/java/awt/font/ShapeGraphicAttribute.java create mode 100644 libjava/classpath/java/awt/font/TextAttribute.java create mode 100644 libjava/classpath/java/awt/font/TextHitInfo.java create mode 100644 libjava/classpath/java/awt/font/TextLayout.java create mode 100644 libjava/classpath/java/awt/font/TextMeasurer.java create mode 100644 libjava/classpath/java/awt/font/TransformAttribute.java create mode 100644 libjava/classpath/java/awt/font/package.html create mode 100644 libjava/classpath/java/awt/geom/AffineTransform.java create mode 100644 libjava/classpath/java/awt/geom/Arc2D.java create mode 100644 libjava/classpath/java/awt/geom/Area.java create mode 100644 libjava/classpath/java/awt/geom/CubicCurve2D.java create mode 100644 libjava/classpath/java/awt/geom/Dimension2D.java create mode 100644 libjava/classpath/java/awt/geom/Ellipse2D.java create mode 100644 libjava/classpath/java/awt/geom/FlatteningPathIterator.java create mode 100644 libjava/classpath/java/awt/geom/GeneralPath.java create mode 100644 libjava/classpath/java/awt/geom/IllegalPathStateException.java create mode 100644 libjava/classpath/java/awt/geom/Line2D.java create mode 100644 libjava/classpath/java/awt/geom/NoninvertibleTransformException.java create mode 100644 libjava/classpath/java/awt/geom/PathIterator.java create mode 100644 libjava/classpath/java/awt/geom/Point2D.java create mode 100644 libjava/classpath/java/awt/geom/QuadCurve2D.java create mode 100644 libjava/classpath/java/awt/geom/Rectangle2D.java create mode 100644 libjava/classpath/java/awt/geom/RectangularShape.java create mode 100644 libjava/classpath/java/awt/geom/RoundRectangle2D.java create mode 100644 libjava/classpath/java/awt/geom/doc-files/Area-1.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-1.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-2.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-3.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-4.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-5.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/Ellipse-1.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/FlatteningPathIterator-1.html create mode 100644 libjava/classpath/java/awt/geom/doc-files/GeneralPath-1.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-1.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-2.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-3.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-4.png create mode 100644 libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-5.png create mode 100644 libjava/classpath/java/awt/geom/package.html create mode 100644 libjava/classpath/java/awt/im/InputContext.java create mode 100644 libjava/classpath/java/awt/im/InputMethodHighlight.java create mode 100644 libjava/classpath/java/awt/im/InputMethodRequests.java create mode 100644 libjava/classpath/java/awt/im/InputSubset.java create mode 100644 libjava/classpath/java/awt/im/package.html create mode 100644 libjava/classpath/java/awt/im/spi/InputMethod.java create mode 100644 libjava/classpath/java/awt/im/spi/InputMethodContext.java create mode 100644 libjava/classpath/java/awt/im/spi/InputMethodDescriptor.java create mode 100644 libjava/classpath/java/awt/im/spi/package.html create mode 100644 libjava/classpath/java/awt/image/AffineTransformOp.java create mode 100644 libjava/classpath/java/awt/image/AreaAveragingScaleFilter.java create mode 100644 libjava/classpath/java/awt/image/BandCombineOp.java create mode 100644 libjava/classpath/java/awt/image/BandedSampleModel.java create mode 100644 libjava/classpath/java/awt/image/BufferStrategy.java create mode 100644 libjava/classpath/java/awt/image/BufferedImage.java create mode 100644 libjava/classpath/java/awt/image/BufferedImageFilter.java create mode 100644 libjava/classpath/java/awt/image/BufferedImageOp.java create mode 100644 libjava/classpath/java/awt/image/ByteLookupTable.java create mode 100644 libjava/classpath/java/awt/image/ColorConvertOp.java create mode 100644 libjava/classpath/java/awt/image/ColorModel.java create mode 100644 libjava/classpath/java/awt/image/ComponentColorModel.java create mode 100644 libjava/classpath/java/awt/image/ComponentSampleModel.java create mode 100644 libjava/classpath/java/awt/image/ConvolveOp.java create mode 100644 libjava/classpath/java/awt/image/CropImageFilter.java create mode 100644 libjava/classpath/java/awt/image/DataBuffer.java create mode 100644 libjava/classpath/java/awt/image/DataBufferByte.java create mode 100644 libjava/classpath/java/awt/image/DataBufferDouble.java create mode 100644 libjava/classpath/java/awt/image/DataBufferFloat.java create mode 100644 libjava/classpath/java/awt/image/DataBufferInt.java create mode 100644 libjava/classpath/java/awt/image/DataBufferShort.java create mode 100644 libjava/classpath/java/awt/image/DataBufferUShort.java create mode 100644 libjava/classpath/java/awt/image/DirectColorModel.java create mode 100644 libjava/classpath/java/awt/image/FilteredImageSource.java create mode 100644 libjava/classpath/java/awt/image/ImageConsumer.java create mode 100644 libjava/classpath/java/awt/image/ImageFilter.java create mode 100644 libjava/classpath/java/awt/image/ImageObserver.java create mode 100644 libjava/classpath/java/awt/image/ImageProducer.java create mode 100644 libjava/classpath/java/awt/image/ImagingOpException.java create mode 100644 libjava/classpath/java/awt/image/IndexColorModel.java create mode 100644 libjava/classpath/java/awt/image/Kernel.java create mode 100644 libjava/classpath/java/awt/image/LookupOp.java create mode 100644 libjava/classpath/java/awt/image/LookupTable.java create mode 100644 libjava/classpath/java/awt/image/MemoryImageSource.java create mode 100644 libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java create mode 100644 libjava/classpath/java/awt/image/PackedColorModel.java create mode 100644 libjava/classpath/java/awt/image/PixelGrabber.java create mode 100644 libjava/classpath/java/awt/image/PixelInterleavedSampleModel.java create mode 100644 libjava/classpath/java/awt/image/RGBImageFilter.java create mode 100644 libjava/classpath/java/awt/image/Raster.java create mode 100644 libjava/classpath/java/awt/image/RasterFormatException.java create mode 100644 libjava/classpath/java/awt/image/RasterOp.java create mode 100644 libjava/classpath/java/awt/image/RenderedImage.java create mode 100644 libjava/classpath/java/awt/image/ReplicateScaleFilter.java create mode 100644 libjava/classpath/java/awt/image/RescaleOp.java create mode 100644 libjava/classpath/java/awt/image/SampleModel.java create mode 100644 libjava/classpath/java/awt/image/ShortLookupTable.java create mode 100644 libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java create mode 100644 libjava/classpath/java/awt/image/TileObserver.java create mode 100644 libjava/classpath/java/awt/image/VolatileImage.java create mode 100644 libjava/classpath/java/awt/image/WritableRaster.java create mode 100644 libjava/classpath/java/awt/image/WritableRenderedImage.java create mode 100644 libjava/classpath/java/awt/image/package.html create mode 100644 libjava/classpath/java/awt/image/renderable/ContextualRenderedImageFactory.java create mode 100644 libjava/classpath/java/awt/image/renderable/ParameterBlock.java create mode 100644 libjava/classpath/java/awt/image/renderable/RenderContext.java create mode 100644 libjava/classpath/java/awt/image/renderable/RenderableImage.java create mode 100644 libjava/classpath/java/awt/image/renderable/RenderableImageOp.java create mode 100644 libjava/classpath/java/awt/image/renderable/RenderableImageProducer.java create mode 100644 libjava/classpath/java/awt/image/renderable/RenderedImageFactory.java create mode 100644 libjava/classpath/java/awt/image/renderable/package.html create mode 100644 libjava/classpath/java/awt/package.html create mode 100644 libjava/classpath/java/awt/peer/ButtonPeer.java create mode 100644 libjava/classpath/java/awt/peer/CanvasPeer.java create mode 100644 libjava/classpath/java/awt/peer/CheckboxMenuItemPeer.java create mode 100644 libjava/classpath/java/awt/peer/CheckboxPeer.java create mode 100644 libjava/classpath/java/awt/peer/ChoicePeer.java create mode 100644 libjava/classpath/java/awt/peer/ComponentPeer.java create mode 100644 libjava/classpath/java/awt/peer/ContainerPeer.java create mode 100644 libjava/classpath/java/awt/peer/DesktopPeer.java create mode 100644 libjava/classpath/java/awt/peer/DialogPeer.java create mode 100644 libjava/classpath/java/awt/peer/FileDialogPeer.java create mode 100644 libjava/classpath/java/awt/peer/FontPeer.java create mode 100644 libjava/classpath/java/awt/peer/FramePeer.java create mode 100644 libjava/classpath/java/awt/peer/LabelPeer.java create mode 100644 libjava/classpath/java/awt/peer/LightweightPeer.java create mode 100644 libjava/classpath/java/awt/peer/ListPeer.java create mode 100644 libjava/classpath/java/awt/peer/MenuBarPeer.java create mode 100644 libjava/classpath/java/awt/peer/MenuComponentPeer.java create mode 100644 libjava/classpath/java/awt/peer/MenuItemPeer.java create mode 100644 libjava/classpath/java/awt/peer/MenuPeer.java create mode 100644 libjava/classpath/java/awt/peer/MouseInfoPeer.java create mode 100644 libjava/classpath/java/awt/peer/PanelPeer.java create mode 100644 libjava/classpath/java/awt/peer/PopupMenuPeer.java create mode 100644 libjava/classpath/java/awt/peer/RobotPeer.java create mode 100644 libjava/classpath/java/awt/peer/ScrollPanePeer.java create mode 100644 libjava/classpath/java/awt/peer/ScrollbarPeer.java create mode 100644 libjava/classpath/java/awt/peer/TextAreaPeer.java create mode 100644 libjava/classpath/java/awt/peer/TextComponentPeer.java create mode 100644 libjava/classpath/java/awt/peer/TextFieldPeer.java create mode 100644 libjava/classpath/java/awt/peer/WindowPeer.java create mode 100644 libjava/classpath/java/awt/peer/package.html create mode 100644 libjava/classpath/java/awt/print/Book.java create mode 100644 libjava/classpath/java/awt/print/NoPrinterJob.java create mode 100644 libjava/classpath/java/awt/print/PageFormat.java create mode 100644 libjava/classpath/java/awt/print/Pageable.java create mode 100644 libjava/classpath/java/awt/print/Paper.java create mode 100644 libjava/classpath/java/awt/print/Printable.java create mode 100644 libjava/classpath/java/awt/print/PrinterAbortException.java create mode 100644 libjava/classpath/java/awt/print/PrinterException.java create mode 100644 libjava/classpath/java/awt/print/PrinterGraphics.java create mode 100644 libjava/classpath/java/awt/print/PrinterIOException.java create mode 100644 libjava/classpath/java/awt/print/PrinterJob.java create mode 100644 libjava/classpath/java/awt/print/package.html create mode 100644 libjava/classpath/java/beans/AppletInitializer.java create mode 100644 libjava/classpath/java/beans/BeanDescriptor.java create mode 100644 libjava/classpath/java/beans/BeanInfo.java create mode 100644 libjava/classpath/java/beans/Beans.java create mode 100644 libjava/classpath/java/beans/ConstructorProperties.java create mode 100644 libjava/classpath/java/beans/Customizer.java create mode 100644 libjava/classpath/java/beans/DefaultPersistenceDelegate.java create mode 100644 libjava/classpath/java/beans/DesignMode.java create mode 100644 libjava/classpath/java/beans/Encoder.java create mode 100644 libjava/classpath/java/beans/EventHandler.java create mode 100644 libjava/classpath/java/beans/EventSetDescriptor.java create mode 100644 libjava/classpath/java/beans/ExceptionListener.java create mode 100644 libjava/classpath/java/beans/Expression.java create mode 100644 libjava/classpath/java/beans/FeatureDescriptor.java create mode 100644 libjava/classpath/java/beans/IndexedPropertyChangeEvent.java create mode 100644 libjava/classpath/java/beans/IndexedPropertyDescriptor.java create mode 100644 libjava/classpath/java/beans/IntrospectionException.java create mode 100644 libjava/classpath/java/beans/Introspector.java create mode 100644 libjava/classpath/java/beans/MethodDescriptor.java create mode 100644 libjava/classpath/java/beans/ParameterDescriptor.java create mode 100644 libjava/classpath/java/beans/PersistenceDelegate.java create mode 100644 libjava/classpath/java/beans/PropertyChangeEvent.java create mode 100644 libjava/classpath/java/beans/PropertyChangeListener.java create mode 100644 libjava/classpath/java/beans/PropertyChangeListenerProxy.java create mode 100644 libjava/classpath/java/beans/PropertyChangeSupport.java create mode 100644 libjava/classpath/java/beans/PropertyDescriptor.java create mode 100644 libjava/classpath/java/beans/PropertyEditor.java create mode 100644 libjava/classpath/java/beans/PropertyEditorManager.java create mode 100644 libjava/classpath/java/beans/PropertyEditorSupport.java create mode 100644 libjava/classpath/java/beans/PropertyVetoException.java create mode 100644 libjava/classpath/java/beans/SimpleBeanInfo.java create mode 100644 libjava/classpath/java/beans/Statement.java create mode 100644 libjava/classpath/java/beans/TODO create mode 100644 libjava/classpath/java/beans/VetoableChangeListener.java create mode 100644 libjava/classpath/java/beans/VetoableChangeListenerProxy.java create mode 100644 libjava/classpath/java/beans/VetoableChangeSupport.java create mode 100644 libjava/classpath/java/beans/Visibility.java create mode 100644 libjava/classpath/java/beans/XMLDecoder.java create mode 100644 libjava/classpath/java/beans/XMLEncoder.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContext.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextChild.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextEvent.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextProxy.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextServices.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java create mode 100644 libjava/classpath/java/beans/beancontext/BeanContextSupport.java create mode 100644 libjava/classpath/java/beans/beancontext/package.html create mode 100644 libjava/classpath/java/beans/package.html create mode 100644 libjava/classpath/java/io/BufferedInputStream.java create mode 100644 libjava/classpath/java/io/BufferedOutputStream.java create mode 100644 libjava/classpath/java/io/BufferedReader.java create mode 100644 libjava/classpath/java/io/BufferedWriter.java create mode 100644 libjava/classpath/java/io/ByteArrayInputStream.java create mode 100644 libjava/classpath/java/io/ByteArrayOutputStream.java create mode 100644 libjava/classpath/java/io/CharArrayReader.java create mode 100644 libjava/classpath/java/io/CharArrayWriter.java create mode 100644 libjava/classpath/java/io/CharConversionException.java create mode 100644 libjava/classpath/java/io/Closeable.java create mode 100644 libjava/classpath/java/io/DataInput.java create mode 100644 libjava/classpath/java/io/DataInputStream.java create mode 100644 libjava/classpath/java/io/DataOutput.java create mode 100644 libjava/classpath/java/io/DataOutputStream.java create mode 100644 libjava/classpath/java/io/DeleteFileHelper.java create mode 100644 libjava/classpath/java/io/EOFException.java create mode 100644 libjava/classpath/java/io/Externalizable.java create mode 100644 libjava/classpath/java/io/File.java create mode 100644 libjava/classpath/java/io/FileDescriptor.java create mode 100644 libjava/classpath/java/io/FileFilter.java create mode 100644 libjava/classpath/java/io/FileInputStream.java create mode 100644 libjava/classpath/java/io/FileNotFoundException.java create mode 100644 libjava/classpath/java/io/FileOutputStream.java create mode 100644 libjava/classpath/java/io/FilePermission.java create mode 100644 libjava/classpath/java/io/FileReader.java create mode 100644 libjava/classpath/java/io/FileWriter.java create mode 100644 libjava/classpath/java/io/FilenameFilter.java create mode 100644 libjava/classpath/java/io/FilterInputStream.java create mode 100644 libjava/classpath/java/io/FilterOutputStream.java create mode 100644 libjava/classpath/java/io/FilterReader.java create mode 100644 libjava/classpath/java/io/FilterWriter.java create mode 100644 libjava/classpath/java/io/Flushable.java create mode 100644 libjava/classpath/java/io/IOException.java create mode 100644 libjava/classpath/java/io/InputStream.java create mode 100644 libjava/classpath/java/io/InputStreamReader.java create mode 100644 libjava/classpath/java/io/InterruptedIOException.java create mode 100644 libjava/classpath/java/io/InvalidClassException.java create mode 100644 libjava/classpath/java/io/InvalidObjectException.java create mode 100644 libjava/classpath/java/io/LineNumberInputStream.java create mode 100644 libjava/classpath/java/io/LineNumberReader.java create mode 100644 libjava/classpath/java/io/NotActiveException.java create mode 100644 libjava/classpath/java/io/NotSerializableException.java create mode 100644 libjava/classpath/java/io/ObjectInput.java create mode 100644 libjava/classpath/java/io/ObjectInputStream.java create mode 100644 libjava/classpath/java/io/ObjectInputValidation.java create mode 100644 libjava/classpath/java/io/ObjectOutput.java create mode 100644 libjava/classpath/java/io/ObjectOutputStream.java create mode 100644 libjava/classpath/java/io/ObjectStreamClass.java create mode 100644 libjava/classpath/java/io/ObjectStreamConstants.java create mode 100644 libjava/classpath/java/io/ObjectStreamException.java create mode 100644 libjava/classpath/java/io/ObjectStreamField.java create mode 100644 libjava/classpath/java/io/OptionalDataException.java create mode 100644 libjava/classpath/java/io/OutputStream.java create mode 100644 libjava/classpath/java/io/OutputStreamWriter.java create mode 100644 libjava/classpath/java/io/PipedInputStream.java create mode 100644 libjava/classpath/java/io/PipedOutputStream.java create mode 100644 libjava/classpath/java/io/PipedReader.java create mode 100644 libjava/classpath/java/io/PipedWriter.java create mode 100644 libjava/classpath/java/io/PrintStream.java create mode 100644 libjava/classpath/java/io/PrintWriter.java create mode 100644 libjava/classpath/java/io/PushbackInputStream.java create mode 100644 libjava/classpath/java/io/PushbackReader.java create mode 100644 libjava/classpath/java/io/RandomAccessFile.java create mode 100644 libjava/classpath/java/io/Reader.java create mode 100644 libjava/classpath/java/io/SequenceInputStream.java create mode 100644 libjava/classpath/java/io/Serializable.java create mode 100644 libjava/classpath/java/io/SerializablePermission.java create mode 100644 libjava/classpath/java/io/StreamCorruptedException.java create mode 100644 libjava/classpath/java/io/StreamTokenizer.java create mode 100644 libjava/classpath/java/io/StringBufferInputStream.java create mode 100644 libjava/classpath/java/io/StringReader.java create mode 100644 libjava/classpath/java/io/StringWriter.java create mode 100644 libjava/classpath/java/io/SyncFailedException.java create mode 100644 libjava/classpath/java/io/UTFDataFormatException.java create mode 100644 libjava/classpath/java/io/UnsupportedEncodingException.java create mode 100644 libjava/classpath/java/io/WriteAbortedException.java create mode 100644 libjava/classpath/java/io/Writer.java create mode 100644 libjava/classpath/java/io/package.html create mode 100644 libjava/classpath/java/lang/AbstractMethodError.java create mode 100644 libjava/classpath/java/lang/AbstractStringBuffer.java create mode 100644 libjava/classpath/java/lang/Appendable.java create mode 100644 libjava/classpath/java/lang/ArithmeticException.java create mode 100644 libjava/classpath/java/lang/ArrayIndexOutOfBoundsException.java create mode 100644 libjava/classpath/java/lang/ArrayStoreException.java create mode 100644 libjava/classpath/java/lang/AssertionError.java create mode 100644 libjava/classpath/java/lang/Boolean.java create mode 100644 libjava/classpath/java/lang/Byte.java create mode 100644 libjava/classpath/java/lang/CharSequence.java create mode 100644 libjava/classpath/java/lang/Character.java create mode 100644 libjava/classpath/java/lang/Class.java create mode 100644 libjava/classpath/java/lang/ClassCastException.java create mode 100644 libjava/classpath/java/lang/ClassCircularityError.java create mode 100644 libjava/classpath/java/lang/ClassFormatError.java create mode 100644 libjava/classpath/java/lang/ClassLoader.java create mode 100644 libjava/classpath/java/lang/ClassNotFoundException.java create mode 100644 libjava/classpath/java/lang/CloneNotSupportedException.java create mode 100644 libjava/classpath/java/lang/Cloneable.java create mode 100644 libjava/classpath/java/lang/Comparable.java create mode 100644 libjava/classpath/java/lang/Compiler.java create mode 100644 libjava/classpath/java/lang/Deprecated.java create mode 100644 libjava/classpath/java/lang/Double.java create mode 100644 libjava/classpath/java/lang/Enum.java create mode 100644 libjava/classpath/java/lang/EnumConstantNotPresentException.java create mode 100644 libjava/classpath/java/lang/Error.java create mode 100644 libjava/classpath/java/lang/Exception.java create mode 100644 libjava/classpath/java/lang/ExceptionInInitializerError.java create mode 100644 libjava/classpath/java/lang/Float.java create mode 100644 libjava/classpath/java/lang/IllegalAccessError.java create mode 100644 libjava/classpath/java/lang/IllegalAccessException.java create mode 100644 libjava/classpath/java/lang/IllegalArgumentException.java create mode 100644 libjava/classpath/java/lang/IllegalMonitorStateException.java create mode 100644 libjava/classpath/java/lang/IllegalStateException.java create mode 100644 libjava/classpath/java/lang/IllegalThreadStateException.java create mode 100644 libjava/classpath/java/lang/IncompatibleClassChangeError.java create mode 100644 libjava/classpath/java/lang/IndexOutOfBoundsException.java create mode 100644 libjava/classpath/java/lang/InheritableThreadLocal.java create mode 100644 libjava/classpath/java/lang/InstantiationError.java create mode 100644 libjava/classpath/java/lang/InstantiationException.java create mode 100644 libjava/classpath/java/lang/Integer.java create mode 100644 libjava/classpath/java/lang/InternalError.java create mode 100644 libjava/classpath/java/lang/InterruptedException.java create mode 100644 libjava/classpath/java/lang/Iterable.java create mode 100644 libjava/classpath/java/lang/LinkageError.java create mode 100644 libjava/classpath/java/lang/Long.java create mode 100644 libjava/classpath/java/lang/Math.java create mode 100644 libjava/classpath/java/lang/NegativeArraySizeException.java create mode 100644 libjava/classpath/java/lang/NoClassDefFoundError.java create mode 100644 libjava/classpath/java/lang/NoSuchFieldError.java create mode 100644 libjava/classpath/java/lang/NoSuchFieldException.java create mode 100644 libjava/classpath/java/lang/NoSuchMethodError.java create mode 100644 libjava/classpath/java/lang/NoSuchMethodException.java create mode 100644 libjava/classpath/java/lang/NullPointerException.java create mode 100644 libjava/classpath/java/lang/Number.java create mode 100644 libjava/classpath/java/lang/NumberFormatException.java create mode 100644 libjava/classpath/java/lang/Object.java create mode 100644 libjava/classpath/java/lang/OutOfMemoryError.java create mode 100644 libjava/classpath/java/lang/Override.java create mode 100644 libjava/classpath/java/lang/Package.java create mode 100644 libjava/classpath/java/lang/Process.java create mode 100644 libjava/classpath/java/lang/ProcessBuilder.java create mode 100644 libjava/classpath/java/lang/Readable.java create mode 100644 libjava/classpath/java/lang/Runnable.java create mode 100644 libjava/classpath/java/lang/Runtime.java create mode 100644 libjava/classpath/java/lang/RuntimeException.java create mode 100644 libjava/classpath/java/lang/RuntimePermission.java create mode 100644 libjava/classpath/java/lang/SecurityException.java create mode 100644 libjava/classpath/java/lang/SecurityManager.java create mode 100644 libjava/classpath/java/lang/Short.java create mode 100644 libjava/classpath/java/lang/StackOverflowError.java create mode 100644 libjava/classpath/java/lang/StackTraceElement.java create mode 100644 libjava/classpath/java/lang/StrictMath.java create mode 100644 libjava/classpath/java/lang/String.java create mode 100644 libjava/classpath/java/lang/StringBuffer.java create mode 100644 libjava/classpath/java/lang/StringBuilder.java create mode 100644 libjava/classpath/java/lang/StringIndexOutOfBoundsException.java create mode 100644 libjava/classpath/java/lang/SuppressWarnings.java create mode 100644 libjava/classpath/java/lang/System.java create mode 100644 libjava/classpath/java/lang/Thread.java create mode 100644 libjava/classpath/java/lang/ThreadDeath.java create mode 100644 libjava/classpath/java/lang/ThreadGroup.java create mode 100644 libjava/classpath/java/lang/ThreadLocal.java create mode 100644 libjava/classpath/java/lang/ThreadLocalMap.java create mode 100644 libjava/classpath/java/lang/Throwable.java create mode 100644 libjava/classpath/java/lang/TypeNotPresentException.java create mode 100644 libjava/classpath/java/lang/UnknownError.java create mode 100644 libjava/classpath/java/lang/UnsatisfiedLinkError.java create mode 100644 libjava/classpath/java/lang/UnsupportedClassVersionError.java create mode 100644 libjava/classpath/java/lang/UnsupportedOperationException.java create mode 100644 libjava/classpath/java/lang/VerifyError.java create mode 100644 libjava/classpath/java/lang/VirtualMachineError.java create mode 100644 libjava/classpath/java/lang/Void.java create mode 100644 libjava/classpath/java/lang/annotation/Annotation.java create mode 100644 libjava/classpath/java/lang/annotation/AnnotationFormatError.java create mode 100644 libjava/classpath/java/lang/annotation/AnnotationTypeMismatchException.java create mode 100644 libjava/classpath/java/lang/annotation/Documented.java create mode 100644 libjava/classpath/java/lang/annotation/ElementType.java create mode 100644 libjava/classpath/java/lang/annotation/IncompleteAnnotationException.java create mode 100644 libjava/classpath/java/lang/annotation/Inherited.java create mode 100644 libjava/classpath/java/lang/annotation/Retention.java create mode 100644 libjava/classpath/java/lang/annotation/RetentionPolicy.java create mode 100644 libjava/classpath/java/lang/annotation/Target.java create mode 100644 libjava/classpath/java/lang/annotation/package.html create mode 100644 libjava/classpath/java/lang/instrument/ClassDefinition.java create mode 100644 libjava/classpath/java/lang/instrument/ClassFileTransformer.java create mode 100644 libjava/classpath/java/lang/instrument/IllegalClassFormatException.java create mode 100644 libjava/classpath/java/lang/instrument/Instrumentation.java create mode 100644 libjava/classpath/java/lang/instrument/UnmodifiableClassException.java create mode 100644 libjava/classpath/java/lang/management/ClassLoadingMXBean.java create mode 100644 libjava/classpath/java/lang/management/CompilationMXBean.java create mode 100644 libjava/classpath/java/lang/management/GarbageCollectorMXBean.java create mode 100644 libjava/classpath/java/lang/management/LockInfo.java create mode 100644 libjava/classpath/java/lang/management/ManagementFactory.java create mode 100644 libjava/classpath/java/lang/management/ManagementPermission.java create mode 100644 libjava/classpath/java/lang/management/MemoryMXBean.java create mode 100644 libjava/classpath/java/lang/management/MemoryManagerMXBean.java create mode 100644 libjava/classpath/java/lang/management/MemoryNotificationInfo.java create mode 100644 libjava/classpath/java/lang/management/MemoryPoolMXBean.java create mode 100644 libjava/classpath/java/lang/management/MemoryType.java create mode 100644 libjava/classpath/java/lang/management/MemoryUsage.java create mode 100644 libjava/classpath/java/lang/management/MonitorInfo.java create mode 100644 libjava/classpath/java/lang/management/OperatingSystemMXBean.java create mode 100644 libjava/classpath/java/lang/management/RuntimeMXBean.java create mode 100644 libjava/classpath/java/lang/management/ThreadInfo.java create mode 100644 libjava/classpath/java/lang/management/ThreadMXBean.java create mode 100644 libjava/classpath/java/lang/management/package.html create mode 100644 libjava/classpath/java/lang/package.html create mode 100644 libjava/classpath/java/lang/ref/PhantomReference.java create mode 100644 libjava/classpath/java/lang/ref/Reference.java create mode 100644 libjava/classpath/java/lang/ref/ReferenceQueue.java create mode 100644 libjava/classpath/java/lang/ref/SoftReference.java create mode 100644 libjava/classpath/java/lang/ref/WeakReference.java create mode 100644 libjava/classpath/java/lang/ref/package.html create mode 100644 libjava/classpath/java/lang/reflect/AccessibleObject.java create mode 100644 libjava/classpath/java/lang/reflect/AnnotatedElement.java create mode 100644 libjava/classpath/java/lang/reflect/Array.java create mode 100644 libjava/classpath/java/lang/reflect/Constructor.java create mode 100644 libjava/classpath/java/lang/reflect/Field.java create mode 100644 libjava/classpath/java/lang/reflect/GenericArrayType.java create mode 100644 libjava/classpath/java/lang/reflect/GenericDeclaration.java create mode 100644 libjava/classpath/java/lang/reflect/GenericSignatureFormatError.java create mode 100644 libjava/classpath/java/lang/reflect/InvocationHandler.java create mode 100644 libjava/classpath/java/lang/reflect/InvocationTargetException.java create mode 100644 libjava/classpath/java/lang/reflect/MalformedParameterizedTypeException.java create mode 100644 libjava/classpath/java/lang/reflect/Member.java create mode 100644 libjava/classpath/java/lang/reflect/Method.java create mode 100644 libjava/classpath/java/lang/reflect/Modifier.java create mode 100644 libjava/classpath/java/lang/reflect/ParameterizedType.java create mode 100644 libjava/classpath/java/lang/reflect/Proxy.java create mode 100644 libjava/classpath/java/lang/reflect/README create mode 100644 libjava/classpath/java/lang/reflect/ReflectPermission.java create mode 100755 libjava/classpath/java/lang/reflect/TODO create mode 100644 libjava/classpath/java/lang/reflect/Type.java create mode 100644 libjava/classpath/java/lang/reflect/TypeVariable.java create mode 100644 libjava/classpath/java/lang/reflect/UndeclaredThrowableException.java create mode 100644 libjava/classpath/java/lang/reflect/WildcardType.java create mode 100644 libjava/classpath/java/lang/reflect/package.html create mode 100644 libjava/classpath/java/math/BigDecimal.java create mode 100644 libjava/classpath/java/math/BigInteger.java create mode 100644 libjava/classpath/java/math/MathContext.java create mode 100644 libjava/classpath/java/math/RoundingMode.java create mode 100644 libjava/classpath/java/math/package.html create mode 100644 libjava/classpath/java/net/Authenticator.java create mode 100644 libjava/classpath/java/net/BindException.java create mode 100644 libjava/classpath/java/net/ConnectException.java create mode 100644 libjava/classpath/java/net/ContentHandler.java create mode 100644 libjava/classpath/java/net/ContentHandlerFactory.java create mode 100644 libjava/classpath/java/net/DatagramPacket.java create mode 100644 libjava/classpath/java/net/DatagramSocket.java create mode 100644 libjava/classpath/java/net/DatagramSocketImpl.java create mode 100644 libjava/classpath/java/net/DatagramSocketImplFactory.java create mode 100644 libjava/classpath/java/net/FileNameMap.java create mode 100644 libjava/classpath/java/net/HttpURLConnection.java create mode 100644 libjava/classpath/java/net/Inet4Address.java create mode 100644 libjava/classpath/java/net/Inet6Address.java create mode 100644 libjava/classpath/java/net/InetAddress.java create mode 100644 libjava/classpath/java/net/InetSocketAddress.java create mode 100644 libjava/classpath/java/net/JarURLConnection.java create mode 100644 libjava/classpath/java/net/MalformedURLException.java create mode 100644 libjava/classpath/java/net/MimeTypeMapper.java create mode 100644 libjava/classpath/java/net/MulticastSocket.java create mode 100644 libjava/classpath/java/net/NetPermission.java create mode 100644 libjava/classpath/java/net/NetworkInterface.java create mode 100644 libjava/classpath/java/net/NoRouteToHostException.java create mode 100644 libjava/classpath/java/net/PasswordAuthentication.java create mode 100644 libjava/classpath/java/net/PortUnreachableException.java create mode 100644 libjava/classpath/java/net/ProtocolException.java create mode 100644 libjava/classpath/java/net/Proxy.java create mode 100644 libjava/classpath/java/net/ProxySelector.java create mode 100644 libjava/classpath/java/net/ResolverCache.java create mode 100644 libjava/classpath/java/net/STATUS create mode 100644 libjava/classpath/java/net/ServerSocket.java create mode 100644 libjava/classpath/java/net/Socket.java create mode 100644 libjava/classpath/java/net/SocketAddress.java create mode 100644 libjava/classpath/java/net/SocketException.java create mode 100644 libjava/classpath/java/net/SocketImpl.java create mode 100644 libjava/classpath/java/net/SocketImplFactory.java create mode 100644 libjava/classpath/java/net/SocketOptions.java create mode 100644 libjava/classpath/java/net/SocketPermission.java create mode 100644 libjava/classpath/java/net/SocketTimeoutException.java create mode 100644 libjava/classpath/java/net/TODO create mode 100644 libjava/classpath/java/net/URI.java create mode 100644 libjava/classpath/java/net/URISyntaxException.java create mode 100644 libjava/classpath/java/net/URL.java create mode 100644 libjava/classpath/java/net/URLClassLoader.java create mode 100644 libjava/classpath/java/net/URLConnection.java create mode 100644 libjava/classpath/java/net/URLDecoder.java create mode 100644 libjava/classpath/java/net/URLEncoder.java create mode 100644 libjava/classpath/java/net/URLStreamHandler.java create mode 100644 libjava/classpath/java/net/URLStreamHandlerFactory.java create mode 100644 libjava/classpath/java/net/UnknownHostException.java create mode 100644 libjava/classpath/java/net/UnknownServiceException.java create mode 100644 libjava/classpath/java/net/package.html create mode 100644 libjava/classpath/java/nio/Buffer.java create mode 100644 libjava/classpath/java/nio/BufferOverflowException.java create mode 100644 libjava/classpath/java/nio/BufferUnderflowException.java create mode 100644 libjava/classpath/java/nio/ByteBuffer.java create mode 100644 libjava/classpath/java/nio/ByteBufferHelper.java create mode 100644 libjava/classpath/java/nio/ByteBufferImpl.java create mode 100644 libjava/classpath/java/nio/ByteOrder.java create mode 100644 libjava/classpath/java/nio/CharBuffer.java create mode 100644 libjava/classpath/java/nio/CharBufferImpl.java create mode 100644 libjava/classpath/java/nio/CharSequenceBuffer.java create mode 100644 libjava/classpath/java/nio/CharViewBufferImpl.java create mode 100644 libjava/classpath/java/nio/DirectByteBufferImpl.java create mode 100644 libjava/classpath/java/nio/DoubleBuffer.java create mode 100644 libjava/classpath/java/nio/DoubleBufferImpl.java create mode 100644 libjava/classpath/java/nio/DoubleViewBufferImpl.java create mode 100644 libjava/classpath/java/nio/FloatBuffer.java create mode 100644 libjava/classpath/java/nio/FloatBufferImpl.java create mode 100644 libjava/classpath/java/nio/FloatViewBufferImpl.java create mode 100644 libjava/classpath/java/nio/IntBuffer.java create mode 100644 libjava/classpath/java/nio/IntBufferImpl.java create mode 100644 libjava/classpath/java/nio/IntViewBufferImpl.java create mode 100644 libjava/classpath/java/nio/InvalidMarkException.java create mode 100644 libjava/classpath/java/nio/LongBuffer.java create mode 100644 libjava/classpath/java/nio/LongBufferImpl.java create mode 100644 libjava/classpath/java/nio/LongViewBufferImpl.java create mode 100644 libjava/classpath/java/nio/MappedByteBuffer.java create mode 100644 libjava/classpath/java/nio/MappedByteBufferImpl.java create mode 100644 libjava/classpath/java/nio/ReadOnlyBufferException.java create mode 100644 libjava/classpath/java/nio/ShortBuffer.java create mode 100644 libjava/classpath/java/nio/ShortBufferImpl.java create mode 100644 libjava/classpath/java/nio/ShortViewBufferImpl.java create mode 100644 libjava/classpath/java/nio/channels/AlreadyConnectedException.java create mode 100644 libjava/classpath/java/nio/channels/AsynchronousCloseException.java create mode 100644 libjava/classpath/java/nio/channels/ByteChannel.java create mode 100644 libjava/classpath/java/nio/channels/CancelledKeyException.java create mode 100644 libjava/classpath/java/nio/channels/Channel.java create mode 100644 libjava/classpath/java/nio/channels/Channels.java create mode 100644 libjava/classpath/java/nio/channels/ClosedByInterruptException.java create mode 100644 libjava/classpath/java/nio/channels/ClosedChannelException.java create mode 100644 libjava/classpath/java/nio/channels/ClosedSelectorException.java create mode 100644 libjava/classpath/java/nio/channels/ConnectionPendingException.java create mode 100644 libjava/classpath/java/nio/channels/DatagramChannel.java create mode 100644 libjava/classpath/java/nio/channels/FileChannel.java create mode 100644 libjava/classpath/java/nio/channels/FileLock.java create mode 100644 libjava/classpath/java/nio/channels/FileLockInterruptionException.java create mode 100644 libjava/classpath/java/nio/channels/GatheringByteChannel.java create mode 100644 libjava/classpath/java/nio/channels/IllegalBlockingModeException.java create mode 100644 libjava/classpath/java/nio/channels/IllegalSelectorException.java create mode 100644 libjava/classpath/java/nio/channels/InterruptibleChannel.java create mode 100644 libjava/classpath/java/nio/channels/NoConnectionPendingException.java create mode 100644 libjava/classpath/java/nio/channels/NonReadableChannelException.java create mode 100644 libjava/classpath/java/nio/channels/NonWritableChannelException.java create mode 100644 libjava/classpath/java/nio/channels/NotYetBoundException.java create mode 100644 libjava/classpath/java/nio/channels/NotYetConnectedException.java create mode 100644 libjava/classpath/java/nio/channels/OverlappingFileLockException.java create mode 100644 libjava/classpath/java/nio/channels/Pipe.java create mode 100644 libjava/classpath/java/nio/channels/ReadableByteChannel.java create mode 100644 libjava/classpath/java/nio/channels/ScatteringByteChannel.java create mode 100644 libjava/classpath/java/nio/channels/SelectableChannel.java create mode 100644 libjava/classpath/java/nio/channels/SelectionKey.java create mode 100644 libjava/classpath/java/nio/channels/Selector.java create mode 100644 libjava/classpath/java/nio/channels/ServerSocketChannel.java create mode 100644 libjava/classpath/java/nio/channels/SocketChannel.java create mode 100644 libjava/classpath/java/nio/channels/UnresolvedAddressException.java create mode 100644 libjava/classpath/java/nio/channels/UnsupportedAddressTypeException.java create mode 100644 libjava/classpath/java/nio/channels/WritableByteChannel.java create mode 100644 libjava/classpath/java/nio/channels/package.html create mode 100644 libjava/classpath/java/nio/channels/spi/AbstractInterruptibleChannel.java create mode 100644 libjava/classpath/java/nio/channels/spi/AbstractSelectableChannel.java create mode 100644 libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java create mode 100644 libjava/classpath/java/nio/channels/spi/AbstractSelector.java create mode 100644 libjava/classpath/java/nio/channels/spi/SelectorProvider.java create mode 100644 libjava/classpath/java/nio/channels/spi/package.html create mode 100644 libjava/classpath/java/nio/charset/CharacterCodingException.java create mode 100644 libjava/classpath/java/nio/charset/Charset.java create mode 100644 libjava/classpath/java/nio/charset/CharsetDecoder.java create mode 100644 libjava/classpath/java/nio/charset/CharsetEncoder.java create mode 100644 libjava/classpath/java/nio/charset/CoderMalfunctionError.java create mode 100644 libjava/classpath/java/nio/charset/CoderResult.java create mode 100644 libjava/classpath/java/nio/charset/CodingErrorAction.java create mode 100644 libjava/classpath/java/nio/charset/IllegalCharsetNameException.java create mode 100644 libjava/classpath/java/nio/charset/MalformedInputException.java create mode 100644 libjava/classpath/java/nio/charset/UnmappableCharacterException.java create mode 100644 libjava/classpath/java/nio/charset/UnsupportedCharsetException.java create mode 100644 libjava/classpath/java/nio/charset/package.html create mode 100644 libjava/classpath/java/nio/charset/spi/CharsetProvider.java create mode 100644 libjava/classpath/java/nio/charset/spi/package.html create mode 100644 libjava/classpath/java/nio/package.html create mode 100644 libjava/classpath/java/rmi/AccessException.java create mode 100644 libjava/classpath/java/rmi/AlreadyBoundException.java create mode 100644 libjava/classpath/java/rmi/ConnectException.java create mode 100644 libjava/classpath/java/rmi/ConnectIOException.java create mode 100644 libjava/classpath/java/rmi/MarshalException.java create mode 100644 libjava/classpath/java/rmi/MarshalledObject.java create mode 100644 libjava/classpath/java/rmi/Naming.java create mode 100644 libjava/classpath/java/rmi/NoSuchObjectException.java create mode 100644 libjava/classpath/java/rmi/NotBoundException.java create mode 100644 libjava/classpath/java/rmi/RMISecurityException.java create mode 100644 libjava/classpath/java/rmi/RMISecurityManager.java create mode 100644 libjava/classpath/java/rmi/Remote.java create mode 100644 libjava/classpath/java/rmi/RemoteException.java create mode 100644 libjava/classpath/java/rmi/ServerError.java create mode 100644 libjava/classpath/java/rmi/ServerException.java create mode 100644 libjava/classpath/java/rmi/ServerRuntimeException.java create mode 100644 libjava/classpath/java/rmi/StubNotFoundException.java create mode 100644 libjava/classpath/java/rmi/UnexpectedException.java create mode 100644 libjava/classpath/java/rmi/UnknownHostException.java create mode 100644 libjava/classpath/java/rmi/UnmarshalException.java create mode 100644 libjava/classpath/java/rmi/activation/Activatable.java create mode 100644 libjava/classpath/java/rmi/activation/ActivateFailedException.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationDesc.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationException.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationGroup.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationGroupDesc.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationGroupID.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationGroup_Stub.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationID.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationInstantiator.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationMonitor.java create mode 100644 libjava/classpath/java/rmi/activation/ActivationSystem.java create mode 100644 libjava/classpath/java/rmi/activation/Activator.java create mode 100644 libjava/classpath/java/rmi/activation/UnknownGroupException.java create mode 100644 libjava/classpath/java/rmi/activation/UnknownObjectException.java create mode 100644 libjava/classpath/java/rmi/activation/package.html create mode 100644 libjava/classpath/java/rmi/dgc/DGC.java create mode 100644 libjava/classpath/java/rmi/dgc/Lease.java create mode 100644 libjava/classpath/java/rmi/dgc/VMID.java create mode 100644 libjava/classpath/java/rmi/dgc/package.html create mode 100644 libjava/classpath/java/rmi/package.html create mode 100644 libjava/classpath/java/rmi/registry/LocateRegistry.java create mode 100644 libjava/classpath/java/rmi/registry/Registry.java create mode 100644 libjava/classpath/java/rmi/registry/RegistryHandler.java create mode 100644 libjava/classpath/java/rmi/registry/package.html create mode 100644 libjava/classpath/java/rmi/server/ExportException.java create mode 100644 libjava/classpath/java/rmi/server/LoaderHandler.java create mode 100644 libjava/classpath/java/rmi/server/LogStream.java create mode 100644 libjava/classpath/java/rmi/server/ObjID.java create mode 100644 libjava/classpath/java/rmi/server/Operation.java create mode 100644 libjava/classpath/java/rmi/server/RMIClassLoader.java create mode 100644 libjava/classpath/java/rmi/server/RMIClassLoaderSpi.java create mode 100644 libjava/classpath/java/rmi/server/RMIClientSocketFactory.java create mode 100644 libjava/classpath/java/rmi/server/RMIFailureHandler.java create mode 100644 libjava/classpath/java/rmi/server/RMIServerSocketFactory.java create mode 100644 libjava/classpath/java/rmi/server/RMISocketFactory.java create mode 100644 libjava/classpath/java/rmi/server/RemoteCall.java create mode 100644 libjava/classpath/java/rmi/server/RemoteObject.java create mode 100644 libjava/classpath/java/rmi/server/RemoteObjectInvocationHandler.java create mode 100644 libjava/classpath/java/rmi/server/RemoteRef.java create mode 100644 libjava/classpath/java/rmi/server/RemoteServer.java create mode 100644 libjava/classpath/java/rmi/server/RemoteStub.java create mode 100644 libjava/classpath/java/rmi/server/ServerCloneException.java create mode 100644 libjava/classpath/java/rmi/server/ServerNotActiveException.java create mode 100644 libjava/classpath/java/rmi/server/ServerRef.java create mode 100644 libjava/classpath/java/rmi/server/Skeleton.java create mode 100644 libjava/classpath/java/rmi/server/SkeletonMismatchException.java create mode 100644 libjava/classpath/java/rmi/server/SkeletonNotFoundException.java create mode 100644 libjava/classpath/java/rmi/server/SocketSecurityException.java create mode 100644 libjava/classpath/java/rmi/server/UID.java create mode 100644 libjava/classpath/java/rmi/server/UnicastRemoteObject.java create mode 100644 libjava/classpath/java/rmi/server/Unreferenced.java create mode 100644 libjava/classpath/java/rmi/server/package.html create mode 100644 libjava/classpath/java/security/AccessControlContext.java create mode 100644 libjava/classpath/java/security/AccessControlException.java create mode 100644 libjava/classpath/java/security/AccessController.java create mode 100644 libjava/classpath/java/security/AlgorithmParameterGenerator.java create mode 100644 libjava/classpath/java/security/AlgorithmParameterGeneratorSpi.java create mode 100644 libjava/classpath/java/security/AlgorithmParameters.java create mode 100644 libjava/classpath/java/security/AlgorithmParametersSpi.java create mode 100644 libjava/classpath/java/security/AllPermission.java create mode 100644 libjava/classpath/java/security/BasicPermission.java create mode 100644 libjava/classpath/java/security/Certificate.java create mode 100644 libjava/classpath/java/security/CodeSource.java create mode 100644 libjava/classpath/java/security/DigestException.java create mode 100644 libjava/classpath/java/security/DigestInputStream.java create mode 100644 libjava/classpath/java/security/DigestOutputStream.java create mode 100644 libjava/classpath/java/security/DomainCombiner.java create mode 100644 libjava/classpath/java/security/DummyKeyPairGenerator.java create mode 100644 libjava/classpath/java/security/DummyMessageDigest.java create mode 100644 libjava/classpath/java/security/DummySignature.java create mode 100644 libjava/classpath/java/security/GeneralSecurityException.java create mode 100644 libjava/classpath/java/security/Guard.java create mode 100644 libjava/classpath/java/security/GuardedObject.java create mode 100644 libjava/classpath/java/security/Identity.java create mode 100644 libjava/classpath/java/security/IdentityScope.java create mode 100644 libjava/classpath/java/security/IntersectingDomainCombiner.java create mode 100644 libjava/classpath/java/security/InvalidAlgorithmParameterException.java create mode 100644 libjava/classpath/java/security/InvalidKeyException.java create mode 100644 libjava/classpath/java/security/InvalidParameterException.java create mode 100644 libjava/classpath/java/security/Key.java create mode 100644 libjava/classpath/java/security/KeyException.java create mode 100644 libjava/classpath/java/security/KeyFactory.java create mode 100644 libjava/classpath/java/security/KeyFactorySpi.java create mode 100644 libjava/classpath/java/security/KeyManagementException.java create mode 100644 libjava/classpath/java/security/KeyPair.java create mode 100644 libjava/classpath/java/security/KeyPairGenerator.java create mode 100644 libjava/classpath/java/security/KeyPairGeneratorSpi.java create mode 100644 libjava/classpath/java/security/KeyStore.java create mode 100644 libjava/classpath/java/security/KeyStoreException.java create mode 100644 libjava/classpath/java/security/KeyStoreSpi.java create mode 100644 libjava/classpath/java/security/MessageDigest.java create mode 100644 libjava/classpath/java/security/MessageDigestSpi.java create mode 100644 libjava/classpath/java/security/NoSuchAlgorithmException.java create mode 100644 libjava/classpath/java/security/NoSuchProviderException.java create mode 100644 libjava/classpath/java/security/Permission.java create mode 100644 libjava/classpath/java/security/PermissionCollection.java create mode 100644 libjava/classpath/java/security/Permissions.java create mode 100644 libjava/classpath/java/security/Policy.java create mode 100644 libjava/classpath/java/security/Principal.java create mode 100644 libjava/classpath/java/security/PrivateKey.java create mode 100644 libjava/classpath/java/security/PrivilegedAction.java create mode 100644 libjava/classpath/java/security/PrivilegedActionException.java create mode 100644 libjava/classpath/java/security/PrivilegedExceptionAction.java create mode 100644 libjava/classpath/java/security/ProtectionDomain.java create mode 100644 libjava/classpath/java/security/Provider.java create mode 100644 libjava/classpath/java/security/ProviderException.java create mode 100644 libjava/classpath/java/security/PublicKey.java create mode 100644 libjava/classpath/java/security/SecureClassLoader.java create mode 100644 libjava/classpath/java/security/SecureRandom.java create mode 100644 libjava/classpath/java/security/SecureRandomSpi.java create mode 100644 libjava/classpath/java/security/Security.java create mode 100644 libjava/classpath/java/security/SecurityPermission.java create mode 100644 libjava/classpath/java/security/Signature.java create mode 100644 libjava/classpath/java/security/SignatureException.java create mode 100644 libjava/classpath/java/security/SignatureSpi.java create mode 100644 libjava/classpath/java/security/SignedObject.java create mode 100644 libjava/classpath/java/security/Signer.java create mode 100644 libjava/classpath/java/security/UnrecoverableKeyException.java create mode 100644 libjava/classpath/java/security/UnresolvedPermission.java create mode 100644 libjava/classpath/java/security/acl/Acl.java create mode 100644 libjava/classpath/java/security/acl/AclEntry.java create mode 100644 libjava/classpath/java/security/acl/AclNotFoundException.java create mode 100644 libjava/classpath/java/security/acl/Group.java create mode 100644 libjava/classpath/java/security/acl/LastOwnerException.java create mode 100644 libjava/classpath/java/security/acl/NotOwnerException.java create mode 100644 libjava/classpath/java/security/acl/Owner.java create mode 100644 libjava/classpath/java/security/acl/Permission.java create mode 100644 libjava/classpath/java/security/acl/package.html create mode 100644 libjava/classpath/java/security/cert/CRL.java create mode 100644 libjava/classpath/java/security/cert/CRLException.java create mode 100644 libjava/classpath/java/security/cert/CRLSelector.java create mode 100644 libjava/classpath/java/security/cert/CertPath.java create mode 100644 libjava/classpath/java/security/cert/CertPathBuilder.java create mode 100644 libjava/classpath/java/security/cert/CertPathBuilderException.java create mode 100644 libjava/classpath/java/security/cert/CertPathBuilderResult.java create mode 100644 libjava/classpath/java/security/cert/CertPathBuilderSpi.java create mode 100644 libjava/classpath/java/security/cert/CertPathParameters.java create mode 100644 libjava/classpath/java/security/cert/CertPathValidator.java create mode 100644 libjava/classpath/java/security/cert/CertPathValidatorException.java create mode 100644 libjava/classpath/java/security/cert/CertPathValidatorResult.java create mode 100644 libjava/classpath/java/security/cert/CertPathValidatorSpi.java create mode 100644 libjava/classpath/java/security/cert/CertSelector.java create mode 100644 libjava/classpath/java/security/cert/CertStore.java create mode 100644 libjava/classpath/java/security/cert/CertStoreException.java create mode 100644 libjava/classpath/java/security/cert/CertStoreParameters.java create mode 100644 libjava/classpath/java/security/cert/CertStoreSpi.java create mode 100644 libjava/classpath/java/security/cert/Certificate.java create mode 100644 libjava/classpath/java/security/cert/CertificateEncodingException.java create mode 100644 libjava/classpath/java/security/cert/CertificateException.java create mode 100644 libjava/classpath/java/security/cert/CertificateExpiredException.java create mode 100644 libjava/classpath/java/security/cert/CertificateFactory.java create mode 100644 libjava/classpath/java/security/cert/CertificateFactorySpi.java create mode 100644 libjava/classpath/java/security/cert/CertificateNotYetValidException.java create mode 100644 libjava/classpath/java/security/cert/CertificateParsingException.java create mode 100644 libjava/classpath/java/security/cert/CollectionCertStoreParameters.java create mode 100644 libjava/classpath/java/security/cert/LDAPCertStoreParameters.java create mode 100644 libjava/classpath/java/security/cert/PKIXBuilderParameters.java create mode 100644 libjava/classpath/java/security/cert/PKIXCertPathBuilderResult.java create mode 100644 libjava/classpath/java/security/cert/PKIXCertPathChecker.java create mode 100644 libjava/classpath/java/security/cert/PKIXCertPathValidatorResult.java create mode 100644 libjava/classpath/java/security/cert/PKIXParameters.java create mode 100644 libjava/classpath/java/security/cert/PolicyNode.java create mode 100644 libjava/classpath/java/security/cert/PolicyQualifierInfo.java create mode 100644 libjava/classpath/java/security/cert/TrustAnchor.java create mode 100644 libjava/classpath/java/security/cert/X509CRL.java create mode 100644 libjava/classpath/java/security/cert/X509CRLEntry.java create mode 100644 libjava/classpath/java/security/cert/X509CRLSelector.java create mode 100644 libjava/classpath/java/security/cert/X509CertSelector.java create mode 100644 libjava/classpath/java/security/cert/X509Certificate.java create mode 100644 libjava/classpath/java/security/cert/X509Extension.java create mode 100644 libjava/classpath/java/security/cert/package.html create mode 100644 libjava/classpath/java/security/interfaces/DSAKey.java create mode 100644 libjava/classpath/java/security/interfaces/DSAKeyPairGenerator.java create mode 100644 libjava/classpath/java/security/interfaces/DSAParams.java create mode 100644 libjava/classpath/java/security/interfaces/DSAPrivateKey.java create mode 100644 libjava/classpath/java/security/interfaces/DSAPublicKey.java create mode 100644 libjava/classpath/java/security/interfaces/RSAKey.java create mode 100644 libjava/classpath/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java create mode 100644 libjava/classpath/java/security/interfaces/RSAPrivateCrtKey.java create mode 100644 libjava/classpath/java/security/interfaces/RSAPrivateKey.java create mode 100644 libjava/classpath/java/security/interfaces/RSAPublicKey.java create mode 100644 libjava/classpath/java/security/interfaces/package.html create mode 100644 libjava/classpath/java/security/package.html create mode 100644 libjava/classpath/java/security/spec/AlgorithmParameterSpec.java create mode 100644 libjava/classpath/java/security/spec/DSAParameterSpec.java create mode 100644 libjava/classpath/java/security/spec/DSAPrivateKeySpec.java create mode 100644 libjava/classpath/java/security/spec/DSAPublicKeySpec.java create mode 100644 libjava/classpath/java/security/spec/EncodedKeySpec.java create mode 100644 libjava/classpath/java/security/spec/InvalidKeySpecException.java create mode 100644 libjava/classpath/java/security/spec/InvalidParameterSpecException.java create mode 100644 libjava/classpath/java/security/spec/KeySpec.java create mode 100644 libjava/classpath/java/security/spec/PKCS8EncodedKeySpec.java create mode 100644 libjava/classpath/java/security/spec/PSSParameterSpec.java create mode 100644 libjava/classpath/java/security/spec/RSAKeyGenParameterSpec.java create mode 100644 libjava/classpath/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java create mode 100644 libjava/classpath/java/security/spec/RSAOtherPrimeInfo.java create mode 100644 libjava/classpath/java/security/spec/RSAPrivateCrtKeySpec.java create mode 100644 libjava/classpath/java/security/spec/RSAPrivateKeySpec.java create mode 100644 libjava/classpath/java/security/spec/RSAPublicKeySpec.java create mode 100644 libjava/classpath/java/security/spec/X509EncodedKeySpec.java create mode 100644 libjava/classpath/java/security/spec/package.html create mode 100644 libjava/classpath/java/sql/Array.java create mode 100644 libjava/classpath/java/sql/BatchUpdateException.java create mode 100644 libjava/classpath/java/sql/Blob.java create mode 100644 libjava/classpath/java/sql/CallableStatement.java create mode 100644 libjava/classpath/java/sql/Clob.java create mode 100644 libjava/classpath/java/sql/Connection.java create mode 100644 libjava/classpath/java/sql/DataTruncation.java create mode 100644 libjava/classpath/java/sql/DatabaseMetaData.java create mode 100644 libjava/classpath/java/sql/Date.java create mode 100644 libjava/classpath/java/sql/Driver.java create mode 100644 libjava/classpath/java/sql/DriverManager.java create mode 100644 libjava/classpath/java/sql/DriverPropertyInfo.java create mode 100644 libjava/classpath/java/sql/ParameterMetaData.java create mode 100644 libjava/classpath/java/sql/PreparedStatement.java create mode 100644 libjava/classpath/java/sql/Ref.java create mode 100644 libjava/classpath/java/sql/ResultSet.java create mode 100644 libjava/classpath/java/sql/ResultSetMetaData.java create mode 100644 libjava/classpath/java/sql/SQLData.java create mode 100644 libjava/classpath/java/sql/SQLException.java create mode 100644 libjava/classpath/java/sql/SQLInput.java create mode 100644 libjava/classpath/java/sql/SQLOutput.java create mode 100644 libjava/classpath/java/sql/SQLPermission.java create mode 100644 libjava/classpath/java/sql/SQLWarning.java create mode 100644 libjava/classpath/java/sql/Savepoint.java create mode 100644 libjava/classpath/java/sql/Statement.java create mode 100644 libjava/classpath/java/sql/Struct.java create mode 100644 libjava/classpath/java/sql/Time.java create mode 100644 libjava/classpath/java/sql/Timestamp.java create mode 100644 libjava/classpath/java/sql/Types.java create mode 100644 libjava/classpath/java/sql/package.html create mode 100644 libjava/classpath/java/text/Annotation.java create mode 100644 libjava/classpath/java/text/AttributedCharacterIterator.java create mode 100644 libjava/classpath/java/text/AttributedString.java create mode 100644 libjava/classpath/java/text/AttributedStringIterator.java create mode 100644 libjava/classpath/java/text/Bidi.java create mode 100644 libjava/classpath/java/text/BreakIterator.java create mode 100644 libjava/classpath/java/text/CharacterIterator.java create mode 100644 libjava/classpath/java/text/ChoiceFormat.java create mode 100644 libjava/classpath/java/text/CollationElementIterator.java create mode 100644 libjava/classpath/java/text/CollationKey.java create mode 100644 libjava/classpath/java/text/Collator.java create mode 100644 libjava/classpath/java/text/DateFormat.java create mode 100644 libjava/classpath/java/text/DateFormatSymbols.java create mode 100644 libjava/classpath/java/text/DecimalFormat.java create mode 100644 libjava/classpath/java/text/DecimalFormatSymbols.java create mode 100644 libjava/classpath/java/text/FieldPosition.java create mode 100644 libjava/classpath/java/text/Format.java create mode 100644 libjava/classpath/java/text/MessageFormat.java create mode 100644 libjava/classpath/java/text/NumberFormat.java create mode 100644 libjava/classpath/java/text/ParseException.java create mode 100644 libjava/classpath/java/text/ParsePosition.java create mode 100644 libjava/classpath/java/text/RuleBasedCollator.java create mode 100644 libjava/classpath/java/text/SimpleDateFormat.java create mode 100644 libjava/classpath/java/text/StringCharacterIterator.java create mode 100644 libjava/classpath/java/text/package.html create mode 100644 libjava/classpath/java/text/spi/BreakIteratorProvider.java create mode 100644 libjava/classpath/java/text/spi/CollatorProvider.java create mode 100644 libjava/classpath/java/text/spi/DateFormatProvider.java create mode 100644 libjava/classpath/java/text/spi/DateFormatSymbolsProvider.java create mode 100644 libjava/classpath/java/text/spi/DecimalFormatSymbolsProvider.java create mode 100644 libjava/classpath/java/text/spi/NumberFormatProvider.java create mode 100644 libjava/classpath/java/text/spi/package.html create mode 100644 libjava/classpath/java/util/.cvsignore create mode 100644 libjava/classpath/java/util/AbstractCollection.java create mode 100644 libjava/classpath/java/util/AbstractList.java create mode 100644 libjava/classpath/java/util/AbstractMap.java create mode 100644 libjava/classpath/java/util/AbstractSequentialList.java create mode 100644 libjava/classpath/java/util/AbstractSet.java create mode 100644 libjava/classpath/java/util/ArrayList.java create mode 100644 libjava/classpath/java/util/Arrays.java create mode 100644 libjava/classpath/java/util/BitSet.java create mode 100644 libjava/classpath/java/util/Calendar.java create mode 100644 libjava/classpath/java/util/Collection.java create mode 100644 libjava/classpath/java/util/Collections.java create mode 100644 libjava/classpath/java/util/Comparator.java create mode 100644 libjava/classpath/java/util/ConcurrentModificationException.java create mode 100644 libjava/classpath/java/util/Currency.java create mode 100644 libjava/classpath/java/util/Date.java create mode 100644 libjava/classpath/java/util/Dictionary.java create mode 100644 libjava/classpath/java/util/DuplicateFormatFlagsException.java create mode 100644 libjava/classpath/java/util/EmptyStackException.java create mode 100644 libjava/classpath/java/util/EnumMap.java create mode 100644 libjava/classpath/java/util/EnumSet.java create mode 100644 libjava/classpath/java/util/Enumeration.java create mode 100644 libjava/classpath/java/util/EventListener.java create mode 100644 libjava/classpath/java/util/EventListenerProxy.java create mode 100644 libjava/classpath/java/util/EventObject.java create mode 100644 libjava/classpath/java/util/FormatFlagsConversionMismatchException.java create mode 100644 libjava/classpath/java/util/Formattable.java create mode 100644 libjava/classpath/java/util/FormattableFlags.java create mode 100644 libjava/classpath/java/util/Formatter.java create mode 100644 libjava/classpath/java/util/FormatterClosedException.java create mode 100644 libjava/classpath/java/util/GregorianCalendar.java create mode 100644 libjava/classpath/java/util/HashMap.java create mode 100644 libjava/classpath/java/util/HashSet.java create mode 100644 libjava/classpath/java/util/Hashtable.java create mode 100644 libjava/classpath/java/util/IdentityHashMap.java create mode 100644 libjava/classpath/java/util/IllegalFormatCodePointException.java create mode 100644 libjava/classpath/java/util/IllegalFormatConversionException.java create mode 100644 libjava/classpath/java/util/IllegalFormatException.java create mode 100644 libjava/classpath/java/util/IllegalFormatFlagsException.java create mode 100644 libjava/classpath/java/util/IllegalFormatPrecisionException.java create mode 100644 libjava/classpath/java/util/IllegalFormatWidthException.java create mode 100644 libjava/classpath/java/util/InputMismatchException.java create mode 100644 libjava/classpath/java/util/InvalidPropertiesFormatException.java create mode 100644 libjava/classpath/java/util/Iterator.java create mode 100644 libjava/classpath/java/util/LinkedHashMap.java create mode 100644 libjava/classpath/java/util/LinkedHashSet.java create mode 100644 libjava/classpath/java/util/LinkedList.java create mode 100644 libjava/classpath/java/util/List.java create mode 100644 libjava/classpath/java/util/ListIterator.java create mode 100644 libjava/classpath/java/util/ListResourceBundle.java create mode 100644 libjava/classpath/java/util/Locale.java create mode 100644 libjava/classpath/java/util/Map.java create mode 100644 libjava/classpath/java/util/MissingFormatArgumentException.java create mode 100644 libjava/classpath/java/util/MissingFormatWidthException.java create mode 100644 libjava/classpath/java/util/MissingResourceException.java create mode 100644 libjava/classpath/java/util/NoSuchElementException.java create mode 100644 libjava/classpath/java/util/Observable.java create mode 100644 libjava/classpath/java/util/Observer.java create mode 100644 libjava/classpath/java/util/PriorityQueue.java create mode 100644 libjava/classpath/java/util/Properties.java create mode 100644 libjava/classpath/java/util/PropertyPermission.java create mode 100644 libjava/classpath/java/util/PropertyPermissionCollection.java create mode 100644 libjava/classpath/java/util/PropertyResourceBundle.java create mode 100644 libjava/classpath/java/util/Random.java create mode 100644 libjava/classpath/java/util/RandomAccess.java create mode 100644 libjava/classpath/java/util/ResourceBundle.java create mode 100644 libjava/classpath/java/util/Scanner.java create mode 100644 libjava/classpath/java/util/ServiceConfigurationError.java create mode 100644 libjava/classpath/java/util/ServiceLoader.java create mode 100644 libjava/classpath/java/util/Set.java create mode 100644 libjava/classpath/java/util/SimpleTimeZone.java create mode 100644 libjava/classpath/java/util/SortedMap.java create mode 100644 libjava/classpath/java/util/SortedSet.java create mode 100644 libjava/classpath/java/util/Stack.java create mode 100644 libjava/classpath/java/util/StringTokenizer.java create mode 100644 libjava/classpath/java/util/TimeZone.java create mode 100644 libjava/classpath/java/util/Timer.java create mode 100644 libjava/classpath/java/util/TimerTask.java create mode 100644 libjava/classpath/java/util/TooManyListenersException.java create mode 100644 libjava/classpath/java/util/TreeMap.java create mode 100644 libjava/classpath/java/util/TreeSet.java create mode 100644 libjava/classpath/java/util/UUID.java create mode 100644 libjava/classpath/java/util/UnknownFormatConversionException.java create mode 100644 libjava/classpath/java/util/UnknownFormatFlagsException.java create mode 100644 libjava/classpath/java/util/Vector.java create mode 100644 libjava/classpath/java/util/WeakHashMap.java create mode 100644 libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java create mode 100644 libjava/classpath/java/util/jar/Attributes.java create mode 100644 libjava/classpath/java/util/jar/JarEntry.java create mode 100644 libjava/classpath/java/util/jar/JarException.java create mode 100644 libjava/classpath/java/util/jar/JarFile.java create mode 100644 libjava/classpath/java/util/jar/JarInputStream.java create mode 100644 libjava/classpath/java/util/jar/JarOutputStream.java create mode 100644 libjava/classpath/java/util/jar/Manifest.java create mode 100644 libjava/classpath/java/util/jar/package.html create mode 100644 libjava/classpath/java/util/logging/ConsoleHandler.java create mode 100644 libjava/classpath/java/util/logging/ErrorManager.java create mode 100644 libjava/classpath/java/util/logging/FileHandler.java create mode 100644 libjava/classpath/java/util/logging/Filter.java create mode 100644 libjava/classpath/java/util/logging/Formatter.java create mode 100644 libjava/classpath/java/util/logging/Handler.java create mode 100644 libjava/classpath/java/util/logging/Level.java create mode 100644 libjava/classpath/java/util/logging/LogManager.java create mode 100644 libjava/classpath/java/util/logging/LogRecord.java create mode 100644 libjava/classpath/java/util/logging/Logger.java create mode 100644 libjava/classpath/java/util/logging/LoggingMXBean.java create mode 100644 libjava/classpath/java/util/logging/LoggingPermission.java create mode 100644 libjava/classpath/java/util/logging/MemoryHandler.java create mode 100644 libjava/classpath/java/util/logging/SimpleFormatter.java create mode 100644 libjava/classpath/java/util/logging/SocketHandler.java create mode 100644 libjava/classpath/java/util/logging/StreamHandler.java create mode 100644 libjava/classpath/java/util/logging/XMLFormatter.java create mode 100644 libjava/classpath/java/util/logging/package.html create mode 100644 libjava/classpath/java/util/package.html create mode 100644 libjava/classpath/java/util/prefs/AbstractPreferences.java create mode 100644 libjava/classpath/java/util/prefs/BackingStoreException.java create mode 100644 libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java create mode 100644 libjava/classpath/java/util/prefs/NodeChangeEvent.java create mode 100644 libjava/classpath/java/util/prefs/NodeChangeListener.java create mode 100644 libjava/classpath/java/util/prefs/PreferenceChangeEvent.java create mode 100644 libjava/classpath/java/util/prefs/PreferenceChangeListener.java create mode 100644 libjava/classpath/java/util/prefs/Preferences.java create mode 100644 libjava/classpath/java/util/prefs/PreferencesFactory.java create mode 100644 libjava/classpath/java/util/prefs/package.html create mode 100644 libjava/classpath/java/util/regex/MatchResult.java create mode 100644 libjava/classpath/java/util/regex/Matcher.java create mode 100644 libjava/classpath/java/util/regex/Pattern.java create mode 100644 libjava/classpath/java/util/regex/PatternSyntaxException.java create mode 100644 libjava/classpath/java/util/regex/package.html create mode 100644 libjava/classpath/java/util/spi/CurrencyNameProvider.java create mode 100644 libjava/classpath/java/util/spi/LocaleNameProvider.java create mode 100644 libjava/classpath/java/util/spi/LocaleServiceProvider.java create mode 100644 libjava/classpath/java/util/spi/TimeZoneNameProvider.java create mode 100644 libjava/classpath/java/util/spi/package.html create mode 100644 libjava/classpath/java/util/zip/Adler32.java create mode 100644 libjava/classpath/java/util/zip/CRC32.java create mode 100644 libjava/classpath/java/util/zip/CheckedInputStream.java create mode 100644 libjava/classpath/java/util/zip/CheckedOutputStream.java create mode 100644 libjava/classpath/java/util/zip/Checksum.java create mode 100644 libjava/classpath/java/util/zip/DataFormatException.java create mode 100644 libjava/classpath/java/util/zip/Deflater.java create mode 100644 libjava/classpath/java/util/zip/DeflaterConstants.java create mode 100644 libjava/classpath/java/util/zip/DeflaterEngine.java create mode 100644 libjava/classpath/java/util/zip/DeflaterHuffman.java create mode 100644 libjava/classpath/java/util/zip/DeflaterOutputStream.java create mode 100644 libjava/classpath/java/util/zip/DeflaterPending.java create mode 100644 libjava/classpath/java/util/zip/GZIPInputStream.java create mode 100644 libjava/classpath/java/util/zip/GZIPOutputStream.java create mode 100644 libjava/classpath/java/util/zip/Inflater.java create mode 100644 libjava/classpath/java/util/zip/InflaterDynHeader.java create mode 100644 libjava/classpath/java/util/zip/InflaterHuffmanTree.java create mode 100644 libjava/classpath/java/util/zip/InflaterInputStream.java create mode 100644 libjava/classpath/java/util/zip/OutputWindow.java create mode 100644 libjava/classpath/java/util/zip/PendingBuffer.java create mode 100644 libjava/classpath/java/util/zip/StreamManipulator.java create mode 100644 libjava/classpath/java/util/zip/ZipConstants.java create mode 100644 libjava/classpath/java/util/zip/ZipEntry.java create mode 100644 libjava/classpath/java/util/zip/ZipException.java create mode 100644 libjava/classpath/java/util/zip/ZipFile.java create mode 100644 libjava/classpath/java/util/zip/ZipInputStream.java create mode 100644 libjava/classpath/java/util/zip/ZipOutputStream.java create mode 100644 libjava/classpath/java/util/zip/package.html (limited to 'libjava/classpath/java') diff --git a/libjava/classpath/java/applet/Applet.java b/libjava/classpath/java/applet/Applet.java new file mode 100644 index 000000000..2e4d46a84 --- /dev/null +++ b/libjava/classpath/java/applet/Applet.java @@ -0,0 +1,592 @@ +/* Applet.java -- Java base applet class + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.applet; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Panel; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Locale; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; + +/** + * This is the base applet class. An applet is a Java program that + * runs inside a web browser or other applet viewer in a restricted + * environment. + * + *

To be useful, a subclass should override at least start(). Also useful + * are init, stop, and destroy for control purposes, and getAppletInfo and + * getParameterInfo for descriptive purposes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Applet extends Panel +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5836846270535785031L; + + /** The applet stub for this applet. */ + private transient AppletStub stub; + + /** Some applets call setSize in their constructors. In that case, + these fields are used to store width and height values until a + stub is set. */ + private transient int width; + private transient int height; + + /** + * The accessibility context for this applet. + * + * @serial the accessibleContext for this + * @since 1.2 + */ + private AccessibleContext accessibleContext; + + /** + * Default constructor for subclasses. + * + * @throws HeadlessException if in a headless environment + */ + public Applet() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } + + /** + * The browser calls this method to set the applet's stub, which is the + * low level interface to the browser. Manually setting this to null is + * asking for problems down the road. + * + * @param stub the applet stub for this applet + */ + public final void setStub(AppletStub stub) + { + this.stub = stub; + + if (width != 0 && height != 0) + stub.appletResize (width, height); + } + + /** + * Tests whether or not this applet is currently active. An applet is active + * just before the browser invokes start(), and becomes inactive just + * before the browser invokes stop(). + * + * @return true if this applet is active + */ + public boolean isActive() + { + return stub.isActive(); + } + + /** + * Returns the basename URL of the document this applet is embedded in. This + * is everything up to the final '/'. + * + * @return the URL of the document this applet is embedded in + * @see #getCodeBase() + */ + public URL getDocumentBase() + { + return stub.getDocumentBase(); + } + + /** + * Returns the URL of the code base for this applet. + * + * @return the URL of the code base for this applet + */ + public URL getCodeBase() + { + return stub.getCodeBase(); + } + + /** + * Returns the value of the specified parameter that was specified in + * the <APPLET> tag for this applet. + * + * @param name the parameter name + * @return the parameter value, or null if the parameter does not exist + * @throws NullPointerException if name is null + */ + public String getParameter(String name) + { + return stub.getParameter(name); + } + + /** + * Returns the applet context for this applet. + * + * @return the applet context for this applet + */ + public AppletContext getAppletContext() + { + return stub.getAppletContext(); + } + + /** + * Requests that the applet window for this applet be resized. + * + * @param width the new width in pixels + * @param height the new height in pixels + */ + public void resize(int width, int height) + { + if (stub == null) + { + this.width = width; + this.height = height; + } + else + stub.appletResize(width, height); + } + + /** + * Requests that the applet window for this applet be resized. + * + * @param dim the requested dimensions + * @throws NullPointerException if dim is null + */ + public void resize(Dimension dim) + { + resize(dim.width, dim.height); + } + + /** + * Displays the specified message in the status window if that window + * exists. + * + * @param message the status message, may be null + */ + public void showStatus(String message) + { + getAppletContext().showStatus(message); + } + + /** + * Returns an image from the specified URL. Note that the image is not + * actually retrieved until the applet attempts to display it, so this + * method returns immediately. + * + * @param url the URL of the image + * @return the retrieved image + * @throws NullPointerException if url is null + */ + public Image getImage(URL url) + { + return getAppletContext().getImage(url); + } + + /** + * Returns an image from the specified absolute URL, and relative path + * from that URL. Note that the image is not actually retrieved until the + * applet attempts to display it, so this method returns immediately. + * This calls getImage(new URL(url, name)), but if building + * the new URL fails, this returns null. + * + * @param url the base URL of the image + * @param name the name of the image relative to the URL + * @return the retrieved image, or null on failure + * @see #getImage(URL) + */ + public Image getImage(URL url, String name) + { + try + { + return getImage(new URL(url, name)); + } + catch (MalformedURLException e) + { + return null; + } + } + + /** + * Returns an audio clip from the specified URL. This clip is not tied to + * any particular applet. + * + * @param url the URL of the audio clip + * @return the retrieved audio clip + * @throws NullPointerException if url is null + * @see #getAudioClip(URL) + * @since 1.2 + */ + public static final AudioClip newAudioClip(URL url) + { + return new URLAudioClip(url); + } + + /** + * Returns an audio clip from the specified URL. Note that the clip is not + * actually retrieved until the applet attempts to play it, so this method + * returns immediately. + * + * @param url the URL of the audio clip + * @return the retrieved audio clip + * @throws NullPointerException if url is null + */ + public AudioClip getAudioClip(URL url) + { + return getAppletContext().getAudioClip(url); + } + + /** + * Returns an audio clip from the specified absolute URL, and relative path + * from that URL. Note that the clip is not actually retrieved until the + * applet attempts to play it, so this method returns immediately. This + * calls getAudioClip(new URL(url, name)), but if building + * the new URL fails, this returns null. + * + * @param url the base URL of the audio clip + * @param name the name of the clip relative to the URL + * @return the retrieved audio clip, or null on failure + * @see #getAudioClip(URL) + */ + public AudioClip getAudioClip(URL url, String name) + { + try + { + return getAudioClip(new URL(url, name)); + } + catch (MalformedURLException e) + { + return null; + } + } + + /** + * Returns a descriptive string with applet defined information. The + * implementation in this class returns null, so subclasses + * must override to return information. + * + * @return a string describing the author, version, and applet copyright + */ + public String getAppletInfo() + { + return null; + } + + /** + * Returns the locale for this applet, if it has been set. If no applet + * specific locale has been set, the default locale is returned. + * + * @return the locale for this applet + * @see Component#setLocale(Locale) + * @since 1.1 + */ + public Locale getLocale() + { + return super.getLocale(); + } + + /** + * Returns a list of parameters this applet supports. Each element of + * the outer array is an array of three strings with the name of the + * parameter, the data type or valid values, and a description. This + * method is optional and the default implementation returns null. + * + * @return the list of parameters supported by this applet + */ + public String[][] getParameterInfo() + { + return null; + } + + /** + * Loads and plays the audio clip pointed to by the specified URL. This does + * nothing if the URL does not point to a valid audio clip. + * + * @param url the URL of the audio clip + * @throws NullPointerException if url is null + * @see #getAudioClip(URL) + */ + public void play(URL url) + { + AudioClip ac = getAudioClip(url); + try + { + ac.play(); + } + catch (Exception ignored) + { + } + } + + /** + * Loads and plays the audio clip pointed to by the specified absolute URL, + * and relative path from that URL. This does nothing if the URL cannot be + * constructed, or if it does not point to a valid audio clip. + * + * @param url the base URL of the audio clip + * @param name the name of the audio clip relative to the URL + * @see #getAudioClip(URL, String) + * @see #play(URL) + */ + public void play(URL url, String name) + { + try + { + getAudioClip(url, name).play(); + } + catch (Exception ignored) + { + } + } + + /** + * This method is called when the applet is first loaded, before start(). + * The default implementation does nothing; override to do any one-time + * initialization. + * + * @see #start() + * @see #stop() + * @see #destroy() + */ + public void init() + { + } + + /** + * This method is called when the applet should start running. This is + * normally each time a web page containing it is loaded. The default + * implemention does nothing; override for your applet to be useful. + * + * @see #init() + * @see #stop() + * @see #destroy() + */ + public void start() + { + } + + /** + * This method is called when the applet should stop running. This is + * normally when the next web page is loaded. The default implementation + * does nothing; override for your applet to stop using resources when + * it is no longer visible, but may be restarted soon. + * + * @see #init() + * @see #start() + * @see #destroy() + */ + public void stop() + { + } + + /** + * This method is called when the applet is being unloaded. The default + * implementation does nothing; override for your applet to clean up + * resources on exit. + * + * @see #init() + * @see #start() + * @see #stop() + */ + public void destroy() + { + } + + /** + * Gets the AccessibleContext associated with this applet, creating one if + * necessary. This always returns an instance of {@link AccessibleApplet}. + * + * @return the accessibility context of this applet + * @since 1.3 + */ + public AccessibleContext getAccessibleContext() + { + if (accessibleContext == null) + accessibleContext = new AccessibleApplet(); + return accessibleContext; + } + + /** + * Read an applet from an object stream. This checks for a headless + * environment, then does the normal read. + * + * @param s the stream to read from + * @throws ClassNotFoundException if a class is not found + * @throws IOException if deserialization fails + * @throws HeadlessException if this is a headless environment + * @see GraphicsEnvironment#isHeadless() + * @since 1.4 + */ + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + s.defaultReadObject(); + } + + /** + * This class provides accessibility support for Applets, and is the + * runtime type returned by {@link #getAccessibleContext()}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + */ + protected class AccessibleApplet extends AccessibleAWTPanel + { + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 8127374778187708896L; + + /** + * The default constructor. + */ + protected AccessibleApplet() + { + } + + /** + * Get the role of this accessible object, a frame. + * + * @return the role of the object + * @see AccessibleRole#FRAME + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.FRAME; + } + + /** + * Get the state set of this accessible object. In addition to the default + * states of a Component, the applet can also be active. + * + * @return the role of the object + * @see AccessibleState + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet s = super.getAccessibleStateSet(); + if (isActive()) + s.add(AccessibleState.ACTIVE); + return s; + } + } // class AccessibleApplet + + private static class URLAudioClip implements AudioClip + { + // The URL we will try to play. + // This is null if we have already tried to create an + // audio input stream from this URL. + private URL url; + + // The real audio clip. This is null before the URL is read, + // and might be null afterward if we were unable to read the URL + // for some reason. + private Clip clip; + + public URLAudioClip(URL url) + { + this.url = url; + } + + private synchronized Clip getClip() + { + if (url == null) + return clip; + try + { + clip = AudioSystem.getClip(); + clip.open(AudioSystem.getAudioInputStream(url)); + } + catch (LineUnavailableException _) + { + // Ignore. + } + catch (IOException _) + { + // Ignore. + } + catch (UnsupportedAudioFileException _) + { + // Ignore. + } + url = null; + return clip; + } + + public void loop() + { + Clip myclip = getClip(); + if (myclip != null) + myclip.loop(Clip.LOOP_CONTINUOUSLY); + } + + public void play() + { + Clip myclip = getClip(); + if (myclip != null) + myclip.start(); + } + + public void stop() + { + Clip myclip = getClip(); + if (myclip != null) + { + myclip.stop(); + myclip.setFramePosition(0); + } + } + } +} // class Applet diff --git a/libjava/classpath/java/applet/AppletContext.java b/libjava/classpath/java/applet/AppletContext.java new file mode 100644 index 000000000..5b6ce4524 --- /dev/null +++ b/libjava/classpath/java/applet/AppletContext.java @@ -0,0 +1,154 @@ +/* AppletContext.java -- access the applet's runtime environment + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.applet; + +import java.awt.Image; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.Iterator; + +/** + * This interface allows an applet access to the browser to retrieve + * additional data files and display documents. It also allows the + * applet to find out other applets in the same document. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.5 + */ +public interface AppletContext +{ + /** + * Returns an audio clip from the specified URL. + * + * @param url the URL of the audio clip + * @return the retrieved audio clip + * @throws NullPointerException if url is null + */ + AudioClip getAudioClip(URL url); + + /** + * Returns an image from the specified URL. Note that the image is not + * actually retrieved until the applet attempts to display it, so this + * method returns immediately. + * + * @param url the absolute URL of the image + * @return the retrieved image + * @throws NullPointerException if url is null + */ + Image getImage(URL url); + + /** + * Returns the applet in the document for this object that has the + * specified name. + * + * @param name the applet name + * @return the requested applet, or null if not found + */ + Applet getApplet(String name); + + /** + * Returns a list of all the applets in the document for this object. + * + * @return a list of all the applets + */ + Enumeration getApplets(); + + /** + * Displays the web page pointed to by the specified URL in the window + * for this object. This page replaces the document that is currently + * there. + * + * @param url the URL of the web page to load; unspecified on an error + */ + void showDocument(URL url); + + /** + * Displays the web page pointed to be the sepcified URL in the window + * with the specified name. The standard names "_top", "_blank", + * "_parent", and "_self" are allowed. An applet viewer may disregard + * this request. + * + * @param url the URL of the web page to load + * @param target the target window + */ + void showDocument(URL url, String target); + + /** + * Displays the specified message in the status window if that window + * exists. + * + * @param message the status message, may be null + */ + void showStatus(String message); + + /** + * Associate a stream to a key for this applet context, possibly replacing + * the old value. Stream associations are local to the applet context, for + * security purposes. + * + * @param key the key to associate with + * @param stream the stream value to tie to the key, or null to remove + * @throws IOException if the stream is too large + * @since 1.4 + */ + void setStream(String key, InputStream stream) throws IOException; + + /** + * Return the stream associated with a given key in this applet context, or + * null if nothing is associated. Stream associations are local to the + * applet context, for security purposes. + * + * @param key the key to look up + * @return the associated stream, or null + * @since 1.4 + */ + InputStream getStream(String key); + + /** + * Iterate over all keys that have associated streams. Stream associated + * are local to the applet context, for security purposes. + * + * @return an iterator over the association keys + * @since 1.4 + */ + Iterator getStreamKeys(); +} // interface AppletContext diff --git a/libjava/classpath/java/applet/AppletStub.java b/libjava/classpath/java/applet/AppletStub.java new file mode 100644 index 000000000..879a01638 --- /dev/null +++ b/libjava/classpath/java/applet/AppletStub.java @@ -0,0 +1,103 @@ +/* AppletStub.java -- low level interface to the browser + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.applet; + +import java.net.URL; + +/** + * This interface is the low level interface between the applet and the + * browser. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Applet#setStub(AppletStub) + * @since 1.0 + * @status updated to 1.4 + */ +public interface AppletStub +{ + /** + * Tests whether or not this applet is currently active. An applet is active + * just before the browser invokes start(), and becomes inactive just + * before the browser invokes stop(). + * + * @return true if this applet is active + */ + boolean isActive(); + + /** + * Returns the basename URL of the document this applet is embedded in. This + * is everything up to the final '/'. + * + * @return the URL of the document this applet is embedded in + * @see #getCodeBase() + */ + URL getDocumentBase(); + + /** + * Returns the URL of the code base for this applet. + * + * @return the URL of the code base for this applet + */ + URL getCodeBase(); + + /** + * Returns the value of the specified parameter that was specified in + * the <APPLET> tag for this applet. + * + * @param name the parameter name + * @return the parameter value, or null if the parameter does not exist + * @throws NullPointerException if name is null + */ + String getParameter(String name); + + /** + * Returns the applet context for this applet. + * + * @return the applet context for this applet + */ + AppletContext getAppletContext(); + + /** + * Requests that the applet window for this applet be resized. + * + * @param width the new width in pixels + * @param height the new height in pixels + */ + void appletResize(int width, int height); +} // interface AppletStub diff --git a/libjava/classpath/java/applet/AudioClip.java b/libjava/classpath/java/applet/AudioClip.java new file mode 100644 index 000000000..eeafa8aa7 --- /dev/null +++ b/libjava/classpath/java/applet/AudioClip.java @@ -0,0 +1,68 @@ +/* AudioClip.java -- play an audio clip in an applet + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.applet; + + +/** + * This interface provides a simple mechanism for playing audio clips. + * If multiple clips are played at once, the browser combines them into a + * composite clip. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface AudioClip +{ + /** + * Plays the audio clip starting from the beginning. + */ + void play(); + + /** + * Stops playing this audio clip. There is no mechanism for restarting + * at the point where the clip is stopped. + */ + void stop(); + + /** + * Plays this audio clip in a continuous loop. + */ + void loop(); +} // interface AudioClip diff --git a/libjava/classpath/java/applet/package.html b/libjava/classpath/java/applet/package.html new file mode 100644 index 000000000..f2cbc5726 --- /dev/null +++ b/libjava/classpath/java/applet/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.applet + + +

Classes and interfaces for small embeddable applications +often used in web pages.

+ + + diff --git a/libjava/classpath/java/awt/AWTError.java b/libjava/classpath/java/awt/AWTError.java new file mode 100644 index 000000000..80356eee4 --- /dev/null +++ b/libjava/classpath/java/awt/AWTError.java @@ -0,0 +1,64 @@ +/* AWTError.java -- A serious AWT error occurred. + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This error is thrown when a critical Abstract Window Toolkit (AWT) error + * occurs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class AWTError extends Error +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -1819846354050686206L; + + /** + * Create a new instance with the specified descriptive error message. + * + * @param message the descriptive error message + */ + public AWTError(String message) + { + super(message); + } +} // class AWTError diff --git a/libjava/classpath/java/awt/AWTEvent.java b/libjava/classpath/java/awt/AWTEvent.java new file mode 100644 index 000000000..c4f772827 --- /dev/null +++ b/libjava/classpath/java/awt/AWTEvent.java @@ -0,0 +1,399 @@ + +/* AWTEvent.java -- the root event in AWT + Copyright (C) 1999, 2000, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.AdjustmentEvent; +import java.awt.event.ComponentEvent; +import java.awt.event.ContainerEvent; +import java.awt.event.FocusEvent; +import java.awt.event.InputMethodEvent; +import java.awt.event.InvocationEvent; +import java.awt.event.ItemEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.PaintEvent; +import java.awt.event.TextEvent; +import java.awt.event.WindowEvent; +import java.util.EventObject; + +/** + * AWTEvent is the root event class for all AWT events in the JDK 1.1 event + * model. It supersedes the Event class from JDK 1.0. Subclasses outside of + * the java.awt package should have IDs greater than RESERVED_ID_MAX. + * + *

Event masks defined here are used by components in + * enableEvents to select event types not selected by registered + * listeners. Event masks are appropriately set when registering on + * components. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class AWTEvent extends EventObject +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1825314779160409405L; + + /** + * The ID of the event. + * + * @see #getID() + * @see #AWTEvent(Object, int) + * @serial the identifier number of this event + */ + protected int id; + + /** + * Indicates if the event has been consumed. False mean it is passed to + * the peer, true means it has already been processed. Semantic events + * generated by low-level events always have the value true. + * + * @see #consume() + * @see #isConsumed() + * @serial whether the event has been consumed + */ + protected boolean consumed; + + /** + * Used for implementing a simple linked list in EventQueue. + */ + transient AWTEvent queueNext; + + /** + * Who knows? It's in the serial version. + * + * @serial No idea what this is for. + */ + byte[] bdata; + + /** + * Indicates if this event is dispatched by the KeyboardFocusManager. + */ + boolean isFocusManagerEvent = false; + + /** Mask for selecting component events. */ + public static final long COMPONENT_EVENT_MASK = 0x00001; + + /** Mask for selecting container events. */ + public static final long CONTAINER_EVENT_MASK = 0x00002; + + /** Mask for selecting component focus events. */ + public static final long FOCUS_EVENT_MASK = 0x00004; + + /** Mask for selecting keyboard events. */ + public static final long KEY_EVENT_MASK = 0x00008; + + /** Mask for mouse button events. */ + public static final long MOUSE_EVENT_MASK = 0x00010; + + /** Mask for mouse motion events. */ + public static final long MOUSE_MOTION_EVENT_MASK = 0x00020; + + /** Mask for window events. */ + public static final long WINDOW_EVENT_MASK = 0x00040; + + /** Mask for action events. */ + public static final long ACTION_EVENT_MASK = 0x00080; + + /** Mask for adjustment events. */ + public static final long ADJUSTMENT_EVENT_MASK = 0x00100; + + /** Mask for item events. */ + public static final long ITEM_EVENT_MASK = 0x00200; + + /** Mask for text events. */ + public static final long TEXT_EVENT_MASK = 0x00400; + + /** + * Mask for input method events. + * @since 1.3 + */ + public static final long INPUT_METHOD_EVENT_MASK = 0x00800; + + /** + * Mask if input methods are enabled. Package visible only. + */ + static final long INPUT_ENABLED_EVENT_MASK = 0x01000; + + /** + * Mask for paint events. + * @since 1.3 + */ + public static final long PAINT_EVENT_MASK = 0x02000; + + /** + * Mask for invocation events. + * @since 1.3 + */ + public static final long INVOCATION_EVENT_MASK = 0x04000; + + /** + * Mask for hierarchy events. + * @since 1.3 + */ + public static final long HIERARCHY_EVENT_MASK = 0x08000; + + /** + * Mask for hierarchy bounds events. + * @since 1.3 + */ + public static final long HIERARCHY_BOUNDS_EVENT_MASK = 0x10000; + + /** + * Mask for mouse wheel events. + * @since 1.4 + */ + public static final long MOUSE_WHEEL_EVENT_MASK = 0x20000; + + /** + * Mask for window state events. + * @since 1.4 + */ + public static final long WINDOW_STATE_EVENT_MASK = 0x40000; + + /** + * Mask for window focus events. + * @since 1.4 + */ + public static final long WINDOW_FOCUS_EVENT_MASK = 0x80000; + + /** + * This is the highest number for event ids that are reserved for use by + * the AWT system itself. Subclasses outside of java.awt should use higher + * ids. + */ + public static final int RESERVED_ID_MAX = 1999; + + + /** + * Initializes a new AWTEvent from the old Java 1.0 event object. + * + * @param event the old-style event + * @throws NullPointerException if event is null + */ + public AWTEvent(Event event) + { + this(event.target, event.id); + consumed = event.consumed; + } + + /** + * Create an event on the specified source object and id. + * + * @param source the object that caused the event + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public AWTEvent(Object source, int id) + { + super(source); + this.id = id; + } + + /** + * Retarget the event, such as converting a heavyweight component to a + * lightweight child of the original. This is not for general use, but + * is for event targeting systems like KeyboardFocusManager. + * + * @param source the new source + */ + public void setSource(Object source) + { + this.source = source; + } + + /** + * Returns the event type id. + * + * @return the id number of this event + */ + public int getID() + { + return id; + } + + /** + * Create a string that represents this event in the format + * classname[eventstring] on sourcecomponentname. + * + * @return a string representing this event + */ + public String toString () + { + String src; + if (source instanceof Component) + src = ((Component) source).getName(); + else if (source instanceof MenuComponent) + src = ((MenuComponent) source).getName(); + else if (source != null) + src = source.toString(); + else + src = "null"; + String string = getClass ().getName () + "[" + paramString () + "] on " + + src; + return string; + } + + /** + * Returns a string representation of the state of this event. It may be + * empty, but must not be null; it is implementation defined. + * + * @return a string representation of this event + */ + public String paramString() + { + return ""; + } + + /** + * Consumes this event so that it will not be processed in the default + * manner. + */ + protected void consume() + { + consumed = true; + } + + /** + * Tests whether not not this event has been consumed. A consumed event + * is not processed in the default manner. + * + * @return true if this event has been consumed + */ + protected boolean isConsumed() + { + return consumed; + } + + /** + * Converts an event id to the appropriate event mask. + * + * @param id the event id + * + * @return the event mask for the specified id + */ + static long eventIdToMask(int id) + { + long mask = 0; + switch (id) + { + case ActionEvent.ACTION_PERFORMED: + mask = ACTION_EVENT_MASK; + break; + case AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED: + mask = ADJUSTMENT_EVENT_MASK; + break; + case ComponentEvent.COMPONENT_MOVED: + case ComponentEvent.COMPONENT_RESIZED: + case ComponentEvent.COMPONENT_SHOWN: + case ComponentEvent.COMPONENT_HIDDEN: + mask = COMPONENT_EVENT_MASK; + break; + case ContainerEvent.COMPONENT_ADDED: + case ContainerEvent.COMPONENT_REMOVED: + mask = CONTAINER_EVENT_MASK; + break; + case FocusEvent.FOCUS_GAINED: + case FocusEvent.FOCUS_LOST: + mask = FOCUS_EVENT_MASK; + break; + case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED: + case InputMethodEvent.CARET_POSITION_CHANGED: + mask = INPUT_METHOD_EVENT_MASK; + break; + case InvocationEvent.INVOCATION_DEFAULT: + mask = INVOCATION_EVENT_MASK; + break; + case ItemEvent.ITEM_STATE_CHANGED: + mask = ITEM_EVENT_MASK; + break; + case KeyEvent.KEY_TYPED: + case KeyEvent.KEY_PRESSED: + case KeyEvent.KEY_RELEASED: + mask = KEY_EVENT_MASK; + break; + case MouseEvent.MOUSE_CLICKED: + case MouseEvent.MOUSE_PRESSED: + case MouseEvent.MOUSE_RELEASED: + mask = MOUSE_EVENT_MASK; + break; + case MouseEvent.MOUSE_MOVED: + case MouseEvent.MOUSE_ENTERED: + case MouseEvent.MOUSE_EXITED: + case MouseEvent.MOUSE_DRAGGED: + mask = MOUSE_MOTION_EVENT_MASK; + break; + case MouseEvent.MOUSE_WHEEL: + mask = MOUSE_WHEEL_EVENT_MASK; + break; + case PaintEvent.PAINT: + case PaintEvent.UPDATE: + mask = PAINT_EVENT_MASK; + break; + case TextEvent.TEXT_VALUE_CHANGED: + mask = TEXT_EVENT_MASK; + break; + case WindowEvent.WINDOW_OPENED: + case WindowEvent.WINDOW_CLOSING: + case WindowEvent.WINDOW_CLOSED: + case WindowEvent.WINDOW_ICONIFIED: + case WindowEvent.WINDOW_DEICONIFIED: + case WindowEvent.WINDOW_ACTIVATED: + case WindowEvent.WINDOW_DEACTIVATED: + mask = WINDOW_EVENT_MASK; + break; + case WindowEvent.WINDOW_GAINED_FOCUS: + case WindowEvent.WINDOW_LOST_FOCUS: + mask = WINDOW_FOCUS_EVENT_MASK; + break; + case WindowEvent.WINDOW_STATE_CHANGED: + mask = WINDOW_STATE_EVENT_MASK; + break; + default: + mask = 0; + } + return mask; + } +} // class AWTEvent diff --git a/libjava/classpath/java/awt/AWTEventMulticaster.java b/libjava/classpath/java/awt/AWTEventMulticaster.java new file mode 100644 index 000000000..722dc752d --- /dev/null +++ b/libjava/classpath/java/awt/AWTEventMulticaster.java @@ -0,0 +1,1210 @@ +/* AWTEventMulticaster.java -- allows multicast chaining of listeners + Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; +import java.awt.event.WindowListener; +import java.awt.event.WindowStateListener; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.EventListener; + +/** + * This class is used to implement a chain of event handlers. Dispatching + * using this class is thread safe. Here is a quick example of how to + * add and delete listeners using this class. For this example, we will + * assume are firing AdjustmentEvent's. However, this + * same approach is useful for all events in the java.awt.event + * package, and more if this class is subclassed. + * + *

+ * AdjustmentListener al; + * public void addAdjustmentListener(AdjustmentListener listener) + * { + * al = AWTEventMulticaster.add(al, listener); + * } + * public void removeAdjustmentListener(AdjustmentListener listener) + * { + * al = AWTEventMulticaster.remove(al, listener); + * } + * + * + *

When it come time to process an event, simply call al, + * assuming it is not null, and all listeners in the chain will + * be fired. + * + *

The first time add is called it is passed + * null and listener as its arguments. This + * starts building the chain. This class returns listener + * which becomes the new al. The next time, add + * is called with al and listener and the + * new listener is then chained to the old. + * + * @author Bryce McKinlay + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class AWTEventMulticaster + implements ComponentListener, ContainerListener, FocusListener, KeyListener, + MouseListener, MouseMotionListener, WindowListener, + WindowFocusListener, WindowStateListener, ActionListener, + ItemListener, AdjustmentListener, TextListener, + InputMethodListener, HierarchyListener, HierarchyBoundsListener, + MouseWheelListener +{ + /** + * A variable in the event chain. + */ + protected final EventListener a; + + /** + * A variable in the event chain. + */ + protected final EventListener b; + + /** + * Initializes a new instance of AWTEventMulticaster with + * the specified event listener parameters. The parameters should not be + * null, although it is not required to enforce this with a + * NullPointerException. + * + * @param a the "a" listener object + * @param b the "b" listener object + */ + protected AWTEventMulticaster(EventListener a, EventListener b) + { + this.a = a; + this.b = b; + } + + /** + * Removes one instance of the specified listener from this multicaster + * chain. This descends recursively if either child is a multicaster, and + * returns a multicaster chain with the old listener removed. + * + * @param oldl the object to remove from this multicaster + * @return the resulting multicaster with the specified listener removed + */ + protected EventListener remove(EventListener oldl) + { + // If oldl is an immediate child, return the other child. + if (a == oldl) + return b; + if (b == oldl) + return a; + // If a and/or b are Multicaster's, search them recursively. + if (a instanceof AWTEventMulticaster) + { + EventListener newa = ((AWTEventMulticaster) a).remove(oldl); + if (newa != a) + return new AWTEventMulticaster(newa, b); + } + if (b instanceof AWTEventMulticaster) + { + EventListener newb = ((AWTEventMulticaster) b).remove(oldl); + if (newb != b) + return new AWTEventMulticaster(a, newb); + } + // oldl was not found. + return this; + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentResized(ComponentEvent e) + { + ((ComponentListener) a).componentResized(e); + ((ComponentListener) b).componentResized(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentMoved(ComponentEvent e) + { + ((ComponentListener) a).componentMoved(e); + ((ComponentListener) b).componentMoved(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentShown(ComponentEvent e) + { + ((ComponentListener) a).componentShown(e); + ((ComponentListener) b).componentShown(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentHidden(ComponentEvent e) + { + ((ComponentListener) a).componentHidden(e); + ((ComponentListener) b).componentHidden(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentAdded(ContainerEvent e) + { + ((ContainerListener) a).componentAdded(e); + ((ContainerListener) b).componentAdded(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void componentRemoved(ContainerEvent e) + { + ((ContainerListener) a).componentRemoved(e); + ((ContainerListener) b).componentRemoved(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void focusGained(FocusEvent e) + { + ((FocusListener) a).focusGained(e); + ((FocusListener) b).focusGained(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void focusLost(FocusEvent e) + { + ((FocusListener) a).focusLost(e); + ((FocusListener) b).focusLost(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void keyTyped(KeyEvent e) + { + ((KeyListener) a).keyTyped(e); + ((KeyListener) b).keyTyped(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void keyPressed(KeyEvent e) + { + ((KeyListener) a).keyPressed(e); + ((KeyListener) b).keyPressed(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void keyReleased(KeyEvent e) + { + ((KeyListener) a).keyReleased(e); + ((KeyListener) b).keyReleased(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseClicked(MouseEvent e) + { + ((MouseListener) a).mouseClicked(e); + ((MouseListener) b).mouseClicked(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mousePressed(MouseEvent e) + { + ((MouseListener) a).mousePressed(e); + ((MouseListener) b).mousePressed(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseReleased(MouseEvent e) + { + ((MouseListener) a).mouseReleased(e); + ((MouseListener) b).mouseReleased(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseEntered(MouseEvent e) + { + ((MouseListener) a).mouseEntered(e); + ((MouseListener) b).mouseEntered(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseExited(MouseEvent e) + { + ((MouseListener) a).mouseExited(e); + ((MouseListener) b).mouseExited(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseDragged(MouseEvent e) + { + ((MouseMotionListener) a).mouseDragged(e); + ((MouseMotionListener) b).mouseDragged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void mouseMoved(MouseEvent e) + { + ((MouseMotionListener) a).mouseMoved(e); + ((MouseMotionListener) b).mouseMoved(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowOpened(WindowEvent e) + { + ((WindowListener) a).windowOpened(e); + ((WindowListener) b).windowOpened(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowClosing(WindowEvent e) + { + ((WindowListener) a).windowClosing(e); + ((WindowListener) b).windowClosing(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowClosed(WindowEvent e) + { + ((WindowListener) a).windowClosed(e); + ((WindowListener) b).windowClosed(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowIconified(WindowEvent e) + { + ((WindowListener) a).windowIconified(e); + ((WindowListener) b).windowIconified(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowDeiconified(WindowEvent e) + { + ((WindowListener) a).windowDeiconified(e); + ((WindowListener) b).windowDeiconified(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowActivated(WindowEvent e) + { + ((WindowListener) a).windowActivated(e); + ((WindowListener) b).windowActivated(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void windowDeactivated(WindowEvent e) + { + ((WindowListener) a).windowDeactivated(e); + ((WindowListener) b).windowDeactivated(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.4 + */ + public void windowStateChanged(WindowEvent e) + { + ((WindowStateListener) a).windowStateChanged(e); + ((WindowStateListener) b).windowStateChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.4 + */ + public void windowGainedFocus(WindowEvent e) + { + ((WindowFocusListener) a).windowGainedFocus(e); + ((WindowFocusListener) b).windowGainedFocus(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.4 + */ + public void windowLostFocus(WindowEvent e) + { + ((WindowFocusListener) a).windowLostFocus(e); + ((WindowFocusListener) b).windowLostFocus(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void actionPerformed(ActionEvent e) + { + ((ActionListener) a).actionPerformed(e); + ((ActionListener) b).actionPerformed(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void itemStateChanged(ItemEvent e) + { + ((ItemListener) a).itemStateChanged(e); + ((ItemListener) b).itemStateChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void adjustmentValueChanged(AdjustmentEvent e) + { + ((AdjustmentListener) a).adjustmentValueChanged(e); + ((AdjustmentListener) b).adjustmentValueChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + */ + public void textValueChanged(TextEvent e) + { + ((TextListener) a).textValueChanged(e); + ((TextListener) b).textValueChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.2 + */ + public void inputMethodTextChanged(InputMethodEvent e) + { + ((InputMethodListener) a).inputMethodTextChanged(e); + ((InputMethodListener) b).inputMethodTextChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.2 + */ + public void caretPositionChanged(InputMethodEvent e) + { + ((InputMethodListener) a).caretPositionChanged(e); + ((InputMethodListener) b).caretPositionChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.3 + */ + public void hierarchyChanged(HierarchyEvent e) + { + ((HierarchyListener) a).hierarchyChanged(e); + ((HierarchyListener) b).hierarchyChanged(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.3 + */ + public void ancestorMoved(HierarchyEvent e) + { + ((HierarchyBoundsListener) a).ancestorMoved(e); + ((HierarchyBoundsListener) b).ancestorMoved(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.3 + */ + public void ancestorResized(HierarchyEvent e) + { + ((HierarchyBoundsListener) a).ancestorResized(e); + ((HierarchyBoundsListener) b).ancestorResized(e); + } + + /** + * Handles this event by dispatching it to the "a" and "b" listener + * instances. + * + * @param e the event to handle + * @since 1.4 + */ + public void mouseWheelMoved(MouseWheelEvent e) + { + ((MouseWheelListener) a).mouseWheelMoved(e); + ((MouseWheelListener) b).mouseWheelMoved(e); + } + + /** + * Chain ComponentListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static ComponentListener add(ComponentListener a, ComponentListener b) + { + return (ComponentListener) addInternal(a, b); + } + + /** + * Chain ContainerListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static ContainerListener add(ContainerListener a, ContainerListener b) + { + return (ContainerListener) addInternal(a, b); + } + + /** + * Chain FocusListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static FocusListener add(FocusListener a, FocusListener b) + { + return (FocusListener) addInternal(a, b); + } + + /** + * Chain KeyListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static KeyListener add(KeyListener a, KeyListener b) + { + return (KeyListener) addInternal(a, b); + } + + /** + * Chain MouseListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static MouseListener add(MouseListener a, MouseListener b) + { + return (MouseListener) addInternal(a, b); + } + + /** + * Chain MouseMotionListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static MouseMotionListener add(MouseMotionListener a, + MouseMotionListener b) + { + return (MouseMotionListener) addInternal(a, b); + } + + /** + * Chain WindowListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static WindowListener add(WindowListener a, WindowListener b) + { + return (WindowListener) addInternal(a, b); + } + + /** + * Chain WindowStateListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.4 + */ + public static WindowStateListener add(WindowStateListener a, + WindowStateListener b) + { + return (WindowStateListener) addInternal(a, b); + } + + /** + * Chain WindowFocusListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.4 + */ + public static WindowFocusListener add(WindowFocusListener a, + WindowFocusListener b) + { + return (WindowFocusListener) addInternal(a, b); + } + + /** + * Chain ActionListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static ActionListener add(ActionListener a, ActionListener b) + { + return (ActionListener) addInternal(a, b); + } + + /** + * Chain ItemListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static ItemListener add(ItemListener a, ItemListener b) + { + return (ItemListener) addInternal(a, b); + } + + /** + * Chain AdjustmentListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static AdjustmentListener add(AdjustmentListener a, + AdjustmentListener b) + { + return (AdjustmentListener) addInternal(a, b); + } + + /** + * Chain AdjustmentListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + public static TextListener add(TextListener a, TextListener b) + { + return (TextListener) addInternal(a, b); + } + + /** + * Chain InputMethodListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.2 + */ + public static InputMethodListener add(InputMethodListener a, + InputMethodListener b) + { + return (InputMethodListener) addInternal(a, b); + } + + /** + * Chain HierarchyListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.3 + */ + public static HierarchyListener add(HierarchyListener a, HierarchyListener b) + { + return (HierarchyListener) addInternal(a, b); + } + + /** + * Chain HierarchyBoundsListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.3 + */ + public static HierarchyBoundsListener add(HierarchyBoundsListener a, + HierarchyBoundsListener b) + { + return (HierarchyBoundsListener) addInternal(a, b); + } + + /** + * Chain MouseWheelListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + * @since 1.4 + */ + public static MouseWheelListener add(MouseWheelListener a, + MouseWheelListener b) + { + return (MouseWheelListener) addInternal(a, b); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static ComponentListener remove(ComponentListener l, + ComponentListener oldl) + { + return (ComponentListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static ContainerListener remove(ContainerListener l, + ContainerListener oldl) + { + return (ContainerListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static FocusListener remove(FocusListener l, FocusListener oldl) + { + return (FocusListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static KeyListener remove(KeyListener l, KeyListener oldl) + { + return (KeyListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static MouseListener remove(MouseListener l, MouseListener oldl) + { + return (MouseListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static MouseMotionListener remove(MouseMotionListener l, + MouseMotionListener oldl) + { + return (MouseMotionListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static WindowListener remove(WindowListener l, WindowListener oldl) + { + return (WindowListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.4 + */ + public static WindowStateListener remove(WindowStateListener l, + WindowStateListener oldl) + { + return (WindowStateListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.4 + */ + public static WindowFocusListener remove(WindowFocusListener l, + WindowFocusListener oldl) + { + return (WindowFocusListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static ActionListener remove(ActionListener l, ActionListener oldl) + { + return (ActionListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static ItemListener remove(ItemListener l, ItemListener oldl) + { + return (ItemListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static AdjustmentListener remove(AdjustmentListener l, + AdjustmentListener oldl) + { + return (AdjustmentListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + public static TextListener remove(TextListener l, TextListener oldl) + { + return (TextListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.2 + */ + public static InputMethodListener remove(InputMethodListener l, + InputMethodListener oldl) + { + return (InputMethodListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.3 + */ + public static HierarchyListener remove(HierarchyListener l, + HierarchyListener oldl) + { + return (HierarchyListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.3 + */ + public static HierarchyBoundsListener remove(HierarchyBoundsListener l, + HierarchyBoundsListener oldl) + { + return (HierarchyBoundsListener) removeInternal(l, oldl); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + * @since 1.4 + */ + public static MouseWheelListener remove(MouseWheelListener l, + MouseWheelListener oldl) + { + return (MouseWheelListener) removeInternal(l, oldl); + } + + /** + * Chain EventListener a and b. + * + * @param a the "a" listener, may be null + * @param b the "b" listener, may be null + * @return latest entry in the chain + */ + protected static EventListener addInternal(EventListener a, EventListener b) + { + if (a == null) + return b; + if (b == null) + return a; + return new AWTEventMulticaster(a, b); + } + + /** + * Removes the listener oldl from the listener l. + * + * @param l the listener chain to reduce + * @param oldl the listener to remove + * @return the resulting listener chain + */ + protected static EventListener removeInternal(EventListener l, + EventListener oldl) + { + if (l == oldl) + return null; + if (l instanceof AWTEventMulticaster) + return ((AWTEventMulticaster) l).remove(oldl); + return l; + } + + /** + * Saves all Serializable listeners to a serialization stream. + * + * @param s the stream to save to + * @param k a prefix stream put before each serializable listener + * @throws IOException if serialization fails + */ + protected void saveInternal(ObjectOutputStream s, String k) + throws IOException + { + // This is not documented by Sun, but I think it is correct. + if (a instanceof AWTEventMulticaster) + ((AWTEventMulticaster) a).saveInternal(s, k); + else if (a instanceof Serializable) + { + s.writeObject(k); + s.writeObject(a); + } + if (b instanceof AWTEventMulticaster) + ((AWTEventMulticaster) b).saveInternal(s, k); + else if (b instanceof Serializable) + { + s.writeObject(k); + s.writeObject(b); + } + } + + /** + * Saves a Serializable listener chain to a serialization stream. + * + * @param s the stream to save to + * @param k a prefix stream put before each serializable listener + * @param l the listener chain to save + * @throws IOException if serialization fails + */ + protected static void save(ObjectOutputStream s, String k, EventListener l) + throws IOException + { + // This is not documented by Sun, but I think it is correct. + if (l instanceof AWTEventMulticaster) + ((AWTEventMulticaster) l).saveInternal(s, k); + else if (l instanceof Serializable) + { + s.writeObject(k); + s.writeObject(l); + } + } + + /** + * Returns an array of all chained listeners of the specified type in the + * given chain. A null listener returns an empty array, and a listener + * which is not an AWTEventMulticaster returns an array of one element. If + * no listeners in the chain are of the specified type, an empty array is + * returned. + * + * @param l the listener chain to convert to an array + * @param type the type of listeners to collect + * @return an array of the listeners of that type in the chain + * @throws ClassCastException if type is not assignable from EventListener + * @throws NullPointerException if type is null + * @throws IllegalArgumentException if type is Void.TYPE + * @since 1.4 + */ + public static T[] getListeners(EventListener l, + Class type) + { + ArrayList list = new ArrayList(); + if (l instanceof AWTEventMulticaster) + ((AWTEventMulticaster) l).getListeners(list, type); + else if (type.isInstance(l)) + list.add(l); + EventListener[] r = (EventListener[]) Array.newInstance(type, list.size()); + list.toArray(r); + return (T[]) r; + } + + /** + * Collects all instances of the given type in the chain into the list. + * + * @param l the list to collect into + * @param type the type of listeners to collect + * @throws NullPointerException if type is null + * @see #getListeners(EventListener, Class) + */ + private void getListeners(ArrayList l, Class type) + { + if (a instanceof AWTEventMulticaster) + ((AWTEventMulticaster) a).getListeners(l, type); + else if (type.isInstance(a)) + l.add(a); + if (b instanceof AWTEventMulticaster) + ((AWTEventMulticaster) b).getListeners(l, type); + else if (type.isInstance(b)) + l.add(b); + } +} // class AWTEventMulticaster diff --git a/libjava/classpath/java/awt/AWTException.java b/libjava/classpath/java/awt/AWTException.java new file mode 100644 index 000000000..2df3dd801 --- /dev/null +++ b/libjava/classpath/java/awt/AWTException.java @@ -0,0 +1,64 @@ +/* AWTException.java -- Generic AWT exception + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This is a generic exception that indicates an exception occurred in the + * Abstract Window Toolkit (AWT) system. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class AWTException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -1900414231151323879L; + + /** + * Create a new instance with the specified detailed error message. + * + * @param message the detailed error message + */ + public AWTException(String message) + { + super(message); + } +} // class AWTException diff --git a/libjava/classpath/java/awt/AWTKeyStroke.java b/libjava/classpath/java/awt/AWTKeyStroke.java new file mode 100644 index 000000000..bdccbf0d7 --- /dev/null +++ b/libjava/classpath/java/awt/AWTKeyStroke.java @@ -0,0 +1,676 @@ +/* AWTKeyStroke.java -- an immutable key stroke + Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * This class mirrors KeyEvents, representing both low-level key presses and + * key releases, and high level key typed inputs. However, this class forms + * immutable strokes, and can be efficiently reused via the factory methods + * for creating them. + * + *

For backwards compatibility with Swing, this supports a way to build + * instances of a subclass, using reflection, provided the subclass has a + * no-arg constructor (of any accessibility). + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see #getAWTKeyStroke(char) + * @since 1.4 + * @status updated to 1.4 + */ +public class AWTKeyStroke implements Serializable +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -6430539691155161871L; + + /** The mask for modifiers. */ + private static final int MODIFIERS_MASK = 0x3fef; + + /** + * The cache of recently created keystrokes. This maps KeyStrokes to + * KeyStrokes in a cache which removes the least recently accessed entry, + * under the assumption that garbage collection of a new keystroke is + * easy when we find the old one that it matches in the cache. + */ + private static final LinkedHashMap cache = + new LinkedHashMap(11, 0.75f, true) + { + /** The largest the keystroke cache can grow. */ + private static final int MAX_CACHE_SIZE = 2048; + + /** Prune stale entries. */ + protected boolean removeEldestEntry(Map.Entry + eldest) + { + return size() > MAX_CACHE_SIZE; + } + }; + + /** The most recently generated keystroke, or null. */ + private static AWTKeyStroke recent; + + /** + * The no-arg constructor of a subclass, or null to use AWTKeyStroke. Note + * that this will be left accessible, to get around private access; but + * it should not be a security risk as it is highly unlikely that creating + * protected instances of the subclass via reflection will do much damage. + */ + private static Constructor ctor; + + /** + * A table of keyCode names to values. This is package-private to + * avoid an accessor method. + * + * @see #getAWTKeyStroke(String) + */ + static final HashMap vktable = new HashMap(); + static + { + // Using reflection saves the hassle of keeping this in sync with KeyEvent, + // at the price of an expensive initialization. + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + Field[] fields = KeyEvent.class.getFields(); + int i = fields.length; + try + { + while (--i >= 0) + { + Field f = fields[i]; + String name = f.getName(); + if (name.startsWith("VK_")) + vktable.put(name.substring(3), f.get(null)); + } + } + catch (Exception e) + { + throw (Error) new InternalError().initCause(e); + } + return null; + } + }); + } + + /** + * The typed character, or CHAR_UNDEFINED for key presses and releases. + * + * @serial the keyChar + */ + private char keyChar; + + /** + * The virtual key code, or VK_UNDEFINED for key typed. Package visible for + * use by Component. + * + * @serial the keyCode + */ + int keyCode; + + /** + * The modifiers in effect. To match Sun, this stores the old style masks + * for shift, control, alt, meta, and alt-graph (but not button1); as well + * as the new style of extended modifiers for all modifiers. + * + * @serial bitwise or of the *_DOWN_MASK modifiers + */ + private int modifiers; + + /** + * True if this is a key release; should only be true if keyChar is + * CHAR_UNDEFINED. + * + * @serial true to distinguish key pressed from key released + */ + private boolean onKeyRelease; + + /** + * Construct a keystroke with default values: it will be interpreted as a + * key typed event with an invalid character and no modifiers. Client code + * should use the factory methods instead. + * + * @see #getAWTKeyStroke(char) + * @see #getAWTKeyStroke(Character, int) + * @see #getAWTKeyStroke(int, int, boolean) + * @see #getAWTKeyStroke(int, int) + * @see #getAWTKeyStrokeForEvent(KeyEvent) + * @see #getAWTKeyStroke(String) + */ + protected AWTKeyStroke() + { + keyChar = KeyEvent.CHAR_UNDEFINED; + } + + /** + * Construct a keystroke with the given values. Client code should use the + * factory methods instead. + * + * @param keyChar the character entered, if this is a key typed + * @param keyCode the key pressed or released, or VK_UNDEFINED for key typed + * @param modifiers the modifier keys for the keystroke, in old or new style + * @param onKeyRelease true if this is a key release instead of a press + * @see #getAWTKeyStroke(char) + * @see #getAWTKeyStroke(Character, int) + * @see #getAWTKeyStroke(int, int, boolean) + * @see #getAWTKeyStroke(int, int) + * @see #getAWTKeyStrokeForEvent(KeyEvent) + * @see #getAWTKeyStroke(String) + */ + protected AWTKeyStroke(char keyChar, int keyCode, int modifiers, + boolean onKeyRelease) + { + this.keyChar = keyChar; + this.keyCode = keyCode; + // No need to call extend(), as only trusted code calls this constructor. + this.modifiers = modifiers; + this.onKeyRelease = onKeyRelease; + } + + /** + * Registers a new subclass as being the type of keystrokes to generate in + * the factory methods. This operation flushes the cache of stored keystrokes + * if the class differs from the current one. The new class must be + * AWTKeyStroke or a subclass, and must have a no-arg constructor (which may + * be private). + * + * @param subclass the new runtime type of generated keystrokes + * @throws IllegalArgumentException subclass doesn't have no-arg constructor + * @throws ClassCastException subclass doesn't extend AWTKeyStroke + */ + protected static void registerSubclass(final Class subclass) + { + if (subclass == null) + throw new IllegalArgumentException(); + if (subclass.equals(ctor == null ? AWTKeyStroke.class + : ctor.getDeclaringClass())) + return; + if (subclass.equals(AWTKeyStroke.class)) + { + cache.clear(); + recent = null; + ctor = null; + return; + } + try + { + ctor = (Constructor) AccessController.doPrivileged + (new PrivilegedExceptionAction() + { + public Object run() + throws NoSuchMethodException, InstantiationException, + IllegalAccessException, InvocationTargetException + { + Constructor c = + subclass.getDeclaredConstructor((Class[])null); + c.setAccessible(true); + // Create a new instance, to make sure that we can, and + // to cause any ClassCastException. + AWTKeyStroke dummy = (AWTKeyStroke) c.newInstance(); + return c; + } + }); + } + catch (PrivilegedActionException e) + { + // e.getCause() will not ever be ClassCastException; that should + // escape on its own. + throw (RuntimeException) + new IllegalArgumentException().initCause(e.getCause()); + } + cache.clear(); + recent = null; + } + + /** + * Returns a keystroke representing a typed character. + * + * @param keyChar the typed character + * @return the specified keystroke + */ + public static AWTKeyStroke getAWTKeyStroke(char keyChar) + { + return getAWTKeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, false); + } + + /** + * Returns a keystroke representing a typed character with the given + * modifiers. Note that keyChar is a Character instead of a + * char to avoid accidental ambiguity with + * getAWTKeyStroke(int, int). The modifiers are the bitwise + * or of the masks found in {@link InputEvent}; the new style (*_DOWN_MASK) + * is preferred, but the old style will work. + * + * @param keyChar the typed character + * @param modifiers the modifiers, or 0 + * @return the specified keystroke + * @throws IllegalArgumentException if keyChar is null + */ + public static AWTKeyStroke getAWTKeyStroke(Character keyChar, int modifiers) + { + if (keyChar == null) + throw new IllegalArgumentException(); + return getAWTKeyStroke(keyChar.charValue(), KeyEvent.VK_UNDEFINED, + extend(modifiers), false); + } + + /** + * Returns a keystroke representing a pressed or released key event, with + * the given modifiers. The "virtual key" should be one of the VK_* + * constants in {@link KeyEvent}. The modifiers are the bitwise or of the + * masks found in {@link InputEvent}; the new style (*_DOWN_MASK) is + * preferred, but the old style will work. + * + * @param keyCode the virtual key + * @param modifiers the modifiers, or 0 + * @param release true if this is a key release instead of a key press + * @return the specified keystroke + */ + public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers, + boolean release) + { + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode, + extend(modifiers), release); + } + + /** + * Returns a keystroke representing a pressed key event, with the given + * modifiers. The "virtual key" should be one of the VK_* constants in + * {@link KeyEvent}. The modifiers are the bitwise or of the masks found + * in {@link InputEvent}; the new style (*_DOWN_MASK) is preferred, but the + * old style will work. + * + * @param keyCode the virtual key + * @param modifiers the modifiers, or 0 + * @return the specified keystroke + */ + public static AWTKeyStroke getAWTKeyStroke(int keyCode, int modifiers) + { + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, keyCode, + extend(modifiers), false); + } + + /** + * Returns a keystroke representing what caused the key event. + * + * @param event the key event to convert + * @return the specified keystroke, or null if the event is invalid + * @throws NullPointerException if event is null + */ + public static AWTKeyStroke getAWTKeyStrokeForEvent(KeyEvent event) + { + switch (event.id) + { + case KeyEvent.KEY_TYPED: + return getAWTKeyStroke(event.getKeyChar(), KeyEvent.VK_UNDEFINED, + extend(event.getModifiersEx()), false); + case KeyEvent.KEY_PRESSED: + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(), + extend(event.getModifiersEx()), false); + case KeyEvent.KEY_RELEASED: + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, event.getKeyCode(), + extend(event.getModifiersEx()), true); + default: + return null; + } + } + + /** + * Parses a string and returns the keystroke that it represents. The syntax + * for keystrokes is listed below, with tokens separated by an arbitrary + * number of spaces: + *

+   * keyStroke := <modifiers>* ( <typedID> | <codeID> )
+   * modifiers := ( shift | control | ctrl | meta | alt
+   *                | button1 | button2 | button3 )
+   * typedID := typed <single Unicode character>
+   * codeID := ( pressed | released )? <name>
+   * name := <the KeyEvent field name less the leading "VK_">
+   * 
+ * + *

Note that the grammar is rather weak, and not all valid keystrokes + * can be generated in this manner (for example, a typed space, or anything + * with the alt-graph modifier!). The output of AWTKeyStroke.toString() + * will not meet the grammar. If pressed or released is not specified, + * pressed is assumed. Examples:
+ * + * "INSERT" => getAWTKeyStroke(KeyEvent.VK_INSERT, 0);
+ * "control DELETE" => + * getAWTKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);
+ * "alt shift X" => getAWTKeyStroke(KeyEvent.VK_X, + * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);
+ * "alt shift released X" => getAWTKeyStroke(KeyEvent.VK_X, + * InputEvent.ALT_MASK | InputEvent.SHIFT_MASK, true);
+ * "typed a" => getAWTKeyStroke('a'); + *
+ * + * @param s the string to parse + * @throws IllegalArgumentException if s is null or cannot be parsed + * @return the specified keystroke + */ + public static AWTKeyStroke getAWTKeyStroke(String s) + { + if (s == null) + throw new IllegalArgumentException("null argument"); + StringTokenizer t = new StringTokenizer(s, " "); + if (! t.hasMoreTokens()) + throw new IllegalArgumentException("no tokens '" + s + "'"); + int modifiers = 0; + boolean released = false; + String token = null; + do + { + token = t.nextToken(); + if ("shift".equals(token)) + { + modifiers |= KeyEvent.SHIFT_MASK; + modifiers |= KeyEvent.SHIFT_DOWN_MASK; + } + else if ("ctrl".equals(token) || "control".equals(token)) + { + modifiers |= KeyEvent.CTRL_MASK; + modifiers |= KeyEvent.CTRL_DOWN_MASK; + } + else if ("meta".equals(token)) + { + modifiers |= KeyEvent.META_MASK; + modifiers |= KeyEvent.META_DOWN_MASK; + } + else if ("alt".equals(token)) + { + modifiers |= KeyEvent.ALT_MASK; + modifiers |= KeyEvent.ALT_DOWN_MASK; + } + else if ("button1".equals(token)) + modifiers |= KeyEvent.BUTTON1_DOWN_MASK; + else if ("button2".equals(token)) + modifiers |= KeyEvent.BUTTON2_DOWN_MASK; + else if ("button3".equals(token)) + modifiers |= KeyEvent.BUTTON3_DOWN_MASK; + else if ("typed".equals(token)) + { + if (t.hasMoreTokens()) + { + token = t.nextToken(); + if (! t.hasMoreTokens() && token.length() == 1) + return getAWTKeyStroke(token.charAt(0), + KeyEvent.VK_UNDEFINED, modifiers, + false); + } + throw new IllegalArgumentException("Invalid 'typed' argument '" + + s + "'"); + } + else if ("pressed".equals(token)) + { + if (t.hasMoreTokens()) + token = t.nextToken(); + break; + } + else if ("released".equals(token)) + { + released = true; + if (t.hasMoreTokens()) + token = t.nextToken(); + break; + } + else + break; + } + while (t.hasMoreTokens()); + // Now token contains the VK name we must parse. + Integer code = (Integer) vktable.get(token); + if (code == null) + throw new IllegalArgumentException("Unknown token '" + token + + "' in '" + s + "'"); + if (t.hasMoreTokens()) + throw new IllegalArgumentException("Too many tokens: " + s); + return getAWTKeyStroke(KeyEvent.CHAR_UNDEFINED, code.intValue(), + modifiers, released); + } + + /** + * Returns the character of this keystroke, if it was typed. + * + * @return the character value, or CHAR_UNDEFINED + * @see #getAWTKeyStroke(char) + */ + public final char getKeyChar() + { + return keyChar; + } + + /** + * Returns the virtual key code of this keystroke, if it was pressed or + * released. This will be a VK_* constant from KeyEvent. + * + * @return the virtual key code value, or VK_UNDEFINED + * @see #getAWTKeyStroke(int, int) + */ + public final int getKeyCode() + { + return keyCode; + } + + /** + * Returns the modifiers for this keystroke. This will be a bitwise or of + * constants from InputEvent; it includes the old style masks for shift, + * control, alt, meta, and alt-graph (but not button1); as well as the new + * style of extended modifiers for all modifiers. + * + * @return the modifiers + * @see #getAWTKeyStroke(Character, int) + * @see #getAWTKeyStroke(int, int) + */ + public final int getModifiers() + { + return modifiers; + } + + /** + * Tests if this keystroke is a key release. + * + * @return true if this is a key release + * @see #getAWTKeyStroke(int, int, boolean) + */ + public final boolean isOnKeyRelease() + { + return onKeyRelease; + } + + /** + * Returns the AWT event type of this keystroke. This is one of + * {@link KeyEvent#KEY_TYPED}, {@link KeyEvent#KEY_PRESSED}, or + * {@link KeyEvent#KEY_RELEASED}. + * + * @return the key event type + */ + public final int getKeyEventType() + { + return keyCode == KeyEvent.VK_UNDEFINED ? KeyEvent.KEY_TYPED + : onKeyRelease ? KeyEvent.KEY_RELEASED : KeyEvent.KEY_PRESSED; + } + + /** + * Returns a hashcode for this key event. It is not documented, but appears + * to be: (getKeyChar() + 1) * (getKeyCode() + 1) + * * (getModifiers() + 1) * 2 + (isOnKeyRelease() ? 1 : 2). + * + * @return the hashcode + */ + public int hashCode() + { + return (keyChar + 1) * (keyCode + 1) * (modifiers + 1) * 2 + + (onKeyRelease ? 1 : 2); + } + + /** + * Tests two keystrokes for equality. + * + * @param o the object to test + * @return true if it is equal + */ + public final boolean equals(Object o) + { + if (! (o instanceof AWTKeyStroke)) + return false; + AWTKeyStroke s = (AWTKeyStroke) o; + return this == o || (keyChar == s.keyChar && keyCode == s.keyCode + && modifiers == s.modifiers + && onKeyRelease == s.onKeyRelease); + } + + /** + * Returns a string representation of this keystroke. For typed keystrokes, + * this is "keyChar " + KeyEvent.getKeyModifiersText(getModifiers()) + + getKeyChar(); for pressed and released keystrokes, this is + * "keyCode " + KeyEvent.getKeyModifiersText(getModifiers()) + * + KeyEvent.getKeyText(getKeyCode()) + * + (isOnKeyRelease() ? "-R" : "-P"). + * + * @return a string representation + */ + public String toString() + { + if (keyCode == KeyEvent.VK_UNDEFINED) + return "keyChar " + KeyEvent.getKeyModifiersText(modifiers) + keyChar; + return "keyCode " + KeyEvent.getKeyModifiersText(modifiers) + + KeyEvent.getKeyText(keyCode) + (onKeyRelease ? "-R" : "-P"); + } + + /** + * Returns a cached version of the deserialized keystroke, if available. + * + * @return a cached replacement + * @throws ObjectStreamException if something goes wrong + */ + protected Object readResolve() throws ObjectStreamException + { + AWTKeyStroke s = cache.get(this); + if (s != null) + return s; + cache.put(this, this); + return this; + } + + /** + * Gets the appropriate keystroke, creating one if necessary. + * + * @param keyChar the keyChar + * @param keyCode the keyCode + * @param modifiers the modifiers + * @param release true for key release + * @return the specified keystroke + */ + private static AWTKeyStroke getAWTKeyStroke(char keyChar, int keyCode, + int modifiers, boolean release) + { + // Check level 0 cache. + AWTKeyStroke stroke = recent; // Avoid thread races. + if (stroke != null && stroke.keyChar == keyChar + && stroke.keyCode == keyCode && stroke.modifiers == modifiers + && stroke.onKeyRelease == release) + return stroke; + // Create a new object, on the assumption that if it has a match in the + // cache, the VM can easily garbage collect it as it is temporary. + Constructor c = ctor; // Avoid thread races. + if (c == null) + stroke = new AWTKeyStroke(keyChar, keyCode, modifiers, release); + else + try + { + stroke = (AWTKeyStroke) c.newInstance(); + stroke.keyChar = keyChar; + stroke.keyCode = keyCode; + stroke.modifiers = modifiers; + stroke.onKeyRelease = release; + } + catch (Exception e) + { + throw (Error) new InternalError().initCause(e); + } + // Check level 1 cache. + AWTKeyStroke cached = cache.get(stroke); + if (cached == null) + cache.put(stroke, stroke); + else + stroke = cached; + return recent = stroke; + } + + /** + * Converts the modifiers to the appropriate format. + * + * @param mod the modifiers to convert + * @return the adjusted modifiers + */ + private static int extend(int mod) + { + if ((mod & (KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK)) != 0) + mod |= KeyEvent.SHIFT_MASK | KeyEvent.SHIFT_DOWN_MASK; + if ((mod & (KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK)) != 0) + mod |= KeyEvent.CTRL_MASK | KeyEvent.CTRL_DOWN_MASK; + if ((mod & (KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) != 0) + mod |= KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK; + if ((mod & (KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK)) != 0) + mod |= KeyEvent.ALT_MASK | KeyEvent.ALT_DOWN_MASK; + if ((mod & (KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK)) != 0) + mod |= KeyEvent.ALT_GRAPH_MASK | KeyEvent.ALT_GRAPH_DOWN_MASK; + if ((mod & KeyEvent.BUTTON1_MASK) != 0) + mod |= KeyEvent.BUTTON1_DOWN_MASK; + return mod & MODIFIERS_MASK; + } +} // class AWTKeyStroke diff --git a/libjava/classpath/java/awt/AWTPermission.java b/libjava/classpath/java/awt/AWTPermission.java new file mode 100644 index 000000000..3e50c059f --- /dev/null +++ b/libjava/classpath/java/awt/AWTPermission.java @@ -0,0 +1,121 @@ +/* AWTPermission.java -- AWT related permissions + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.security.BasicPermission; + +/** + * This class implements permissions for AWT. This is a named + * permission. No actions are defined. + * + *

The following table provides a list of all the possible AWTPermission + * permission names with a description of what that permission allows.
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Permission NamePermission AllowsRisks + *
accessClipboardposting and reading the AWT clipboardthe clipboard may contain sensitive data
accessEventQueueaccess to the AWT event queuemalicious code could remove real events and replace them with bogus + * ones, including simulating the user granting permission
listenToAllAWTEventslisten to system-wide AWT eventsmalicious code can read passwords entered in an AWT event, and in + * combination with accessEventQueue, could fake system events
showWindowWithoutWarningBannerdisplay a window without a banner notification of insecuritymalicious code could install a Trojan horse applet that looks like + * a normal window, and thus steal data like passwords
readDisplayPixelsread back pixels from the display screenmalicious code could snoop on the user's actions
createRobotcreate an instance of java.awt.Robotthese objects can generate events as though they were the user; so + * malicious code could control the system
fullScreenExclusiveenter full-screen exclusive modemalicious code could masquerade as a trusted program
+ * + * @author Tom Tromey (tromey@redhat.com) + * @since 1.2 + * @status updated to 1.4 + */ +public final class AWTPermission extends BasicPermission +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 8890392402588814465L; + + /** + * Construct a AWTPermission with the given name. + * + * @param name the permission name + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name is invalid + */ + public AWTPermission(String name) + { + super(name); + } + + /** + * Create a new permission with the specified name. The actions argument + * is ignored, as AWT permissions have no actions. + * + * @param name the permission name + * @param actions ignored + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name is invalid + */ + public AWTPermission(String name, String actions) + { + super(name); + } +} // class AWTPermission diff --git a/libjava/classpath/java/awt/ActiveEvent.java b/libjava/classpath/java/awt/ActiveEvent.java new file mode 100644 index 000000000..e42959fe3 --- /dev/null +++ b/libjava/classpath/java/awt/ActiveEvent.java @@ -0,0 +1,61 @@ +/* ActiveEvent.java -- a self-dispatching event + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * An interface for events which can dispatch themselves in another thread. + * This has two uses: first, if your code is in a critical section, calling a + * synchronized method might deadlock. But by using an ActiveEvent to call + * the second section, it will not obtain the lock until you have left the + * critical section, avoiding deadlock. The second use is for calling + * untrusted code. For example, system code should use an ActiveEvent to + * invoke user code securely. + * + * @author Tom Tromey (tromey@cygnus.com) + * @since 1.2 + * @status updated to 1.4 + */ +public interface ActiveEvent +{ + /** + * Dispatch the event, according to what the event needs done. Invoked + * automatically if this is placed on the EventDispatchQueue. + */ + void dispatch(); +} // interface ActiveEvent diff --git a/libjava/classpath/java/awt/Adjustable.java b/libjava/classpath/java/awt/Adjustable.java new file mode 100644 index 000000000..8f633e91a --- /dev/null +++ b/libjava/classpath/java/awt/Adjustable.java @@ -0,0 +1,171 @@ +/* Adjustable.java -- Objects with a numeric adjustment scale + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.AdjustmentListener; + +/** + * This interface is for objects that take a numeric value that can be + * adjusted within a bounded range. For example, a scroll bar. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface Adjustable +{ + /** Constant for an adjustable object with horizontal orientation. */ + int HORIZONTAL = 0; + + /** Constant for an adjustable object with vertical orientation. */ + int VERTICAL = 1; + + /** Constant for an adjustable object with no orientation. */ + int NO_ORIENTATION = 2; + + /** + * Returns a constant representing the orientation of the object. + * + * @return the orientation of this object + * @see #HORIZONTAL + * @see #VERTICAL + * @see #NO_ORIENTATION + */ + int getOrientation(); + + /** + * Sets the minimum value this object can have. + * + * @param minimum the new minimum value + */ + void setMinimum(int minimum); + + /** + * Returns the minimum value this object can have. + * + * @return the minimum value + */ + int getMinimum(); + + /** + * Sets the maximum value this object can have. + * + * @param maximum the new maximum value + */ + void setMaximum(int maximum); + + /** + * Returns the maximum value this object can have. + * + * @return the maximum value + */ + int getMaximum(); + + /** + * Sets the increment value for incrementing the value by units. + * + * @param increment the unit increment value + */ + void setUnitIncrement(int increment); + + /** + * Returns the increment value for incrementing the value by units. + * + * @return the unit increment value + */ + int getUnitIncrement(); + + /** + * Sets the increment value for incrementing the value by blocks. + * + * @param increment the block increment value + */ + void setBlockIncrement(int increment); + + /** + * Returns the increment value for incrementing the value by blocks. + * + * @return the block increment value + */ + int getBlockIncrement(); + + /** + * Sets the length of the indicator for this object to the specified value. + * + * @param length the indicator length + */ + void setVisibleAmount(int length); + + /** + * Returns the length of the indicator for this object. + * + * @return the indicator length + */ + int getVisibleAmount(); + + /** + * Sets the current value of the object. + * + * @param value the new value + */ + void setValue(int value); + + /** + * Returns the current value of the object. + * + * @return the current value + */ + int getValue(); + + /** + * Adds a listener that will receive adjustment events for this object. + * + * @param listener the adjustment listener to add + * @see java.awt.event.AdjustmentEvent + */ + void addAdjustmentListener(AdjustmentListener listener); + + /** + * Removes an adjustment listener from this object. + * + * @param listener the adjustment listener to remove + * @see java.awt.event.AdjustmentEvent + */ + void removeAdjustmentListener(AdjustmentListener listener); +} // interface Adjustable diff --git a/libjava/classpath/java/awt/AlphaComposite.java b/libjava/classpath/java/awt/AlphaComposite.java new file mode 100644 index 000000000..d6c43d139 --- /dev/null +++ b/libjava/classpath/java/awt/AlphaComposite.java @@ -0,0 +1,215 @@ +/* AlphaComposite.java -- provides a context for performing alpha compositing + Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.java2d.AlphaCompositeContext; + +import java.awt.image.ColorModel; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Composite + * @see CompositeContext + * @since 1.3 + * @status updated to 1.4 except for createContext, needs documentation + */ +public final class AlphaComposite implements Composite +{ + /** Map Long to AlphaComposites. See getInstance for details. */ + private static final LinkedHashMap cache = new LinkedHashMap(11, 0.75f, true) + { + /** The largest the alpha composite cache can grow. */ + private static final int MAX_CACHE_SIZE = 2048; + + /** Prune stale entries. */ + protected boolean removeEldestEntry(Map.Entry eldest) + { + return size() > MAX_CACHE_SIZE; + } + }; + + public static final int CLEAR = 1; + public static final int SRC = 2; + public static final int DST = 9; + public static final int SRC_OVER = 3; + public static final int DST_OVER = 4; + public static final int SRC_IN = 5; + public static final int DST_IN = 6; + public static final int SRC_OUT = 7; + public static final int DST_OUT = 8; + public static final int SRC_ATOP = 10; + public static final int DST_ATOP = 11; + public static final int XOR = 12; + public static final AlphaComposite Clear = getInstance(CLEAR); + public static final AlphaComposite Src = getInstance(SRC); + public static final AlphaComposite Dst = getInstance(DST); + public static final AlphaComposite SrcOver = getInstance(SRC_OVER); + public static final AlphaComposite DstOver = getInstance(DST_OVER); + public static final AlphaComposite SrcIn = getInstance(SRC_IN); + public static final AlphaComposite DstIn = getInstance(DST_IN); + public static final AlphaComposite SrcOut = getInstance(SRC_OUT); + public static final AlphaComposite DstOut = getInstance(DST_OUT); + public static final AlphaComposite SrcAtop = getInstance(SRC_ATOP); + public static final AlphaComposite DstAtop = getInstance(DST_ATOP); + public static final AlphaComposite Xor = getInstance(XOR); + + private final int rule; + private final float alpha; + private AlphaComposite(int rule, float alpha) + { + this.rule = rule; + this.alpha = alpha; + } + + /** + * Creates an AlphaComposite object with the specified rule. + * + * @param rule The compositing rule. + * + * @exception IllegalArgumentException If rule is not one of the following: + * CLEAR, SRC, DST, SRC_OVER, DST_OVER, SRC_IN, DST_IN, SRC_OUT, DST_OUT, + * SRC_ATOP, DST_ATOP, or XOR. + */ + public static AlphaComposite getInstance(int rule) + { + return getInstance(rule, 1); + } + + /** + * Creates an AlphaComposite object with the specified rule and the constant + * alpha to multiply with the alpha of the source. The source is multiplied + * with the specified alpha before being composited with the destination. + * + * @param rule The compositing rule. + * + * @exception IllegalArgumentException If rule is not one of the following: + * CLEAR, SRC, DST, SRC_OVER, DST_OVER, SRC_IN, DST_IN, SRC_OUT, DST_OUT, + * SRC_ATOP, DST_ATOP, or XOR. + */ + public static AlphaComposite getInstance(int rule, float alpha) + { + if (rule < CLEAR || rule > XOR || ! (alpha >= 0 && alpha <= 1)) + throw new IllegalArgumentException(); + // This long is guaranteed unique for all valid alpha composites. + Long l = new Long(rule + Double.doubleToLongBits(alpha)); + AlphaComposite a = (AlphaComposite) cache.get(l); + if (a == null) + { + a = new AlphaComposite(rule, alpha); + cache.put(l, a); + } + return a; + } + + /** + * Creates a {@link CompositeContext} that can be used to perform + * compositing operations according to this AlphaComposite settings. + * + * @param srcColorModel the color model of the source raster + * @param dstColorModel the color model of the destination raster + * @param hints the rendering hints to use + * + * @return a {@link CompositeContext} that can be used to perform + * compositing operations according to this AlphaComposite settings + */ + public CompositeContext createContext(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints) + { + return new AlphaCompositeContext(this, srcColorModel, dstColorModel); + } + + /** + * Return an AlphaComposite similar to this, + * that uses the specified rule. If rule is the same as + * this.rule, then this is returned. + * + * @since 1.6 + */ + public AlphaComposite derive(int rule) + { + if (this.rule == rule) + return this; + else + return AlphaComposite.getInstance(rule, this.getAlpha()); + } + + /** + * Return an AlphaComposite similar to this, + * that uses the specified alpha. + * + * If alph is the same as this.alpha, + * then this is returned. + * + * @since 1.6 + */ + public AlphaComposite derive(float alpha) + { + if (this.getAlpha() == alpha) + return this; + else + return AlphaComposite.getInstance(this.getRule(), alpha); + } + + public float getAlpha() + { + return alpha; + } + + public int getRule() + { + return rule; + } + + public int hashCode() + { + return 31 * Float.floatToIntBits(alpha) + rule; + } + + public boolean equals(Object o) + { + if (! (o instanceof AlphaComposite)) + return false; + AlphaComposite a = (AlphaComposite) o; + return rule == a.rule && alpha == a.alpha; + } +} // class AlphaComposite diff --git a/libjava/classpath/java/awt/AttributeValue.java b/libjava/classpath/java/awt/AttributeValue.java new file mode 100644 index 000000000..080e92e22 --- /dev/null +++ b/libjava/classpath/java/awt/AttributeValue.java @@ -0,0 +1,98 @@ +/* AttributeValue.java -- parent of type-safe enums of attributes + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class is undocumented by Sun, but it is the parent of several other + * classes, all of which are type-safe enumerations. This takes care of + * equals, toString, and hashCode, so + * that you don't have to (although hashCode is commonly overridden). + * + * @author Eric Blake (ebb9@email.byu.edu) + */ +class AttributeValue +{ + /** The value of the enumeration. Package visible for speed. */ + final int value; + + /** The list of enumeration names for the given subclass. */ + private final String[] names; + + /** + * Construct a type-safe enumeration element. For example,
+ *

+   * class Foo extends AttributeValue
+   * {
+   *   private static final String[] names = { "one", "two" }
+   *   public static final Foo ONE = new Foo(0);
+   *   public static final Foo TWO = new Foo(1);
+   *   private Foo(int value) { super(value, names); }
+   * }
+   * 
+ * + * @param value the position of this enumeration element, consecutive from 0 + * @param names the constant list of enumeration names for the subclass + */ + AttributeValue(int value, String[] names) + { + this.value = value; + this.names = names; + } + + /** + * Returns the hashcode of this element. This is the index of the element + * in the enumeration. Note that equals defaults to the == relation. + * + * @return the hashcode + */ + public int hashCode() + { + return value; + } + + /** + * Returns the name of this enumeration element. + * + * @return the element name + */ + public String toString() + { + return names[value]; + } +} // class AttributeValue diff --git a/libjava/classpath/java/awt/BasicStroke.java b/libjava/classpath/java/awt/BasicStroke.java new file mode 100644 index 000000000..eac69d986 --- /dev/null +++ b/libjava/classpath/java/awt/BasicStroke.java @@ -0,0 +1,902 @@ +/* BasicStroke.java -- + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.java2d.CubicSegment; +import gnu.java.awt.java2d.LineSegment; +import gnu.java.awt.java2d.QuadSegment; +import gnu.java.awt.java2d.Segment; + +import java.awt.geom.FlatteningPathIterator; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.util.Arrays; + +/** + * A general purpose {@link Stroke} implementation that can represent a wide + * variety of line styles for use with subclasses of {@link Graphics2D}. + *

+ * The line cap and join styles can be set using the options illustrated + * here: + *

+ * Illustration of line cap and join styles + *

+ * A dash array can be used to specify lines with alternating opaque and + * transparent sections. + */ +public class BasicStroke implements Stroke +{ + /** + * Indicates a mitered line join style. See the class overview for an + * illustration. + */ + public static final int JOIN_MITER = 0; + + /** + * Indicates a rounded line join style. See the class overview for an + * illustration. + */ + public static final int JOIN_ROUND = 1; + + /** + * Indicates a bevelled line join style. See the class overview for an + * illustration. + */ + public static final int JOIN_BEVEL = 2; + + /** + * Indicates a flat line cap style. See the class overview for an + * illustration. + */ + public static final int CAP_BUTT = 0; + + /** + * Indicates a rounded line cap style. See the class overview for an + * illustration. + */ + public static final int CAP_ROUND = 1; + + /** + * Indicates a square line cap style. See the class overview for an + * illustration. + */ + public static final int CAP_SQUARE = 2; + + /** The stroke width. */ + private final float width; + + /** The line cap style. */ + private final int cap; + + /** The line join style. */ + private final int join; + + /** The miter limit. */ + private final float limit; + + /** The dash array. */ + private final float[] dash; + + /** The dash phase. */ + private final float phase; + + // The inner and outer paths of the stroke + private Segment start, end; + + /** + * Creates a new BasicStroke instance with the given attributes. + * + * @param width the line width (>= 0.0f). + * @param cap the line cap style (one of {@link #CAP_BUTT}, + * {@link #CAP_ROUND} or {@link #CAP_SQUARE}). + * @param join the line join style (one of {@link #JOIN_ROUND}, + * {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}). + * @param miterlimit the limit to trim the miter join. The miterlimit must be + * greater than or equal to 1.0f. + * @param dash The array representing the dashing pattern. There must be at + * least one non-zero entry. + * @param dashPhase is negative and dash is not null. + * + * @throws IllegalArgumentException If one input parameter doesn't meet + * its needs. + */ + public BasicStroke(float width, int cap, int join, float miterlimit, + float[] dash, float dashPhase) + { + if (width < 0.0f ) + throw new IllegalArgumentException("width " + width + " < 0"); + else if (cap < CAP_BUTT || cap > CAP_SQUARE) + throw new IllegalArgumentException("cap " + cap + " out of range [" + + CAP_BUTT + ".." + CAP_SQUARE + "]"); + else if (miterlimit < 1.0f && join == JOIN_MITER) + throw new IllegalArgumentException("miterlimit " + miterlimit + + " < 1.0f while join == JOIN_MITER"); + else if (join < JOIN_MITER || join > JOIN_BEVEL) + throw new IllegalArgumentException("join " + join + " out of range [" + + JOIN_MITER + ".." + JOIN_BEVEL + + "]"); + else if (dashPhase < 0.0f && dash != null) + throw new IllegalArgumentException("dashPhase " + dashPhase + + " < 0.0f while dash != null"); + else if (dash != null) + if (dash.length == 0) + throw new IllegalArgumentException("dash.length is 0"); + else + { + boolean allZero = true; + + for ( int i = 0; i < dash.length; ++i) + { + if (dash[i] != 0.0f) + { + allZero = false; + break; + } + } + + if (allZero) + throw new IllegalArgumentException("all dashes are 0.0f"); + } + + this.width = width; + this.cap = cap; + this.join = join; + limit = miterlimit; + this.dash = dash == null ? null : (float[]) dash.clone(); + phase = dashPhase; + } + + /** + * Creates a new BasicStroke instance with the given attributes. + * + * @param width the line width (>= 0.0f). + * @param cap the line cap style (one of {@link #CAP_BUTT}, + * {@link #CAP_ROUND} or {@link #CAP_SQUARE}). + * @param join the line join style (one of {@link #JOIN_ROUND}, + * {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}). + * @param miterlimit the limit to trim the miter join. The miterlimit must be + * greater than or equal to 1.0f. + * + * @throws IllegalArgumentException If one input parameter doesn't meet + * its needs. + */ + public BasicStroke(float width, int cap, int join, float miterlimit) + { + this(width, cap, join, miterlimit, null, 0); + } + + /** + * Creates a new BasicStroke instance with the given attributes. + * The miter limit defaults to 10.0. + * + * @param width the line width (>= 0.0f). + * @param cap the line cap style (one of {@link #CAP_BUTT}, + * {@link #CAP_ROUND} or {@link #CAP_SQUARE}). + * @param join the line join style (one of {@link #JOIN_ROUND}, + * {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}). + * + * @throws IllegalArgumentException If one input parameter doesn't meet + * its needs. + */ + public BasicStroke(float width, int cap, int join) + { + this(width, cap, join, 10, null, 0); + } + + /** + * Creates a new BasicStroke instance with the given line + * width. The default values are: + *

    + *
  • line cap style: {@link #CAP_SQUARE};
  • + *
  • line join style: {@link #JOIN_MITER};
  • + *
  • miter limit: 10.0f. + *
+ * + * @param width the line width (>= 0.0f). + * + * @throws IllegalArgumentException If width is negative. + */ + public BasicStroke(float width) + { + this(width, CAP_SQUARE, JOIN_MITER, 10, null, 0); + } + + /** + * Creates a new BasicStroke instance. The default values are: + *
    + *
  • line width: 1.0f;
  • + *
  • line cap style: {@link #CAP_SQUARE};
  • + *
  • line join style: {@link #JOIN_MITER};
  • + *
  • miter limit: 10.0f. + *
+ */ + public BasicStroke() + { + this(1, CAP_SQUARE, JOIN_MITER, 10, null, 0); + } + + /** + * Creates a shape representing the stroked outline of the given shape. + * THIS METHOD IS NOT YET IMPLEMENTED. + * + * @param s the shape. + */ + public Shape createStrokedShape(Shape s) + { + PathIterator pi = s.getPathIterator(null); + + if( dash == null ) + return solidStroke( pi ); + + return dashedStroke( pi ); + } + + /** + * Returns the line width. + * + * @return The line width. + */ + public float getLineWidth() + { + return width; + } + + /** + * Returns a code indicating the line cap style (one of {@link #CAP_BUTT}, + * {@link #CAP_ROUND}, {@link #CAP_SQUARE}). + * + * @return A code indicating the line cap style. + */ + public int getEndCap() + { + return cap; + } + + /** + * Returns a code indicating the line join style (one of {@link #JOIN_BEVEL}, + * {@link #JOIN_MITER} or {@link #JOIN_ROUND}). + * + * @return A code indicating the line join style. + */ + public int getLineJoin() + { + return join; + } + + /** + * Returns the miter limit. + * + * @return The miter limit. + */ + public float getMiterLimit() + { + return limit; + } + + /** + * Returns the dash array, which defines the length of alternate opaque and + * transparent sections in lines drawn with this stroke. If + * null, a continuous line will be drawn. + * + * @return The dash array (possibly null). + */ + public float[] getDashArray() + { + return dash; + } + + /** + * Returns the dash phase for the stroke. This is the offset from the start + * of a path at which the pattern defined by {@link #getDashArray()} is + * rendered. + * + * @return The dash phase. + */ + public float getDashPhase() + { + return phase; + } + + /** + * Returns the hash code for this object. The hash is calculated by + * xoring the hash, cap, join, limit, dash array and phase values + * (converted to int first with + * Float.floatToIntBits() if the value is a + * float). + * + * @return The hash code. + */ + public int hashCode() + { + int hash = Float.floatToIntBits(width); + hash ^= cap; + hash ^= join; + hash ^= Float.floatToIntBits(limit); + + if (dash != null) + for (int i = 0; i < dash.length; i++) + hash ^= Float.floatToIntBits(dash[i]); + + hash ^= Float.floatToIntBits(phase); + + return hash; + } + + /** + * Compares this BasicStroke for equality with an arbitrary + * object. This method returns true if and only if: + *
    + *
  • o is an instanceof BasicStroke;
  • + *
  • this object has the same width, line cap style, line join style, + * miter limit, dash array and dash phase as o.
  • + *
+ * + * @param o the object (null permitted). + * + * @return true if this stroke is equal to o and + * false otherwise. + */ + public boolean equals(Object o) + { + if (! (o instanceof BasicStroke)) + return false; + BasicStroke s = (BasicStroke) o; + return width == s.width && cap == s.cap && join == s.join + && limit == s.limit && Arrays.equals(dash, s.dash) && phase == s.phase; + } + + private Shape solidStroke(PathIterator pi) + { + double[] coords = new double[6]; + double x, y, x0, y0; + boolean pathOpen = false; + GeneralPath output = new GeneralPath( ); + Segment[] p; + x = x0 = y = y0 = 0; + + while( !pi.isDone() ) + { + switch( pi.currentSegment(coords) ) + { + case PathIterator.SEG_MOVETO: + x0 = x = coords[0]; + y0 = y = coords[1]; + if( pathOpen ) + { + capEnds(); + convertPath(output, start); + start = end = null; + pathOpen = false; + } + break; + + case PathIterator.SEG_LINETO: + p = (new LineSegment(x, y, coords[0], coords[1])). + getDisplacedSegments(width/2.0); + if( !pathOpen ) + { + start = p[0]; + end = p[1]; + pathOpen = true; + } + else + addSegments(p); + + x = coords[0]; + y = coords[1]; + break; + + case PathIterator.SEG_QUADTO: + p = (new QuadSegment(x, y, coords[0], coords[1], coords[2], + coords[3])).getDisplacedSegments(width/2.0); + if( !pathOpen ) + { + start = p[0]; + end = p[1]; + pathOpen = true; + } + else + addSegments(p); + + x = coords[2]; + y = coords[3]; + break; + + case PathIterator.SEG_CUBICTO: + p = new CubicSegment(x, y, coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5]).getDisplacedSegments(width/2.0); + if( !pathOpen ) + { + start = p[0]; + end = p[1]; + pathOpen = true; + } + else + addSegments(p); + + x = coords[4]; + y = coords[5]; + break; + + case PathIterator.SEG_CLOSE: + if (x == x0 && y == y0) + { + joinSegments(new Segment[] { start.first, end.first }); + } + else + { + p = (new LineSegment(x, y, x0, y0)).getDisplacedSegments(width / 2.0); + addSegments(p); + } + convertPath(output, start); + convertPath(output, end); + start = end = null; + pathOpen = false; + output.setWindingRule(GeneralPath.WIND_EVEN_ODD); + break; + } + pi.next(); + } + + if( pathOpen ) + { + capEnds(); + convertPath(output, start); + } + return output; + } + + private Shape dashedStroke(PathIterator pi) + { + // The choice of (flatnessSq == width / 3) is made to be consistent with + // the flattening in CubicSegment.getDisplacedSegments + FlatteningPathIterator flat = new FlatteningPathIterator(pi, + Math.sqrt(width / 3)); + + // Holds the endpoint of the current segment (or piece of a segment) + double[] coords = new double[2]; + + // Holds end of the last segment + double x, y, x0, y0; + x = x0 = y = y0 = 0; + + // Various useful flags + boolean pathOpen = false; + boolean dashOn = true; + boolean offsetting = (phase != 0); + + // How far we are into the current dash + double distance = 0; + int dashIndex = 0; + + // And variables to hold the final output + GeneralPath output = new GeneralPath(); + Segment[] p; + + // Iterate over the FlatteningPathIterator + while (! flat.isDone()) + { + switch (flat.currentSegment(coords)) + { + case PathIterator.SEG_MOVETO: + x0 = x = coords[0]; + y0 = y = coords[1]; + + if (pathOpen) + { + capEnds(); + convertPath(output, start); + start = end = null; + pathOpen = false; + } + + break; + + case PathIterator.SEG_LINETO: + boolean segmentConsumed = false; + + while (! segmentConsumed) + { + // Find the total remaining length of this segment + double segLength = Math.sqrt((x - coords[0]) * (x - coords[0]) + + (y - coords[1]) + * (y - coords[1])); + boolean spanBoundary = true; + double[] segmentEnd = null; + + // The current segment fits entirely inside the current dash + if ((offsetting && distance + segLength <= phase) + || distance + segLength <= dash[dashIndex]) + { + spanBoundary = false; + } + + // Otherwise, we need to split the segment in two, as this + // segment spans a dash boundry + else + { + segmentEnd = (double[]) coords.clone(); + + // Calculate the remaining distance in this dash, + // and coordinates of the dash boundary + double reqLength; + if (offsetting) + reqLength = phase - distance; + else + reqLength = dash[dashIndex] - distance; + + coords[0] = x + ((coords[0] - x) * reqLength / segLength); + coords[1] = y + ((coords[1] - y) * reqLength / segLength); + } + + if (offsetting || ! dashOn) + { + // Dash is off, or we are in offset - treat this as a + // moveTo + x0 = x = coords[0]; + y0 = y = coords[1]; + + if (pathOpen) + { + capEnds(); + convertPath(output, start); + start = end = null; + pathOpen = false; + } + } + else + { + // Dash is on - treat this as a lineTo + p = (new LineSegment(x, y, coords[0], coords[1])).getDisplacedSegments(width / 2.0); + + if (! pathOpen) + { + start = p[0]; + end = p[1]; + pathOpen = true; + } + else + addSegments(p); + + x = coords[0]; + y = coords[1]; + } + + // Update variables depending on whether we spanned a + // dash boundary or not + if (! spanBoundary) + { + distance += segLength; + segmentConsumed = true; + } + else + { + if (offsetting) + offsetting = false; + dashOn = ! dashOn; + distance = 0; + coords = segmentEnd; + + if (dashIndex + 1 == dash.length) + dashIndex = 0; + else + dashIndex++; + + // Since the value of segmentConsumed is still false, + // the next run of the while loop will complete the segment + } + } + break; + + // This is a flattened path, so we don't need to deal with curves + } + flat.next(); + } + + if (pathOpen) + { + capEnds(); + convertPath(output, start); + } + return output; + } + + /** + * Cap the ends of the path (joining the start and end list of segments) + */ + private void capEnds() + { + Segment returnPath = end.last; + + end.reverseAll(); // reverse the path. + end = null; + capEnd(start, returnPath); + start.last = returnPath.last; + end = null; + + capEnd(start, start); + } + + /** + * Append the Segments in s to the GeneralPath p + */ + private void convertPath(GeneralPath p, Segment s) + { + Segment v = s; + p.moveTo((float)s.P1.getX(), (float)s.P1.getY()); + + do + { + if(v instanceof LineSegment) + p.lineTo((float)v.P2.getX(), (float)v.P2.getY()); + else if(v instanceof QuadSegment) + p.quadTo((float)((QuadSegment)v).cp.getX(), + (float)((QuadSegment)v).cp.getY(), + (float)v.P2.getX(), + (float)v.P2.getY()); + else if(v instanceof CubicSegment) + p.curveTo((float)((CubicSegment)v).cp1.getX(), + (float)((CubicSegment)v).cp1.getY(), + (float)((CubicSegment)v).cp2.getX(), + (float)((CubicSegment)v).cp2.getY(), + (float)v.P2.getX(), + (float)v.P2.getY()); + v = v.next; + } while(v != s && v != null); + + p.closePath(); + } + + /** + * Add the segments to start and end (the inner and outer edges of the stroke) + */ + private void addSegments(Segment[] segments) + { + joinSegments(segments); + start.add(segments[0]); + end.add(segments[1]); + } + + private void joinSegments(Segment[] segments) + { + double[] p0 = start.last.cp2(); + double[] p1 = new double[]{start.last.P2.getX(), start.last.P2.getY()}; + double[] p2 = new double[]{segments[0].first.P1.getX(), segments[0].first.P1.getY()}; + double[] p3 = segments[0].cp1(); + Point2D p; + + p = lineIntersection(p0[0],p0[1],p1[0],p1[1], + p2[0],p2[1],p3[0],p3[1], false); + + double det = (p1[0] - p0[0])*(p3[1] - p2[1]) - + (p3[0] - p2[0])*(p1[1] - p0[1]); + + if( det > 0 ) + { + // start and segment[0] form the 'inner' part of a join, + // connect the overlapping segments + joinInnerSegments(start, segments[0], p); + joinOuterSegments(end, segments[1], p); + } + else + { + // end and segment[1] form the 'inner' part + joinInnerSegments(end, segments[1], p); + joinOuterSegments(start, segments[0], p); + } + } + + /** + * Make a cap between a and b segments, + * where a-->b is the direction of iteration. + */ + private void capEnd(Segment a, Segment b) + { + double[] p0, p1; + double dx, dy, l; + Point2D c1,c2; + + switch( cap ) + { + case CAP_BUTT: + a.add(new LineSegment(a.last.P2, b.P1)); + break; + + case CAP_SQUARE: + p0 = a.last.cp2(); + p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; + dx = p1[0] - p0[0]; + dy = p1[1] - p0[1]; + l = Math.sqrt(dx * dx + dy * dy); + dx = 0.5*width*dx/l; + dy = 0.5*width*dy/l; + c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); + c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy); + a.add(new LineSegment(a.last.P2, c1)); + a.add(new LineSegment(c1, c2)); + a.add(new LineSegment(c2, b.P1)); + break; + + case CAP_ROUND: + p0 = a.last.cp2(); + p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; + dx = p1[0] - p0[0]; + dy = p1[1] - p0[1]; + if (dx != 0 && dy != 0) + { + l = Math.sqrt(dx * dx + dy * dy); + dx = (2.0/3.0)*width*dx/l; + dy = (2.0/3.0)*width*dy/l; + } + + c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); + c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy); + a.add(new CubicSegment(a.last.P2, c1, c2, b.P1)); + break; + } + a.add(b); + } + + /** + * Returns the intersection of two lines, or null if there isn't one. + * @param infinite - true if the lines should be regarded as infinite, false + * if the intersection must be within the given segments. + * @return a Point2D or null. + */ + private Point2D lineIntersection(double X1, double Y1, + double X2, double Y2, + double X3, double Y3, + double X4, double Y4, + boolean infinite) + { + double x1 = X1; + double y1 = Y1; + double rx = X2 - x1; + double ry = Y2 - y1; + + double x2 = X3; + double y2 = Y3; + double sx = X4 - x2; + double sy = Y4 - y2; + + double determinant = sx * ry - sy * rx; + double nom = (sx * (y2 - y1) + sy * (x1 - x2)); + + // lines can be considered parallel. + if (Math.abs(determinant) < 1E-6) + return null; + + nom = nom / determinant; + + // check if lines are within the bounds + if(!infinite && (nom > 1.0 || nom < 0.0)) + return null; + + return new Point2D.Double(x1 + nom * rx, y1 + nom * ry); + } + + /** + * Join a and b segments, where a-->b is the direction of iteration. + * + * insideP is the inside intersection point of the join, needed for + * calculating miter lengths. + */ + private void joinOuterSegments(Segment a, Segment b, Point2D insideP) + { + double[] p0, p1; + double dx, dy, l; + Point2D c1,c2; + + switch( join ) + { + case JOIN_MITER: + p0 = a.last.cp2(); + p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; + double[] p2 = new double[]{b.P1.getX(), b.P1.getY()}; + double[] p3 = b.cp1(); + Point2D p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], true); + if( p == null || insideP == null ) + a.add(new LineSegment(a.last.P2, b.P1)); + else if((p.distance(insideP)/width) < limit) + { + a.add(new LineSegment(a.last.P2, p)); + a.add(new LineSegment(p, b.P1)); + } + else + { + // outside miter limit, do a bevel join. + a.add(new LineSegment(a.last.P2, b.P1)); + } + break; + + case JOIN_ROUND: + p0 = a.last.cp2(); + p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()}; + dx = p1[0] - p0[0]; + dy = p1[1] - p0[1]; + l = Math.sqrt(dx * dx + dy * dy); + dx = 0.5*width*dx/l; + dy = 0.5*width*dy/l; + c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy); + + p0 = new double[]{b.P1.getX(), b.P1.getY()}; + p1 = b.cp1(); + + dx = p0[0] - p1[0]; // backwards direction. + dy = p0[1] - p1[1]; + l = Math.sqrt(dx * dx + dy * dy); + dx = 0.5*width*dx/l; + dy = 0.5*width*dy/l; + c2 = new Point2D.Double(p0[0] + dx, p0[1] + dy); + a.add(new CubicSegment(a.last.P2, c1, c2, b.P1)); + break; + + case JOIN_BEVEL: + a.add(new LineSegment(a.last.P2, b.P1)); + break; + } + } + + /** + * Join a and b segments, removing any overlap + */ + private void joinInnerSegments(Segment a, Segment b, Point2D p) + { + double[] p0 = a.last.cp2(); + double[] p1 = new double[] { a.last.P2.getX(), a.last.P2.getY() }; + double[] p2 = new double[] { b.P1.getX(), b.P1.getY() }; + double[] p3 = b.cp1(); + + if (p == null) + { + // Dodgy. + a.add(new LineSegment(a.last.P2, b.P1)); + p = new Point2D.Double((b.P1.getX() + a.last.P2.getX()) / 2.0, + (b.P1.getY() + a.last.P2.getY()) / 2.0); + } + else + // This assumes segments a and b are single segments, which is + // incorrect - if they are a linked list of segments (ie, passed in + // from a flattening operation), this produces strange results!! + a.last.P2 = b.P1 = p; + } +} diff --git a/libjava/classpath/java/awt/BorderLayout.java b/libjava/classpath/java/awt/BorderLayout.java new file mode 100644 index 000000000..c06eb834b --- /dev/null +++ b/libjava/classpath/java/awt/BorderLayout.java @@ -0,0 +1,742 @@ +/* BorderLayout.java -- A layout manager class + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + + +/** + * This class implements a layout manager that positions components + * in certain sectors of the parent container. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class BorderLayout implements LayoutManager2, java.io.Serializable +{ + + /** + * Constant indicating the top of the container + */ + public static final String NORTH = "North"; + + /** + * Constant indicating the bottom of the container + */ + public static final String SOUTH = "South"; + + /** + * Constant indicating the right side of the container + */ + public static final String EAST = "East"; + + /** + * Constant indicating the left side of the container + */ + public static final String WEST = "West"; + + /** + * Constant indicating the center of the container + */ + public static final String CENTER = "Center"; + + + /** + * The constant indicating the position before the first line of the + * layout. The exact position depends on the writing system: For a + * top-to-bottom orientation, it is the same as {@link #NORTH}, for + * a bottom-to-top orientation, it is the same as {@link #SOUTH}. + * + *

This constant is an older name for {@link #PAGE_START} which + * has exactly the same value. + * + * @since 1.2 + */ + public static final String BEFORE_FIRST_LINE = "First"; + + /** + * The constant indicating the position after the last line of the + * layout. The exact position depends on the writing system: For a + * top-to-bottom orientation, it is the same as {@link #SOUTH}, for + * a bottom-to-top orientation, it is the same as {@link #NORTH}. + * + *

This constant is an older name for {@link #PAGE_END} which + * has exactly the same value. + * + * @since 1.2 + */ + public static final String AFTER_LAST_LINE = "Last"; + + /** + * The constant indicating the position before the first item of the + * layout. The exact position depends on the writing system: For a + * left-to-right orientation, it is the same as {@link #WEST}, for + * a right-to-left orientation, it is the same as {@link #EAST}. + * + *

This constant is an older name for {@link #LINE_START} which + * has exactly the same value. + * + * @since 1.2 + */ + public static final String BEFORE_LINE_BEGINS = "Before"; + + /** + * The constant indicating the position after the last item of the + * layout. The exact position depends on the writing system: For a + * left-to-right orientation, it is the same as {@link #EAST}, for + * a right-to-left orientation, it is the same as {@link #WEST}. + * + *

This constant is an older name for {@link #LINE_END} which + * has exactly the same value. + * + * @since 1.2 + */ + public static final String AFTER_LINE_ENDS = "After"; + + /** + * The constant indicating the position before the first line of the + * layout. The exact position depends on the writing system: For a + * top-to-bottom orientation, it is the same as {@link #NORTH}, for + * a bottom-to-top orientation, it is the same as {@link #SOUTH}. + * + * @since 1.4 + */ + public static final String PAGE_START = BEFORE_FIRST_LINE; + + /** + * The constant indicating the position after the last line of the + * layout. The exact position depends on the writing system: For a + * top-to-bottom orientation, it is the same as {@link #SOUTH}, for + * a bottom-to-top orientation, it is the same as {@link #NORTH}. + * + * @since 1.4 + */ + public static final String PAGE_END = AFTER_LAST_LINE; + + /** + * The constant indicating the position before the first item of the + * layout. The exact position depends on the writing system: For a + * left-to-right orientation, it is the same as {@link #WEST}, for + * a right-to-left orientation, it is the same as {@link #EAST}. + * + * @since 1.4 + */ + public static final String LINE_START = BEFORE_LINE_BEGINS; + + /** + * The constant indicating the position after the last item of the + * layout. The exact position depends on the writing system: For a + * left-to-right orientation, it is the same as {@link #EAST}, for + * a right-to-left orientation, it is the same as {@link #WEST}. + * + * @since 1.4 + */ + public static final String LINE_END = AFTER_LINE_ENDS; + + + /** + * Serialization constant. + */ + private static final long serialVersionUID = -8658291919501921765L; + + + /** + * @serial + */ + private Component north; + + /** + * @serial + */ + private Component south; + + /** + * @serial + */ + private Component east; + + /** + * @serial + */ + private Component west; + + /** + * @serial + */ + private Component center; + + /** + * @serial + */ + private Component firstLine; + + /** + * @serial + */ + private Component lastLine; + + /** + * @serial + */ + private Component firstItem; + + /** + * @serial + */ + private Component lastItem; + + /** + * @serial The horizontal gap between components + */ + private int hgap; + + /** + * @serial The vertical gap between components + */ + private int vgap; + + + // Some constants for use with calcSize(). + private static final int MIN = 0; + private static final int MAX = 1; + private static final int PREF = 2; + + + /** + * Initializes a new instance of BorderLayout with no + * horiztonal or vertical gaps between components. + */ + public BorderLayout() + { + this(0,0); + } + + /** + * Initializes a new instance of BorderLayout with the + * specified horiztonal and vertical gaps between components. + * + * @param hgap The horizontal gap between components. + * @param vgap The vertical gap between components. + */ + public BorderLayout(int hgap, int vgap) + { + this.hgap = hgap; + this.vgap = vgap; + } + + /** + * Returns the horitzontal gap value. + * + * @return The horitzontal gap value. + */ + public int getHgap() + { + return(hgap); + } + + /** + * Sets the horizontal gap to the specified value. + * + * @param hgap The new horizontal gap. + */ + public void setHgap(int hgap) + { + this.hgap = hgap; + } + + /** + * Returns the vertical gap value. + * + * @return The vertical gap value. + */ + public int getVgap() + { + return(vgap); + } + + /** + * Sets the vertical gap to the specified value. + * + * @param vgap The new vertical gap value. + */ + public void setVgap(int vgap) + { + this.vgap = vgap; + } + + /** + * Adds a component to the layout in the specified constraint position, + * which must be one of the string constants defined in this class. + * + * @param component The component to add. + * @param constraints The constraint string. + * + * @exception IllegalArgumentException If the constraint object is not + * a string, or is not one of the specified constants in this class. + */ + public void addLayoutComponent(Component component, Object constraints) + { + if (constraints != null && ! (constraints instanceof String)) + throw new IllegalArgumentException("Constraint must be a string"); + + addLayoutComponent((String) constraints, component); + } + + /** + * Adds a component to the layout in the specified constraint position, + * which must be one of the string constants defined in this class. + * + * @param constraints The constraint string. + * @param component The component to add. + * + * @exception IllegalArgumentException If the constraint object is not + * one of the specified constants in this class. + * + * @deprecated This method is deprecated in favor of + * addLayoutComponent(Component, Object). + */ + public void addLayoutComponent(String constraints, Component component) + { + String str = constraints; + + if (str == null || str.equals(CENTER)) + center = component; + else if (str.equals(NORTH)) + north = component; + else if (str.equals(SOUTH)) + south = component; + else if (str.equals(EAST)) + east = component; + else if (str.equals(WEST)) + west = component; + else if (str.equals(BEFORE_FIRST_LINE)) + firstLine = component; + else if (str.equals(AFTER_LAST_LINE)) + lastLine = component; + else if (str.equals(BEFORE_LINE_BEGINS)) + firstItem = component; + else if (str.equals(AFTER_LINE_ENDS)) + lastItem = component; + else + throw new IllegalArgumentException("Constraint value not valid: " + str); + } + + /** + * Removes the specified component from the layout. + * + * @param component The component to remove from the layout. + */ + public void removeLayoutComponent(Component component) + { + if (north == component) + north = null; + if (south == component) + south = null; + if (east == component) + east = null; + if (west == component) + west = null; + if (center == component) + center = null; + if (firstItem == component) + firstItem = null; + if (lastItem == component) + lastItem = null; + if (firstLine == component) + firstLine = null; + if (lastLine == component) + lastLine = null; + } + + /** + * Returns the minimum size of the specified container using this layout. + * + * @param target The container to calculate the minimum size for. + * + * @return The minimum size of the container + */ + public Dimension minimumLayoutSize(Container target) + { + return calcSize(target, MIN); + } + + /** + * Returns the preferred size of the specified container using this layout. + * + * @param target The container to calculate the preferred size for. + * + * @return The preferred size of the container + */ + public Dimension preferredLayoutSize(Container target) + { + return calcSize(target, PREF); + } + + /** + * Returns the maximum size of the specified container using this layout. + * + * @param target The container to calculate the maximum size for. + * + * @return The maximum size of the container + */ + public Dimension maximumLayoutSize(Container target) + { + return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + /** + * Returns the X axis alignment, which is a float indicating + * where along the X axis this container wishs to position its layout. + * 0 indicates align to the left, 1 indicates align to the right, and 0.5 + * indicates align to the center. + * + * @param parent The parent container. + * + * @return The X alignment value. + */ + public float getLayoutAlignmentX(Container parent) + { + return 0.5F; + } + + /** + * Returns the Y axis alignment, which is a float indicating + * where along the Y axis this container wishs to position its layout. + * 0 indicates align to the top, 1 indicates align to the bottom, and 0.5 + * indicates align to the center. + * + * @param parent The parent container. + * + * @return The Y alignment value. + */ + public float getLayoutAlignmentY(Container parent) + { + return 0.5F; + } + + /** + * Instructs this object to discard any layout information it might + * have cached. + * + * @param parent The parent container. + */ + public void invalidateLayout(Container parent) + { + // Nothing to do here. + } + + /** + * Lays out the specified container according to the constraints in this + * object. + * + * @param target The container to lay out. + */ + public void layoutContainer(Container target) + { + synchronized (target.getTreeLock()) + { + Insets i = target.getInsets(); + int top = i.top; + int bottom = target.height - i.bottom; + int left = i.left; + int right = target.width - i.right; + + boolean left_to_right = target.getComponentOrientation().isLeftToRight(); + + Component my_north = north; + Component my_east = east; + Component my_south = south; + Component my_west = west; + + // Note that we currently don't handle vertical layouts. + // Neither does JDK 1.3. + if (firstLine != null) + my_north = firstLine; + if (lastLine != null) + my_south = lastLine; + if (firstItem != null) + { + if (left_to_right) + my_west = firstItem; + else + my_east = firstItem; + } + if (lastItem != null) + { + if (left_to_right) + my_east = lastItem; + else + my_west = lastItem; + } + + if (my_north != null) + { + Dimension n = calcCompSize(my_north, PREF); + my_north.setBounds(left, top, right - left, n.height); + top += n.height + vgap; + } + + if (my_south != null) + { + Dimension s = calcCompSize(my_south, PREF); + my_south.setBounds(left, bottom - s.height, right - left, s.height); + bottom -= s.height + vgap; + } + + if (my_east != null) + { + Dimension e = calcCompSize(my_east, PREF); + my_east.setBounds(right - e.width, top, e.width, bottom - top); + right -= e.width + hgap; + } + + if (my_west != null) + { + Dimension w = calcCompSize(my_west, PREF); + my_west.setBounds(left, top, w.width, bottom - top); + left += w.width + hgap; + } + + if (center != null) + center.setBounds(left, top, right - left, bottom - top); + } + } + + /** + * Returns a string representation of this layout manager. + * + * @return A string representation of this object. + */ + public String toString() + { + return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]"; + } + + private Dimension calcCompSize(Component comp, int what) + { + if (comp == null || ! comp.isVisible()) + return new Dimension(0, 0); + if (what == MIN) + return comp.getMinimumSize(); + else if (what == MAX) + return comp.getMaximumSize(); + return comp.getPreferredSize(); + } + + /** + * This is a helper function used to compute the various sizes for this + * layout. + */ + private Dimension calcSize(Container target, int what) + { + synchronized (target.getTreeLock()) + { + Insets ins = target.getInsets(); + + ComponentOrientation orient = target.getComponentOrientation (); + boolean left_to_right = orient.isLeftToRight (); + + Component my_north = north; + Component my_east = east; + Component my_south = south; + Component my_west = west; + + // Note that we currently don't handle vertical layouts. Neither + // does JDK 1.3. + if (firstLine != null) + my_north = firstLine; + if (lastLine != null) + my_south = lastLine; + if (firstItem != null) + { + if (left_to_right) + my_west = firstItem; + else + my_east = firstItem; + } + if (lastItem != null) + { + if (left_to_right) + my_east = lastItem; + else + my_west = lastItem; + } + + Dimension ndim = calcCompSize(my_north, what); + Dimension sdim = calcCompSize(my_south, what); + Dimension edim = calcCompSize(my_east, what); + Dimension wdim = calcCompSize(my_west, what); + Dimension cdim = calcCompSize(center, what); + + int width = edim.width + cdim.width + wdim.width + (hgap * 2); + // Check for overflow. + if (width < edim.width || width < cdim.width || width < cdim.width) + width = Integer.MAX_VALUE; + + if (ndim.width > width) + width = ndim.width; + if (sdim.width > width) + width = sdim.width; + + width += (ins.left + ins.right); + + int height = edim.height; + if (cdim.height > height) + height = cdim.height; + if (wdim.height > height) + height = wdim.height; + + int addedHeight = height + (ndim.height + sdim.height + (vgap * 2) + + ins.top + ins.bottom); + // Check for overflow. + if (addedHeight < height) + height = Integer.MAX_VALUE; + else + height = addedHeight; + + return(new Dimension(width, height)); + } + } + + /** + * Return the component at the indicated location, or null if no component + * is at that location. The constraints argument must be one of the + * location constants specified by this class. + * @param constraints the location + * @return the component at that location, or null + * @throws IllegalArgumentException if the constraints argument is not + * recognized + * @since 1.5 + */ + public Component getLayoutComponent(Object constraints) + { + if (constraints == CENTER) + return center; + if (constraints == NORTH) + return north; + if (constraints == EAST) + return east; + if (constraints == SOUTH) + return south; + if (constraints == WEST) + return west; + if (constraints == PAGE_START) + return firstLine; + if (constraints == PAGE_END) + return lastLine; + if (constraints == LINE_START) + return firstItem; + if (constraints == LINE_END) + return lastItem; + throw new IllegalArgumentException("constraint " + constraints + + " is not recognized"); + } + + /** + * Return the component at the specified location, which must be one + * of the absolute constants such as CENTER or SOUTH. The container's + * orientation is used to map this location to the correct corresponding + * component, so for instance in a right-to-left container, a request + * for the EAST component could return the LINE_END component. This will + * return null if no component is available at the given location. + * @param container the container whose orientation is used + * @param constraints the absolute location of the component + * @return the component at the location, or null + * @throws IllegalArgumentException if the constraint is not recognized + */ + public Component getLayoutComponent(Container container, Object constraints) + { + ComponentOrientation orient = container.getComponentOrientation(); + if (constraints == CENTER) + return center; + // Note that we don't support vertical layouts. + if (constraints == NORTH) + return north; + if (constraints == SOUTH) + return south; + if (constraints == WEST) + { + // Note that relative layout takes precedence. + if (orient.isLeftToRight()) + return firstItem == null ? west : firstItem; + return lastItem == null ? west : lastItem; + } + if (constraints == EAST) + { + // Note that relative layout takes precedence. + if (orient.isLeftToRight()) + return lastItem == null ? east : lastItem; + return firstItem == null ? east : firstItem; + } + throw new IllegalArgumentException("constraint " + constraints + + " is not recognized"); + } + + /** + * Return the constraint corresponding to a component in this layout. + * If the component is null, or is not in this layout, returns null. + * Otherwise, this will return one of the constraint constants defined + * in this class. + * @param c the component + * @return the constraint, or null + * @since 1.5 + */ + public Object getConstraints(Component c) + { + if (c == null) + return null; + if (c == center) + return CENTER; + if (c == north) + return NORTH; + if (c == east) + return EAST; + if (c == south) + return SOUTH; + if (c == west) + return WEST; + if (c == firstLine) + return PAGE_START; + if (c == lastLine) + return PAGE_END; + if (c == firstItem) + return LINE_START; + if (c == lastItem) + return LINE_END; + return null; + } +} diff --git a/libjava/classpath/java/awt/BufferCapabilities.java b/libjava/classpath/java/awt/BufferCapabilities.java new file mode 100644 index 000000000..522fdf7f6 --- /dev/null +++ b/libjava/classpath/java/awt/BufferCapabilities.java @@ -0,0 +1,253 @@ +/* BufferCapabilities.java -- double-buffering capabilities descriptor + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.BufferStrategy; + +/** + * A double-buffering capability descriptor. This class exposes + * details about the double-buffering algorithms used by image + * buffers. + * + * BufferCapabilities represents algorithms that involve at least two + * buffers but it can also specify so-called "multi-buffer" schemes + * involving more than two buffers. This class describes the + * capabilities of the front and back buffers as well as the results + * of "flipping" -- that is, what happens when an image is transferred + * from the back buffer to the front buffer. + * + * Flipping may or may not be supported or may be supported only in + * fullscreen mode. If it is not supported then "blitting" is implied + * -- that is, the contents of the back buffer are copied using a fast + * block transfer operation from the back buffer to the front buffer. + * + * The front buffer is the one that is displayed. + * + * @author Eric Blake (ebb9@email.byu.edu) + * + * @see BufferStrategy#getCapabilities() + * @see GraphicsConfiguration#getBufferCapabilities() + * + * @since 1.4 + */ +public class BufferCapabilities implements Cloneable +{ + /** + * A type-safe enumeration of buffer flipping results. + * + * @see AttributeValue + */ + public static final class FlipContents extends AttributeValue + { + /** + * The names of the different flipping results. + */ + private static final String[] NAMES + = { "undefined", "background", "prior", "copied" }; + + /** + * The contents of the back buffer are undefined after flipping. + */ + public static final FlipContents UNDEFINED = new FlipContents(0); + + /** + * The back buffer is cleared with the background color after + * flipping. + */ + public static final FlipContents BACKGROUND = new FlipContents(1); + + /** + * The back buffer contains the pre-flipping contents of the front + * buffer after flipping. In other words a true "flip" has been + * performed. + */ + public static final FlipContents PRIOR = new FlipContents(2); + + /** + * The back buffer has the same contents as the front buffer after + * flipping. + */ + public static final FlipContents COPIED = new FlipContents(3); + + /** + * Create a new flipping result descriptor. + * + * @param value the enumeration value + */ + private FlipContents(int value) + { + super(value, NAMES); + } + } + + /** + * Front buffer capabilities descriptor. + */ + private final ImageCapabilities front; + + /** + * Back buffer capabilities descriptor. + */ + private final ImageCapabilities back; + + /** + * Describes the results of a "flip" operation. + */ + private final FlipContents flip; + + /** + * Creates a buffer capabilities object. + * + * @param frontCaps front buffer capabilities descriptor + * @param backCaps back buffer capabilities descriptor + * @param flip the results of a flip operation or null if + * flipping is not supported + * + * @exception IllegalArgumentException if frontCaps or backCaps is + * null + */ + public BufferCapabilities(ImageCapabilities frontCaps, + ImageCapabilities backCaps, + FlipContents flip) + { + if (frontCaps == null || backCaps == null) + throw new IllegalArgumentException(); + this.front = frontCaps; + this.back = backCaps; + this.flip = flip; + } + + /** + * Retrieve the front buffer's image capabilities. + * + * @return the front buffer's image capabilities + */ + public ImageCapabilities getFrontBufferCapabilities() + { + return front; + } + + /** + * Retrieve the back buffer's image capabilities. + * + * @return the back buffer's image capabilities + */ + public ImageCapabilities getBackBufferCapabilities() + { + return back; + } + + /** + * Return whether or not flipping is supported. + * + * @return true if flipping is supported, false otherwise + */ + public boolean isPageFlipping() + { + return flip != null; + } + + /** + * Retrieve the result of a flipping operation. If this method + * returns null then flipping is not supported. This implies that + * "blitting", a fast block transfer, is used to copy the contents + * of the back buffer to the front buffer. Other possible return + * values are: + *

    + *
  • FlipContents.UNDEFINED the contents of the + * back buffer are undefined after flipping.
  • + *
  • FlipContents.BACKGROUND the contents of the + * back buffer are cleared to the background color after + * flipping.
  • + *
  • FlipContents.PRIOR the back buffer contains + * the pre-flipping contents of the front * buffer after + * flipping.
  • + *
  • FlipContents.COPIED the back buffer has the + * same contents as the front buffer after flipping.
  • + *
+ * + * @return the result of a flipping operation or null if flipping is + * not supported + */ + public FlipContents getFlipContents() + { + return flip; + } + + /** + * Returns true if flipping is only supported in fullscreen mode. + * + * @return true if flipping is only supported in fullscreen mode, + * false otherwise + */ + public boolean isFullScreenRequired() + { + return true; + } + + /** + * Returns true if flipping can involve more than two buffers. One + * or more intermediate buffers may be available in addition to the + * front and back buffers. + * + * @return true if there are more than two buffers available for + * flipping, false otherwise + */ + public boolean isMultiBufferAvailable() + { + return false; + } + + /** + * Clone this buffering capability descriptor. + * + * @return a clone of this buffer capability descriptor + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); + } + } +} diff --git a/libjava/classpath/java/awt/Button.java b/libjava/classpath/java/awt/Button.java new file mode 100644 index 000000000..458a45e53 --- /dev/null +++ b/libjava/classpath/java/awt/Button.java @@ -0,0 +1,467 @@ +/* Button.java -- AWT button widget + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.peer.ButtonPeer; +import java.lang.reflect.Array; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleValue; + +/** + * This class provides a button widget for the AWT. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class Button extends Component + implements java.io.Serializable, Accessible +{ + +/* + * Static Variables + */ + +// FIXME: Need readObject/writeObject for serialization + +// Serialization version constant +private static final long serialVersionUID = -8774683716313001058L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The action command name for this button. + * This is package-private to avoid an accessor method. + */ +String actionCommand; + +/** + * @serial The label for this button. + * This is package-private to avoid an accessor method. + */ +String label; + +// List of ActionListeners for this class. +private transient ActionListener action_listeners; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_button_number; + + protected class AccessibleAWTButton extends AccessibleAWTComponent + implements AccessibleAction, AccessibleValue + { + private static final long serialVersionUID = -5932203980244017102L; + + protected AccessibleAWTButton() + { + // Do nothing here. + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#getAccessibleActionCount() + */ + public int getAccessibleActionCount() + { + // Only 1 action possible + return 1; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int) + */ + public String getAccessibleActionDescription(int i) + { + // JDK 1.4.2 returns the string "click" for action 0. However, the API + // docs don't say what the string to be returned is, beyond being a + // description of the action. So we return the same thing for + // compatibility with 1.4.2. + if (i == 0) + return "click"; + return null; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#doAccessibleAction(int) + */ + public boolean doAccessibleAction(int i) + { + if (i != 0) + return false; + processActionEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand)); + return true; + } + + public String getAccessibleName() + { + return label; + } + + public AccessibleAction getAccessibleAction() + { + return this; + } + + public AccessibleValue getAccessibleValue() + { + return this; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue() + */ + public Number getCurrentAccessibleValue() + { + // Docs say return 1 if selected, but buttons can't be selected, right? + return new Integer(0); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number) + */ + public boolean setCurrentAccessibleValue(Number number) + { + // Since there's no selection with buttons, we're ignoring this. + // TODO someone who knows shoulw check this. + return false; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue() + */ + public Number getMinimumAccessibleValue() + { + return new Integer(0); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue() + */ + public Number getMaximumAccessibleValue() + { + return new Integer(0); + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.PUSH_BUTTON; + } + } + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of Button with no label. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true + */ +public +Button() +{ + this(""); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of Button with the specified + * label. The action command name is also initialized to this value. + * + * @param label The label to display on the button. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true + */ +public +Button(String label) +{ + this.label = label; + actionCommand = label; + + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the label for this button. + * + * @return The label for this button. + */ +public String +getLabel() +{ + return(label); +} + +/*************************************************************************/ + +/** + * Sets the label for this button to the specified value. + * + * @param label The new label for this button. + */ +public synchronized void +setLabel(String label) +{ + this.label = label; + actionCommand = label; + if (peer != null) + { + ButtonPeer bp = (ButtonPeer) peer; + bp.setLabel (label); + } +} + +/*************************************************************************/ + +/** + * Returns the action command name for this button. + * + * @return The action command name for this button. + */ +public String +getActionCommand() +{ + return(actionCommand); +} + +/*************************************************************************/ + +/** + * Sets the action command name for this button to the specified value. + * + * @param actionCommand The new action command name. + */ +public void +setActionCommand(String actionCommand) +{ + this.actionCommand = actionCommand == null ? label : actionCommand; +} + +/*************************************************************************/ + +/** + * Adds a new entry to the list of listeners that will receive + * action events from this button. + * + * @param listener The listener to add. + */ +public synchronized void +addActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.add(action_listeners, listener); +} + +/*************************************************************************/ + +/** + * Removes the specified listener from the list of listeners that will + * receive action events from this button. + * + * @param listener The listener to remove. + */ +public synchronized void +removeActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.remove(action_listeners, listener); +} + + /** + * Returns all added ActionListener objects. + * + * @return an array of listeners + * + * @since 1.4 + */ + public synchronized ActionListener[] getActionListeners() + { + return (ActionListener[]) + AWTEventMulticaster.getListeners(action_listeners, + ActionListener.class); + } + +/** + * Returns all registered EventListers of the given listenerType. + * listenerType must be a subclass of EventListener, or a + * ClassClassException is thrown. + * + * @param listenerType the listener type to return + * + * @return an array of listeners + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements @see java.util.EventListener. + * + * @since 1.3 + */ + public T[] getListeners(Class listenerType) + { + if (listenerType == ActionListener.class) + return (T[]) getActionListeners(); + return (T[]) Array.newInstance(listenerType, 0); + } + +/*************************************************************************/ + +/** + * Notifies this button that it should create its native peer object. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createButton (this); + super.addNotify(); +} + +/*************************************************************************/ + +/** + * Processes an event for this button. If the specified event is an + * instance of ActionEvent, then the + * processActionEvent() method is called to dispatch it + * to any registered listeners. Otherwise, the superclass method + * will be invoked. Note that this method will not be called at all + * unless ActionEvent's are enabled. This will be done + * implicitly if any listeners are added. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * This method dispatches an action event for this button to any + * registered listeners. + * + * @param event The event to process. + */ +protected void +processActionEvent(ActionEvent event) +{ + if (action_listeners != null) + action_listeners.actionPerformed(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this button. + * + * @return A debugging string for this button. + */ +protected String +paramString() +{ + return getName () + "," + getX () + "," + getY () + "," + + getWidth () + "x" + getHeight () + ",label=" + getLabel (); +} + +/** + * Gets the AccessibleContext associated with this Button. + * The context is created, if necessary. + * + * @return the associated context + */ +public AccessibleContext getAccessibleContext() +{ + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTButton(); + return accessibleContext; +} + + /** + * Generate a unique name for this button. + * + * @return A unique name for this button. + */ + String generateName () + { + return "button" + getUniqueLong (); + } + + private static synchronized long getUniqueLong () + { + return next_button_number++; + } + +} // class Button diff --git a/libjava/classpath/java/awt/Canvas.java b/libjava/classpath/java/awt/Canvas.java new file mode 100644 index 000000000..ab7a8e3fa --- /dev/null +++ b/libjava/classpath/java/awt/Canvas.java @@ -0,0 +1,354 @@ +/* Canvas.java -- + Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.BufferStrategy; +import java.awt.peer.ComponentPeer; +import java.io.Serializable; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * The Canvas component provides a blank rectangular + * area, which the client application can use for drawing and for + * capturing events. By overriding the paint() method, + * the canvas can be used for anything from simple line drawings to + * full-scale custom components. + * + * @author Original author unknown + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.0 + */ + +public class Canvas + extends Component + implements Serializable, Accessible +{ + + /** + * Compatible with Sun's JDK. + */ + private static final long serialVersionUID = -2284879212465893870L; + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_canvas_number; + + /** + * The buffer strategy associated with this canvas. + */ + transient BufferStrategy bufferStrategy; + + /** + * Initializes a new instance of Canvas. + */ + public Canvas() + { + } + + /** + * Initializes a new instance of Canvas + * with the supplied graphics configuration. + * + * @param graphicsConfiguration the graphics configuration to use + * for this particular canvas. + */ + public Canvas(GraphicsConfiguration graphicsConfiguration) + { + this.graphicsConfig = graphicsConfiguration; + } + + /** + * Creates the native peer for this object. + */ + public void addNotify() + { + if (peer == null) + peer = (ComponentPeer) getToolkit().createCanvas(this); + super.addNotify(); + } + + /** + * Repaints the canvas window. This method should be overridden by + * a subclass to do something useful, as this method simply paints + * the window with the background color. + * + * @param gfx the Graphics to use for painting + */ + public void paint(Graphics gfx) + { + /* This implementation doesn't make much sense since the filling + of background color is guaranteed for heavyweight components + such as this. But there's no need to worry, since paint() is + usually overridden anyway. */ + gfx.setColor(getBackground()); + Dimension size = getSize(); + gfx.fillRect(0, 0, size.width, size.height); + } + + /** + * This class provides accessibility support for the canvas. + */ + protected class AccessibleAWTCanvas + extends AccessibleAWTComponent + { + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = -6325592262103146699L; + + /** + * Constructor for the accessible canvas. + */ + protected AccessibleAWTCanvas() + { + } + + /** + * Returns the accessible role for the canvas. + * + * @return an instance of AccessibleRole, describing + * the role of the canvas. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.CANVAS; + } + + } + + /** + * Gets the AccessibleContext associated with this Canvas. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTCanvas(); + return accessibleContext; + } + + /** + * A BltBufferStrategy for canvases. + */ + private class CanvasBltBufferStrategy extends BltBufferStrategy + { + /** + * Creates a block transfer strategy for this canvas. + * + * @param numBuffers the number of buffers in this strategy + * @param accelerated true if the buffer should be accelerated, + * false otherwise + */ + CanvasBltBufferStrategy(int numBuffers, boolean accelerated) + { + super(numBuffers, + new BufferCapabilities(new ImageCapabilities(accelerated), + new ImageCapabilities(accelerated), + BufferCapabilities.FlipContents.COPIED)); + } + } + + /** + * A FlipBufferStrategy for canvases. + */ + private class CanvasFlipBufferStrategy extends FlipBufferStrategy + { + /** + * Creates a flip buffer strategy for this canvas. + * + * @param numBuffers the number of buffers in this strategy + * + * @throws AWTException if the requested number of buffers is not + * supported + */ + CanvasFlipBufferStrategy(int numBuffers) + throws AWTException + { + super(numBuffers, + new BufferCapabilities(new ImageCapabilities(true), + new ImageCapabilities(true), + BufferCapabilities.FlipContents.COPIED)); + } + } + + /** + * Creates a buffering strategy that manages how this canvas is + * repainted. This method attempts to create the optimum strategy + * based on the desired number of buffers. Hardware or software + * acceleration may be used. + * + * createBufferStrategy attempts different levels of optimization, + * but guarantees that some strategy with the requested number of + * buffers will be created even if it is not optimal. First it + * attempts to create a page flipping strategy, then an accelerated + * blitting strategy, then an unaccelerated blitting strategy. + * + * Calling this method causes any existing buffer strategy to be + * destroyed. + * + * @param numBuffers the number of buffers in this strategy + * + * @throws IllegalArgumentException if requested number of buffers + * is less than one + * @throws IllegalStateException if this canvas is not displayable + * + * @since 1.4 + */ + public void createBufferStrategy(int numBuffers) + { + if (numBuffers < 1) + throw new IllegalArgumentException("Canvas.createBufferStrategy: number" + + " of buffers is less than one"); + + if (!isDisplayable()) + throw new IllegalStateException("Canvas.createBufferStrategy: canvas is" + + " not displayable"); + + BufferStrategy newStrategy = null; + + // try a flipping strategy + try + { + newStrategy = new CanvasFlipBufferStrategy(numBuffers); + } + catch (AWTException e) + { + } + + // fall back to an accelerated blitting strategy + if (newStrategy == null) + newStrategy = new CanvasBltBufferStrategy(numBuffers, true); + + bufferStrategy = newStrategy; + } + + /** + * Creates a buffering strategy that manages how this canvas is + * repainted. This method attempts to create a strategy based on + * the specified capabilities and throws an exception if the + * requested strategy is not supported. + * + * Calling this method causes any existing buffer strategy to be + * destroyed. + * + * @param numBuffers the number of buffers in this strategy + * @param caps the requested buffering capabilities + * + * @throws AWTException if the requested capabilities are not + * supported + * @throws IllegalArgumentException if requested number of buffers + * is less than one or if caps is null + * + * @since 1.4 + */ + public void createBufferStrategy(int numBuffers, BufferCapabilities caps) + throws AWTException + { + if (numBuffers < 1) + throw new IllegalArgumentException("Canvas.createBufferStrategy: number" + + " of buffers is less than one"); + + if (caps == null) + throw new IllegalArgumentException("Canvas.createBufferStrategy:" + + " capabilities object is null"); + + // a flipping strategy was requested + if (caps.isPageFlipping()) + bufferStrategy = new CanvasFlipBufferStrategy(numBuffers); + else + bufferStrategy = new CanvasBltBufferStrategy(numBuffers, true); + } + + /** + * Returns the buffer strategy used by the canvas. + * + * @return the buffer strategy. + * @since 1.4 + */ + public BufferStrategy getBufferStrategy() + { + return bufferStrategy; + } + + /** + * Updates the canvas in response to a request to + * repaint() it. The canvas is cleared + * with the current background colour, before paint() + * is called to add the new contents. Subclasses + * which override this method should either call this + * method via super.update(graphics) or re-implement + * this behaviour, so as to ensure that the canvas is + * clear before painting takes place. + * + * @param graphics the graphics context. + */ + public void update(Graphics graphics) + { + Dimension size; + + /* Clear the canvas */ + size = getSize(); + graphics.clearRect(0, 0, size.width, size.height); + /* Call the paint method */ + paint(graphics); + } + + /** + * Generate a unique name for this Canvas. + * + * @return A unique name for this Canvas. + */ + String generateName() + { + return "canvas" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_canvas_number++; + } +} diff --git a/libjava/classpath/java/awt/CardLayout.java b/libjava/classpath/java/awt/CardLayout.java new file mode 100644 index 000000000..c36daf482 --- /dev/null +++ b/libjava/classpath/java/awt/CardLayout.java @@ -0,0 +1,501 @@ +/* CardLayout.java -- Card-based layout engine + Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * This class implements a card-based layout scheme. Each included + * component is treated as a card. Only one card can be shown at a + * time. This class includes methods for changing which card is + * shown. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class CardLayout implements LayoutManager2, Serializable +{ + private static final long serialVersionUID = -4328196481005934313L; + + /** + * Initializes a new instance of CardLayout with horizontal + * and vertical gaps of 0. + */ + public CardLayout () + { + this (0, 0); + } + + /** + * Create a new CardLayout object with the specified + * horizontal and vertical gaps. + * + * @param hgap The horizontal gap + * @param vgap The vertical gap + */ + public CardLayout (int hgap, int vgap) + { + this.hgap = hgap; + this.vgap = vgap; + this.tab = new Hashtable (); + } + + /** + * Add a new component to the layout. The constraint must be a + * string which is used to name the component. This string can + * later be used to refer to the particular component. + * + * @param comp The component to add + * @param constraints The name by which the component can later be called + * + * @exception IllegalArgumentException If `constraints' is not a + * String + */ + public void addLayoutComponent (Component comp, Object constraints) + { + if (! (constraints instanceof String)) + throw new IllegalArgumentException ("Object " + constraints + + " is not a string"); + addLayoutComponent ((String) constraints, comp); + } + + /** + * Add a new component to the layout. The name can be used later + * to refer to the component. + * + * @param name The name by which the component can later be called + * @param comp The component to add + * + * @deprecated This method is deprecated in favor of + * addLayoutComponent(Component, Object). + */ + public void addLayoutComponent (String name, Component comp) + { + tab.put (name, comp); + // First component added is the default component. + comp.setVisible(tab.size() == 1); + } + + /** + * Cause the first component in the container to be displayed. + * + * @param parent The parent container, not null. + */ + public void first (Container parent) + { + gotoComponent (parent, FIRST); + } + + /** + * Return this layout manager's horizontal gap. + * + * @return the horizontal gap + */ + public int getHgap () + { + return hgap; + } + + /** + * Return this layout manager's x alignment. This method always + * returns Component.CENTER_ALIGNMENT. + * + * @param parent Container using this layout manager instance + * + * @return the x-axis alignment + */ + public float getLayoutAlignmentX (Container parent) + { + return Component.CENTER_ALIGNMENT; + } + + /** + * Returns this layout manager's y alignment. This method always + * returns Component.CENTER_ALIGNMENT. + * + * @param parent Container using this layout manager instance + * + * @return the y-axis alignment + */ + public float getLayoutAlignmentY (Container parent) + { + return Component.CENTER_ALIGNMENT; + } + + /** + * Return this layout manager's vertical gap. + * + * @return the vertical gap + */ + public int getVgap () + { + return vgap; + } + + /** + * Invalidate this layout manager's state. + */ + public void invalidateLayout (Container target) + { + // Do nothing. + } + + /** + * Cause the last component in the container to be displayed. + * + * @param parent The parent container, not null. + */ + public void last (Container parent) + { + gotoComponent (parent, LAST); + } + + /** + * Lays out the container. This is done by resizing the child components + * to be the same size as the parent, less insets and gaps. + * + * @param parent The parent container. + */ + public void layoutContainer (Container parent) + { + synchronized (parent.getTreeLock ()) + { + int width = parent.width; + int height = parent.height; + + Insets ins = parent.getInsets (); + + int num = parent.ncomponents; + Component[] comps = parent.component; + + int x = ins.left + hgap; + int y = ins.top + vgap; + width = width - 2 * hgap - ins.left - ins.right; + height = height - 2 * vgap - ins.top - ins.bottom; + + for (int i = 0; i < num; ++i) + comps[i].setBounds (x, y, width, height); + } + } + + /** + * Get the maximum layout size of the container. + * + * @param target The parent container + * + * @return the maximum layout size + */ + public Dimension maximumLayoutSize (Container target) + { + if (target == null || target.ncomponents == 0) + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + // The JCL says that this returns Integer.MAX_VALUE for both + // dimensions. But that just seems wrong to me. + return getSize (target, MAX); + } + + /** + * Get the minimum layout size of the container. + * + * @param target The parent container + * + * @return the minimum layout size + */ + public Dimension minimumLayoutSize (Container target) + { + return getSize (target, MIN); + } + + /** + * Cause the next component in the container to be displayed. If + * this current card is the last one in the deck, the first + * component is displayed. + * + * @param parent The parent container, not null. + */ + public void next (Container parent) + { + gotoComponent (parent, NEXT); + } + + /** + * Get the preferred layout size of the container. + * + * @param parent The parent container + * + * @return the preferred layout size + */ + public Dimension preferredLayoutSize (Container parent) + { + return getSize (parent, PREF); + } + + /** + * Cause the previous component in the container to be displayed. + * If this current card is the first one in the deck, the last + * component is displayed. + * + * @param parent The parent container, not null. + */ + public void previous (Container parent) + { + gotoComponent (parent, PREV); + } + + /** + * Remove the indicated component from this layout manager. + * + * @param comp The component to remove + */ + public void removeLayoutComponent (Component comp) + { + Enumeration e = tab.keys (); + while (e.hasMoreElements ()) + { + Object key = e.nextElement (); + if (tab.get (key) == comp) + { + tab.remove (key); + Container parent = comp.getParent(); + next(parent); + break; + } + } + } + + /** + * Set this layout manager's horizontal gap. + * + * @param hgap The new gap + */ + public void setHgap (int hgap) + { + this.hgap = hgap; + } + + /** + * Set this layout manager's vertical gap. + * + * @param vgap The new gap + */ + public void setVgap (int vgap) + { + this.vgap = vgap; + } + + /** + * Cause the named component to be shown. If the component name is + * unknown or null, this method does nothing. + * + * @param parent The parent container, not null. + * @param name The name of the component to show + */ + public void show (Container parent, String name) + { + if (name == null) + return; + + if (parent.getLayout() != this) + throw new IllegalArgumentException("parent's layout is not this CardLayout"); + + Object target = tab.get (name); + if (target != null) + { + int num = parent.ncomponents; + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + for (int i = 0; i < num; ++i) + { + if (comps[i].isVisible()) + { + if (target == comps[i]) + return; + comps[i].setVisible (false); + } + } + ((Component) target).setVisible (true); + parent.validate(); + } + } + + /** + * Returns a string representation of this layout manager. + * + * @return A string representation of this object. + */ + public String toString () + { + return getClass ().getName () + "[hgap=" + hgap + ",vgap=" + vgap + "]"; + } + + /** + * This implements first(), last(), next(), and previous(). + * + * @param parent The parent container + * @param what The type of goto: FIRST, LAST, NEXT or PREV + * + * @throws IllegalArgumentException if parent has not this + * CardLayout set as its layout. + */ + private void gotoComponent (Container parent, int what) + { + if (parent.getLayout() != this) + throw new IllegalArgumentException("parent's layout is not this CardLayout"); + + synchronized (parent.getTreeLock ()) + { + int num = parent.ncomponents; + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + if (num == 1) + { + comps[0].setVisible(true); + return; + } + + int choice = -1; + + if (what == FIRST) + choice = 0; + else if (what == LAST) + choice = num - 1; + + for (int i = 0; i < num; ++i) + { + if (comps[i].isVisible ()) + { + if (choice == i) + { + // Do nothing if we're already looking at the right + // component. + return; + } + else if (what == PREV) + { + choice = i - 1; + if (choice < 0) + choice = num - 1; + } + else if (what == NEXT) + { + choice = i + 1; + if (choice == num) + choice = 0; + } + comps[i].setVisible (false); + + if (choice >= 0) + break; + } else + { + comps[i].setVisible(true); + } + } + + if (choice >= 0 && choice < num) + comps[choice].setVisible (true); + } + } + + // Compute the size according to WHAT. + private Dimension getSize (Container parent, int what) + { + synchronized (parent.getTreeLock ()) + { + int w = 0, h = 0, num = parent.ncomponents; + Component[] comps = parent.component; + + for (int i = 0; i < num; ++i) + { + Dimension d; + + if (what == MIN) + d = comps[i].getMinimumSize (); + else if (what == MAX) + d = comps[i].getMaximumSize (); + else + d = comps[i].getPreferredSize (); + + w = Math.max (d.width, w); + h = Math.max (d.height, h); + } + + Insets i = parent.getInsets (); + w += 2 * hgap + i.right + i.left; + h += 2 * vgap + i.bottom + i.top; + + // Handle overflow. + if (w < 0) + w = Integer.MAX_VALUE; + if (h < 0) + h = Integer.MAX_VALUE; + + return new Dimension (w, h); + } + } + + /** + * @serial Horizontal gap value. + */ + private int hgap; + + /** + * @serial Vertical gap value. + */ + private int vgap; + + /** + * @serial Table of named components. + */ + private Hashtable tab; + + // These constants are used by the private gotoComponent method. + private static final int FIRST = 0; + private static final int LAST = 1; + private static final int NEXT = 2; + private static final int PREV = 3; + + // These constants are used by the private getSize method. + private static final int MIN = 0; + private static final int MAX = 1; + private static final int PREF = 2; +} diff --git a/libjava/classpath/java/awt/Checkbox.java b/libjava/classpath/java/awt/Checkbox.java new file mode 100644 index 000000000..dffb57da3 --- /dev/null +++ b/libjava/classpath/java/awt/Checkbox.java @@ -0,0 +1,665 @@ +/* Checkbox.java -- An AWT checkbox widget + Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.peer.CheckboxPeer; +import java.io.Serializable; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleValue; + +/** + * This class implements a component which has an on/off state. Two + * or more Checkboxes can be grouped by a CheckboxGroup. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class Checkbox extends Component + implements ItemSelectable, Accessible, Serializable +{ + +// FIXME: Need readObject/writeObject for this. + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = 7270714317450821763L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The checkbox group for this checkbox. + */ +private CheckboxGroup group; + +/** + * @serial The label on this checkbox. + */ +private String label; + +/** + * @serial The state of this checkbox. + * This is package-private to avoid an accessor method. + */ +boolean state; + +// The list of listeners for this object. +private transient ItemListener item_listeners; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_checkbox_number; + +/** + * This class provides accessibility support for the + * checkbox. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +protected class AccessibleAWTCheckbox + extends AccessibleAWTComponent + implements ItemListener, AccessibleAction, AccessibleValue +{ + /** + * Serialization constant to match JDK 1.5 + */ + private static final long serialVersionUID = 7881579233144754107L; + + /** + * Default constructor which simply calls the + * super class for generic component accessibility + * handling. + */ + public AccessibleAWTCheckbox() + { + super(); + } + + /** + * Captures changes to the state of the checkbox and + * fires appropriate accessible property change events. + * + * @param event the event fired. + * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent) + */ + public void itemStateChanged(ItemEvent event) + { + firePropertyChange(ACCESSIBLE_STATE_PROPERTY, + state ? null : AccessibleState.CHECKED, + state ? AccessibleState.CHECKED : null); + } + + /** + * Returns an implementation of the AccessibleAction + * interface for this accessible object. In this case, the + * current instance is simply returned (with a more appropriate + * type), as it also implements the accessible action as well as + * the context. + * + * @return the accessible action associated with this context. + * @see javax.accessibility.AccessibleAction + */ + public AccessibleAction getAccessibleAction() + { + return this; + } + + /** + * Returns an implementation of the AccessibleValue + * interface for this accessible object. In this case, the + * current instance is simply returned (with a more appropriate + * type), as it also implements the accessible value as well as + * the context. + * + * @return the accessible value associated with this context. + * @see javax.accessibility.AccessibleValue + */ + public AccessibleValue getAccessibleValue() + { + return this; + } + + /* + * The following methods are implemented in the JDK (up to + * 1.5) as stubs. We do likewise here. + */ + + /** + * Returns the number of actions associated with this accessible + * object. This default implementation returns 0. + * + * @return the number of accessible actions available. + * @see javax.accessibility.AccessibleAction#getAccessibleActionCount() + */ + public int getAccessibleActionCount() + { + // 1.4.1 and 1.5 do this + return 0; + } + + /** + * Returns a description of the action with the supplied id. + * This default implementation always returns null. + * + * @param i the id of the action whose description should be + * retrieved. + * @return a String describing the action. + * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int) + */ + public String getAccessibleActionDescription(int i) + { + // 1.5 does this + return null; + } + + /** + * Executes the action with the specified id. This + * default implementation simply returns false. + * + * @param i the id of the action to perform. + * @return true if the action was performed. + * @see javax.accessibility.AccessibleAction#doAccessibleAction(int) + */ + public boolean doAccessibleAction(int i) + { + // 1.5 does this + return false; + } + + /** + * Returns the current value of this accessible object. + * If no value has been set, null is returned. This + * default implementation always returns null, regardless. + * + * @return the numeric value of this object, or null if + * no value has been set. + * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue() + */ + public Number getCurrentAccessibleValue() + { + // 1.5 does this + return null; + } + + /** + * Sets the current value of this accessible object + * to that supplied. In this default implementation, + * the value is never set and the method always returns + * false. + * + * @param number the new accessible value. + * @return true if the value was set. + * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number) + */ + public boolean setCurrentAccessibleValue(Number number) + { + // 1.5 does this + return false; + } + + /** + * Returns the minimum acceptable accessible value used + * by this object, or null if no minimum value exists. + * This default implementation always returns null. + * + * @return the minimum acceptable accessible value, or null + * if there is no minimum. + * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue() + */ + public Number getMinimumAccessibleValue() + { + return null; + } + + /** + * Returns the maximum acceptable accessible value used + * by this object, or null if no maximum value exists. + * This default implementation always returns null. + * + * @return the maximum acceptable accessible value, or null + * if there is no maximum. + * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue() + */ + public Number getMaximumAccessibleValue() + { + return null; + } + + /** + * Returns the role of this accessible object. + * + * @return the instance of AccessibleRole, + * which describes this object. + * @see javax.accessibility.AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.CHECK_BOX; + } + + /** + * Returns the state set of this accessible object. + * + * @return a set of AccessibleStates + * which represent the current state of the + * accessible object. + * @see javax.accessibility.AccessibleState + * @see javax.accessibility.AccessibleStateSet + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet set = super.getAccessibleStateSet(); + if (state) + set.add(AccessibleState.CHECKED); + return set; + } + +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of Checkbox with no label, + * an initial state of off, and that is not part of any checkbox group. + */ +public +Checkbox() +{ + this("", false, null); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of Checkbox with the specified + * label, an initial state of off, and that is not part of any checkbox + * group. + * + * @param label The label for this checkbox. + */ +public +Checkbox(String label) +{ + this(label, false, null); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of Checkbox with the specified + * label and initial state, and that is not part of any checkbox + * group. + * + * @param label The label for this checkbox. + * @param state The initial state of the checkbox, true for + * on, false for off. + */ +public +Checkbox(String label, boolean state) +{ + this(label, state, null); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of Checkbox with the specified + * label, initial state, and checkbox group. + * + * @param label The label for this checkbox. + * @param group The checkbox group for this box, or null + * if there is no checkbox group. + * @param state The initial state of the checkbox, true for + * on, false for off. + */ +public +Checkbox(String label, CheckboxGroup group, boolean state) +{ + this(label, state, group); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of Checkbox with the specified + * label, initial state, and checkbox group. + * + * @param label The label for this checkbox. + * @param state The initial state of the checkbox, true for + * on, false for off. + * @param group The checkbox group for this box, or null + * if there is no checkbox group. + */ +public +Checkbox(String label, boolean state, CheckboxGroup group) +{ + this.label = label; + this.state = state; + this.group = group; + + if ( state && group != null ) + { + group.setSelectedCheckbox(this); + } +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the label for this checkbox. + * + * @return The label for this checkbox. + */ +public String +getLabel() +{ + return(label); +} + +/*************************************************************************/ + +/** + * Sets the label for this checkbox to the specified value. + * + * @param label The new checkbox label. + */ +public synchronized void +setLabel(String label) +{ + this.label = label; + if (peer != null) + { + CheckboxPeer cp = (CheckboxPeer) peer; + cp.setLabel(label); + } +} + +/*************************************************************************/ + +/** + * Returns the state of this checkbox. + * + * @return The state of this checkbox, which will be true for + * on and false for off. + */ +public boolean +getState() +{ + return(state); +} + +/*************************************************************************/ + +/** + * Sets the state of this checkbox to the specified value. + * + * @param state The new state of the checkbox, which will be true + * for on or false for off. + */ +public synchronized void +setState(boolean state) +{ + if (this.state != state) + { + this.state = state; + if (peer != null) + { + CheckboxPeer cp = (CheckboxPeer) peer; + cp.setState (state); + } + } +} + +/*************************************************************************/ + +/** + * Returns an array of length one containing the checkbox label if this + * checkbox is selected. Otherwise null is returned. + * + * @return The selection state of this checkbox. + */ +public Object[] +getSelectedObjects() +{ + if (state == false) + return(null); + + Object[] objs = new Object[1]; + objs[0] = label; + + return(objs); +} + +/*************************************************************************/ + +/** + * Returns the checkbox group this object is a member of, if any. + * + * @return This object's checkbox group, of null if it is + * not a member of any group. + */ +public CheckboxGroup +getCheckboxGroup() +{ + return(group); +} + +/*************************************************************************/ + +/** + * Sets this object's checkbox group to the specified group. + * + * @param group The new checkbox group, or null to make this + * object part of no checkbox group. + */ +public synchronized void +setCheckboxGroup(CheckboxGroup group) +{ + this.group = group; + if (peer != null) + { + CheckboxPeer cp = (CheckboxPeer) peer; + cp.setCheckboxGroup (group); + } +} + +/*************************************************************************/ + +/** + * Creates this object's native peer. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createCheckbox (this); + super.addNotify (); +} + + public ItemListener[] getItemListeners () + { + return (ItemListener[]) + AWTEventMulticaster.getListeners (item_listeners, ItemListener.class); + } + +/** + * Adds a new listeners to the list of registered listeners for this object. + * + * @param listener The new listener to add. + */ +public synchronized void +addItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.add(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Removes a listener from the list of registered listeners for this object. + * + * @param listener The listener to remove. + */ +public synchronized void +removeItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.remove(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Processes this event by calling processItemEvent() if it + * is any instance of ItemEvent. Otherwise it is passed to + * the superclass for processing. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ItemEvent) + processItemEvent((ItemEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * Processes this event by dispatching it to any registered listeners. + * + * @param event The ItemEvent to process. + */ +protected void +processItemEvent(ItemEvent event) +{ + if (item_listeners != null) + item_listeners.itemStateChanged(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ItemEvent.ITEM_LAST + && e.id >= ItemEvent.ITEM_FIRST) + { + ItemEvent ie = (ItemEvent) e; + int itemState = ie.getStateChange(); + setState(itemState == ItemEvent.SELECTED ? true : false); + if (item_listeners != null + || (eventMask & AWTEvent.ITEM_EVENT_MASK) != 0) + processEvent(e); + } + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + */ +protected String +paramString() +{ + // Note: We cannot add the checkbox group information here because this + // would trigger infinite recursion when CheckboxGroup.toString() is + // called and the box is in its selected state. + return ("label=" + label + ",state=" + state + "," + super.paramString()); +} + +/** + * Gets the AccessibleContext associated with this Checkbox. + * The context is created, if necessary. + * + * @return the associated context + */ +public AccessibleContext getAccessibleContext() +{ + /* Create the context if this is the first request */ + if (accessibleContext == null) + { + AccessibleAWTCheckbox ac = new AccessibleAWTCheckbox(); + accessibleContext = ac; + addItemListener(ac); + } + return accessibleContext; +} + + /** + * Generate a unique name for this checkbox. + * + * @return A unique name for this checkbox. + */ + String generateName() + { + return "checkbox" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_checkbox_number++; + } +} diff --git a/libjava/classpath/java/awt/CheckboxGroup.java b/libjava/classpath/java/awt/CheckboxGroup.java new file mode 100644 index 000000000..30f3e1635 --- /dev/null +++ b/libjava/classpath/java/awt/CheckboxGroup.java @@ -0,0 +1,172 @@ +/* CheckboxGroup.java -- A grouping class for checkboxes. + Copyright (C) 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class if for combining checkboxes into groups so that only + * one checkbox in the group can be selected at any one time. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class CheckboxGroup implements java.io.Serializable +{ + +/* + * Static Variables + */ + +// Serialization constant +private static final long serialVersionUID = 3729780091441768983L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The currently selected checkbox. + */ +private Checkbox selectedCheckbox; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of CheckboxGroup. + */ +public +CheckboxGroup() +{ +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the currently selected checkbox, or null if none + * of the checkboxes in this group are selected. + * + * @return The selected checkbox. + */ +public Checkbox +getSelectedCheckbox() +{ + return getCurrent (); +} + +/*************************************************************************/ + +/** + * Returns the currently selected checkbox, or null if none + * of the checkboxes in this group are selected. + * + * @return The selected checkbox. + * + * @deprecated This method is deprecated in favor of + * getSelectedCheckbox(). + */ +public Checkbox +getCurrent() +{ + return(selectedCheckbox); +} + +/*************************************************************************/ + +/** + * This method sets the specified checkbox to be the selected on in this + * group, and unsets all others. + * + * @param selectedCheckbox The new selected checkbox. + */ +public void +setSelectedCheckbox(Checkbox selectedCheckbox) +{ + setCurrent (selectedCheckbox); +} + +/*************************************************************************/ + +/** + * This method sets the specified checkbox to be the selected on in this + * group, and unsets all others. + * + * @param selectedCheckbox The new selected checkbox. + * + * @deprecated This method is deprecated in favor of + * setSelectedCheckbox(). + */ +public void +setCurrent(Checkbox selectedCheckbox) +{ + if (this.selectedCheckbox != null) + { + if (this.selectedCheckbox.getCheckboxGroup() != this) + return; + + this.selectedCheckbox.setState(false); + } + + this.selectedCheckbox = selectedCheckbox; + if (selectedCheckbox != null) + selectedCheckbox.setState(true); +} + +/*************************************************************************/ + +/** + * Returns a string representation of this checkbox group. + * + * @return A string representation of this checkbox group. + */ +public String +toString() +{ + return(getClass().getName() + "[selectedCheckbox=" + selectedCheckbox + "]"); +} + +} // class CheckboxGroup diff --git a/libjava/classpath/java/awt/CheckboxMenuItem.java b/libjava/classpath/java/awt/CheckboxMenuItem.java new file mode 100644 index 000000000..597e1964b --- /dev/null +++ b/libjava/classpath/java/awt/CheckboxMenuItem.java @@ -0,0 +1,376 @@ +/* CheckboxMenuItem.java -- A menu option with a checkbox on it. + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.peer.CheckboxMenuItemPeer; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleValue; + +/** + * This class implements a menu item that has a checkbox on it indicating + * the selected state of some option. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class CheckboxMenuItem extends MenuItem + implements ItemSelectable, Accessible +{ + +/* + * Static Variables + */ + +/** + * The number used to generate the name returned by getName. + */ +private static transient long next_chkmenuitem_number; + +// Serialization constant +private static final long serialVersionUID = 6190621106981774043L; + +/* + * Instance Variables + */ + +/** + * @serial The state of the checkbox, with true being on and + * false being off. + */ +private boolean state; + +// List of registered ItemListeners +private transient ItemListener item_listeners; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of CheckboxMenuItem with no + * label and an initial state of off. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +CheckboxMenuItem() +{ + this("", false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of CheckboxMenuItem with the + * specified label and an initial state of off. + * + * @param label The label of the menu item. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +CheckboxMenuItem(String label) +{ + this(label, false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of CheckboxMenuItem with the + * specified label and initial state. + * + * @param label The label of the menu item. + * @param state The initial state of the menu item, where true + * is on, and false is off. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +CheckboxMenuItem(String label, boolean state) +{ + super(label); + this.state = state; + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the state of this menu item. + * + * @return The state of this menu item. + */ +public boolean +getState() +{ + return(state); +} + +/*************************************************************************/ + +/** + * Sets the state of this menu item. + * + * @param state The initial state of the menu item, where true + * is on, and false is off. + */ +public synchronized void +setState(boolean state) +{ + this.state = state; + if (peer != null) + { + CheckboxMenuItemPeer cp = (CheckboxMenuItemPeer) peer; + cp.setState (state); + } +} + +/*************************************************************************/ + +/** + * Returns an array of length 1 with the menu item label for this object + * if the state is on. Otherwise null is returned. + * + * @return An array with this menu item's label if it has a state of on, + * or null otherwise. + */ +public Object[] +getSelectedObjects() +{ + if (state == false) + return(null); + + Object[] obj = new Object[1]; + obj[0] = getLabel(); + + return(obj); +} + +/*************************************************************************/ + +/** + * Create's this object's native peer + */ +public synchronized void +addNotify() +{ + if (peer == null) + peer = getToolkit().createCheckboxMenuItem(this); + + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Adds the specified listener to the list of registered item listeners + * for this object. + * + * @param listener The listener to add. + */ +public synchronized void +addItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.add(item_listeners, listener); + + enableEvents(AWTEvent.ITEM_EVENT_MASK); +} + +/*************************************************************************/ + +/** + * Removes the specified listener from the list of registered item + * listeners for this object. + * + * @param listener The listener to remove. + */ +public synchronized void +removeItemListener(ItemListener listener) +{ + item_listeners = AWTEventMulticaster.remove(item_listeners, listener); +} + +/*************************************************************************/ + +/** + * Processes the specified event by calling processItemEvent() + * if it is an instance of ItemEvent or calling the superclass + * method otherwise. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ItemEvent) + processItemEvent((ItemEvent)event); + else + super.processEvent(event); +} + +/*************************************************************************/ + +/** + * Processes the specified event by dispatching it to any registered listeners. + * + * @param event The event to process. + */ +protected void +processItemEvent(ItemEvent event) +{ + if (item_listeners != null) + item_listeners.itemStateChanged(event); +} + +void +dispatchEventImpl(AWTEvent e) +{ + if (e instanceof ItemEvent) + { + synchronized (this) + { + state = (((ItemEvent) e).getStateChange() == ItemEvent.SELECTED); + } + } + + if (e.id <= ItemEvent.ITEM_LAST + && e.id >= ItemEvent.ITEM_FIRST + && (item_listeners != null + || (eventMask & AWTEvent.ITEM_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +public String +paramString() +{ + return ("label=" + getLabel() + ",state=" + state + + "," + super.paramString()); +} + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this CheckboxMenuItem. FooListeners are registered using + * the addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + */ + public T[] getListeners (Class listenerType) + { + if (listenerType == ItemListener.class) + return AWTEventMulticaster.getListeners (item_listeners, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Returns an aray of all item listeners currently registered to this + * CheckBoxMenuItem. + */ + public ItemListener[] getItemListeners () + { + return (ItemListener[]) getListeners (ItemListener.class); + } + + + protected class AccessibleAWTCheckboxMenuItem extends AccessibleAWTMenuItem + implements AccessibleAction, AccessibleValue + { + // I think the base class provides the necessary implementation + + private static final long serialVersionUID = -1122642964303476L; + } + + /** + * Gets the AccessibleContext associated with this CheckboxMenuItem. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTCheckboxMenuItem(); + return accessibleContext; + } + + /** + * Generate a unique name for this CheckboxMenuItem. + * + * @return A unique name for this CheckboxMenuItem. + */ + String generateName() + { + return "chkmenuitem" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_chkmenuitem_number++; + } + +} // class CheckboxMenuItem diff --git a/libjava/classpath/java/awt/Choice.java b/libjava/classpath/java/awt/Choice.java new file mode 100644 index 000000000..5d2cfaf67 --- /dev/null +++ b/libjava/classpath/java/awt/Choice.java @@ -0,0 +1,569 @@ +/* Choice.java -- Java choice button widget. + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.peer.ChoicePeer; +import java.io.Serializable; +import java.util.EventListener; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This class implements a drop down choice list. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Choice extends Component + implements ItemSelectable, Serializable, Accessible +{ + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_choice_number; + + // Serialization constant + private static final long serialVersionUID = -4075310674757313071L; + + /** + * @serial A list of items for the choice box, which can be null. + * This is package-private to avoid an accessor method. + */ + Vector pItems = new Vector(); + + /** + * @serial The index of the selected item in the choice box. + */ + private int selectedIndex = -1; + + /** + * ItemListener chain + */ + private ItemListener item_listeners; + + /** + * This class provides accessibility support for the + * combo box. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + protected class AccessibleAWTChoice + extends AccessibleAWTComponent + implements AccessibleAction + { + + /** + * Serialization constant to match JDK 1.5 + */ + private static final long serialVersionUID = 7175603582428509322L; + + /** + * Default constructor which simply calls the + * super class for generic component accessibility + * handling. + */ + public AccessibleAWTChoice() + { + super(); + } + + /** + * Returns an implementation of the AccessibleAction + * interface for this accessible object. In this case, the + * current instance is simply returned (with a more appropriate + * type), as it also implements the accessible action as well as + * the context. + * + * @return the accessible action associated with this context. + * @see javax.accessibility.AccessibleAction + */ + public AccessibleAction getAccessibleAction() + { + return this; + } + + /** + * Returns the role of this accessible object. + * + * @return the instance of AccessibleRole, + * which describes this object. + * @see javax.accessibility.AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.COMBO_BOX; + } + + /** + * Returns the number of actions associated with this accessible + * object. In this case, it is the number of choices available. + * + * @return the number of choices available. + * @see javax.accessibility.AccessibleAction#getAccessibleActionCount() + */ + public int getAccessibleActionCount() + { + return pItems.size(); + } + + /** + * Returns a description of the action with the supplied id. + * In this case, it is the text used in displaying the particular + * choice on-screen. + * + * @param i the id of the choice whose description should be + * retrieved. + * @return the String used to describe the choice. + * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int) + */ + public String getAccessibleActionDescription(int i) + { + return (String) pItems.get(i); + } + + /** + * Executes the action with the specified id. In this case, + * calling this method provides the same behaviour as would + * choosing a choice from the list in a visual manner. + * + * @param i the id of the choice to select. + * @return true if a valid choice was specified. + * @see javax.accessibility.AccessibleAction#doAccessibleAction(int) + */ + public boolean doAccessibleAction(int i) + { + if (i < 0 || i >= pItems.size()) + return false; + + Choice.this.select( i ); + + return true; + } + } + + /** + * Initializes a new instance of Choice. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true + */ + public Choice() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); + } + + /** + * Returns the number of items in the list. + * + * @return The number of items in the list. + */ + public int getItemCount() + { + return countItems (); + } + + /** + * Returns the number of items in the list. + * + * @return The number of items in the list. + * + * @deprecated This method is deprecated in favor of getItemCount. + */ + public int countItems() + { + return pItems.size(); + } + + /** + * Returns the item at the specified index in the list. + * + * @param index The index into the list to return the item from. + * + * @exception ArrayIndexOutOfBoundsException If the index is invalid. + */ + public String getItem(int index) + { + return (String)pItems.elementAt(index); + } + + /** + * Adds the specified item to this choice box. + * + * @param item The item to add. + * + * @exception NullPointerException If the item's value is null + * + * @since 1.1 + */ + public synchronized void add(String item) + { + if (item == null) + throw new NullPointerException ("item must be non-null"); + + pItems.addElement(item); + + if (peer != null) + ((ChoicePeer) peer).add(item, getItemCount() - 1); + + if (selectedIndex == -1) + select( 0 ); + } + + /** + * Adds the specified item to this choice box. + * + * This method is oboslete since Java 2 platform 1.1. Please use + * {@link #add(String)} instead. + * + * @param item The item to add. + * + * @exception NullPointerException If the item's value is equal to null + */ + public synchronized void addItem(String item) + { + add(item); + } + + /** Inserts an item into this Choice. Existing items are shifted + * upwards. If the new item is the only item, then it is selected. + * If the currently selected item is shifted, then the first item is + * selected. If the currently selected item is not shifted, then it + * remains selected. + * + * @param item The item to add. + * @param index The index at which the item should be inserted. + * + * @exception IllegalArgumentException If index is less than 0 + */ + public synchronized void insert(String item, int index) + { + if (index < 0) + throw new IllegalArgumentException ("index may not be less then 0"); + + if (index > getItemCount ()) + index = getItemCount (); + + pItems.insertElementAt(item, index); + + if (peer != null) + ((ChoicePeer) peer).add (item, index); + + if (selectedIndex == -1 || selectedIndex >= index) + select(0); + } + + /** + * Removes the specified item from the choice box. + * + * @param item The item to remove. + * + * @exception IllegalArgumentException If the specified item doesn't exist. + */ + public synchronized void remove(String item) + { + int index = pItems.indexOf(item); + if (index == -1) + throw new IllegalArgumentException ("item \"" + + item + "\" not found in Choice"); + remove(index); + } + + /** + * Removes the item at the specified index from the choice box. + * + * @param index The index of the item to remove. + * + * @exception IndexOutOfBoundsException If the index is not valid. + */ + public synchronized void remove(int index) + { + pItems.removeElementAt(index); + + if (peer != null) + ((ChoicePeer) peer).remove( index ); + + if( getItemCount() == 0 ) + selectedIndex = -1; + else + { + if( selectedIndex > index ) + selectedIndex--; + else if( selectedIndex == index ) + selectedIndex = 0; + + if( peer != null ) + ((ChoicePeer)peer).select( selectedIndex ); + } + } + + /** + * Removes all of the objects from this choice box. + */ + public synchronized void removeAll() + { + if (getItemCount() <= 0) + return; + + pItems.removeAllElements (); + + if (peer != null) + { + ChoicePeer cp = (ChoicePeer) peer; + cp.removeAll (); + } + + selectedIndex = -1; + } + + /** + * Returns the currently selected item, or null if no item is + * selected. + * + * @return The currently selected item. + */ + public synchronized String getSelectedItem() + { + return (selectedIndex == -1 + ? null + : ((String)pItems.elementAt(selectedIndex))); + } + + /** + * Returns an array with one row containing the selected item. + * + * @return An array containing the selected item. + */ + public synchronized Object[] getSelectedObjects() + { + if (selectedIndex == -1) + return null; + + Object[] objs = new Object[1]; + objs[0] = pItems.elementAt(selectedIndex); + + return objs; + } + + /** + * Returns the index of the selected item. + * + * @return The index of the selected item. + */ + public int getSelectedIndex() + { + return selectedIndex; + } + + /** + * Forces the item at the specified index to be selected. + * + * @param index The index of the row to make selected. + * + * @exception IllegalArgumentException If the specified index is invalid. + */ + public synchronized void select(int index) + { + if ((index < 0) || (index >= getItemCount())) + throw new IllegalArgumentException("Bad index: " + index); + + if( selectedIndex == index ) + return; + + selectedIndex = index; + if( peer != null ) + ((ChoicePeer)peer).select( index ); + } + + /** + * Forces the named item to be selected. + * + * @param item The item to be selected. + * + * @exception IllegalArgumentException If the specified item does not exist. + */ + public synchronized void select(String item) + { + int index = pItems.indexOf(item); + if( index >= 0 ) + select( index ); + } + + /** + * Creates the native peer for this object. + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit ().createChoice (this); + super.addNotify (); + } + + /** + * Adds the specified listener to the list of registered listeners for + * this object. + * + * @param listener The listener to add. + */ + public synchronized void addItemListener(ItemListener listener) + { + item_listeners = AWTEventMulticaster.add(item_listeners, listener); + } + + /** + * Removes the specified listener from the list of registered listeners for + * this object. + * + * @param listener The listener to remove. + */ + public synchronized void removeItemListener(ItemListener listener) + { + item_listeners = AWTEventMulticaster.remove(item_listeners, listener); + } + + /** + * Processes this event by invoking processItemEvent() if the + * event is an instance of ItemEvent, otherwise the event + * is passed to the superclass. + * + * @param event The event to process. + */ + protected void processEvent(AWTEvent event) + { + if (event instanceof ItemEvent) + processItemEvent((ItemEvent)event); + else + super.processEvent(event); + } + + void dispatchEventImpl(AWTEvent e) + { + super.dispatchEventImpl(e); + + if( e.id <= ItemEvent.ITEM_LAST && e.id >= ItemEvent.ITEM_FIRST && + ( item_listeners != null || + ( eventMask & AWTEvent.ITEM_EVENT_MASK ) != 0 ) ) + processEvent(e); + } + + /** + * Processes item event by dispatching to any registered listeners. + * + * @param event The event to process. + */ + protected void processItemEvent(ItemEvent event) + { + int index = pItems.indexOf((String) event.getItem()); + if (item_listeners != null) + item_listeners.itemStateChanged(event); + } + + /** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ + protected String paramString() + { + return "selectedIndex=" + selectedIndex + "," + super.paramString(); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this Choice. FooListeners are registered using the addFooListener + * method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + * + * @since 1.3 + */ + public T[] getListeners (Class listenerType) + { + if (listenerType == ItemListener.class) + return AWTEventMulticaster.getListeners (item_listeners, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Returns all registered item listeners. + * + * @since 1.4 + */ + public ItemListener[] getItemListeners () + { + return (ItemListener[]) getListeners (ItemListener.class); + } + + /** + * Gets the AccessibleContext associated with this Choice. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTChoice(); + return accessibleContext; + } + + /** + * Generate a unique name for this Choice. + * + * @return A unique name for this Choice. + */ + String generateName() + { + return "choice" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_choice_number++; + } +} // class Choice diff --git a/libjava/classpath/java/awt/Color.java b/libjava/classpath/java/awt/Color.java new file mode 100644 index 000000000..243a5e86f --- /dev/null +++ b/libjava/classpath/java/awt/Color.java @@ -0,0 +1,1025 @@ +/* Color.java -- represents a color in Java + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.color.ColorSpace; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.io.Serializable; + +/** + * This class represents a color value in the AWT system. It uses the sRGB + * (standard Red-Green-Blue) system, along with an alpha value ranging from + * transparent (0.0f or 0) and opaque (1.0f or 255). The color is not + * pre-multiplied by the alpha value an any of the accessor methods. Further + * information about sRGB can be found at + * + * http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ColorSpace + * @see AlphaComposite + * @since 1.0 + * @status updated to 1.4 + */ +public class Color implements Paint, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 118526816881161077L; + + /** Constant for the color white: R=255, G=255, B=255. */ + public static final Color white = new Color(0xffffff, false); + + /** + * Constant for the color white: R=255, G=255, B=255. + * + * @since 1.4 + */ + public static final Color WHITE = white; + + /** Constant for the color light gray: R=192, G=192, B=192. */ + public static final Color lightGray = new Color(0xc0c0c0, false); + + /** + * Constant for the color light gray: R=192, G=192, B=192. + * + * @since 1.4 + */ + public static final Color LIGHT_GRAY = lightGray; + + /** Constant for the color gray: R=128, G=128, B=128. */ + public static final Color gray = new Color(0x808080, false); + + /** + * Constant for the color gray: R=128, G=128, B=128. + * + * @since 1.4 + */ + public static final Color GRAY = gray; + + /** Constant for the color dark gray: R=64, G=64, B=64. */ + public static final Color darkGray = new Color(0x404040, false); + + /** + * Constant for the color dark gray: R=64, G=64, B=64. + * + * @since 1.4 + */ + public static final Color DARK_GRAY = darkGray; + + /** Constant for the color black: R=0, G=0, B=0. */ + public static final Color black = new Color(0x000000, false); + + /** + * Constant for the color black: R=0, G=0, B=0. + * + * @since 1.4 + */ + public static final Color BLACK = black; + + /** Constant for the color red: R=255, G=0, B=0. */ + public static final Color red = new Color(0xff0000, false); + + /** + * Constant for the color red: R=255, G=0, B=0. + * + * @since 1.4 + */ + public static final Color RED = red; + + /** Constant for the color pink: R=255, G=175, B=175. */ + public static final Color pink = new Color(0xffafaf, false); + + /** + * Constant for the color pink: R=255, G=175, B=175. + * + * @since 1.4 + */ + public static final Color PINK = pink; + + /** Constant for the color orange: R=255, G=200, B=0. */ + public static final Color orange = new Color(0xffc800, false); + + /** + * Constant for the color orange: R=255, G=200, B=0. + * + * @since 1.4 + */ + public static final Color ORANGE = orange; + + /** Constant for the color yellow: R=255, G=255, B=0. */ + public static final Color yellow = new Color(0xffff00, false); + + /** + * Constant for the color yellow: R=255, G=255, B=0. + * + * @since 1.4 + */ + public static final Color YELLOW = yellow; + + /** Constant for the color green: R=0, G=255, B=0. */ + public static final Color green = new Color(0x00ff00, false); + + /** + * Constant for the color green: R=0, G=255, B=0. + * + * @since 1.4 + */ + public static final Color GREEN = green; + + /** Constant for the color magenta: R=255, G=0, B=255. */ + public static final Color magenta = new Color(0xff00ff, false); + + /** + * Constant for the color magenta: R=255, G=0, B=255. + * + * @since 1.4 + */ + public static final Color MAGENTA = magenta; + + /** Constant for the color cyan: R=0, G=255, B=255. */ + public static final Color cyan = new Color(0x00ffff, false); + + /** + * Constant for the color cyan: R=0, G=255, B=255. + * + * @since 1.4 + */ + public static final Color CYAN = cyan; + + /** Constant for the color blue: R=0, G=0, B=255. */ + public static final Color blue = new Color(0x0000ff, false); + + /** + * Constant for the color blue: R=0, G=0, B=255. + * + * @since 1.4 + */ + public static final Color BLUE = blue; + + /** Internal mask for red. */ + private static final int RED_MASK = 255 << 16; + + /** Internal mask for green. */ + private static final int GREEN_MASK = 255 << 8; + + /** Internal mask for blue. */ + private static final int BLUE_MASK = 255; + + /** Internal mask for alpha. Package visible for use in subclass. */ + static final int ALPHA_MASK = 255 << 24; + + /** Amount to scale a color by when brightening or darkening. */ + private static final float BRIGHT_SCALE = 0.7f; + + /** + * The color value, in sRGB. Note that the actual color may be more + * precise if frgbvalue or fvalue is non-null. This class stores alpha, red, + * green, and blue, each 0-255, packed in an int. However, the subclass + * SystemColor stores an index into an array. Therefore, for serial + * compatibility (and because of poor design on Sun's part), this value + * cannot be used directly; instead you must use getRGB(). + * + * @see #getRGB() + * @serial the value of the color, whether an RGB literal or array index + */ + final int value; + + /** + * The color value, in sRGB. This may be null if the color was constructed + * with ints; and it does not include alpha. This stores red, green, and + * blue, in the range 0.0f - 1.0f. + * + * @see #getRGBColorComponents(float[]) + * @see #getRGBComponents(float[]) + * @serial the rgb components of the value + * @since 1.2 + */ + private float[] frgbvalue; + + /** + * The color value, in the native ColorSpace components. This may be null + * if the color was constructed with ints or in the sRGB color space; and + * it does not include alpha. + * + * @see #getRGBColorComponents(float[]) + * @see #getRGBComponents(float[]) + * @serial the original color space components of the color + * @since 1.2 + */ + private float[] fvalue; + + /** + * The alpha value. This is in the range 0.0f - 1.0f, but is invalid if + * deserialized as 0.0 when frgbvalue is null. + * + * @see #getRGBComponents(float[]) + * @see #getComponents(float[]) + * @serial the alpha component of this color + * @since 1.2 + */ + private final float falpha; + + /** + * The ColorSpace. Null means the default sRGB space. + * + * @see #getColor(String) + * @see #getColorSpace() + * @see #getColorComponents(float[]) + * @serial the color space for this color + * @since 1.2 + */ + private final ColorSpace cs; + + /** + * The paint context for this solid color. Package visible for use in + * subclass. + */ + transient ColorPaintContext context; + + /** + * Initializes a new instance of Color using the specified + * red, green, and blue values, which must be given as integers in the + * range of 0-255. Alpha will default to 255 (opaque). When drawing to + * screen, the actual color may be adjusted to the best match of hardware + * capabilities. + * + * @param red the red component of the RGB value + * @param green the green component of the RGB value + * @param blue the blue component of the RGB value + * @throws IllegalArgumentException if the values are out of range 0-255 + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getRGB() + * @see #Color(int, int, int, int) + */ + public Color(int red, int green, int blue) + { + this(red, green, blue, 255); + } + + /** + * Initializes a new instance of Color using the specified + * red, green, blue, and alpha values, which must be given as integers in + * the range of 0-255. When drawing to screen, the actual color may be + * adjusted to the best match of hardware capabilities. + * + * @param red the red component of the RGB value + * @param green the green component of the RGB value + * @param blue the blue component of the RGB value + * @param alpha the alpha value of the color + * @throws IllegalArgumentException if the values are out of range 0-255 + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getAlpha() + * @see #getRGB() + */ + public Color(int red, int green, int blue, int alpha) + { + if ((red & 255) != red || (green & 255) != green || (blue & 255) != blue + || (alpha & 255) != alpha) + throw new IllegalArgumentException("Bad RGB values" + +" red=0x"+Integer.toHexString(red) + +" green=0x"+Integer.toHexString(green) + +" blue=0x"+Integer.toHexString(blue) + +" alpha=0x"+Integer.toHexString(alpha) ); + + value = (alpha << 24) | (red << 16) | (green << 8) | blue; + falpha = 1; + cs = null; + } + + /** + * Initializes a new instance of Color using the specified + * RGB value. The blue value is in bits 0-7, green in bits 8-15, and + * red in bits 16-23. The other bits are ignored. The alpha value is set + * to 255 (opaque). When drawing to screen, the actual color may be + * adjusted to the best match of hardware capabilities. + * + * @param value the RGB value + * @see ColorModel#getRGBdefault() + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getRGB() + * @see #Color(int, boolean) + */ + public Color(int value) + { + this(value, false); + } + + /** + * Initializes a new instance of Color using the specified + * RGB value. The blue value is in bits 0-7, green in bits 8-15, and + * red in bits 16-23. The alpha value is in bits 24-31, unless hasalpha + * is false, in which case alpha is set to 255. When drawing to screen, the + * actual color may be adjusted to the best match of hardware capabilities. + * + * @param value the RGB value + * @param hasalpha true if value includes the alpha + * @see ColorModel#getRGBdefault() + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getAlpha() + * @see #getRGB() + */ + public Color(int value, boolean hasalpha) + { + // Note: SystemColor calls this constructor, setting falpha to 0; but + // code in getRGBComponents correctly reports falpha as 1.0 to the user + // for all instances of SystemColor since frgbvalue is left null here. + if (hasalpha) + falpha = ((value & ALPHA_MASK) >> 24) / 255f; + else + { + value |= ALPHA_MASK; + falpha = 1; + } + this.value = value; + cs = null; + } + + /** + * Initializes a new instance of Color using the specified + * RGB values. These must be in the range of 0.0-1.0. Alpha is assigned + * the value of 1.0 (opaque). When drawing to screen, the actual color may + * be adjusted to the best match of hardware capabilities. + * + * @param red the red component of the RGB value + * @param green the green component of the RGB value + * @param blue the blue component of the RGB value + * @throws IllegalArgumentException tf the values are out of range 0.0f-1.0f + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getRGB() + * @see #Color(float, float, float, float) + */ + public Color(float red, float green, float blue) + { + this(red, green, blue, 1.0f); + } + + /** + * Initializes a new instance of Color using the specified + * RGB and alpha values. These must be in the range of 0.0-1.0. When drawing + * to screen, the actual color may be adjusted to the best match of + * hardware capabilities. + * + * @param red the red component of the RGB value + * @param green the green component of the RGB value + * @param blue the blue component of the RGB value + * @param alpha the alpha value of the color + * @throws IllegalArgumentException tf the values are out of range 0.0f-1.0f + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getAlpha() + * @see #getRGB() + */ + public Color(float red, float green, float blue, float alpha) + { + value = convert(red, green, blue, alpha); + frgbvalue = new float[] {red, green, blue}; + falpha = alpha; + cs = null; + } + + /** + * Creates a color in the given ColorSpace with the specified alpha. The + * array must be non-null and have enough elements for the color space + * (for example, RGB requires 3 elements, CMYK requires 4). When drawing + * to screen, the actual color may be adjusted to the best match of + * hardware capabilities. + * + * @param space the color space of components + * @param components the color components, except alpha + * @param alpha the alpha value of the color + * @throws NullPointerException if cpsace or components is null + * @throws ArrayIndexOutOfBoundsException if components is too small + * @throws IllegalArgumentException if alpha or any component is out of range + * @see #getComponents(float[]) + * @see #getColorComponents(float[]) + */ + public Color(ColorSpace space, float[] components, float alpha) + { + frgbvalue = space.toRGB(components); + fvalue = components; + falpha = alpha; + cs = space; + value = convert(frgbvalue[0], frgbvalue[1], frgbvalue[2], alpha); + } + + /** + * Returns the red value for this color, as an integer in the range 0-255 + * in the sRGB color space. + * + * @return the red value for this color + * @see #getRGB() + */ + public int getRed() + { + // Do not inline getRGB() to value, because of SystemColor. + return (getRGB() & RED_MASK) >> 16; + } + + /** + * Returns the green value for this color, as an integer in the range 0-255 + * in the sRGB color space. + * + * @return the green value for this color + * @see #getRGB() + */ + public int getGreen() + { + // Do not inline getRGB() to value, because of SystemColor. + return (getRGB() & GREEN_MASK) >> 8; + } + + /** + * Returns the blue value for this color, as an integer in the range 0-255 + * in the sRGB color space. + * + * @return the blue value for this color + * @see #getRGB() + */ + public int getBlue() + { + // Do not inline getRGB() to value, because of SystemColor. + return getRGB() & BLUE_MASK; + } + + /** + * Returns the alpha value for this color, as an integer in the range 0-255. + * + * @return the alpha value for this color + * @see #getRGB() + */ + public int getAlpha() + { + // Do not inline getRGB() to value, because of SystemColor. + return (getRGB() & ALPHA_MASK) >>> 24; + } + + /** + * Returns the RGB value for this color, in the sRGB color space. The blue + * value will be in bits 0-7, green in 8-15, red in 16-23, and alpha value in + * 24-31. + * + * @return the RGB value for this color + * @see ColorModel#getRGBdefault() + * @see #getRed() + * @see #getGreen() + * @see #getBlue() + * @see #getAlpha() + */ + public int getRGB() + { + return value; + } + + /** + * Returns a brighter version of this color. This is done by increasing the + * RGB values by an arbitrary scale factor. The new color is opaque (an + * alpha of 255). Note that this method and the darker() + * method are not necessarily inverses. + * + * @return a brighter version of this color + * @see #darker() + */ + public Color brighter() + { + // Do not inline getRGB() to this.value, because of SystemColor. + int value = getRGB(); + 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); + } + + /** + * Returns a darker version of this color. This is done by decreasing the + * RGB values by an arbitrary scale factor. The new color is opaque (an + * alpha of 255). Note that this method and the brighter() + * method are not necessarily inverses. + * + * @return a darker version of this color + * @see #brighter() + */ + public Color darker() + { + // Do not inline getRGB() to this.value, because of SystemColor. + int value = getRGB(); + return new Color((int) (((value & RED_MASK) >> 16) * BRIGHT_SCALE), + (int) (((value & GREEN_MASK) >> 8) * BRIGHT_SCALE), + (int) ((value & BLUE_MASK) * BRIGHT_SCALE), 255); + } + + /** + * Returns a hash value for this color. This is simply the color in 8-bit + * precision, in the format 0xAARRGGBB (alpha, red, green, blue). + * + * @return a hash value for this color + */ + public int hashCode() + { + return value; + } + + /** + * Tests this object for equality against the specified object. This will + * be true if and only if the specified object is an instance of + * Color and has the same 8-bit integer red, green, and blue + * values as this object. Note that two colors may be slightly different + * as float values, but round to the same integer values. Also note that + * this does not accurately compare SystemColors, since that class does + * not store its internal data in RGB format like regular colors. + * + * @param obj the object to compare to + * @return true if the specified object is semantically equal to this one + */ + public boolean equals(Object obj) + { + return obj instanceof Color && ((Color) obj).value == value; + } + + /** + * Returns a string representation of this object. Subclasses may return + * any desired format, except for null, but this implementation returns + * getClass().getName() + "[r=" + getRed() + ",g=" + getGreen() + * + ",b=" + getBlue() + ']'. + * + * @return a string representation of this object + */ + public String toString() + { + return getClass().getName() + "[r=" + ((value & RED_MASK) >> 16) + + ",g=" + ((value & GREEN_MASK) >> 8) + ",b=" + (value & BLUE_MASK) + + ']'; + } + + /** + * Converts the specified string to a number, using Integer.decode, and + * creates a new instance of Color from the value. The alpha + * value will be 255 (opaque). + * + * @param str the numeric color string + * @return a new instance of Color for the string + * @throws NumberFormatException if the string cannot be parsed + * @throws NullPointerException if the string is null + * @see Integer#decode(String) + * @see #Color(int) + * @since 1.1 + */ + public static Color decode(String str) + { + return new Color(Integer.decode(str).intValue(), false); + } + + /** + * Returns a new instance of Color from the value of the + * system property named by the specified string. If the property does not + * exist, or cannot be parsed, then null will be returned. + * + * @param prop the system property to retrieve + * @throws SecurityException if getting the property is denied + * @see #getColor(String, Color) + * @see Integer#getInteger(String) + */ + public static Color getColor(String prop) + { + return getColor(prop, null); + } + + /** + * Returns a new instance of Color from the value of the + * system property named by the specified string. If the property does + * not exist, or cannot be parsed, then the default color value will be + * returned. + * + * @param prop the system property to retrieve + * @param defcolor the default color + * @throws SecurityException if getting the property is denied + * @see Integer#getInteger(String) + */ + public static Color getColor(String prop, Color defcolor) + { + Integer val = Integer.getInteger(prop, null); + return val == null ? defcolor + : new Color(val.intValue(), false); + } + + /** + * Returns a new instance of Color from the value of the + * system property named by the specified string. If the property does + * not exist, or cannot be parsed, then the default RGB value will be + * used to create a return value. + * + * @param prop the system property to retrieve + * @param defrgb the default RGB value + * @throws SecurityException if getting the property is denied + * @see #getColor(String, Color) + * @see Integer#getInteger(String, int) + */ + public static Color getColor(String prop, int defrgb) + { + Color c = getColor(prop, null); + return c == null ? new Color(defrgb, false) : c; + } + + /** + * Converts from the HSB (hue, saturation, brightness) color model to the + * RGB (red, green, blue) color model. The hue may be any floating point; + * it's fractional portion is used to select the angle in the HSB model. + * The saturation and brightness must be between 0 and 1. The result is + * suitable for creating an RGB color with the one-argument constructor. + * + * @param hue the hue of the HSB value + * @param saturation the saturation of the HSB value + * @param brightness the brightness of the HSB value + * @return the RGB value + * @see #getRGB() + * @see #Color(int) + * @see ColorModel#getRGBdefault() + */ + public static int HSBtoRGB(float hue, float saturation, float brightness) + { + if (saturation == 0) + return convert(brightness, brightness, brightness, 0); + if (saturation < 0 || saturation > 1 || brightness < 0 || brightness > 1) + throw new IllegalArgumentException(); + hue = hue - (float) Math.floor(hue); + int i = (int) (6 * hue); + float f = 6 * hue - i; + float p = brightness * (1 - saturation); + float q = brightness * (1 - saturation * f); + float t = brightness * (1 - saturation * (1 - f)); + switch (i) + { + case 0: + return convert(brightness, t, p, 0); + case 1: + return convert(q, brightness, p, 0); + case 2: + return convert(p, brightness, t, 0); + case 3: + return convert(p, q, brightness, 0); + case 4: + return convert(t, p, brightness, 0); + case 5: + return convert(brightness, p, q, 0); + default: + throw new InternalError("impossible"); + } + } + + /** + * Converts from the RGB (red, green, blue) color model to the HSB (hue, + * saturation, brightness) color model. If the array is null, a new one + * is created, otherwise it is recycled. The results will be in the range + * 0.0-1.0 if the inputs are in the range 0-255. + * + * @param red the red part of the RGB value + * @param green the green part of the RGB value + * @param blue the blue part of the RGB value + * @param array an array for the result (at least 3 elements), or null + * @return the array containing HSB value + * @throws ArrayIndexOutOfBoundsException of array is too small + * @see #getRGB() + * @see #Color(int) + * @see ColorModel#getRGBdefault() + */ + public static float[] RGBtoHSB(int red, int green, int blue, float array[]) + { + if (array == null) + array = new float[3]; + // Calculate brightness. + int min; + int max; + if (red < green) + { + min = red; + max = green; + } + else + { + min = green; + max = red; + } + if (blue > max) + max = blue; + else if (blue < min) + min = blue; + array[2] = max / 255f; + // Calculate saturation. + if (max == 0) + array[1] = 0; + else + array[1] = ((float) (max - min)) / ((float) max); + // Calculate hue. + if (array[1] == 0) + array[0] = 0; + else + { + float delta = (max - min) * 6; + if (red == max) + array[0] = (green - blue) / delta; + else if (green == max) + array[0] = 1f / 3 + (blue - red) / delta; + else + array[0] = 2f / 3 + (red - green) / delta; + if (array[0] < 0) + array[0]++; + } + return array; + } + + /** + * Returns a new instance of Color based on the specified + * HSB values. The hue may be any floating point; it's fractional portion + * is used to select the angle in the HSB model. The saturation and + * brightness must be between 0 and 1. + * + * @param hue the hue of the HSB value + * @param saturation the saturation of the HSB value + * @param brightness the brightness of the HSB value + * @return the new Color object + */ + public static Color getHSBColor(float hue, float saturation, + float brightness) + { + return new Color(HSBtoRGB(hue, saturation, brightness), false); + } + + /** + * Returns a float array with the red, green, and blue components, and the + * alpha value, in the default sRGB space, with values in the range 0.0-1.0. + * If the array is null, a new one is created, otherwise it is recycled. + * + * @param array the array to put results into (at least 4 elements), or null + * @return the RGB components and alpha value + * @throws ArrayIndexOutOfBoundsException if array is too small + */ + public float[] getRGBComponents(float[] array) + { + if (array == null) + array = new float[4]; + getRGBColorComponents(array); + // Stupid serialization issues require this check. + array[3] = (falpha == 0 && frgbvalue == null + ? ((getRGB() & ALPHA_MASK) >> 24) / 255f : falpha); + return array; + } + + /** + * Returns a float array with the red, green, and blue components, in the + * default sRGB space, with values in the range 0.0-1.0. If the array is + * null, a new one is created, otherwise it is recycled. + * + * @param array the array to put results into (at least 3 elements), or null + * @return the RGB components + * @throws ArrayIndexOutOfBoundsException if array is too small + */ + public float[] getRGBColorComponents(float[] array) + { + if (array == null) + array = new float[3]; + else if (array == frgbvalue) + return array; // Optimization for getColorComponents(float[]). + if (frgbvalue == null) + { + // Do not inline getRGB() to this.value, because of SystemColor. + int value = getRGB(); + frgbvalue = new float[] { ((value & RED_MASK) >> 16) / 255f, + ((value & GREEN_MASK) >> 8) / 255f, + (value & BLUE_MASK) / 255f }; + } + array[0] = frgbvalue[0]; + array[1] = frgbvalue[1]; + array[2] = frgbvalue[2]; + return array; + } + + /** + * Returns a float array containing the color and alpha components of this + * color in the ColorSpace it was created with (the constructors which do + * not take a ColorSpace parameter use a default sRGB ColorSpace). If the + * array is null, a new one is created, otherwise it is recycled, and must + * have at least one more position than components used in the color space. + * + * @param array the array to put results into, or null + * @return the original color space components and alpha value + * @throws ArrayIndexOutOfBoundsException if array is too small + */ + public float[] getComponents(float[] array) + { + int numComponents = cs == null ? 3 : cs.getNumComponents(); + if (array == null) + array = new float[1 + numComponents]; + getColorComponents(array); + // Stupid serialization issues require this check. + array[numComponents] = (falpha == 0 && frgbvalue == null + ? ((getRGB() & ALPHA_MASK) >> 24) / 255f : falpha); + return array; + } + + /** + * Returns a float array containing the color components of this color in + * the ColorSpace it was created with (the constructors which do not take + * a ColorSpace parameter use a default sRGB ColorSpace). If the array is + * null, a new one is created, otherwise it is recycled, and must have at + * least as many positions as used in the color space. + * + * @param array the array to put results into, or null + * @return the original color space components + * @throws ArrayIndexOutOfBoundsException if array is too small + */ + public float[] getColorComponents(float[] array) + { + int numComponents = cs == null ? 3 : cs.getNumComponents(); + if (array == null) + array = new float[numComponents]; + if (fvalue == null) // If fvalue is null, cs should be null too. + fvalue = getRGBColorComponents(frgbvalue); + System.arraycopy(fvalue, 0, array, 0, numComponents); + return array; + } + + /** + * Returns a float array containing the color and alpha components of this + * color in the given ColorSpace. If the array is null, a new one is + * created, otherwise it is recycled, and must have at least one more + * position than components used in the color space. + * + * @param space the color space to translate to + * @param array the array to put results into, or null + * @return the color space components and alpha value + * @throws ArrayIndexOutOfBoundsException if array is too small + * @throws NullPointerException if space is null + */ + public float[] getComponents(ColorSpace space, float[] array) + { + int numComponents = space.getNumComponents(); + if (array == null) + array = new float[1 + numComponents]; + getColorComponents(space, array); + // Stupid serialization issues require this check. + array[numComponents] = (falpha == 0 && frgbvalue == null + ? ((getRGB() & ALPHA_MASK) >> 24) / 255f : falpha); + return array; + } + + /** + * Returns a float array containing the color components of this color in + * the given ColorSpace. If the array is null, a new one is created, + * otherwise it is recycled, and must have at least as many positions as + * used in the color space. + * + * @param space the color space to translate to + * @return the color space components + * @throws ArrayIndexOutOfBoundsException if array is too small + * @throws NullPointerException if space is null + */ + public float[] getColorComponents(ColorSpace space, float[] array) + { + float[] components = space.fromRGB(getRGBColorComponents(frgbvalue)); + if (array == null) + return components; + System.arraycopy(components, 0, array, 0, components.length); + return array; + } + + /** + * Returns the color space of this color. Except for the constructor which + * takes a ColorSpace argument, this will be an implementation of + * ColorSpace.CS_sRGB. + * + * @return the color space + */ + public ColorSpace getColorSpace() + { + return cs == null ? ColorSpace.getInstance(ColorSpace.CS_sRGB) : cs; + } + + /** + * Returns a paint context, used for filling areas of a raster scan with + * this color. Since the color is constant across the entire rectangle, and + * since it is always in sRGB space, this implementation returns the same + * object, regardless of the parameters. Subclasses, however, may have a + * mutable result. + * + * @param cm the requested color model + * @param deviceBounds the bounding box in device coordinates, ignored + * @param userBounds the bounding box in user coordinates, ignored + * @param xform the bounds transformation, ignored + * @param hints any rendering hints, ignored + * @return a context for painting this solid color + */ + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + if (context == null || !context.getColorModel().equals(cm)) + context = new ColorPaintContext(cm,value); + return context; + } + + /** + * Returns the transparency level of this color. + * + * @return one of {@link #OPAQUE}, {@link #BITMASK}, or {@link #TRANSLUCENT} + */ + public int getTransparency() + { + // Do not inline getRGB() to this.value, because of SystemColor. + int alpha = getRGB() & ALPHA_MASK; + return alpha == (255 << 24) ? OPAQUE : alpha == 0 ? BITMASK : TRANSLUCENT; + } + + /** + * Converts float values to integer value. + * + * @param red the red value + * @param green the green value + * @param blue the blue value + * @param alpha the alpha value + * @return the integer value made of 8-bit sections + * @throws IllegalArgumentException if parameters are out of range 0.0-1.0 + */ + private static int convert(float red, float green, float blue, float alpha) + { + if (red < 0 || red > 1 || green < 0 || green > 1 || blue < 0 || blue > 1 + || alpha < 0 || alpha > 1) + throw new IllegalArgumentException("Bad RGB values"); + int redval = Math.round(255 * red); + int greenval = Math.round(255 * green); + int blueval = Math.round(255 * blue); + int alphaval = Math.round(255 * alpha); + return (alphaval << 24) | (redval << 16) | (greenval << 8) | blueval; + } +} // class Color diff --git a/libjava/classpath/java/awt/ColorPaintContext.java b/libjava/classpath/java/awt/ColorPaintContext.java new file mode 100644 index 000000000..5ba688617 --- /dev/null +++ b/libjava/classpath/java/awt/ColorPaintContext.java @@ -0,0 +1,195 @@ +/* ColorPaintContext.java -- context for painting solid colors + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ColorModel; +import java.awt.image.Raster; + +/** + * This class provides a paint context which will fill a rectanglar region of + * a raster scan with the given color. However, it is not yet completely + * implemented. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ +class ColorPaintContext implements PaintContext +{ + /** + * The color to fill any raster with. Package visible for use in + * SystemColor. + */ + final int color; + final ColorModel colorModel; + + private ColorRaster cachedRaster; + + + /** + * Create the context for a given color. + * + * @param colorRGB The solid color to use. + */ + ColorPaintContext(int colorRGB) + { + this(ColorModel.getRGBdefault(), colorRGB); + } + + /** + * Create the context for a given color. + * + * @param cm The color model of this context. + * @param colorRGB The solid color to use. + */ + ColorPaintContext(ColorModel cm,int colorRGB) + { + color = colorRGB; + colorModel = cm; + } + + /** + * Release the resources allocated for the paint. As the color is constant, + * there aren't any resources. + */ + public void dispose() + { + } + + /** + * Return the color model of this context. + * + * @return the context color model + */ + public ColorModel getColorModel() + { + return colorModel; + } + + /** + * Return a raster containing the colors for the graphics operation. + * + * @param x the x-coordinate, in device space + * @param y the y-coordinate, in device space + * @param width the width, in device space + * @param height the height, in device space + * @return a raster for the given area and color + */ + public Raster getRaster(int x, int y, int width, int height) + { + if( cachedRaster == null + || cachedRaster.getWidth() < width + || cachedRaster.getHeight() < height) + { + cachedRaster = new ColorRaster(colorModel, 0, 0, width, height, color); + } + return cachedRaster.createChild(0 ,0 ,width ,height ,0 ,0 , null); + } + + /** + * A ColorRaster is a raster that is completely filled with one color. The + * data layout is taken from the color model given to the constructor. + */ + private class ColorRaster extends Raster + { + + /** + * Create a raster that is compaltible with the given color model and + * filled with the given color. + * @param cm The color model for this raster. + * @param x The smallest horizontal corrdinate in the raster. + * @param y The smallest vertical coordinate in the raster. + * @param width The width of the raster. + * @param height The height of the raster. + * @param rgbPixel The RGB value of the color for this raster. + */ + ColorRaster(ColorModel cm,int x, int y, int width, int height, int rgbPixel) + { + super(cm.createCompatibleSampleModel(width,height),new Point(x,y)); + Object pixel = cm.getDataElements(rgbPixel,null); + int[] pixelComps = cm.getComponents(pixel, null, 0); + int[] d = (int[]) multiplyData(pixelComps,null,width*height); + getSampleModel().setPixels(0, 0, width, height, d, + dataBuffer); + } + + + + private Object multiplyData(Object src, Object dest, int factor) + { + Object from; + int srcLength = 0; + if (src instanceof byte[]) + { + srcLength = ((byte[])src).length; + + if (dest == null) dest = new byte[factor * srcLength]; + } + else if (src instanceof short[]) + { + srcLength = ((short[])src).length; + if (dest == null) dest = new short[factor * srcLength]; + } + else if (src instanceof int[]) + { + srcLength = ((int[]) src).length; + if (dest == null) dest = new int[factor * srcLength]; + } + else + { + throw new ClassCastException("Unknown data buffer type"); + } + + System.arraycopy(src,0,dest,0,srcLength); + + int count = 1; + while(count*2 < factor) + { + System.arraycopy(dest, 0, dest, count * srcLength, count*srcLength); + count *= 2; + } + + if(factor > count) + System.arraycopy(dest,0, dest, count * srcLength, + (factor - count) * srcLength ); + + return dest; + } + + } + +} // class ColorPaintContext diff --git a/libjava/classpath/java/awt/Component.java b/libjava/classpath/java/awt/Component.java new file mode 100644 index 000000000..d2878a9fd --- /dev/null +++ b/libjava/classpath/java/awt/Component.java @@ -0,0 +1,7099 @@ +/* Component.java -- a graphics component + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006 + Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +//import gnu.java.awt.dnd.peer.gtk.GtkDropTargetContextPeer; + +import gnu.java.awt.ComponentReshapeEvent; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.dnd.DropTarget; +import java.awt.event.ActionEvent; +import java.awt.event.AdjustmentEvent; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.event.InputEvent; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.event.PaintEvent; +import java.awt.event.WindowEvent; +import java.awt.im.InputContext; +import java.awt.im.InputMethodRequests; +import java.awt.image.BufferStrategy; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; +import java.awt.peer.ComponentPeer; +import java.awt.peer.LightweightPeer; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.Collections; +import java.util.EventListener; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Set; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleComponent; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * The root of all evil. All graphical representations are subclasses of this + * giant class, which is designed for screen display and user interaction. + * This class can be extended directly to build a lightweight component (one + * not associated with a native window); lightweight components must reside + * inside a heavyweight window. + * + *

This class is Serializable, which has some big implications. A user can + * save the state of all graphical components in one VM, and reload them in + * another. Note that this class will only save Serializable listeners, and + * ignore the rest, without causing any serialization exceptions. However, by + * making a listener serializable, and adding it to another element, you link + * in that entire element to the state of this component. To get around this, + * use the idiom shown in the example below - make listeners non-serializable + * in inner classes, rather than using this object itself as the listener, if + * external objects do not need to save the state of this object. + * + *

+ * import java.awt.*;
+ * import java.awt.event.*;
+ * import java.io.Serializable;
+ * class MyApp implements Serializable
+ * {
+ *   BigObjectThatShouldNotBeSerializedWithAButton bigOne;
+ *   // Serializing aButton will not suck in an instance of MyApp, with its
+ *   // accompanying field bigOne.
+ *   Button aButton = new Button();
+ *   class MyActionListener implements ActionListener
+ *   {
+ *     public void actionPerformed(ActionEvent e)
+ *     {
+ *       System.out.println("Hello There");
+ *     }
+ *   }
+ *   MyApp()
+ *   {
+ *     aButton.addActionListener(new MyActionListener());
+ *   }
+ * }
+ * 
+ * + *

Status: Incomplete. The event dispatch mechanism is implemented. All + * other methods defined in the J2SE 1.3 API javadoc exist, but are mostly + * incomplete or only stubs; except for methods relating to the Drag and + * Drop, Input Method, and Accessibility frameworks: These methods are + * present but commented out. + * + * @author original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status still missing 1.4 support + */ +public abstract class Component + implements ImageObserver, MenuContainer, Serializable +{ + // Word to the wise - this file is huge. Search for '\f' (^L) for logical + // sectioning by fields, public API, private API, and nested classes. + + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -7644114512714619750L; + + /** + * Constant returned by the getAlignmentY method to indicate + * that the component wishes to be aligned to the top relative to + * other components. + * + * @see #getAlignmentY() + */ + public static final float TOP_ALIGNMENT = 0; + + /** + * Constant returned by the getAlignmentY and + * getAlignmentX methods to indicate + * that the component wishes to be aligned to the centdisper relative to + * other components. + * + * @see #getAlignmentX() + * @see #getAlignmentY() + */ + public static final float CENTER_ALIGNMENT = 0.5f; + + /** + * Constant returned by the getAlignmentY method to indicate + * that the component wishes to be aligned to the bottom relative to + * other components. + * + * @see #getAlignmentY() + */ + public static final float BOTTOM_ALIGNMENT = 1; + + /** + * Constant returned by the getAlignmentX method to indicate + * that the component wishes to be aligned to the right relative to + * other components. + * + * @see #getAlignmentX() + */ + public static final float RIGHT_ALIGNMENT = 1; + + /** + * Constant returned by the getAlignmentX method to indicate + * that the component wishes to be aligned to the left relative to + * other components. + * + * @see #getAlignmentX() + */ + public static final float LEFT_ALIGNMENT = 0; + + /** + * Make the treelock a String so that it can easily be identified + * in debug dumps. We clone the String in order to avoid a conflict in + * the unlikely event that some other package uses exactly the same string + * as a lock object. + */ + static final Object treeLock = new String("AWT_TREE_LOCK"); + + /** + * The default maximum size. + */ + private static final Dimension DEFAULT_MAX_SIZE + = new Dimension(Short.MAX_VALUE, Short.MAX_VALUE); + + // Serialized fields from the serialization spec. + + /** + * The x position of the component in the parent's coordinate system. + * + * @see #getLocation() + * @serial the x position + */ + int x; + + /** + * The y position of the component in the parent's coordinate system. + * + * @see #getLocation() + * @serial the y position + */ + int y; + + /** + * The component width. + * + * @see #getSize() + * @serial the width + */ + int width; + + /** + * The component height. + * + * @see #getSize() + * @serial the height + */ + int height; + + /** + * The foreground color for the component. This may be null. + * + * @see #getForeground() + * @see #setForeground(Color) + * @serial the foreground color + */ + Color foreground; + + /** + * The background color for the component. This may be null. + * + * @see #getBackground() + * @see #setBackground(Color) + * @serial the background color + */ + Color background; + + /** + * The default font used in the component. This may be null. + * + * @see #getFont() + * @see #setFont(Font) + * @serial the font + */ + Font font; + + /** + * The font in use by the peer, or null if there is no peer. + * + * @serial the peer's font + */ + Font peerFont; + + /** + * The cursor displayed when the pointer is over this component. This may + * be null. + * + * @see #getCursor() + * @see #setCursor(Cursor) + */ + Cursor cursor; + + /** + * The locale for the component. + * + * @see #getLocale() + * @see #setLocale(Locale) + */ + Locale locale = Locale.getDefault (); + + /** + * True if the object should ignore repaint events (usually because it is + * not showing). + * + * @see #getIgnoreRepaint() + * @see #setIgnoreRepaint(boolean) + * @serial true to ignore repaints + * @since 1.4 + */ + boolean ignoreRepaint; + + /** + * True when the object is visible (although it is only showing if all + * ancestors are likewise visible). For component, this defaults to true. + * + * @see #isVisible() + * @see #setVisible(boolean) + * @serial true if visible + */ + boolean visible = true; + + /** + * True if the object is enabled, meaning it can interact with the user. + * For component, this defaults to true. + * + * @see #isEnabled() + * @see #setEnabled(boolean) + * @serial true if enabled + */ + boolean enabled = true; + + /** + * True if the object is valid. This is set to false any time a size + * adjustment means the component need to be layed out again. + * + * @see #isValid() + * @see #validate() + * @see #invalidate() + * @serial true if layout is valid + */ + boolean valid; + + /** + * The DropTarget for drag-and-drop operations. + * + * @see #getDropTarget() + * @see #setDropTarget(DropTarget) + * @serial the drop target, or null + * @since 1.2 + */ + DropTarget dropTarget; + + /** + * The list of popup menus for this component. + * + * @see #add(PopupMenu) + * @serial the list of popups + */ + Vector popups; + + /** + * The component's name. May be null, in which case a default name is + * generated on the first use. + * + * @see #getName() + * @see #setName(String) + * @serial the name + */ + String name; + + /** + * True once the user has set the name. Note that the user may set the name + * to null. + * + * @see #name + * @see #getName() + * @see #setName(String) + * @serial true if the name has been explicitly set + */ + boolean nameExplicitlySet; + + /** + * Indicates if the object can be focused. Defaults to true for components. + * + * @see #isFocusable() + * @see #setFocusable(boolean) + * @since 1.4 + */ + boolean focusable = true; + + /** + * Tracks whether this component's {@link #isFocusTraversable} + * method has been overridden. + * + * @since 1.4 + */ + int isFocusTraversableOverridden; + + /** + * The focus traversal keys, if not inherited from the parent or + * default keyboard focus manager. These sets will contain only + * AWTKeyStrokes that represent press and release events to use as + * focus control. + * + * @see #getFocusTraversalKeys(int) + * @see #setFocusTraversalKeys(int, Set) + * @since 1.4 + */ + Set[] focusTraversalKeys; + + /** + * True if focus traversal keys are enabled. This defaults to true for + * Component. If this is true, keystrokes in focusTraversalKeys are trapped + * and processed automatically rather than being passed on to the component. + * + * @see #getFocusTraversalKeysEnabled() + * @see #setFocusTraversalKeysEnabled(boolean) + * @since 1.4 + */ + boolean focusTraversalKeysEnabled = true; + + /** + * Cached information on the minimum size. Should have been transient. + * + * @serial ignore + */ + Dimension minSize; + + /** + * Flag indicating whether the minimum size for the component has been set + * by a call to {@link #setMinimumSize(Dimension)} with a non-null value. + */ + boolean minSizeSet; + + /** + * The maximum size for the component. + * @see #setMaximumSize(Dimension) + */ + Dimension maxSize; + + /** + * A flag indicating whether the maximum size for the component has been set + * by a call to {@link #setMaximumSize(Dimension)} with a non-null value. + */ + boolean maxSizeSet; + + /** + * Cached information on the preferred size. Should have been transient. + * + * @serial ignore + */ + Dimension prefSize; + + /** + * Flag indicating whether the preferred size for the component has been set + * by a call to {@link #setPreferredSize(Dimension)} with a non-null value. + */ + boolean prefSizeSet; + + /** + * Set to true if an event is to be handled by this component, false if + * it is to be passed up the hierarcy. + * + * @see #dispatchEvent(AWTEvent) + * @serial true to process event locally + */ + boolean newEventsOnly; + + /** + * Set by subclasses to enable event handling of particular events, and + * left alone when modifying listeners. For component, this defaults to + * enabling only input methods. + * + * @see #enableInputMethods(boolean) + * @see AWTEvent + * @serial the mask of events to process + */ + long eventMask = AWTEvent.INPUT_ENABLED_EVENT_MASK; + + /** + * Describes all registered PropertyChangeListeners. + * + * @see #addPropertyChangeListener(PropertyChangeListener) + * @see #removePropertyChangeListener(PropertyChangeListener) + * @see #firePropertyChange(String, Object, Object) + * @serial the property change listeners + * @since 1.2 + */ + PropertyChangeSupport changeSupport; + + /** + * True if the component has been packed (layed out). + * + * @serial true if this is packed + */ + boolean isPacked; + + /** + * The serialization version for this class. Currently at version 4. + * + * XXX How do we handle prior versions? + * + * @serial the serialization version + */ + int componentSerializedDataVersion = 4; + + /** + * The accessible context associated with this component. This is only set + * by subclasses. + * + * @see #getAccessibleContext() + * @serial the accessibility context + * @since 1.2 + */ + AccessibleContext accessibleContext; + + + // Guess what - listeners are special cased in serialization. See + // readObject and writeObject. + + /** Component listener chain. */ + transient ComponentListener componentListener; + + /** Focus listener chain. */ + transient FocusListener focusListener; + + /** Key listener chain. */ + transient KeyListener keyListener; + + /** Mouse listener chain. */ + transient MouseListener mouseListener; + + /** Mouse motion listener chain. */ + transient MouseMotionListener mouseMotionListener; + + /** + * Mouse wheel listener chain. + * + * @since 1.4 + */ + transient MouseWheelListener mouseWheelListener; + + /** + * Input method listener chain. + * + * @since 1.2 + */ + transient InputMethodListener inputMethodListener; + + /** + * Hierarcy listener chain. + * + * @since 1.3 + */ + transient HierarchyListener hierarchyListener; + + /** + * Hierarcy bounds listener chain. + * + * @since 1.3 + */ + transient HierarchyBoundsListener hierarchyBoundsListener; + + // Anything else is non-serializable, and should be declared "transient". + + /** The parent. */ + transient Container parent; + + /** The associated native peer. */ + transient ComponentPeer peer; + + /** The preferred component orientation. */ + transient ComponentOrientation componentOrientation = ComponentOrientation.UNKNOWN; + + /** + * The associated graphics configuration. + * + * @since 1.4 + */ + transient GraphicsConfiguration graphicsConfig; + + /** + * The buffer strategy for repainting. + * + * @since 1.4 + */ + transient BufferStrategy bufferStrategy; + + /** + * The number of hierarchy listeners of this container plus all of its + * children. This is needed for efficient handling of HierarchyEvents. + * These must be propagated to all child components with HierarchyListeners + * attached. To avoid traversal of the whole subtree, we keep track of + * the number of HierarchyListeners here and only walk the paths that + * actually have listeners. + */ + int numHierarchyListeners; + int numHierarchyBoundsListeners; + + /** + * true if requestFocus was called on this component when its + * top-level ancestor was not focusable. + */ + private transient FocusEvent pendingFocusRequest = null; + + /** + * The system properties that affect image updating. + */ + private static transient boolean incrementalDraw; + private static transient Long redrawRate; + + static + { + incrementalDraw = Boolean.getBoolean ("awt.image.incrementalDraw"); + redrawRate = Long.getLong ("awt.image.redrawrate"); + } + + // Public and protected API. + + /** + * Default constructor for subclasses. When Component is extended directly, + * it forms a lightweight component that must be hosted in an opaque native + * container higher in the tree. + */ + protected Component() + { + // Nothing to do here. + } + + /** + * Returns the name of this component. + * + * @return the name of this component + * @see #setName(String) + * @since 1.1 + */ + public String getName() + { + if (name == null && ! nameExplicitlySet) + name = generateName(); + return name; + } + + /** + * Sets the name of this component to the specified name (this is a bound + * property with the name 'name'). + * + * @param name the new name (null permitted). + * @see #getName() + * @since 1.1 + */ + public void setName(String name) + { + nameExplicitlySet = true; + String old = this.name; + this.name = name; + firePropertyChange("name", old, name); + } + + /** + * Returns the parent of this component. + * + * @return the parent of this component + */ + public Container getParent() + { + return parent; + } + + /** + * Returns the native windowing system peer for this component. Only the + * platform specific implementation code should call this method. + * + * @return the peer for this component + * @deprecated user programs should not directly manipulate peers; use + * {@link #isDisplayable()} instead + */ + // Classpath's Gtk peers rely on this. + public ComponentPeer getPeer() + { + return peer; + } + + /** + * Set the associated drag-and-drop target, which receives events when this + * is enabled. + * + * @param dt the new drop target + * @see #isEnabled() + */ + public void setDropTarget(DropTarget dt) + { + this.dropTarget = dt; + + if (peer != null) + dropTarget.addNotify(peer); + } + + /** + * Gets the associated drag-and-drop target, if there is one. + * + * @return the drop target + */ + public DropTarget getDropTarget() + { + return dropTarget; + } + + /** + * Returns the graphics configuration of this component, if there is one. + * If it has not been set, it is inherited from the parent. + * + * @return the graphics configuration, or null + * @since 1.3 + */ + public GraphicsConfiguration getGraphicsConfiguration() + { + GraphicsConfiguration conf = null; + synchronized (getTreeLock()) + { + if (graphicsConfig != null) + { + conf = graphicsConfig; + } + else + { + Component par = getParent(); + if (par != null) + { + conf = parent.getGraphicsConfiguration(); + } + } + } + return conf; + } + + /** + * Returns the object used for synchronization locks on this component + * when performing tree and layout functions. + * + * @return the synchronization lock for this component + */ + public final Object getTreeLock() + { + return treeLock; + } + + /** + * Returns the toolkit in use for this component. The toolkit is associated + * with the frame this component belongs to. + * + * @return the toolkit for this component + */ + public Toolkit getToolkit() + { + // Only heavyweight peers can handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + Toolkit tk = null; + if (p != null) + { + tk = peer.getToolkit(); + } + if (tk == null) + tk = Toolkit.getDefaultToolkit(); + return tk; + } + + /** + * Tests whether or not this component is valid. A invalid component needs + * to have its layout redone. + * + * @return true if this component is valid + * @see #validate() + * @see #invalidate() + */ + public boolean isValid() + { + // Tests show that components are invalid as long as they are not showing, even after validate() + // has been called on them. + return peer != null && valid; + } + + /** + * Tests if the component is displayable. It must be connected to a native + * screen resource. This reduces to checking that peer is not null. A + * containment hierarchy is made displayable when a window is packed or + * made visible. + * + * @return true if the component is displayable + * @see Container#add(Component) + * @see Container#remove(Component) + * @see Window#pack() + * @see Window#show() + * @see Window#dispose() + * @since 1.2 + */ + public boolean isDisplayable() + { + return peer != null; + } + + /** + * Tests whether or not this component is visible. Except for top-level + * frames, components are initially visible. + * + * @return true if the component is visible + * @see #setVisible(boolean) + */ + public boolean isVisible() + { + return visible; + } + + /** + * Tests whether or not this component is actually being shown on + * the screen. This will be true if and only if it this component is + * visible and its parent components are all visible. + * + * @return true if the component is showing on the screen + * @see #setVisible(boolean) + */ + public boolean isShowing() + { + Component par = parent; + return visible && peer != null && (par == null || par.isShowing()); + } + + /** + * Tests whether or not this component is enabled. Components are enabled + * by default, and must be enabled to receive user input or generate events. + * + * @return true if the component is enabled + * @see #setEnabled(boolean) + */ + public boolean isEnabled() + { + return enabled; + } + + /** + * Enables or disables this component. The component must be enabled to + * receive events (except that lightweight components always receive mouse + * events). + * + * @param enabled true to enable this component + * + * @see #isEnabled() + * @see #isLightweight() + * + * @since 1.1 + */ + public void setEnabled(boolean enabled) + { + enable(enabled); + } + + /** + * Enables this component. + * + * @deprecated use {@link #setEnabled(boolean)} instead + */ + public void enable() + { + if (! enabled) + { + // Need to lock the tree here, because the peers are involved. + synchronized (getTreeLock()) + { + enabled = true; + ComponentPeer p = peer; + if (p != null) + p.enable(); + } + } + } + + /** + * Enables or disables this component. + * + * @param enabled true to enable this component + * + * @deprecated use {@link #setEnabled(boolean)} instead + */ + public void enable(boolean enabled) + { + if (enabled) + enable(); + else + disable(); + } + + /** + * Disables this component. + * + * @deprecated use {@link #setEnabled(boolean)} instead + */ + public void disable() + { + if (enabled) + { + // Need to lock the tree here, because the peers are involved. + synchronized (getTreeLock()) + { + enabled = false; + ComponentPeer p = peer; + if (p != null) + p.disable(); + } + } + } + + /** + * Checks if this image is painted to an offscreen image buffer that is + * later copied to screen (double buffering reduces flicker). This version + * returns false, so subclasses must override it if they provide double + * buffering. + * + * @return true if this is double buffered; defaults to false + */ + public boolean isDoubleBuffered() + { + return false; + } + + /** + * Enables or disables input method support for this component. By default, + * components have this enabled. Input methods are given the opportunity + * to process key events before this component and its listeners. + * + * @param enable true to enable input method processing + * @see #processKeyEvent(KeyEvent) + * @since 1.2 + */ + public void enableInputMethods(boolean enable) + { + if (enable) + eventMask |= AWTEvent.INPUT_ENABLED_EVENT_MASK; + else + eventMask &= ~AWTEvent.INPUT_ENABLED_EVENT_MASK; + } + + /** + * Makes this component visible or invisible. Note that it wtill might + * not show the component, if a parent is invisible. + * + * @param visible true to make this component visible + * + * @see #isVisible() + * + * @since 1.1 + */ + public void setVisible(boolean visible) + { + // Inspection by subclassing shows that Sun's implementation calls + // show(boolean) which then calls show() or hide(). It is the show() + // method that is overriden in subclasses like Window. + show(visible); + } + + /** + * Makes this component visible on the screen. + * + * @deprecated use {@link #setVisible(boolean)} instead + */ + public void show() + { + // We must set visible before showing the peer. Otherwise the + // peer could post paint events before visible is true, in which + // case lightweight components are not initially painted -- + // Container.paint first calls isShowing () before painting itself + // and its children. + if(! visible) + { + // Need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + visible = true; + // Avoid NullPointerExceptions by creating a local reference. + ComponentPeer currentPeer = peer; + if (currentPeer != null) + { + currentPeer.show(); + + // Fire HierarchyEvent. + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, + this, parent, + HierarchyEvent.SHOWING_CHANGED); + + // The JDK repaints the component before invalidating the parent. + // So do we. + if (peer instanceof LightweightPeer) + repaint(); + } + + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) + { + ComponentEvent ce = + new ComponentEvent(this,ComponentEvent.COMPONENT_SHOWN); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + + // Invalidate the parent if we have one. The component itself must + // not be invalidated. We also avoid NullPointerException with + // a local reference here. + Container currentParent = parent; + if (currentParent != null) + currentParent.invalidate(); + + } + } + + /** + * Makes this component visible or invisible. + * + * @param visible true to make this component visible + * + * @deprecated use {@link #setVisible(boolean)} instead + */ + public void show(boolean visible) + { + if (visible) + show(); + else + hide(); + } + + /** + * Hides this component so that it is no longer shown on the screen. + * + * @deprecated use {@link #setVisible(boolean)} instead + */ + public void hide() + { + if (visible) + { + // Need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + visible = false; + + // Avoid NullPointerExceptions by creating a local reference. + ComponentPeer currentPeer = peer; + if (currentPeer != null) + { + currentPeer.hide(); + + // Fire hierarchy event. + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, + this, parent, + HierarchyEvent.SHOWING_CHANGED); + // The JDK repaints the component before invalidating the + // parent. So do we. This only applies for lightweights. + if (peer instanceof LightweightPeer) + repaint(); + } + + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) + { + ComponentEvent ce = + new ComponentEvent(this,ComponentEvent.COMPONENT_HIDDEN); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + + // Invalidate the parent if we have one. The component itself need + // not be invalidated. We also avoid NullPointerException with + // a local reference here. + Container currentParent = parent; + if (currentParent != null) + currentParent.invalidate(); + + } + } + + /** + * Returns this component's foreground color. If not set, this is inherited + * from the parent. + * + * @return this component's foreground color, or null + * @see #setForeground(Color) + */ + public Color getForeground() + { + if (foreground != null) + return foreground; + return parent == null ? null : parent.getForeground(); + } + + /** + * Sets this component's foreground color to the specified color. This is a + * bound property. + * + * @param c the new foreground color + * @see #getForeground() + */ + public void setForeground(Color c) + { + if (peer != null) + peer.setForeground(c); + + Color previous = foreground; + foreground = c; + firePropertyChange("foreground", previous, c); + } + + /** + * Tests if the foreground was explicitly set, or just inherited from the + * parent. + * + * @return true if the foreground has been set + * @since 1.4 + */ + public boolean isForegroundSet() + { + return foreground != null; + } + + /** + * Returns this component's background color. If not set, this is inherited + * from the parent. + * + * @return the background color of the component, or null + * @see #setBackground(Color) + */ + public Color getBackground() + { + if (background != null) + return background; + return parent == null ? null : parent.getBackground(); + } + + /** + * Sets this component's background color to the specified color. The parts + * of the component affected by the background color may by system dependent. + * This is a bound property. + * + * @param c the new background color + * @see #getBackground() + */ + public void setBackground(Color c) + { + // return if the background is already set to that color. + if ((c != null) && c.equals(background)) + return; + + Color previous = background; + background = c; + if (peer != null && c != null) + peer.setBackground(c); + firePropertyChange("background", previous, c); + } + + /** + * Tests if the background was explicitly set, or just inherited from the + * parent. + * + * @return true if the background has been set + * @since 1.4 + */ + public boolean isBackgroundSet() + { + return background != null; + } + + /** + * Returns the font in use for this component. If not set, this is inherited + * from the parent. + * + * @return the font for this component + * @see #setFont(Font) + */ + public Font getFont() + { + return getFontImpl(); + } + + /** + * Implementation of getFont(). This is pulled out of getFont() to prevent + * client programs from overriding this. + * + * @return the font of this component + */ + private final Font getFontImpl() + { + Font f = font; + if (f == null) + { + Component p = parent; + if (p != null) + f = p.getFontImpl(); + else + { + // It is important to return null here and not some kind of default + // font, otherwise the Swing UI would not install its fonts because + // it keeps non-UIResource fonts. + f = null; + } + } + return f; + } + + /** + * Sets the font for this component to the specified font. This is a bound + * property. + * + * @param f the new font for this component + * + * @see #getFont() + */ + public void setFont(Font f) + { + Font oldFont; + Font newFont; + // Synchronize on the tree because getFontImpl() relies on the hierarchy + // not beeing changed. + synchronized (getTreeLock()) + { + // Synchronize on this here to guarantee thread safety wrt to the + // property values. + synchronized (this) + { + oldFont = font; + font = f; + newFont = f; + } + // Create local variable here for thread safety. + ComponentPeer p = peer; + if (p != null) + { + // The peer receives the real font setting, which can depend on + // the parent font when this component's font has been set to null. + f = getFont(); + if (f != null) + { + p.setFont(f); + peerFont = f; + } + } + } + + // Fire property change event. + firePropertyChange("font", oldFont, newFont); + + // Invalidate when necessary as font changes can change the size of the + // component. + if (valid) + invalidate(); + } + + /** + * Tests if the font was explicitly set, or just inherited from the parent. + * + * @return true if the font has been set + * @since 1.4 + */ + public boolean isFontSet() + { + return font != null; + } + + /** + * Returns the locale for this component. If this component does not + * have a locale, the locale of the parent component is returned. + * + * @return the locale for this component + * @throws IllegalComponentStateException if it has no locale or parent + * @see #setLocale(Locale) + * @since 1.1 + */ + public Locale getLocale() + { + if (locale != null) + return locale; + if (parent == null) + throw new IllegalComponentStateException + ("Component has no parent: can't determine Locale"); + return parent.getLocale(); + } + + /** + * Sets the locale for this component to the specified locale. This is a + * bound property. + * + * @param newLocale the new locale for this component + */ + public void setLocale(Locale newLocale) + { + if (locale == newLocale) + return; + + Locale oldLocale = locale; + locale = newLocale; + firePropertyChange("locale", oldLocale, newLocale); + // New writing/layout direction or more/less room for localized labels. + invalidate(); + } + + /** + * Returns the color model of the device this componet is displayed on. + * + * @return this object's color model + * @see Toolkit#getColorModel() + */ + public ColorModel getColorModel() + { + GraphicsConfiguration config = getGraphicsConfiguration(); + return config != null ? config.getColorModel() + : getToolkit().getColorModel(); + } + + /** + * Returns the location of this component's top left corner relative to + * its parent component. This may be outdated, so for synchronous behavior, + * you should use a component listner. + * + * @return the location of this component + * @see #setLocation(int, int) + * @see #getLocationOnScreen() + * @since 1.1 + */ + public Point getLocation() + { + return location (); + } + + /** + * Returns the location of this component's top left corner in screen + * coordinates. + * + * @return the location of this component in screen coordinates + * @throws IllegalComponentStateException if the component is not showing + */ + public Point getLocationOnScreen() + { + if (! isShowing()) + throw new IllegalComponentStateException("component " + + getClass().getName() + + " not showing"); + + // Need to lock the tree here. We get crazy races and explosions when + // the tree changes while we are trying to find the location of this + // component. + synchronized (getTreeLock()) + { + // Only a heavyweight peer can answer the question for the screen + // location. So we are going through the hierarchy until we find + // one and add up the offsets while doing so. + int offsX = 0; + int offsY = 0; + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + offsX += comp.x; + offsY += comp.y; + comp = comp.parent; + p = comp == null ? null: comp.peer; + } + // Now we have a heavyweight component. + assert ! (p instanceof LightweightPeer); + Point loc = p.getLocationOnScreen(); + loc.x += offsX; + loc.y += offsY; + return loc; + } + } + + /** + * Returns the location of this component's top left corner relative to + * its parent component. + * + * @return the location of this component + * @deprecated use {@link #getLocation()} instead + */ + public Point location() + { + return new Point (x, y); + } + + /** + * Moves this component to the specified location, relative to the parent's + * coordinates. The coordinates are the new upper left corner of this + * component. + * + * @param x the new X coordinate of this component + * @param y the new Y coordinate of this component + * @see #getLocation() + * @see #setBounds(int, int, int, int) + */ + public void setLocation(int x, int y) + { + move (x, y); + } + + /** + * Moves this component to the specified location, relative to the parent's + * coordinates. The coordinates are the new upper left corner of this + * component. + * + * @param x the new X coordinate of this component + * @param y the new Y coordinate of this component + * @deprecated use {@link #setLocation(int, int)} instead + */ + public void move(int x, int y) + { + setBounds(x, y, this.width, this.height); + } + + /** + * Moves this component to the specified location, relative to the parent's + * coordinates. The coordinates are the new upper left corner of this + * component. + * + * @param p new coordinates for this component + * @throws NullPointerException if p is null + * @see #getLocation() + * @see #setBounds(int, int, int, int) + * @since 1.1 + */ + public void setLocation(Point p) + { + setLocation(p.x, p.y); + } + + /** + * Returns the size of this object. + * + * @return the size of this object + * @see #setSize(int, int) + * @since 1.1 + */ + public Dimension getSize() + { + return size (); + } + + /** + * Returns the size of this object. + * + * @return the size of this object + * @deprecated use {@link #getSize()} instead + */ + public Dimension size() + { + return new Dimension (width, height); + } + + /** + * Sets the size of this component to the specified width and height. + * + * @param width the new width of this component + * @param height the new height of this component + * @see #getSize() + * @see #setBounds(int, int, int, int) + */ + public void setSize(int width, int height) + { + resize (width, height); + } + + /** + * Sets the size of this component to the specified value. + * + * @param width the new width of the component + * @param height the new height of the component + * @deprecated use {@link #setSize(int, int)} instead + */ + public void resize(int width, int height) + { + setBounds(this.x, this.y, width, height); + } + + /** + * Sets the size of this component to the specified value. + * + * @param d the new size of this component + * @throws NullPointerException if d is null + * @see #setSize(int, int) + * @see #setBounds(int, int, int, int) + * @since 1.1 + */ + public void setSize(Dimension d) + { + resize (d); + } + + /** + * Sets the size of this component to the specified value. + * + * @param d the new size of this component + * @throws NullPointerException if d is null + * @deprecated use {@link #setSize(Dimension)} instead + */ + public void resize(Dimension d) + { + resize (d.width, d.height); + } + + /** + * Returns a bounding rectangle for this component. Note that the + * returned rectange is relative to this component's parent, not to + * the screen. + * + * @return the bounding rectangle for this component + * @see #setBounds(int, int, int, int) + * @see #getLocation() + * @see #getSize() + */ + public Rectangle getBounds() + { + return bounds (); + } + + /** + * Returns a bounding rectangle for this component. Note that the + * returned rectange is relative to this component's parent, not to + * the screen. + * + * @return the bounding rectangle for this component + * @deprecated use {@link #getBounds()} instead + */ + public Rectangle bounds() + { + return new Rectangle (x, y, width, height); + } + + /** + * Sets the bounding rectangle for this component to the specified values. + * Note that these coordinates are relative to the parent, not to the screen. + * + * @param x the X coordinate of the upper left corner of the rectangle + * @param y the Y coordinate of the upper left corner of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @see #getBounds() + * @see #setLocation(int, int) + * @see #setLocation(Point) + * @see #setSize(int, int) + * @see #setSize(Dimension) + * @since 1.1 + */ + public void setBounds(int x, int y, int w, int h) + { + reshape (x, y, w, h); + } + + /** + * Sets the bounding rectangle for this component to the specified values. + * Note that these coordinates are relative to the parent, not to the screen. + * + * @param x the X coordinate of the upper left corner of the rectangle + * @param y the Y coordinate of the upper left corner of the rectangle + * @param width the width of the rectangle + * @param height the height of the rectangle + * @deprecated use {@link #setBounds(int, int, int, int)} instead + */ + public void reshape(int x, int y, int width, int height) + { + // We need to lock the tree here, otherwise we risk races and + // inconsistencies. + synchronized (getTreeLock()) + { + int oldx = this.x; + int oldy = this.y; + int oldwidth = this.width; + int oldheight = this.height; + + boolean resized = oldwidth != width || oldheight != height; + boolean moved = oldx != x || oldy != y; + + if (resized || moved) + { + // Update the fields. + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + if (peer != null) + { + peer.setBounds (x, y, width, height); + if (resized) + invalidate(); + if (parent != null && parent.valid) + parent.invalidate(); + } + + // Send some events to interested listeners. + notifyReshape(resized, moved); + + // Repaint this component and the parent if appropriate. + if (parent != null && peer instanceof LightweightPeer + && isShowing()) + { + // The parent repaints the area that we occupied before. + parent.repaint(oldx, oldy, oldwidth, oldheight); + // This component repaints the area that we occupy now. + repaint(); + } + } + } + } + + /** + * Sends notification to interested listeners about resizing and/or moving + * the component. If this component has interested + * component listeners or the corresponding event mask enabled, then + * COMPONENT_MOVED and/or COMPONENT_RESIZED events are posted to the event + * queue. + * + * @param resized true if the component has been resized, false otherwise + * @param moved true if the component has been moved, false otherwise + */ + void notifyReshape(boolean resized, boolean moved) + { + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) + { + // Fire component event on this component. + if (moved) + { + ComponentEvent ce = new ComponentEvent(this, + ComponentEvent.COMPONENT_MOVED); + getToolkit().getSystemEventQueue().postEvent(ce); + } + if (resized) + { + ComponentEvent ce = new ComponentEvent(this, + ComponentEvent.COMPONENT_RESIZED); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + } + + /** + * Sets the bounding rectangle for this component to the specified + * rectangle. Note that these coordinates are relative to the parent, not + * to the screen. + * + * @param r the new bounding rectangle + * @throws NullPointerException if r is null + * @see #getBounds() + * @see #setLocation(Point) + * @see #setSize(Dimension) + * @since 1.1 + */ + public void setBounds(Rectangle r) + { + setBounds (r.x, r.y, r.width, r.height); + } + + /** + * Gets the x coordinate of the upper left corner. This is more efficient + * than getBounds().x or getLocation().x. + * + * @return the current x coordinate + * @since 1.2 + */ + public int getX() + { + return x; + } + + /** + * Gets the y coordinate of the upper left corner. This is more efficient + * than getBounds().y or getLocation().y. + * + * @return the current y coordinate + * @since 1.2 + */ + public int getY() + { + return y; + } + + /** + * Gets the width of the component. This is more efficient than + * getBounds().width or getSize().width. + * + * @return the current width + * @since 1.2 + */ + public int getWidth() + { + return width; + } + + /** + * Gets the height of the component. This is more efficient than + * getBounds().height or getSize().height. + * + * @return the current width + * @since 1.2 + */ + public int getHeight() + { + return height; + } + + /** + * Returns the bounds of this component. This allows reuse of an existing + * rectangle, if r is non-null. + * + * @param r the rectangle to use, or null + * @return the bounds + */ + public Rectangle getBounds(Rectangle r) + { + if (r == null) + r = new Rectangle(); + r.x = x; + r.y = y; + r.width = width; + r.height = height; + return r; + } + + /** + * Returns the size of this component. This allows reuse of an existing + * dimension, if d is non-null. + * + * @param d the dimension to use, or null + * @return the size + */ + public Dimension getSize(Dimension d) + { + if (d == null) + d = new Dimension(); + d.width = width; + d.height = height; + return d; + } + + /** + * Returns the location of this component. This allows reuse of an existing + * point, if p is non-null. + * + * @param p the point to use, or null + * @return the location + */ + public Point getLocation(Point p) + { + if (p == null) + p = new Point(); + p.x = x; + p.y = y; + return p; + } + + /** + * Tests if this component is opaque. All "heavyweight" (natively-drawn) + * components are opaque. A component is opaque if it draws all pixels in + * the bounds; a lightweight component is partially transparent if it lets + * pixels underneath show through. Subclasses that guarantee that all pixels + * will be drawn should override this. + * + * @return true if this is opaque + * @see #isLightweight() + * @since 1.2 + */ + public boolean isOpaque() + { + return ! isLightweight(); + } + + /** + * Return whether the component is lightweight. That means the component has + * no native peer, but is displayable. This applies to subclasses of + * Component not in this package, such as javax.swing. + * + * @return true if the component has a lightweight peer + * @see #isDisplayable() + * @since 1.2 + */ + public boolean isLightweight() + { + return peer instanceof LightweightPeer; + } + + /** + * Returns the component's preferred size. + * + * @return the component's preferred size + * @see #getMinimumSize() + * @see #setPreferredSize(Dimension) + * @see LayoutManager + */ + public Dimension getPreferredSize() + { + return preferredSize(); + } + + /** + * Sets the preferred size that will be returned by + * {@link #getPreferredSize()} always, and sends a + * {@link PropertyChangeEvent} (with the property name 'preferredSize') to + * all registered listeners. + * + * @param size the preferred size (null permitted). + * + * @since 1.5 + * + * @see #getPreferredSize() + */ + public void setPreferredSize(Dimension size) + { + Dimension old = prefSizeSet ? prefSize : null; + prefSize = size; + prefSizeSet = (size != null); + firePropertyChange("preferredSize", old, size); + } + + /** + * Returns true if the current preferred size is not + * null and was set by a call to + * {@link #setPreferredSize(Dimension)}, otherwise returns false. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isPreferredSizeSet() + { + return prefSizeSet; + } + + /** + * Returns the component's preferred size. + * + * @return the component's preferred size + * @deprecated use {@link #getPreferredSize()} instead + */ + public Dimension preferredSize() + { + // Create a new Dimension object, so that the application doesn't mess + // with the actual values. + return new Dimension(preferredSizeImpl()); + } + + /** + * The actual calculation is pulled out of preferredSize() so that + * we can call it from Container.preferredSize() and avoid creating a + * new intermediate Dimension object. + * + * @return the preferredSize of the component + */ + Dimension preferredSizeImpl() + { + Dimension size = prefSize; + // Try to use a cached value. + if (size == null || !(valid || prefSizeSet)) + { + // We need to lock here, because the calculation depends on the + // component structure not changing. + synchronized (getTreeLock()) + { + ComponentPeer p = peer; + if (p != null) + size = peer.preferredSize(); + else + size = minimumSizeImpl(); + } + } + return size; + } + + /** + * Returns the component's minimum size. + * + * @return the component's minimum size + * @see #getPreferredSize() + * @see #setMinimumSize(Dimension) + * @see LayoutManager + */ + public Dimension getMinimumSize() + { + return minimumSize(); + } + + /** + * Sets the minimum size that will be returned by {@link #getMinimumSize()} + * always, and sends a {@link PropertyChangeEvent} (with the property name + * 'minimumSize') to all registered listeners. + * + * @param size the minimum size (null permitted). + * + * @since 1.5 + * + * @see #getMinimumSize() + */ + public void setMinimumSize(Dimension size) + { + Dimension old = minSizeSet ? minSize : null; + minSize = size; + minSizeSet = (size != null); + firePropertyChange("minimumSize", old, size); + } + + /** + * Returns true if the current minimum size is not + * null and was set by a call to + * {@link #setMinimumSize(Dimension)}, otherwise returns false. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isMinimumSizeSet() + { + return minSizeSet; + } + + /** + * Returns the component's minimum size. + * + * @return the component's minimum size + * @deprecated use {@link #getMinimumSize()} instead + */ + public Dimension minimumSize() + { + // Create a new Dimension object, so that the application doesn't mess + // with the actual values. + return new Dimension(minimumSizeImpl()); + } + + /** + * The actual calculation is pulled out of minimumSize() so that + * we can call it from Container.preferredSize() and + * Component.preferredSizeImpl and avoid creating a + * new intermediate Dimension object. + * + * @return the minimum size of the component + */ + Dimension minimumSizeImpl() + { + Dimension size = minSize; + if (size == null || !(valid || minSizeSet)) + { + // We need to lock here, because the calculation depends on the + // component structure not changing. + synchronized (getTreeLock()) + { + ComponentPeer p = peer; + if (p != null) + size = peer.minimumSize(); + else + size = size(); + } + } + return size; + } + + /** + * Returns the component's maximum size. + * + * @return the component's maximum size + * @see #getMinimumSize() + * @see #setMaximumSize(Dimension) + * @see #getPreferredSize() + * @see LayoutManager + */ + public Dimension getMaximumSize() + { + return new Dimension(maximumSizeImpl()); + } + + /** + * This is pulled out from getMaximumSize(), so that we can access it + * from Container.getMaximumSize() without creating an additional + * intermediate Dimension object. + * + * @return the maximum size of the component + */ + Dimension maximumSizeImpl() + { + Dimension size; + if (maxSizeSet) + size = maxSize; + else + size = DEFAULT_MAX_SIZE; + return size; + } + + /** + * Sets the maximum size that will be returned by {@link #getMaximumSize()} + * always, and sends a {@link PropertyChangeEvent} (with the property name + * 'maximumSize') to all registered listeners. + * + * @param size the maximum size (null permitted). + * + * @since 1.5 + * + * @see #getMaximumSize() + */ + public void setMaximumSize(Dimension size) + { + Dimension old = maxSizeSet ? maxSize : null; + maxSize = size; + maxSizeSet = (size != null); + firePropertyChange("maximumSize", old, size); + } + + /** + * Returns true if the current maximum size is not + * null and was set by a call to + * {@link #setMaximumSize(Dimension)}, otherwise returns false. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isMaximumSizeSet() + { + return maxSizeSet; + } + + /** + * Returns the preferred horizontal alignment of this component. The value + * returned will be between {@link #LEFT_ALIGNMENT} and + * {@link #RIGHT_ALIGNMENT}, inclusive. + * + * @return the preferred horizontal alignment of this component + */ + public float getAlignmentX() + { + return CENTER_ALIGNMENT; + } + + /** + * Returns the preferred vertical alignment of this component. The value + * returned will be between {@link #TOP_ALIGNMENT} and + * {@link #BOTTOM_ALIGNMENT}, inclusive. + * + * @return the preferred vertical alignment of this component + */ + public float getAlignmentY() + { + return CENTER_ALIGNMENT; + } + + /** + * Calls the layout manager to re-layout the component. This is called + * during validation of a container in most cases. + * + * @see #validate() + * @see LayoutManager + */ + public void doLayout() + { + layout (); + } + + /** + * Calls the layout manager to re-layout the component. This is called + * during validation of a container in most cases. + * + * @deprecated use {@link #doLayout()} instead + */ + public void layout() + { + // Nothing to do unless we're a container. + } + + /** + * Called to ensure that the layout for this component is valid. This is + * usually called on containers. + * + * @see #invalidate() + * @see #doLayout() + * @see LayoutManager + * @see Container#validate() + */ + public void validate() + { + if (! valid) + { + // Synchronize on the tree here as this might change the layout + // of the hierarchy. + synchronized (getTreeLock()) + { + // Create local variables for thread safety. + ComponentPeer p = peer; + if (p != null) + { + // Possibly update the peer's font. + Font newFont = getFont(); + Font oldFont = peerFont; + // Only update when the font really changed. + if (newFont != oldFont + && (oldFont == null || ! oldFont.equals(newFont))) + { + p.setFont(newFont); + peerFont = newFont; + } + // Let the peer perform any layout. + p.layout(); + } + } + valid = true; + } + } + + /** + * Invalidates this component and all of its parent components. This will + * cause them to have their layout redone. This is called frequently, so + * make it fast. + */ + public void invalidate() + { + // Need to lock here, to avoid races and other ugly stuff when doing + // layout or structure changes in other threads. + synchronized (getTreeLock()) + { + // Invalidate. + valid = false; + + // Throw away cached layout information. + if (! minSizeSet) + minSize = null; + if (! prefSizeSet) + prefSize = null; + if (! maxSizeSet) + maxSize = null; + + // Also invalidate the parent, if it hasn't already been invalidated. + if (parent != null && parent.isValid()) + parent.invalidate(); + } + } + + /** + * Returns a graphics object for this component. Returns null + * if this component is not currently displayed on the screen. + * + * @return a graphics object for this component + * @see #paint(Graphics) + */ + public Graphics getGraphics() + { + // Only heavyweight peers can handle this. + ComponentPeer p = peer; + Graphics g = null; + if (p instanceof LightweightPeer) + { + if (parent != null) + { + g = parent.getGraphics(); + if (g != null) + { + g.translate(x, y); + g.setClip(0, 0, width, height); + g.setFont(getFont()); + } + } + } + else + { + if (p != null) + g = p.getGraphics(); + } + return g; + } + + /** + * Returns the font metrics for the specified font in this component. + * + * @param font the font to retrieve metrics for + * @return the font metrics for the specified font + * @throws NullPointerException if font is null + * @see #getFont() + * @see Toolkit#getFontMetrics(Font) + */ + public FontMetrics getFontMetrics(Font font) + { + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + return p == null ? getToolkit().getFontMetrics(font) + : p.getFontMetrics(font); + } + + /** + * Sets the cursor for this component to the specified cursor. The cursor + * is displayed when the point is contained by the component, and the + * component is visible, displayable, and enabled. This is inherited by + * subcomponents unless they set their own cursor. + * + * @param cursor the new cursor for this component + * @see #isEnabled() + * @see #isShowing() + * @see #getCursor() + * @see #contains(int, int) + * @see Toolkit#createCustomCursor(Image, Point, String) + */ + public void setCursor(Cursor cursor) + { + this.cursor = cursor; + + // Only heavyweight peers handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + if (p != null) + p.setCursor(cursor); + } + + /** + * Returns the cursor for this component. If not set, this is inherited + * from the parent, or from Cursor.getDefaultCursor(). + * + * @return the cursor for this component + */ + public Cursor getCursor() + { + if (cursor != null) + return cursor; + return parent != null ? parent.getCursor() : Cursor.getDefaultCursor(); + } + + /** + * Tests if the cursor was explicitly set, or just inherited from the parent. + * + * @return true if the cursor has been set + * @since 1.4 + */ + public boolean isCursorSet() + { + return cursor != null; + } + + /** + * Paints this component on the screen. The clipping region in the graphics + * context will indicate the region that requires painting. This is called + * whenever the component first shows, or needs to be repaired because + * something was temporarily drawn on top. It is not necessary for + * subclasses to call super.paint(g). Components with no area + * are not painted. + * + * @param g the graphics context for this paint job + * @see #update(Graphics) + */ + public void paint(Graphics g) + { + // This is a callback method and is meant to be overridden by subclasses + // that want to perform custom painting. + } + + /** + * Updates this component. This is called for heavyweight components in + * response to {@link #repaint()}. The default implementation simply forwards + * to {@link #paint(Graphics)}. The coordinates of the graphics are + * relative to this component. Subclasses should call either + * super.update(g) or paint(g). + * + * @param g the graphics context for this update + * + * @see #paint(Graphics) + * @see #repaint() + */ + public void update(Graphics g) + { + // Note 1: We used to clear the background here for lightweights and + // toplevel components. Tests show that this is not what the JDK does + // here. Note that there is some special handling and background + // clearing code in Container.update(Graphics). + + // Note 2 (for peer implementors): The JDK doesn't seem call update() for + // toplevel components, even when an UPDATE event is sent (as a result + // of repaint). + paint(g); + } + + /** + * Paints this entire component, including any sub-components. + * + * @param g the graphics context for this paint job + * + * @see #paint(Graphics) + */ + public void paintAll(Graphics g) + { + if (isShowing()) + { + validate(); + if (peer instanceof LightweightPeer) + paint(g); + else + peer.paint(g); + } + } + + /** + * Repaint this entire component. The update() method + * on this component will be called as soon as possible. + * + * @see #update(Graphics) + * @see #repaint(long, int, int, int, int) + */ + public void repaint() + { + repaint(0, 0, 0, width, height); + } + + /** + * Repaint this entire component. The update() method on this + * component will be called in approximate the specified number of + * milliseconds. + * + * @param tm milliseconds before this component should be repainted + * @see #paint(Graphics) + * @see #repaint(long, int, int, int, int) + */ + public void repaint(long tm) + { + repaint(tm, 0, 0, width, height); + } + + /** + * Repaints the specified rectangular region within this component. The + * update method on this component will be called as soon as + * possible. The coordinates are relative to this component. + * + * @param x the X coordinate of the upper left of the region to repaint + * @param y the Y coordinate of the upper left of the region to repaint + * @param w the width of the region to repaint + * @param h the height of the region to repaint + * @see #update(Graphics) + * @see #repaint(long, int, int, int, int) + */ + public void repaint(int x, int y, int w, int h) + { + repaint(0, x, y, w, h); + } + + /** + * Repaints the specified rectangular region within this component. The + * update method on this component will be called in + * approximately the specified number of milliseconds. The coordinates + * are relative to this component. + * + * @param tm milliseconds before this component should be repainted + * @param x the X coordinate of the upper left of the region to repaint + * @param y the Y coordinate of the upper left of the region to repaint + * @param width the width of the region to repaint + * @param height the height of the region to repaint + * @see #update(Graphics) + */ + public void repaint(long tm, int x, int y, int width, int height) + { + // The repaint() call has previously been delegated to + // {@link ComponentPeer.repaint()}. Testing on the JDK using some + // dummy peers show that this methods is never called. I think it makes + // sense to actually perform the tasks below here, since it's pretty + // much peer independent anyway, and makes sure only heavyweights are + // bothered by this. + ComponentPeer p = peer; + + // Let the nearest heavyweight parent handle repainting for lightweight + // components. + // We need to recursivly call repaint() on the parent here, since + // a (lightweight) parent component might have overridden repaint() + // to perform additional custom tasks. + + if (p instanceof LightweightPeer) + { + // We perform some boundary checking to restrict the paint + // region to this component. + if (parent != null) + { + int px = this.x + Math.max(0, x); + int py = this.y + Math.max(0, y); + int pw = Math.min(this.width, width); + int ph = Math.min(this.height, height); + parent.repaint(tm, px, py, pw, ph); + } + } + else + { + // Now send an UPDATE event to the heavyweight component that we've found. + if (isVisible() && p != null && width > 0 && height > 0) + { + PaintEvent pe = new PaintEvent(this, PaintEvent.UPDATE, + new Rectangle(x, y, width, height)); + getToolkit().getSystemEventQueue().postEvent(pe); + } + } + } + + /** + * Prints this component. This method is provided so that printing can be + * done in a different manner from painting. However, the implementation + * in this class simply calls the paint() method. + * + * @param g the graphics context of the print device + * + * @see #paint(Graphics) + */ + public void print(Graphics g) + { + paint(g); + } + + /** + * Prints this component, including all sub-components. + * + * @param g the graphics context of the print device + * + * @see #paintAll(Graphics) + */ + public void printAll(Graphics g) + { + if( peer != null ) + peer.print( g ); + paintAll( g ); + } + + /** + * Called when an image has changed so that this component is repainted. + * This incrementally draws an image as more bits are available, when + * possible. Incremental drawing is enabled if the system property + * awt.image.incrementalDraw is not present or is true, in which + * case the redraw rate is set to 100ms or the value of the system property + * awt.image.redrawrate. + * + *

The coordinate system used depends on the particular flags. + * + * @param img the image that has been updated + * @param flags tlags as specified in ImageObserver + * @param x the X coordinate + * @param y the Y coordinate + * @param w the width + * @param h the height + * @return false if the image is completely loaded, loading has been + * aborted, or an error has occurred. true if more updates are + * required. + * @see ImageObserver + * @see Graphics#drawImage(Image, int, int, Color, ImageObserver) + * @see Graphics#drawImage(Image, int, int, ImageObserver) + * @see Graphics#drawImage(Image, int, int, int, int, Color, ImageObserver) + * @see Graphics#drawImage(Image, int, int, int, int, ImageObserver) + * @see ImageObserver#imageUpdate(Image, int, int, int, int, int) + */ + public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) + { + if ((flags & (FRAMEBITS | ALLBITS)) != 0) + repaint(); + else if ((flags & SOMEBITS) != 0) + { + if (incrementalDraw) + { + if (redrawRate != null) + { + long tm = redrawRate.longValue(); + if (tm < 0) + tm = 0; + repaint(tm); + } + else + repaint(100); + } + } + return (flags & (ALLBITS | ABORT | ERROR)) == 0; + } + + /** + * Creates an image from the specified producer. + * + * @param producer the image procedure to create the image from + * @return the resulting image + */ + public Image createImage(ImageProducer producer) + { + // Only heavyweight peers can handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + // Sun allows producer to be null. + Image im; + if (p != null) + im = p.createImage(producer); + else + im = getToolkit().createImage(producer); + return im; + } + + /** + * Creates an image with the specified width and height for use in + * double buffering. Headless environments do not support images. + * + * @param width the width of the image + * @param height the height of the image + * @return the requested image, or null if it is not supported + */ + public Image createImage (int width, int height) + { + Image returnValue = null; + if (!GraphicsEnvironment.isHeadless ()) + { + // Only heavyweight peers can handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + if (p != null) + returnValue = p.createImage(width, height); + } + return returnValue; + } + + /** + * Creates an image with the specified width and height for use in + * double buffering. Headless environments do not support images. + * + * @param width the width of the image + * @param height the height of the image + * @return the requested image, or null if it is not supported + * @since 1.4 + */ + public VolatileImage createVolatileImage(int width, int height) + { + // Only heavyweight peers can handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + VolatileImage im = null; + if (p != null) + im = p.createVolatileImage(width, height); + return im; + } + + /** + * Creates an image with the specified width and height for use in + * double buffering. Headless environments do not support images. The image + * will support the specified capabilities. + * + * @param width the width of the image + * @param height the height of the image + * @param caps the requested capabilities + * @return the requested image, or null if it is not supported + * @throws AWTException if a buffer with the capabilities cannot be created + * @since 1.4 + */ + public VolatileImage createVolatileImage(int width, int height, + ImageCapabilities caps) + throws AWTException + { + // Only heavyweight peers can handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + VolatileImage im = null; + if (p != null) + im = peer.createVolatileImage(width, height); + return im; + } + + /** + * Prepares the specified image for rendering on this component. + * + * @param image the image to prepare for rendering + * @param observer the observer to notify of image preparation status + * @return true if the image is already fully prepared + * @throws NullPointerException if image is null + */ + public boolean prepareImage(Image image, ImageObserver observer) + { + return prepareImage(image, image.getWidth(observer), + image.getHeight(observer), observer); + } + + /** + * Prepares the specified image for rendering on this component at the + * specified scaled width and height + * + * @param image the image to prepare for rendering + * @param width the scaled width of the image + * @param height the scaled height of the image + * @param observer the observer to notify of image preparation status + * @return true if the image is already fully prepared + */ + public boolean prepareImage(Image image, int width, int height, + ImageObserver observer) + { + // Only heavyweight peers handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + boolean retval; + if (p != null) + retval = p.prepareImage(image, width, height, observer); + else + retval = getToolkit().prepareImage(image, width, height, observer); + return retval; + } + + /** + * Returns the status of the loading of the specified image. The value + * returned will be those flags defined in ImageObserver. + * + * @param image the image to check on + * @param observer the observer to notify of image loading progress + * @return the image observer flags indicating the status of the load + * @see #prepareImage(Image, int, int, ImageObserver) + * @see Toolkit#checkImage(Image, int, int, ImageObserver) + * @throws NullPointerException if image is null + */ + public int checkImage(Image image, ImageObserver observer) + { + return checkImage(image, -1, -1, observer); + } + + /** + * Returns the status of the loading of the specified image. The value + * returned will be those flags defined in ImageObserver. + * + * @param image the image to check on + * @param width the scaled image width + * @param height the scaled image height + * @param observer the observer to notify of image loading progress + * @return the image observer flags indicating the status of the load + * @see #prepareImage(Image, int, int, ImageObserver) + * @see Toolkit#checkImage(Image, int, int, ImageObserver) + */ + public int checkImage(Image image, int width, int height, + ImageObserver observer) + { + // Only heavyweight peers handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + int retval; + if (p != null) + retval = p.checkImage(image, width, height, observer); + else + retval = getToolkit().checkImage(image, width, height, observer); + return retval; + } + + /** + * Sets whether paint messages delivered by the operating system should be + * ignored. This does not affect messages from AWT, except for those + * triggered by OS messages. Setting this to true can allow faster + * performance in full-screen mode or page-flipping. + * + * @param ignoreRepaint the new setting for ignoring repaint events + * @see #getIgnoreRepaint() + * @see BufferStrategy + * @see GraphicsDevice#setFullScreenWindow(Window) + * @since 1.4 + */ + public void setIgnoreRepaint(boolean ignoreRepaint) + { + this.ignoreRepaint = ignoreRepaint; + } + + /** + * Test whether paint events from the operating system are ignored. + * + * @return the status of ignoring paint events + * @see #setIgnoreRepaint(boolean) + * @since 1.4 + */ + public boolean getIgnoreRepaint() + { + return ignoreRepaint; + } + + /** + * Tests whether or not the specified point is contained within this + * component. Coordinates are relative to this component. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is within this component + * @see #getComponentAt(int, int) + */ + public boolean contains(int x, int y) + { + return inside (x, y); + } + + /** + * Tests whether or not the specified point is contained within this + * component. Coordinates are relative to this component. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is within this component + * @deprecated use {@link #contains(int, int)} instead + */ + public boolean inside(int x, int y) + { + return x >= 0 && y >= 0 && x < width && y < height; + } + + /** + * Tests whether or not the specified point is contained within this + * component. Coordinates are relative to this component. + * + * @param p the point to test + * @return true if the point is within this component + * @throws NullPointerException if p is null + * @see #getComponentAt(Point) + * @since 1.1 + */ + public boolean contains(Point p) + { + return contains (p.x, p.y); + } + + /** + * Returns the component occupying the position (x,y). This will either + * be this component, an immediate child component, or null + * if neither of the first two occupies the specified location. + * + * @param x the X coordinate to search for components at + * @param y the Y coordinate to search for components at + * @return the component at the specified location, or null + * @see #contains(int, int) + */ + public Component getComponentAt(int x, int y) + { + return locate (x, y); + } + + /** + * Returns the component occupying the position (x,y). This will either + * be this component, an immediate child component, or null + * if neither of the first two occupies the specified location. + * + * @param x the X coordinate to search for components at + * @param y the Y coordinate to search for components at + * @return the component at the specified location, or null + * @deprecated use {@link #getComponentAt(int, int)} instead + */ + public Component locate(int x, int y) + { + return contains (x, y) ? this : null; + } + + /** + * Returns the component occupying the position (x,y). This will either + * be this component, an immediate child component, or null + * if neither of the first two occupies the specified location. + * + * @param p the point to search for components at + * @return the component at the specified location, or null + * @throws NullPointerException if p is null + * @see #contains(Point) + * @since 1.1 + */ + public Component getComponentAt(Point p) + { + return getComponentAt (p.x, p.y); + } + + /** + * AWT 1.0 event delivery. + * + * Deliver an AWT 1.0 event to this Component. This method simply + * calls {@link #postEvent}. + * + * @param e the event to deliver + * @deprecated use {@link #dispatchEvent (AWTEvent)} instead + */ + public void deliverEvent (Event e) + { + postEvent (e); + } + + /** + * Forwards AWT events to processEvent() if:

    + *
  • Events have been enabled for this type of event via + * enableEvents()
  • , + *
  • There is at least one registered listener for this type of event
  • + *
+ * + * @param e the event to dispatch + */ + public final void dispatchEvent(AWTEvent e) + { + // Some subclasses in the AWT package need to override this behavior, + // hence the use of dispatchEventImpl(). + dispatchEventImpl(e); + } + + /** + * By default, no old mouse events should be ignored. + * This can be overridden by subclasses. + * + * @return false, no mouse events are ignored. + */ + static boolean ignoreOldMouseEvents() + { + return false; + } + + /** + * AWT 1.0 event handler. + * + * This method simply calls handleEvent and returns the result. + * + * @param e the event to handle + * @return true if the event was handled, false otherwise + * @deprecated use {@link #dispatchEvent(AWTEvent)} instead + */ + public boolean postEvent (Event e) + { + boolean handled = handleEvent (e); + + if (!handled && getParent() != null) + // FIXME: need to translate event coordinates to parent's + // coordinate space. + handled = getParent ().postEvent (e); + + return handled; + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see ComponentEvent + * @see #removeComponentListener(ComponentListener) + * @see #getComponentListeners() + * @since 1.1 + */ + public synchronized void addComponentListener(ComponentListener listener) + { + if (listener != null) + { + componentListener = AWTEventMulticaster.add(componentListener, + listener); + newEventsOnly = true; + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see ComponentEvent + * @see #addComponentListener(ComponentListener) + * @see #getComponentListeners() + * @since 1.1 + */ + public synchronized void removeComponentListener(ComponentListener listener) + { + componentListener = AWTEventMulticaster.remove(componentListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addComponentListener(ComponentListener) + * @see #removeComponentListener(ComponentListener) + * @since 1.4 + */ + public synchronized ComponentListener[] getComponentListeners() + { + return (ComponentListener[]) + AWTEventMulticaster.getListeners(componentListener, + ComponentListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see FocusEvent + * @see #removeFocusListener(FocusListener) + * @see #getFocusListeners() + * @since 1.1 + */ + public synchronized void addFocusListener(FocusListener listener) + { + if (listener != null) + { + focusListener = AWTEventMulticaster.add(focusListener, listener); + newEventsOnly = true; + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see FocusEvent + * @see #addFocusListener(FocusListener) + * @see #getFocusListeners() + * @since 1.1 + */ + public synchronized void removeFocusListener(FocusListener listener) + { + focusListener = AWTEventMulticaster.remove(focusListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addFocusListener(FocusListener) + * @see #removeFocusListener(FocusListener) + * @since 1.4 + */ + public synchronized FocusListener[] getFocusListeners() + { + return (FocusListener[]) + AWTEventMulticaster.getListeners(focusListener, FocusListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see HierarchyEvent + * @see #removeHierarchyListener(HierarchyListener) + * @see #getHierarchyListeners() + * @since 1.3 + */ + public synchronized void addHierarchyListener(HierarchyListener listener) + { + if (listener != null) + { + hierarchyListener = AWTEventMulticaster.add(hierarchyListener, + listener); + newEventsOnly = true; + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyListeners++; + if (parent != null) + parent.updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + 1); + } + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see HierarchyEvent + * @see #addHierarchyListener(HierarchyListener) + * @see #getHierarchyListeners() + * @since 1.3 + */ + public synchronized void removeHierarchyListener(HierarchyListener listener) + { + hierarchyListener = AWTEventMulticaster.remove(hierarchyListener, listener); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyListeners--; + if (parent != null) + parent.updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + -1); + } + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addHierarchyListener(HierarchyListener) + * @see #removeHierarchyListener(HierarchyListener) + * @since 1.4 + */ + public synchronized HierarchyListener[] getHierarchyListeners() + { + return (HierarchyListener[]) + AWTEventMulticaster.getListeners(hierarchyListener, + HierarchyListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see HierarchyEvent + * @see #removeHierarchyBoundsListener(HierarchyBoundsListener) + * @see #getHierarchyBoundsListeners() + * @since 1.3 + */ + public synchronized void + addHierarchyBoundsListener(HierarchyBoundsListener listener) + { + if (listener != null) + { + hierarchyBoundsListener = + AWTEventMulticaster.add(hierarchyBoundsListener, listener); + newEventsOnly = true; + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyBoundsListeners++; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, 1); + } + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see HierarchyEvent + * @see #addHierarchyBoundsListener(HierarchyBoundsListener) + * @see #getHierarchyBoundsListeners() + * @since 1.3 + */ + public synchronized void + removeHierarchyBoundsListener(HierarchyBoundsListener listener) + { + hierarchyBoundsListener = + AWTEventMulticaster.remove(hierarchyBoundsListener, listener); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyBoundsListeners--; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + -1); + } + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addHierarchyBoundsListener(HierarchyBoundsListener) + * @see #removeHierarchyBoundsListener(HierarchyBoundsListener) + * @since 1.4 + */ + public synchronized HierarchyBoundsListener[] getHierarchyBoundsListeners() + { + return (HierarchyBoundsListener[]) + AWTEventMulticaster.getListeners(hierarchyBoundsListener, + HierarchyBoundsListener.class); + } + + /** + * Fires a HierarchyEvent or HierarchyChangeEvent on this component. + * + * @param id the event id + * @param changed the changed component + * @param parent the parent + * @param flags the event flags + */ + void fireHierarchyEvent(int id, Component changed, Container parent, + long flags) + { + boolean enabled = false; + switch (id) + { + case HierarchyEvent.HIERARCHY_CHANGED: + enabled = hierarchyListener != null + || (eventMask & AWTEvent.HIERARCHY_EVENT_MASK) != 0; + break; + case HierarchyEvent.ANCESTOR_MOVED: + case HierarchyEvent.ANCESTOR_RESIZED: + enabled = hierarchyBoundsListener != null + || (eventMask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0; + break; + default: + assert false : "Should not reach here"; + } + if (enabled) + { + HierarchyEvent ev = new HierarchyEvent(this, id, changed, parent, + flags); + dispatchEvent(ev); + } + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see KeyEvent + * @see #removeKeyListener(KeyListener) + * @see #getKeyListeners() + * @since 1.1 + */ + public synchronized void addKeyListener(KeyListener listener) + { + if (listener != null) + { + keyListener = AWTEventMulticaster.add(keyListener, listener); + newEventsOnly = true; + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see KeyEvent + * @see #addKeyListener(KeyListener) + * @see #getKeyListeners() + * @since 1.1 + */ + public synchronized void removeKeyListener(KeyListener listener) + { + keyListener = AWTEventMulticaster.remove(keyListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addKeyListener(KeyListener) + * @see #removeKeyListener(KeyListener) + * @since 1.4 + */ + public synchronized KeyListener[] getKeyListeners() + { + return (KeyListener[]) + AWTEventMulticaster.getListeners(keyListener, KeyListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see MouseEvent + * @see #removeMouseListener(MouseListener) + * @see #getMouseListeners() + * @since 1.1 + */ + public synchronized void addMouseListener(MouseListener listener) + { + if (listener != null) + { + mouseListener = AWTEventMulticaster.add(mouseListener, listener); + newEventsOnly = true; + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see MouseEvent + * @see #addMouseListener(MouseListener) + * @see #getMouseListeners() + * @since 1.1 + */ + public synchronized void removeMouseListener(MouseListener listener) + { + mouseListener = AWTEventMulticaster.remove(mouseListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addMouseListener(MouseListener) + * @see #removeMouseListener(MouseListener) + * @since 1.4 + */ + public synchronized MouseListener[] getMouseListeners() + { + return (MouseListener[]) + AWTEventMulticaster.getListeners(mouseListener, MouseListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see MouseEvent + * @see #removeMouseMotionListener(MouseMotionListener) + * @see #getMouseMotionListeners() + * @since 1.1 + */ + public synchronized void addMouseMotionListener(MouseMotionListener listener) + { + if (listener != null) + { + mouseMotionListener = AWTEventMulticaster.add(mouseMotionListener, + listener); + newEventsOnly = true; + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see MouseEvent + * @see #addMouseMotionListener(MouseMotionListener) + * @see #getMouseMotionListeners() + * @since 1.1 + */ + public synchronized void removeMouseMotionListener(MouseMotionListener listener) + { + mouseMotionListener = AWTEventMulticaster.remove(mouseMotionListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addMouseMotionListener(MouseMotionListener) + * @see #removeMouseMotionListener(MouseMotionListener) + * @since 1.4 + */ + public synchronized MouseMotionListener[] getMouseMotionListeners() + { + return (MouseMotionListener[]) + AWTEventMulticaster.getListeners(mouseMotionListener, + MouseMotionListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see MouseEvent + * @see MouseWheelEvent + * @see #removeMouseWheelListener(MouseWheelListener) + * @see #getMouseWheelListeners() + * @since 1.4 + */ + public synchronized void addMouseWheelListener(MouseWheelListener listener) + { + if (listener != null) + { + mouseWheelListener = AWTEventMulticaster.add(mouseWheelListener, + listener); + newEventsOnly = true; + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see MouseEvent + * @see MouseWheelEvent + * @see #addMouseWheelListener(MouseWheelListener) + * @see #getMouseWheelListeners() + * @since 1.4 + */ + public synchronized void removeMouseWheelListener(MouseWheelListener listener) + { + mouseWheelListener = AWTEventMulticaster.remove(mouseWheelListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addMouseWheelListener(MouseWheelListener) + * @see #removeMouseWheelListener(MouseWheelListener) + * @since 1.4 + */ + public synchronized MouseWheelListener[] getMouseWheelListeners() + { + return (MouseWheelListener[]) + AWTEventMulticaster.getListeners(mouseWheelListener, + MouseWheelListener.class); + } + + /** + * Adds the specified listener to this component. This is harmless if the + * listener is null, but if the listener has already been registered, it + * will now be registered twice. + * + * @param listener the new listener to add + * @see InputMethodEvent + * @see #removeInputMethodListener(InputMethodListener) + * @see #getInputMethodListeners() + * @see #getInputMethodRequests() + * @since 1.2 + */ + public synchronized void addInputMethodListener(InputMethodListener listener) + { + if (listener != null) + { + inputMethodListener = AWTEventMulticaster.add(inputMethodListener, + listener); + newEventsOnly = true; + } + } + + /** + * Removes the specified listener from the component. This is harmless if + * the listener was not previously registered. + * + * @param listener the listener to remove + * @see InputMethodEvent + * @see #addInputMethodListener(InputMethodListener) + * @see #getInputMethodRequests() + * @since 1.2 + */ + public synchronized void removeInputMethodListener(InputMethodListener listener) + { + inputMethodListener = AWTEventMulticaster.remove(inputMethodListener, listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addInputMethodListener(InputMethodListener) + * @see #removeInputMethodListener(InputMethodListener) + * @since 1.4 + */ + public synchronized InputMethodListener[] getInputMethodListeners() + { + return (InputMethodListener[]) + AWTEventMulticaster.getListeners(inputMethodListener, + InputMethodListener.class); + } + + /** + * Returns all registered {@link EventListener}s of the given + * listenerType. + * + * @param listenerType the class of listeners to filter (null + * not permitted). + * + * @return An array of registered listeners. + * + * @throws ClassCastException if listenerType does not implement + * the {@link EventListener} interface. + * @throws NullPointerException if listenerType is + * null. + * + * @see #getComponentListeners() + * @see #getFocusListeners() + * @see #getHierarchyListeners() + * @see #getHierarchyBoundsListeners() + * @see #getKeyListeners() + * @see #getMouseListeners() + * @see #getMouseMotionListeners() + * @see #getMouseWheelListeners() + * @see #getInputMethodListeners() + * @see #getPropertyChangeListeners() + * @since 1.3 + */ + public T[] getListeners(Class listenerType) + { + if (listenerType == ComponentListener.class) + return (T[]) getComponentListeners(); + if (listenerType == FocusListener.class) + return (T[]) getFocusListeners(); + if (listenerType == HierarchyListener.class) + return (T[]) getHierarchyListeners(); + if (listenerType == HierarchyBoundsListener.class) + return (T[]) getHierarchyBoundsListeners(); + if (listenerType == KeyListener.class) + return (T[]) getKeyListeners(); + if (listenerType == MouseListener.class) + return (T[]) getMouseListeners(); + if (listenerType == MouseMotionListener.class) + return (T[]) getMouseMotionListeners(); + if (listenerType == MouseWheelListener.class) + return (T[]) getMouseWheelListeners(); + if (listenerType == InputMethodListener.class) + return (T[]) getInputMethodListeners(); + if (listenerType == PropertyChangeListener.class) + return (T[]) getPropertyChangeListeners(); + return (T[]) Array.newInstance(listenerType, 0); + } + + /** + * Returns the input method request handler, for subclasses which support + * on-the-spot text input. By default, input methods are handled by AWT, + * and this returns null. + * + * @return the input method handler, null by default + * @since 1.2 + */ + public InputMethodRequests getInputMethodRequests() + { + return null; + } + + /** + * Gets the input context of this component, which is inherited from the + * parent unless this is overridden. + * + * @return the text input context + * @since 1.2 + */ + public InputContext getInputContext() + { + return parent == null ? null : parent.getInputContext(); + } + + /** + * Enables the specified events. The events to enable are specified + * by OR-ing together the desired masks from AWTEvent. + * + *

Events are enabled by default when a listener is attached to the + * component for that event type. This method can be used by subclasses + * to ensure the delivery of a specified event regardless of whether + * or not a listener is attached. + * + * @param eventsToEnable the desired events to enable + * @see #processEvent(AWTEvent) + * @see #disableEvents(long) + * @see AWTEvent + * @since 1.1 + */ + protected final void enableEvents(long eventsToEnable) + { + // Update the counter for hierarchy (bounds) listeners. + if ((eventsToEnable & AWTEvent.HIERARCHY_EVENT_MASK) != 0 + && (eventMask & AWTEvent.HIERARCHY_EVENT_MASK) == 0) + { + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyListeners++; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_EVENT_MASK, + 1); + } + } + if ((eventsToEnable & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0 + && (eventMask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) == 0) + { + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyBoundsListeners++; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + 1); + } + } + + eventMask |= eventsToEnable; + newEventsOnly = true; + + // Only heavyweight peers handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + if (p != null) + p.setEventMask(eventMask); + + } + + /** + * Disables the specified events. The events to disable are specified + * by OR-ing together the desired masks from AWTEvent. + * + * @param eventsToDisable the desired events to disable + * @see #enableEvents(long) + * @since 1.1 + */ + protected final void disableEvents(long eventsToDisable) + { + // Update the counter for hierarchy (bounds) listeners. + if ((eventsToDisable & AWTEvent.HIERARCHY_EVENT_MASK) != 0 + && (eventMask & AWTEvent.HIERARCHY_EVENT_MASK) != 0) + { + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyListeners--; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_EVENT_MASK, + -1); + } + } + if ((eventsToDisable & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0 + && (eventMask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0) + { + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyBoundsListeners--; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + -1); + } + } + + eventMask &= ~eventsToDisable; + + // Only heavyweight peers handle this. + ComponentPeer p = peer; + Component comp = this; + while (p instanceof LightweightPeer) + { + comp = comp.parent; + p = comp == null ? null : comp.peer; + } + + if (p != null) + p.setEventMask(eventMask); + + } + + /** + * This is called by the EventQueue if two events with the same event id + * and owner component are queued. Returns a new combined event, or null if + * no combining is done. The coelesced events are currently mouse moves + * (intermediate ones are discarded) and paint events (a merged paint is + * created in place of the two events). + * + * @param existingEvent the event on the queue + * @param newEvent the new event that might be entered on the queue + * @return null if both events are kept, or the replacement coelesced event + */ + protected AWTEvent coalesceEvents(AWTEvent existingEvent, AWTEvent newEvent) + { + AWTEvent coalesced = null; + switch (existingEvent.id) + { + case MouseEvent.MOUSE_MOVED: + case MouseEvent.MOUSE_DRAGGED: + // Just drop the old (intermediate) event and return the new one. + MouseEvent me1 = (MouseEvent) existingEvent; + MouseEvent me2 = (MouseEvent) newEvent; + if (me1.getModifiers() == me2.getModifiers()) + coalesced = newEvent; + break; + case PaintEvent.PAINT: + case PaintEvent.UPDATE: + // For heavyweights the EventQueue should ask the peer. + if (peer == null || peer instanceof LightweightPeer) + { + PaintEvent pe1 = (PaintEvent) existingEvent; + PaintEvent pe2 = (PaintEvent) newEvent; + Rectangle r1 = pe1.getUpdateRect(); + Rectangle r2 = pe2.getUpdateRect(); + if (r1.contains(r2)) + coalesced = existingEvent; + else if (r2.contains(r1)) + coalesced = newEvent; + } + else + { + // Replace the event and let the heavyweight figure out the expanding + // of the repaint area. + coalesced = newEvent; + } + break; + default: + coalesced = null; + } + return coalesced; + } + + /** + * Processes the specified event. In this class, this method simply + * calls one of the more specific event handlers. + * + * @param e the event to process + * @throws NullPointerException if e is null + * @see #processComponentEvent(ComponentEvent) + * @see #processFocusEvent(FocusEvent) + * @see #processKeyEvent(KeyEvent) + * @see #processMouseEvent(MouseEvent) + * @see #processMouseMotionEvent(MouseEvent) + * @see #processInputMethodEvent(InputMethodEvent) + * @see #processHierarchyEvent(HierarchyEvent) + * @see #processMouseWheelEvent(MouseWheelEvent) + * @since 1.1 + */ + protected void processEvent(AWTEvent e) + { + /* Note: the order of these if statements are + important. Subclasses must be checked first. Eg. MouseEvent + must be checked before ComponentEvent, since a MouseEvent + object is also an instance of a ComponentEvent. */ + + if (e instanceof FocusEvent) + processFocusEvent((FocusEvent) e); + else if (e instanceof MouseWheelEvent) + processMouseWheelEvent((MouseWheelEvent) e); + else if (e instanceof MouseEvent) + { + if (e.id == MouseEvent.MOUSE_MOVED + || e.id == MouseEvent.MOUSE_DRAGGED) + processMouseMotionEvent((MouseEvent) e); + else + processMouseEvent((MouseEvent) e); + } + else if (e instanceof KeyEvent) + processKeyEvent((KeyEvent) e); + else if (e instanceof InputMethodEvent) + processInputMethodEvent((InputMethodEvent) e); + else if (e instanceof ComponentEvent) + processComponentEvent((ComponentEvent) e); + else if (e instanceof HierarchyEvent) + { + if (e.id == HierarchyEvent.HIERARCHY_CHANGED) + processHierarchyEvent((HierarchyEvent) e); + else + processHierarchyBoundsEvent((HierarchyEvent) e); + } + } + + /** + * Called when a component event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the ComponentEvent to process + * @throws NullPointerException if e is null + * @see ComponentListener + * @see #addComponentListener(ComponentListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processComponentEvent(ComponentEvent e) + { + if (componentListener == null) + return; + switch (e.id) + { + case ComponentEvent.COMPONENT_HIDDEN: + componentListener.componentHidden(e); + break; + case ComponentEvent.COMPONENT_MOVED: + componentListener.componentMoved(e); + break; + case ComponentEvent.COMPONENT_RESIZED: + componentListener.componentResized(e); + break; + case ComponentEvent.COMPONENT_SHOWN: + componentListener.componentShown(e); + break; + } + } + + /** + * Called when a focus event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the FocusEvent to process + * @throws NullPointerException if e is null + * @see FocusListener + * @see #addFocusListener(FocusListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processFocusEvent(FocusEvent e) + { + if (focusListener == null) + return; + + switch (e.id) + { + case FocusEvent.FOCUS_GAINED: + focusListener.focusGained(e); + break; + case FocusEvent.FOCUS_LOST: + focusListener.focusLost(e); + break; + } + } + + /** + * Called when a key event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the KeyEvent to process + * @throws NullPointerException if e is null + * @see KeyListener + * @see #addKeyListener(KeyListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processKeyEvent(KeyEvent e) + { + if (keyListener == null) + return; + switch (e.id) + { + case KeyEvent.KEY_PRESSED: + keyListener.keyPressed(e); + break; + case KeyEvent.KEY_RELEASED: + keyListener.keyReleased(e); + break; + case KeyEvent.KEY_TYPED: + keyListener.keyTyped(e); + break; + } + } + + /** + * Called when a regular mouse event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the MouseEvent to process + * @throws NullPointerException if e is null + * @see MouseListener + * @see #addMouseListener(MouseListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processMouseEvent(MouseEvent e) + { + if (mouseListener == null) + return; + switch (e.id) + { + case MouseEvent.MOUSE_CLICKED: + mouseListener.mouseClicked(e); + break; + case MouseEvent.MOUSE_ENTERED: + if( isLightweight() ) + setCursor( getCursor() ); + mouseListener.mouseEntered(e); + break; + case MouseEvent.MOUSE_EXITED: + mouseListener.mouseExited(e); + break; + case MouseEvent.MOUSE_PRESSED: + mouseListener.mousePressed(e); + break; + case MouseEvent.MOUSE_RELEASED: + mouseListener.mouseReleased(e); + break; + } + } + + /** + * Called when a mouse motion event is dispatched and component events are + * enabled. This method passes the event along to any listeners + * that are attached. + * + * @param e the MouseMotionEvent to process + * @throws NullPointerException if e is null + * @see MouseMotionListener + * @see #addMouseMotionListener(MouseMotionListener) + * @see #enableEvents(long) + * @since 1.1 + */ + protected void processMouseMotionEvent(MouseEvent e) + { + if (mouseMotionListener == null) + return; + switch (e.id) + { + case MouseEvent.MOUSE_DRAGGED: + mouseMotionListener.mouseDragged(e); + break; + case MouseEvent.MOUSE_MOVED: + mouseMotionListener.mouseMoved(e); + break; + } + e.consume(); + } + + /** + * Called when a mouse wheel event is dispatched and component events are + * enabled. This method passes the event along to any listeners that are + * attached. + * + * @param e the MouseWheelEvent to process + * @throws NullPointerException if e is null + * @see MouseWheelListener + * @see #addMouseWheelListener(MouseWheelListener) + * @see #enableEvents(long) + * @since 1.4 + */ + protected void processMouseWheelEvent(MouseWheelEvent e) + { + if (mouseWheelListener != null + && e.id == MouseEvent.MOUSE_WHEEL) + { + mouseWheelListener.mouseWheelMoved(e); + e.consume(); + } + } + + /** + * Called when an input method event is dispatched and component events are + * enabled. This method passes the event along to any listeners that are + * attached. + * + * @param e the InputMethodEvent to process + * @throws NullPointerException if e is null + * @see InputMethodListener + * @see #addInputMethodListener(InputMethodListener) + * @see #enableEvents(long) + * @since 1.2 + */ + protected void processInputMethodEvent(InputMethodEvent e) + { + if (inputMethodListener == null) + return; + switch (e.id) + { + case InputMethodEvent.CARET_POSITION_CHANGED: + inputMethodListener.caretPositionChanged(e); + break; + case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED: + inputMethodListener.inputMethodTextChanged(e); + break; + } + } + + /** + * Called when a hierarchy change event is dispatched and component events + * are enabled. This method passes the event along to any listeners that are + * attached. + * + * @param e the HierarchyEvent to process + * @throws NullPointerException if e is null + * @see HierarchyListener + * @see #addHierarchyListener(HierarchyListener) + * @see #enableEvents(long) + * @since 1.3 + */ + protected void processHierarchyEvent(HierarchyEvent e) + { + if (hierarchyListener == null) + return; + if (e.id == HierarchyEvent.HIERARCHY_CHANGED) + hierarchyListener.hierarchyChanged(e); + } + + /** + * Called when a hierarchy bounds event is dispatched and component events + * are enabled. This method passes the event along to any listeners that are + * attached. + * + * @param e the HierarchyEvent to process + * @throws NullPointerException if e is null + * @see HierarchyBoundsListener + * @see #addHierarchyBoundsListener(HierarchyBoundsListener) + * @see #enableEvents(long) + * @since 1.3 + */ + protected void processHierarchyBoundsEvent(HierarchyEvent e) + { + if (hierarchyBoundsListener == null) + return; + switch (e.id) + { + case HierarchyEvent.ANCESTOR_MOVED: + hierarchyBoundsListener.ancestorMoved(e); + break; + case HierarchyEvent.ANCESTOR_RESIZED: + hierarchyBoundsListener.ancestorResized(e); + break; + } + } + + /** + * AWT 1.0 event handler. + * + * This method calls one of the event-specific handler methods. For + * example for key events, either {@link #keyDown(Event,int)} + * or {@link #keyUp(Event,int)} is called. A derived + * component can override one of these event-specific methods if it + * only needs to handle certain event types. Otherwise it can + * override handleEvent itself and handle any event. + * + * @param evt the event to handle + * @return true if the event was handled, false otherwise + * @deprecated use {@link #processEvent(AWTEvent)} instead + */ + public boolean handleEvent (Event evt) + { + switch (evt.id) + { + // Handle key events. + case Event.KEY_ACTION: + case Event.KEY_PRESS: + return keyDown (evt, evt.key); + case Event.KEY_ACTION_RELEASE: + case Event.KEY_RELEASE: + return keyUp (evt, evt.key); + + // Handle mouse events. + case Event.MOUSE_DOWN: + return mouseDown (evt, evt.x, evt.y); + case Event.MOUSE_UP: + return mouseUp (evt, evt.x, evt.y); + case Event.MOUSE_MOVE: + return mouseMove (evt, evt.x, evt.y); + case Event.MOUSE_DRAG: + return mouseDrag (evt, evt.x, evt.y); + case Event.MOUSE_ENTER: + return mouseEnter (evt, evt.x, evt.y); + case Event.MOUSE_EXIT: + return mouseExit (evt, evt.x, evt.y); + + // Handle focus events. + case Event.GOT_FOCUS: + return gotFocus (evt, evt.arg); + case Event.LOST_FOCUS: + return lostFocus (evt, evt.arg); + + // Handle action event. + case Event.ACTION_EVENT: + return action (evt, evt.arg); + } + // Unknown event. + return false; + } + + /** + * AWT 1.0 MOUSE_DOWN event handler. This method is meant to be + * overridden by components providing their own MOUSE_DOWN handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseEvent(MouseEvent)} instead + */ + public boolean mouseDown(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_DRAG event handler. This method is meant to be + * overridden by components providing their own MOUSE_DRAG handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseMotionEvent(MouseEvent)} instead + */ + public boolean mouseDrag(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_UP event handler. This method is meant to be + * overridden by components providing their own MOUSE_UP handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseEvent(MouseEvent)} instead + */ + public boolean mouseUp(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_MOVE event handler. This method is meant to be + * overridden by components providing their own MOUSE_MOVE handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseMotionEvent(MouseEvent)} instead + */ + public boolean mouseMove(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_ENTER event handler. This method is meant to be + * overridden by components providing their own MOUSE_ENTER handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseEvent(MouseEvent)} instead + */ + public boolean mouseEnter(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 MOUSE_EXIT event handler. This method is meant to be + * overridden by components providing their own MOUSE_EXIT handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param x the x coordinate, ignored + * @param y the y coordinate, ignored + * @return false + * @deprecated use {@link #processMouseEvent(MouseEvent)} instead + */ + public boolean mouseExit(Event evt, int x, int y) + { + return false; + } + + /** + * AWT 1.0 KEY_PRESS and KEY_ACTION event handler. This method is + * meant to be overridden by components providing their own key + * press handler. The default implementation simply returns false. + * + * @param evt the event to handle + * @param key the key pressed, ignored + * @return false + * @deprecated use {@link #processKeyEvent(KeyEvent)} instead + */ + public boolean keyDown(Event evt, int key) + { + return false; + } + + /** + * AWT 1.0 KEY_RELEASE and KEY_ACTION_RELEASE event handler. This + * method is meant to be overridden by components providing their + * own key release handler. The default implementation simply + * returns false. + * + * @param evt the event to handle + * @param key the key pressed, ignored + * @return false + * @deprecated use {@link #processKeyEvent(KeyEvent)} instead + */ + public boolean keyUp(Event evt, int key) + { + return false; + } + + /** + * AWT 1.0 ACTION_EVENT event handler. This method is meant to be + * overridden by components providing their own action event + * handler. The default implementation simply returns false. + * + * @param evt the event to handle + * @param what the object acted on, ignored + * @return false + * @deprecated in classes which support actions, use + * processActionEvent(ActionEvent) instead + */ + public boolean action(Event evt, Object what) + { + return false; + } + + /** + * Called when the parent of this Component is made visible or when + * the Component is added to an already visible Container and needs + * to be shown. A native peer - if any - is created at this + * time. This method is called automatically by the AWT system and + * should not be called by user level code. + * + * @see #isDisplayable() + * @see #removeNotify() + */ + public void addNotify() + { + // We need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + if (peer == null) + peer = getToolkit().createComponent(this); + else if (parent != null && parent.isLightweight()) + new HeavyweightInLightweightListener(parent); + // Now that all the children has gotten their peers, we should + // have the event mask needed for this component and its + //lightweight subcomponents. + peer.setEventMask(eventMask); + + // We used to leave the invalidate() to the peer. However, I put it + // back here for 2 reasons: 1) The RI does call invalidate() from + // addNotify(); 2) The peer shouldn't be bother with validation too + // much. + invalidate(); + + if (dropTarget != null) + dropTarget.addNotify(peer); + + // Fetch the peerFont for later installation in validate(). + peerFont = getFont(); + + // Notify hierarchy listeners. + long flags = HierarchyEvent.DISPLAYABILITY_CHANGED; + if (isHierarchyVisible()) + flags |= HierarchyEvent.SHOWING_CHANGED; + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, this, parent, + flags); + } + } + + /** + * Called to inform this component is has been removed from its + * container. Its native peer - if any - is destroyed at this time. + * This method is called automatically by the AWT system and should + * not be called by user level code. + * + * @see #isDisplayable() + * @see #addNotify() + */ + public void removeNotify() + { + // We need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + // We null our peer field before disposing of it, such that if we're + // not the event dispatch thread and the dispatch thread is awoken by + // the dispose call, there will be no race checking the peer's null + // status. + + ComponentPeer tmp = peer; + peer = null; + peerFont = null; + if (tmp != null) + { + tmp.hide(); + tmp.dispose(); + } + + // Notify hierarchy listeners. + long flags = HierarchyEvent.DISPLAYABILITY_CHANGED; + if (isHierarchyVisible()) + flags |= HierarchyEvent.SHOWING_CHANGED; + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, this, parent, + flags); + } + } + + /** + * AWT 1.0 GOT_FOCUS event handler. This method is meant to be + * overridden by components providing their own GOT_FOCUS handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param what the Object focused, ignored + * @return false + * @deprecated use {@link #processFocusEvent(FocusEvent)} instead + */ + public boolean gotFocus(Event evt, Object what) + { + return false; + } + + /** + * AWT 1.0 LOST_FOCUS event handler. This method is meant to be + * overridden by components providing their own LOST_FOCUS handler. + * The default implementation simply returns false. + * + * @param evt the event to handle + * @param what the Object focused, ignored + * @return false + * @deprecated use {@link #processFocusEvent(FocusEvent)} instead + */ + public boolean lostFocus(Event evt, Object what) + { + return false; + } + + /** + * Tests whether or not this component is in the group that can be + * traversed using the keyboard traversal mechanism (such as the TAB key). + * + * @return true if the component is traversed via the TAB key + * @see #setFocusable(boolean) + * @since 1.1 + * @deprecated use {@link #isFocusable()} instead + */ + public boolean isFocusTraversable() + { + return enabled && visible && (peer == null || isLightweight() || peer.isFocusTraversable()); + } + + /** + * Tests if this component can receive focus. + * + * @return true if this component can receive focus + * @since 1.4 + */ + public boolean isFocusable() + { + return focusable; + } + + /** + * Specify whether this component can receive focus. This method also + * sets the {@link #isFocusTraversableOverridden} field to 1, which + * appears to be the undocumented way {@link + * DefaultFocusTraversalPolicy#accept(Component)} determines whether to + * respect the {@link #isFocusable()} method of the component. + * + * @param focusable the new focusable status + * @since 1.4 + */ + public void setFocusable(boolean focusable) + { + firePropertyChange("focusable", this.focusable, focusable); + this.focusable = focusable; + this.isFocusTraversableOverridden = 1; + } + + /** + * Sets the focus traversal keys for one of the three focus + * traversal directions supported by Components: + * {@link KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS}, + * {@link KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS}, or + * {@link KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS}. Normally, the + * default values should match the operating system's native + * choices. To disable a given traversal, use + * Collections.EMPTY_SET. The event dispatcher will + * consume PRESSED, RELEASED, and TYPED events for the specified + * key, although focus can only transfer on PRESSED or RELEASED. + * + *

The defaults are: + * + * + * + * + * + * + * + * + * + * + *
IdentifierMeaningDefault
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYSNormal forward traversalTAB on KEY_PRESSED, Ctrl-TAB on KEY_PRESSED
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYSNormal backward traversalShift-TAB on KEY_PRESSED, Ctrl-Shift-TAB on KEY_PRESSED
KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYSGo up a traversal cycleNone
+ * + * If keystrokes is null, this component's focus traversal key set + * is inherited from one of its ancestors. If none of its ancestors + * has its own set of focus traversal keys, the focus traversal keys + * are set to the defaults retrieved from the current + * KeyboardFocusManager. If not null, the set must contain only + * AWTKeyStrokes that are not already focus keys and are not + * KEY_TYPED events. + * + * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, or + * UP_CYCLE_TRAVERSAL_KEYS + * @param keystrokes a set of keys, or null + * @throws IllegalArgumentException if id or keystrokes is invalid + * @see #getFocusTraversalKeys(int) + * @see KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS + * @since 1.4 + */ + public void setFocusTraversalKeys(int id, + Set keystrokes) + { + if (keystrokes == null) + { + Container parent = getParent (); + + while (parent != null) + { + if (parent.areFocusTraversalKeysSet (id)) + { + keystrokes = parent.getFocusTraversalKeys (id); + break; + } + parent = parent.getParent (); + } + + if (keystrokes == null) + keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager (). + getDefaultFocusTraversalKeys (id); + } + + Set sa; + Set sb; + String name; + switch (id) + { + case KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + name = "forwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + name = "backwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + name = "upCycleFocusTraversalKeys"; + break; + default: + throw new IllegalArgumentException (); + } + + int i = keystrokes.size (); + Iterator iter = keystrokes.iterator (); + + while (--i >= 0) + { + Object o = iter.next (); + if (!(o instanceof AWTKeyStroke) + || sa.contains (o) || sb.contains (o) + || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) + throw new IllegalArgumentException (); + } + + if (focusTraversalKeys == null) + focusTraversalKeys = new Set[3]; + + keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); + firePropertyChange (name, focusTraversalKeys[id], keystrokes); + + focusTraversalKeys[id] = keystrokes; + } + + /** + * Returns the set of keys for a given focus traversal action, as + * defined in setFocusTraversalKeys. If not set, this + * is inherited from the parent component, which may have gotten it + * from the KeyboardFocusManager. + * + * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, + * or UP_CYCLE_TRAVERSAL_KEYS + * + * @return set of traversal keys + * + * @throws IllegalArgumentException if id is invalid + * + * @see #setFocusTraversalKeys (int, Set) + * @see KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS + * + * @since 1.4 + */ + public Set getFocusTraversalKeys (int id) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException(); + + Set s = null; + + if (focusTraversalKeys != null) + s = focusTraversalKeys[id]; + + if (s == null && parent != null) + s = parent.getFocusTraversalKeys (id); + + return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager() + .getDefaultFocusTraversalKeys(id)) : s; + } + + /** + * Tests whether the focus traversal keys for a given action are explicitly + * set or inherited. + * + * @param id one of FORWARD_TRAVERSAL_KEYS, BACKWARD_TRAVERSAL_KEYS, + * or UP_CYCLE_TRAVERSAL_KEYS + * @return true if that set is explicitly specified + * @throws IllegalArgumentException if id is invalid + * @see #getFocusTraversalKeys (int) + * @see KeyboardFocusManager#FORWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#BACKWARD_TRAVERSAL_KEYS + * @see KeyboardFocusManager#UP_CYCLE_TRAVERSAL_KEYS + * @since 1.4 + */ + public boolean areFocusTraversalKeysSet (int id) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + return focusTraversalKeys != null && focusTraversalKeys[id] != null; + } + + /** + * Enable or disable focus traversal keys on this Component. If + * they are, then the keyboard focus manager consumes and acts on + * key press and release events that trigger focus traversal, and + * discards the corresponding key typed events. If focus traversal + * keys are disabled, then all key events that would otherwise + * trigger focus traversal are sent to this Component. + * + * @param focusTraversalKeysEnabled the new value of the flag + * @see #getFocusTraversalKeysEnabled () + * @see #setFocusTraversalKeys (int, Set) + * @see #getFocusTraversalKeys (int) + * @since 1.4 + */ + public void setFocusTraversalKeysEnabled (boolean focusTraversalKeysEnabled) + { + firePropertyChange ("focusTraversalKeysEnabled", + this.focusTraversalKeysEnabled, + focusTraversalKeysEnabled); + this.focusTraversalKeysEnabled = focusTraversalKeysEnabled; + } + + /** + * Check whether or not focus traversal keys are enabled on this + * Component. If they are, then the keyboard focus manager consumes + * and acts on key press and release events that trigger focus + * traversal, and discards the corresponding key typed events. If + * focus traversal keys are disabled, then all key events that would + * otherwise trigger focus traversal are sent to this Component. + * + * @return true if focus traversal keys are enabled + * @see #setFocusTraversalKeysEnabled (boolean) + * @see #setFocusTraversalKeys (int, Set) + * @see #getFocusTraversalKeys (int) + * @since 1.4 + */ + public boolean getFocusTraversalKeysEnabled () + { + return focusTraversalKeysEnabled; + } + + /** + * Request that this Component be given the keyboard input focus and + * that its top-level ancestor become the focused Window. + * + * For the request to be granted, the Component must be focusable, + * displayable and showing and the top-level Window to which it + * belongs must be focusable. If the request is initially denied on + * the basis that the top-level Window is not focusable, the request + * will be remembered and granted when the Window does become + * focused. + * + * Never assume that this Component is the focus owner until it + * receives a FOCUS_GAINED event. + * + * The behaviour of this method is platform-dependent. + * {@link #requestFocusInWindow()} should be used instead. + * + * @see #requestFocusInWindow () + * @see FocusEvent + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + */ + public void requestFocus () + { + requestFocusImpl(false, true); + } + + /** + * Request that this Component be given the keyboard input focus and + * that its top-level ancestor become the focused Window. + * + * For the request to be granted, the Component must be focusable, + * displayable and showing and the top-level Window to which it + * belongs must be focusable. If the request is initially denied on + * the basis that the top-level Window is not focusable, the request + * will be remembered and granted when the Window does become + * focused. + * + * Never assume that this Component is the focus owner until it + * receives a FOCUS_GAINED event. + * + * The behaviour of this method is platform-dependent. + * {@link #requestFocusInWindow()} should be used instead. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. This method is meant to be called by derived + * lightweight Components that want to avoid unnecessary repainting + * when they know a given focus transfer need only be temporary. + * + * @param temporary true if the focus request is temporary + * @return true if the request has a chance of success + * @see #requestFocusInWindow () + * @see FocusEvent + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + * @since 1.4 + */ + protected boolean requestFocus (boolean temporary) + { + return requestFocusImpl(temporary, true); + } + + /** + * Request that this component be given the keyboard input focus, if + * its top-level ancestor is the currently focused Window. A + * FOCUS_GAINED event will be fired if and only if this + * request is successful. To be successful, the component must be + * displayable, showing, and focusable, and its ancestor top-level + * Window must be focused. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. + * + * @return true if the request has a chance of success + * @see #requestFocus () + * @see FocusEvent + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + * @since 1.4 + */ + public boolean requestFocusInWindow () + { + return requestFocusImpl(false, false); + } + + /** + * Request that this component be given the keyboard input focus, if + * its top-level ancestor is the currently focused Window. A + * FOCUS_GAINED event will be fired if and only if this + * request is successful. To be successful, the component must be + * displayable, showing, and focusable, and its ancestor top-level + * Window must be focused. + * + * If the return value is false, the request is guaranteed to fail. + * If the return value is true, the request will succeed unless it + * is vetoed or something in the native windowing system intervenes, + * preventing this Component's top-level ancestor from becoming + * focused. This method is meant to be called by derived + * lightweight Components that want to avoid unnecessary repainting + * when they know a given focus transfer need only be temporary. + * + * @param temporary true if the focus request is temporary + * @return true if the request has a chance of success + * @see #requestFocus () + * @see FocusEvent + * @see #addFocusListener (FocusListener) + * @see #isFocusable () + * @see #isDisplayable () + * @see KeyboardFocusManager#clearGlobalFocusOwner () + * @since 1.4 + */ + protected boolean requestFocusInWindow (boolean temporary) + { + return requestFocusImpl(temporary, false); + } + + /** + * Helper method for all 4 requestFocus variants. + * + * @param temporary indicates if the focus change is temporary + * @param focusWindow indicates if the window focus may be changed + * + * @return false if the request has been definitely denied, + * true otherwise + */ + private boolean requestFocusImpl(boolean temporary, boolean focusWindow) + { + boolean retval = false; + + // Don't try to focus non-focusable and non-visible components. + if (isFocusable() && isVisible()) + { + ComponentPeer myPeer = peer; + if (peer != null) + { + // Find Window ancestor and find out if we're showing while + // doing this. + boolean showing = true; + Component window = this; + while (! (window instanceof Window)) + { + if (! window.isVisible()) + showing = false; + window = window.parent; + } + // Don't allow focus when there is no window or the window + // is not focusable. + if (window != null && ((Window) window).isFocusableWindow() + && showing) + { + // Search for nearest heavy ancestor (including this + // component). + Component heavyweightParent = this; + while (heavyweightParent.peer instanceof LightweightPeer) + heavyweightParent = heavyweightParent.parent; + + // Don't allow focus on lightweight components without + // visible heavyweight ancestor + if (heavyweightParent != null && heavyweightParent.isVisible()) + { + // Don't allow focus when heavyweightParent has no peer. + myPeer = heavyweightParent.peer; + if (myPeer != null) + { + // Register lightweight focus request. + if (heavyweightParent != this) + { + KeyboardFocusManager + .addLightweightFocusRequest(heavyweightParent, + this); + } + + // Try to focus the component. + long time = EventQueue.getMostRecentEventTime(); + boolean success = myPeer.requestFocus(this, temporary, + focusWindow, + time); + if (! success) + { + // Dequeue key events if focus request failed. + KeyboardFocusManager kfm = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + kfm.dequeueKeyEvents(time, this); + } + retval = success; + } + } + } + } + } + return retval; + } + + /** + * Transfers focus to the next component in the focus traversal + * order, as though this were the current focus owner. + * + * @see #requestFocus() + * @since 1.1 + */ + public void transferFocus () + { + nextFocus (); + } + + /** + * Returns the root container that owns the focus cycle where this + * component resides. A focus cycle root is in two cycles, one as + * the ancestor, and one as the focusable element; this call always + * returns the ancestor. + * + * @return the ancestor container that owns the focus cycle + * @since 1.4 + */ + public Container getFocusCycleRootAncestor () + { + Container parent = getParent (); + + while (parent != null && !parent.isFocusCycleRoot()) + parent = parent.getParent (); + + return parent; + } + + /** + * Tests if the container is the ancestor of the focus cycle that + * this component belongs to. + * + * @param c the container to test + * @return true if c is the focus cycle root + * @since 1.4 + */ + public boolean isFocusCycleRoot (Container c) + { + return c == getFocusCycleRootAncestor (); + } + + /** + * AWT 1.0 focus event processor. Transfers focus to the next + * component in the focus traversal order, as though this were the + * current focus owner. + * + * @deprecated use {@link #transferFocus ()} instead + */ + public void nextFocus () + { + // Find the nearest valid (== showing && focusable && enabled) focus + // cycle root ancestor and the focused component in it. + Container focusRoot = getFocusCycleRootAncestor(); + Component focusComp = this; + while (focusRoot != null + && ! (focusRoot.isShowing() && focusRoot.isFocusable() + && focusRoot.isEnabled())) + { + focusComp = focusRoot; + focusRoot = focusComp.getFocusCycleRootAncestor(); + } + + if (focusRoot != null) + { + // First try to get the componentBefore from the policy. + FocusTraversalPolicy policy = focusRoot.getFocusTraversalPolicy(); + Component nextFocus = policy.getComponentAfter(focusRoot, focusComp); + + // If this fails, then ask for the defaultComponent. + if (nextFocus == null) + nextFocus = policy.getDefaultComponent(focusRoot); + + // Request focus on this component, if not null. + if (nextFocus != null) + nextFocus.requestFocus(); + } + } + + /** + * Transfers focus to the previous component in the focus traversal + * order, as though this were the current focus owner. + * + * @see #requestFocus () + * @since 1.4 + */ + public void transferFocusBackward () + { + // Find the nearest valid (== showing && focusable && enabled) focus + // cycle root ancestor and the focused component in it. + Container focusRoot = getFocusCycleRootAncestor(); + Component focusComp = this; + while (focusRoot != null + && ! (focusRoot.isShowing() && focusRoot.isFocusable() + && focusRoot.isEnabled())) + { + focusComp = focusRoot; + focusRoot = focusComp.getFocusCycleRootAncestor(); + } + + if (focusRoot != null) + { + // First try to get the componentBefore from the policy. + FocusTraversalPolicy policy = focusRoot.getFocusTraversalPolicy(); + Component nextFocus = policy.getComponentBefore(focusRoot, focusComp); + + // If this fails, then ask for the defaultComponent. + if (nextFocus == null) + nextFocus = policy.getDefaultComponent(focusRoot); + + // Request focus on this component, if not null. + if (nextFocus != null) + nextFocus.requestFocus(); + } + } + + /** + * Transfers focus to the focus cycle root of this component. + * However, if this is a Window, the default focus owner in the + * window in the current focus cycle is focused instead. + * + * @see #requestFocus() + * @see #isFocusCycleRoot(Container) + * @since 1.4 + */ + public void transferFocusUpCycle () + { + // Find the nearest focus cycle root ancestor that is itself + // focusable, showing and enabled. + Container focusCycleRoot = getFocusCycleRootAncestor(); + while (focusCycleRoot != null && + ! (focusCycleRoot.isShowing() && focusCycleRoot.isFocusable() + && focusCycleRoot.isEnabled())) + { + focusCycleRoot = focusCycleRoot.getFocusCycleRootAncestor(); + } + + KeyboardFocusManager fm = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + + if (focusCycleRoot != null) + { + // If we found a focus cycle root, then we make this the new + // focused component, and make it's focus cycle root the new + // global focus cycle root. If the found root has no focus cycle + // root ancestor itself, then the component will be both the focused + // component and the new global focus cycle root. + Container focusCycleAncestor = + focusCycleRoot.getFocusCycleRootAncestor(); + Container globalFocusCycleRoot; + if (focusCycleAncestor == null) + globalFocusCycleRoot = focusCycleRoot; + else + globalFocusCycleRoot = focusCycleAncestor; + + fm.setGlobalCurrentFocusCycleRoot(globalFocusCycleRoot); + focusCycleRoot.requestFocus(); + } + else + { + // If this component has no applicable focus cycle root, we try + // find the nearest window and set this as the new global focus cycle + // root and the default focus component of this window the new focused + // component. + Container cont; + if (this instanceof Container) + cont = (Container) this; + else + cont = getParent(); + + while (cont != null && !(cont instanceof Window)) + cont = cont.getParent(); + + if (cont != null) + { + FocusTraversalPolicy policy = cont.getFocusTraversalPolicy(); + Component focusComp = policy.getDefaultComponent(cont); + if (focusComp != null) + { + fm.setGlobalCurrentFocusCycleRoot(cont); + focusComp.requestFocus(); + } + } + } + } + + /** + * Tests if this component is the focus owner. Use {@link + * #isFocusOwner ()} instead. + * + * @return true if this component owns focus + * @since 1.2 + */ + public boolean hasFocus () + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + Component focusOwner = manager.getFocusOwner (); + + return this == focusOwner; + } + + /** + * Tests if this component is the focus owner. + * + * @return true if this component owns focus + * @since 1.4 + */ + public boolean isFocusOwner() + { + return hasFocus (); + } + + /** + * Adds the specified popup menu to this component. + * + * @param popup the popup menu to be added + * + * @see #remove(MenuComponent) + * + * @since 1.1 + */ + public synchronized void add(PopupMenu popup) + { + if (popups == null) + popups = new Vector(); + popups.add(popup); + + if (popup.parent != null) + popup.parent.remove(popup); + popup.parent = this; + if (peer != null) + popup.addNotify(); + } + + /** + * Removes the specified popup menu from this component. + * + * @param popup the popup menu to remove + * @see #add(PopupMenu) + * @since 1.1 + */ + public synchronized void remove(MenuComponent popup) + { + if (popups != null) + popups.remove(popup); + } + + /** + * Returns a debugging string representing this component. The string may + * be empty but not null. + * + * @return a string representing this component + */ + protected String paramString() + { + CPStringBuilder param = new CPStringBuilder(); + String name = getName(); + if (name != null) + param.append(name).append(","); + param.append(x).append(",").append(y).append(",").append(width) + .append("x").append(height); + if (! isValid()) + param.append(",invalid"); + if (! isVisible()) + param.append(",invisible"); + if (! isEnabled()) + param.append(",disabled"); + if (! isOpaque()) + param.append(",translucent"); + if (isDoubleBuffered()) + param.append(",doublebuffered"); + if (parent == null) + param.append(",parent=null"); + else + param.append(",parent=").append(parent.getName()); + return param.toString(); + } + + /** + * Returns a string representation of this component. This is implemented + * as getClass().getName() + '[' + paramString() + ']'. + * + * @return a string representation of this component + */ + public String toString() + { + return getClass().getName() + '[' + paramString() + ']'; + } + + /** + * Prints a listing of this component to System.out. + * + * @see #list(PrintStream) + */ + public void list() + { + list(System.out, 0); + } + + /** + * Prints a listing of this component to the specified print stream. + * + * @param out the PrintStream to print to + */ + public void list(PrintStream out) + { + list(out, 0); + } + + /** + * Prints a listing of this component to the specified print stream, + * starting at the specified indentation point. + * + * @param out the PrintStream to print to + * @param indent the indentation point + */ + public void list(PrintStream out, int indent) + { + for (int i = 0; i < indent; ++i) + out.print(' '); + out.println(toString()); + } + + /** + * Prints a listing of this component to the specified print writer. + * + * @param out the PrintWrinter to print to + * @since 1.1 + */ + public void list(PrintWriter out) + { + list(out, 0); + } + + /** + * Prints a listing of this component to the specified print writer, + * starting at the specified indentation point. + * + * @param out the PrintWriter to print to + * @param indent the indentation point + * @since 1.1 + */ + public void list(PrintWriter out, int indent) + { + for (int i = 0; i < indent; ++i) + out.print(' '); + out.println(toString()); + } + + /** + * Adds the specified property listener to this component. This is harmless + * if the listener is null, but if the listener has already been registered, + * it will now be registered twice. The property listener ignores inherited + * properties. Recognized properties include:
+ *

    + *
  • the font ("font")
  • + *
  • the background color ("background")
  • + *
  • the foreground color ("foreground")
  • + *
  • the focusability ("focusable")
  • + *
  • the focus key traversal enabled state + * ("focusTraversalKeysEnabled")
  • + *
  • the set of forward traversal keys + * ("forwardFocusTraversalKeys")
  • + *
  • the set of backward traversal keys + * ("backwardFocusTraversalKeys")
  • + *
  • the set of up-cycle traversal keys + * ("upCycleFocusTraversalKeys")
  • + *
+ * + * @param listener the new listener to add + * @see #removePropertyChangeListener(PropertyChangeListener) + * @see #getPropertyChangeListeners() + * @see #addPropertyChangeListener(String, PropertyChangeListener) + * @since 1.1 + */ + public void addPropertyChangeListener(PropertyChangeListener listener) + { + if (changeSupport == null) + changeSupport = new PropertyChangeSupport(this); + changeSupport.addPropertyChangeListener(listener); + } + + /** + * Removes the specified property listener from the component. This is + * harmless if the listener was not previously registered. + * + * @param listener the listener to remove + * @see #addPropertyChangeListener(PropertyChangeListener) + * @see #getPropertyChangeListeners() + * @see #removePropertyChangeListener(String, PropertyChangeListener) + * @since 1.1 + */ + public void removePropertyChangeListener(PropertyChangeListener listener) + { + if (changeSupport != null) + changeSupport.removePropertyChangeListener(listener); + } + + /** + * Returns an array of all specified listeners registered on this component. + * + * @return an array of listeners + * @see #addPropertyChangeListener(PropertyChangeListener) + * @see #removePropertyChangeListener(PropertyChangeListener) + * @see #getPropertyChangeListeners(String) + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners() + { + return changeSupport == null ? new PropertyChangeListener[0] + : changeSupport.getPropertyChangeListeners(); + } + + /** + * Adds the specified property listener to this component. This is harmless + * if the listener is null, but if the listener has already been registered, + * it will now be registered twice. The property listener ignores inherited + * properties. The listener is keyed to a single property. Recognized + * properties include:
+ *
    + *
  • the font ("font")
  • + *
  • the background color ("background")
  • + *
  • the foreground color ("foreground")
  • + *
  • the focusability ("focusable")
  • + *
  • the focus key traversal enabled state + * ("focusTraversalKeysEnabled")
  • + *
  • the set of forward traversal keys + * ("forwardFocusTraversalKeys")
  • +p *
  • the set of backward traversal keys + * ("backwardFocusTraversalKeys")
  • + *
  • the set of up-cycle traversal keys + * ("upCycleFocusTraversalKeys")
  • + *
+ * + * @param propertyName the property name to filter on + * @param listener the new listener to add + * @see #removePropertyChangeListener(String, PropertyChangeListener) + * @see #getPropertyChangeListeners(String) + * @see #addPropertyChangeListener(PropertyChangeListener) + * @since 1.1 + */ + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener) + { + if (changeSupport == null) + changeSupport = new PropertyChangeSupport(this); + changeSupport.addPropertyChangeListener(propertyName, listener); + } + + /** + * Removes the specified property listener on a particular property from + * the component. This is harmless if the listener was not previously + * registered. + * + * @param propertyName the property name to filter on + * @param listener the listener to remove + * @see #addPropertyChangeListener(String, PropertyChangeListener) + * @see #getPropertyChangeListeners(String) + * @see #removePropertyChangeListener(PropertyChangeListener) + * @since 1.1 + */ + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener) + { + if (changeSupport != null) + changeSupport.removePropertyChangeListener(propertyName, listener); + } + + /** + * Returns an array of all specified listeners on the named property that + * are registered on this component. + * + * @return an array of listeners + * @see #addPropertyChangeListener(String, PropertyChangeListener) + * @see #removePropertyChangeListener(String, PropertyChangeListener) + * @see #getPropertyChangeListeners() + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners(String property) + { + return changeSupport == null ? new PropertyChangeListener[0] + : changeSupport.getPropertyChangeListeners(property); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + */ + protected void firePropertyChange(String propertyName, Object oldValue, + Object newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + */ + protected void firePropertyChange(String propertyName, boolean oldValue, + boolean newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + */ + protected void firePropertyChange(String propertyName, int oldValue, + int newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, oldValue, newValue); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + * + * @since 1.5 + */ + public void firePropertyChange(String propertyName, byte oldValue, + byte newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, new Byte(oldValue), + new Byte(newValue)); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + * + * @since 1.5 + */ + public void firePropertyChange(String propertyName, char oldValue, + char newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, new Character(oldValue), + new Character(newValue)); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + * + * @since 1.5 + */ + public void firePropertyChange(String propertyName, short oldValue, + short newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, new Short(oldValue), + new Short(newValue)); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + * + * @since 1.5 + */ + public void firePropertyChange(String propertyName, long oldValue, + long newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, new Long(oldValue), + new Long(newValue)); + } + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + * + * @since 1.5 + */ + public void firePropertyChange(String propertyName, float oldValue, + float newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, new Float(oldValue), + new Float(newValue)); + } + + + /** + * Report a change in a bound property to any registered property listeners. + * + * @param propertyName the property that changed + * @param oldValue the old property value + * @param newValue the new property value + * + * @since 1.5 + */ + public void firePropertyChange(String propertyName, double oldValue, + double newValue) + { + if (changeSupport != null) + changeSupport.firePropertyChange(propertyName, new Double(oldValue), + new Double(newValue)); + } + + /** + * Sets the text layout orientation of this component. New components default + * to UNKNOWN (which behaves like LEFT_TO_RIGHT). This method affects only + * the current component, while + * {@link #applyComponentOrientation(ComponentOrientation)} affects the + * entire hierarchy. + * + * @param o the new orientation (null is accepted) + * @see #getComponentOrientation() + */ + public void setComponentOrientation(ComponentOrientation o) + { + + ComponentOrientation oldOrientation = componentOrientation; + componentOrientation = o; + firePropertyChange("componentOrientation", oldOrientation, o); + } + + /** + * Determines the text layout orientation used by this component. + * + * @return the component orientation (this can be null) + * @see #setComponentOrientation(ComponentOrientation) + */ + public ComponentOrientation getComponentOrientation() + { + return componentOrientation; + } + + /** + * Sets the text layout orientation of this component. New components default + * to UNKNOWN (which behaves like LEFT_TO_RIGHT). This method affects the + * entire hierarchy, while + * {@link #setComponentOrientation(ComponentOrientation)} affects only the + * current component. + * + * @param o the new orientation + * @throws NullPointerException if o is null + * @see #getComponentOrientation() + * @since 1.4 + */ + public void applyComponentOrientation(ComponentOrientation o) + { + setComponentOrientation(o); + } + + /** + * Returns the accessibility framework context of this class. Component is + * not accessible, so the default implementation returns null. Subclasses + * must override this behavior, and return an appropriate subclass of + * {@link AccessibleAWTComponent}. + * + * @return the accessibility context + */ + public AccessibleContext getAccessibleContext() + { + return null; + } + + + // Helper methods; some are package visible for use by subclasses. + + /** + * Subclasses should override this to return unique component names like + * "menuitem0". + * + * @return the generated name for this component + */ + String generateName() + { + // Component is abstract. + return null; + } + + /** + * Sets the peer for this component. + * + * @param peer the new peer + */ + final void setPeer(ComponentPeer peer) + { + this.peer = peer; + } + + /** + * Translate an AWT 1.1 event ({@link AWTEvent}) into an AWT 1.0 + * event ({@link Event}). + * + * @param e an AWT 1.1 event to translate + * + * @return an AWT 1.0 event representing e + */ + static Event translateEvent (AWTEvent e) + { + Object target = e.getSource (); + Event translated = null; + + if (e instanceof WindowEvent) + { + WindowEvent we = (WindowEvent) e; + int id = we.id; + int newId = 0; + + switch (id) + { + case WindowEvent.WINDOW_DEICONIFIED: + newId = Event.WINDOW_DEICONIFY; + break; + case WindowEvent.WINDOW_CLOSED: + case WindowEvent.WINDOW_CLOSING: + newId = Event.WINDOW_DESTROY; + break; + case WindowEvent.WINDOW_ICONIFIED: + newId = Event.WINDOW_ICONIFY; + break; + case WindowEvent.WINDOW_GAINED_FOCUS: + newId = Event.GOT_FOCUS; + break; + case WindowEvent.WINDOW_LOST_FOCUS: + newId = Event.LOST_FOCUS; + break; + default: + return null; + } + + translated = new Event(target, 0, newId, 0, 0, 0, 0); + } + else if (e instanceof InputEvent) + { + InputEvent ie = (InputEvent) e; + long when = ie.getWhen (); + + int oldID = 0; + int id = e.getID (); + + int oldMods = 0; + int mods = ie.getModifiersEx (); + + if ((mods & InputEvent.BUTTON2_DOWN_MASK) != 0) + oldMods |= Event.META_MASK; + else if ((mods & InputEvent.BUTTON3_DOWN_MASK) != 0) + oldMods |= Event.ALT_MASK; + + if ((mods & InputEvent.SHIFT_DOWN_MASK) != 0) + oldMods |= Event.SHIFT_MASK; + + if ((mods & InputEvent.CTRL_DOWN_MASK) != 0) + oldMods |= Event.CTRL_MASK; + + if ((mods & InputEvent.META_DOWN_MASK) != 0) + oldMods |= Event.META_MASK; + + if ((mods & InputEvent.ALT_DOWN_MASK) != 0) + oldMods |= Event.ALT_MASK; + + if (e instanceof MouseEvent && !ignoreOldMouseEvents()) + { + if (id == MouseEvent.MOUSE_PRESSED) + oldID = Event.MOUSE_DOWN; + else if (id == MouseEvent.MOUSE_RELEASED) + oldID = Event.MOUSE_UP; + else if (id == MouseEvent.MOUSE_MOVED) + oldID = Event.MOUSE_MOVE; + else if (id == MouseEvent.MOUSE_DRAGGED) + oldID = Event.MOUSE_DRAG; + else if (id == MouseEvent.MOUSE_ENTERED) + oldID = Event.MOUSE_ENTER; + else if (id == MouseEvent.MOUSE_EXITED) + oldID = Event.MOUSE_EXIT; + else + // No analogous AWT 1.0 mouse event. + return null; + + MouseEvent me = (MouseEvent) e; + + translated = new Event (target, when, oldID, + me.getX (), me.getY (), 0, oldMods); + } + else if (e instanceof KeyEvent) + { + if (id == KeyEvent.KEY_PRESSED) + oldID = Event.KEY_PRESS; + else if (e.getID () == KeyEvent.KEY_RELEASED) + oldID = Event.KEY_RELEASE; + else + // No analogous AWT 1.0 key event. + return null; + + int oldKey = 0; + int newKey = ((KeyEvent) e).getKeyCode (); + switch (newKey) + { + case KeyEvent.VK_BACK_SPACE: + oldKey = Event.BACK_SPACE; + break; + case KeyEvent.VK_CAPS_LOCK: + oldKey = Event.CAPS_LOCK; + break; + case KeyEvent.VK_DELETE: + oldKey = Event.DELETE; + break; + case KeyEvent.VK_DOWN: + case KeyEvent.VK_KP_DOWN: + oldKey = Event.DOWN; + break; + case KeyEvent.VK_END: + oldKey = Event.END; + break; + case KeyEvent.VK_ENTER: + oldKey = Event.ENTER; + break; + case KeyEvent.VK_ESCAPE: + oldKey = Event.ESCAPE; + break; + case KeyEvent.VK_F1: + oldKey = Event.F1; + break; + case KeyEvent.VK_F10: + oldKey = Event.F10; + break; + case KeyEvent.VK_F11: + oldKey = Event.F11; + break; + case KeyEvent.VK_F12: + oldKey = Event.F12; + break; + case KeyEvent.VK_F2: + oldKey = Event.F2; + break; + case KeyEvent.VK_F3: + oldKey = Event.F3; + break; + case KeyEvent.VK_F4: + oldKey = Event.F4; + break; + case KeyEvent.VK_F5: + oldKey = Event.F5; + break; + case KeyEvent.VK_F6: + oldKey = Event.F6; + break; + case KeyEvent.VK_F7: + oldKey = Event.F7; + break; + case KeyEvent.VK_F8: + oldKey = Event.F8; + break; + case KeyEvent.VK_F9: + oldKey = Event.F9; + break; + case KeyEvent.VK_HOME: + oldKey = Event.HOME; + break; + case KeyEvent.VK_INSERT: + oldKey = Event.INSERT; + break; + case KeyEvent.VK_LEFT: + case KeyEvent.VK_KP_LEFT: + oldKey = Event.LEFT; + break; + case KeyEvent.VK_NUM_LOCK: + oldKey = Event.NUM_LOCK; + break; + case KeyEvent.VK_PAUSE: + oldKey = Event.PAUSE; + break; + case KeyEvent.VK_PAGE_DOWN: + oldKey = Event.PGDN; + break; + case KeyEvent.VK_PAGE_UP: + oldKey = Event.PGUP; + break; + case KeyEvent.VK_PRINTSCREEN: + oldKey = Event.PRINT_SCREEN; + break; + case KeyEvent.VK_RIGHT: + case KeyEvent.VK_KP_RIGHT: + oldKey = Event.RIGHT; + break; + case KeyEvent.VK_SCROLL_LOCK: + oldKey = Event.SCROLL_LOCK; + break; + case KeyEvent.VK_TAB: + oldKey = Event.TAB; + break; + case KeyEvent.VK_UP: + case KeyEvent.VK_KP_UP: + oldKey = Event.UP; + break; + default: + oldKey = ((KeyEvent) e).getKeyChar(); + } + + translated = new Event (target, when, oldID, + 0, 0, oldKey, oldMods); + } + } + else if (e instanceof AdjustmentEvent) + { + AdjustmentEvent ae = (AdjustmentEvent) e; + int type = ae.getAdjustmentType(); + int oldType; + if (type == AdjustmentEvent.BLOCK_DECREMENT) + oldType = Event.SCROLL_PAGE_UP; + else if (type == AdjustmentEvent.BLOCK_INCREMENT) + oldType = Event.SCROLL_PAGE_DOWN; + else if (type == AdjustmentEvent.TRACK) + oldType = Event.SCROLL_ABSOLUTE; + else if (type == AdjustmentEvent.UNIT_DECREMENT) + oldType = Event.SCROLL_LINE_UP; + else if (type == AdjustmentEvent.UNIT_INCREMENT) + oldType = Event.SCROLL_LINE_DOWN; + else + oldType = type; + translated = new Event(target, oldType, new Integer(ae.getValue())); + } + else if (e instanceof ActionEvent) + translated = new Event (target, Event.ACTION_EVENT, + ((ActionEvent) e).getActionCommand ()); + + return translated; + } + + /** + * Implementation of dispatchEvent. Allows trusted package classes + * to dispatch additional events first. This implementation first + * translates e to an AWT 1.0 event and sends the + * result to {@link #postEvent}. If the AWT 1.0 event is not + * handled, and events of type e are enabled for this + * component, e is passed on to {@link #processEvent}. + * + * @param e the event to dispatch + */ + void dispatchEventImpl(AWTEvent e) + { + // Update the component's knowledge about the size. + // Important: Please look at the big comment in ComponentReshapeEvent + // to learn why we did it this way. If you change this code, make + // sure that the peer->AWT bounds update still works. + // (for instance: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29448 ) + if (e instanceof ComponentReshapeEvent) + { + ComponentReshapeEvent reshape = (ComponentReshapeEvent) e; + x = reshape.x; + y = reshape.y; + width = reshape.width; + height = reshape.height; + return; + } + + // Retarget focus events before dispatching it to the KeyboardFocusManager + // in order to handle lightweight components properly. + boolean dispatched = false; + if (! e.isFocusManagerEvent) + { + e = KeyboardFocusManager.retargetFocusEvent(e); + dispatched = KeyboardFocusManager.getCurrentKeyboardFocusManager() + .dispatchEvent(e); + } + + if (! dispatched) + { + // Give toolkit a chance to dispatch the event + // to globally registered listeners. + Toolkit.getDefaultToolkit().globalDispatchEvent(e); + + if (newEventsOnly) + { + if (eventTypeEnabled(e.id)) + processEvent(e); + } + else + { + Event oldEvent = translateEvent(e); + if (oldEvent != null) + postEvent (oldEvent); + } + if (peer != null) + peer.handleEvent(e); + } + } + + /** + * Tells whether or not an event type is enabled. + */ + boolean eventTypeEnabled (int type) + { + if (type > AWTEvent.RESERVED_ID_MAX) + return true; + + switch (type) + { + case HierarchyEvent.HIERARCHY_CHANGED: + return (hierarchyListener != null + || (eventMask & AWTEvent.HIERARCHY_EVENT_MASK) != 0); + + case HierarchyEvent.ANCESTOR_MOVED: + case HierarchyEvent.ANCESTOR_RESIZED: + return (hierarchyBoundsListener != null + || (eventMask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0); + + case ComponentEvent.COMPONENT_HIDDEN: + case ComponentEvent.COMPONENT_MOVED: + case ComponentEvent.COMPONENT_RESIZED: + case ComponentEvent.COMPONENT_SHOWN: + return (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0); + + case KeyEvent.KEY_PRESSED: + case KeyEvent.KEY_RELEASED: + case KeyEvent.KEY_TYPED: + return (keyListener != null + || (eventMask & AWTEvent.KEY_EVENT_MASK) != 0); + + case MouseEvent.MOUSE_CLICKED: + case MouseEvent.MOUSE_ENTERED: + case MouseEvent.MOUSE_EXITED: + case MouseEvent.MOUSE_PRESSED: + case MouseEvent.MOUSE_RELEASED: + return (mouseListener != null + || (eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0); + case MouseEvent.MOUSE_MOVED: + case MouseEvent.MOUSE_DRAGGED: + return (mouseMotionListener != null + || (eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0); + case MouseEvent.MOUSE_WHEEL: + return (mouseWheelListener != null + || (eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0); + + case FocusEvent.FOCUS_GAINED: + case FocusEvent.FOCUS_LOST: + return (focusListener != null + || (eventMask & AWTEvent.FOCUS_EVENT_MASK) != 0); + + case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED: + case InputMethodEvent.CARET_POSITION_CHANGED: + return (inputMethodListener != null + || (eventMask & AWTEvent.INPUT_METHOD_EVENT_MASK) != 0); + + case PaintEvent.PAINT: + case PaintEvent.UPDATE: + return (eventMask & AWTEvent.PAINT_EVENT_MASK) != 0; + + default: + return false; + } + } + + /** + * Returns true when this component and all of its ancestors + * are visible, false otherwise. + * + * @return true when this component and all of its ancestors + * are visible, false otherwise + */ + boolean isHierarchyVisible() + { + boolean visible = isVisible(); + Component comp = parent; + while (comp != null && visible) + { + comp = comp.parent; + if (comp != null) + visible = visible && comp.isVisible(); + } + return visible; + } + + /** + * Returns the mouse pointer position relative to this Component's + * top-left corner. + * + * @return relative mouse pointer position + * + * @throws HeadlessException if in a headless environment + */ + public Point getMousePosition() throws HeadlessException + { + return getMousePositionHelper(true); + } + + Point getMousePositionHelper(boolean allowChildren) throws HeadlessException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException("can't get mouse position" + + " in headless environment"); + if (!isShowing()) + return null; + + Component parent = this; + int windowRelativeXOffset = 0; + int windowRelativeYOffset = 0; + while (parent != null && !(parent instanceof Window)) + { + windowRelativeXOffset += parent.getX(); + windowRelativeYOffset += parent.getY(); + parent = parent.getParent(); + } + if (parent == null) + return null; + + Window window = (Window) parent; + if (!Toolkit.getDefaultToolkit() + .getMouseInfoPeer().isWindowUnderMouse(window)) + return null; + + PointerInfo info = MouseInfo.getPointerInfo(); + Point mouseLocation = info.getLocation(); + Point windowLocation = window.getLocationOnScreen(); + + int x = mouseLocation.x - windowLocation.x; + int y = mouseLocation.y - windowLocation.y; + + if (!mouseOverComponent(window.getComponentAt(x, y), allowChildren)) + return null; + + return new Point(x - windowRelativeXOffset, y - windowRelativeYOffset); + } + + boolean mouseOverComponent(Component component, boolean allowChildren) + { + return component == this; + } + + /** + * This method is used to implement transferFocus(). CHILD is the child + * making the request. This is overridden by Container; when called for an + * ordinary component there is no child and so we always return null. + * + * FIXME: is this still needed, in light of focus traversal policies? + * + * @param child the component making the request + * @return the next component to focus on + */ + Component findNextFocusComponent(Component child) + { + return null; + } + + /** + * Deserializes this component. This regenerates all serializable listeners + * which were registered originally. + * + * @param s the stream to read from + * @throws ClassNotFoundException if deserialization fails + * @throws IOException if the stream fails + */ + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + String key = (String) s.readObject(); + while (key != null) + { + Object listener = s.readObject(); + if ("componentL".equals(key)) + addComponentListener((ComponentListener) listener); + else if ("focusL".equals(key)) + addFocusListener((FocusListener) listener); + else if ("keyL".equals(key)) + addKeyListener((KeyListener) listener); + else if ("mouseL".equals(key)) + addMouseListener((MouseListener) listener); + else if ("mouseMotionL".equals(key)) + addMouseMotionListener((MouseMotionListener) listener); + else if ("inputMethodL".equals(key)) + addInputMethodListener((InputMethodListener) listener); + else if ("hierarchyL".equals(key)) + addHierarchyListener((HierarchyListener) listener); + else if ("hierarchyBoundsL".equals(key)) + addHierarchyBoundsListener((HierarchyBoundsListener) listener); + else if ("mouseWheelL".equals(key)) + addMouseWheelListener((MouseWheelListener) listener); + key = (String) s.readObject(); + } + } + + /** + * Serializes this component. This ignores all listeners which do not + * implement Serializable, but includes those that do. + * + * @param s the stream to write to + * @throws IOException if the stream fails + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + AWTEventMulticaster.save(s, "componentL", componentListener); + AWTEventMulticaster.save(s, "focusL", focusListener); + AWTEventMulticaster.save(s, "keyL", keyListener); + AWTEventMulticaster.save(s, "mouseL", mouseListener); + AWTEventMulticaster.save(s, "mouseMotionL", mouseMotionListener); + AWTEventMulticaster.save(s, "inputMethodL", inputMethodListener); + AWTEventMulticaster.save(s, "hierarchyL", hierarchyListener); + AWTEventMulticaster.save(s, "hierarchyBoundsL", hierarchyBoundsListener); + AWTEventMulticaster.save(s, "mouseWheelL", mouseWheelListener); + s.writeObject(null); + } + + + // Nested classes. + + /** + * This class fixes the bounds for a Heavyweight component that + * is placed inside a Lightweight container. When the lightweight is + * moved or resized, setBounds for the lightweight peer does nothing. + * Therefore, it was never moved on the screen. This class is + * attached to the lightweight, and it adjusts the position and size + * of the peer when notified. + * This is the same for show and hide. + */ + class HeavyweightInLightweightListener + implements ComponentListener + { + + /** + * Constructor. Adds component listener to lightweight parent. + * + * @param parent - the lightweight container. + */ + public HeavyweightInLightweightListener(Container parent) + { + parent.addComponentListener(this); + } + + /** + * This method is called when the component is resized. + * + * @param event the ComponentEvent indicating the resize + */ + public void componentResized(ComponentEvent event) + { + // Nothing to do here, componentMoved will be called. + } + + /** + * This method is called when the component is moved. + * + * @param event the ComponentEvent indicating the move + */ + public void componentMoved(ComponentEvent event) + { + if (peer != null) + peer.setBounds(x, y, width, height); + } + + /** + * This method is called when the component is made visible. + * + * @param event the ComponentEvent indicating the visibility + */ + public void componentShown(ComponentEvent event) + { + if (isShowing()) + peer.show(); + } + + /** + * This method is called when the component is hidden. + * + * @param event the ComponentEvent indicating the visibility + */ + public void componentHidden(ComponentEvent event) + { + if (isShowing()) + peer.hide(); + } + } + + /** + * This class provides accessibility support for subclasses of container. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ + protected abstract class AccessibleAWTComponent extends AccessibleContext + implements Serializable, AccessibleComponent + { + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = 642321655757800191L; + + /** + * Converts show/hide events to PropertyChange events, and is registered + * as a component listener on this component. + * + * @serial the component handler + */ + protected ComponentListener accessibleAWTComponentHandler + = new AccessibleAWTComponentHandler(); + + /** + * Converts focus events to PropertyChange events, and is registered + * as a focus listener on this component. + * + * @serial the focus handler + */ + protected FocusListener accessibleAWTFocusHandler + = new AccessibleAWTFocusHandler(); + + /** + * The default constructor. + */ + protected AccessibleAWTComponent() + { + Component.this.addComponentListener(accessibleAWTComponentHandler); + Component.this.addFocusListener(accessibleAWTFocusHandler); + } + + /** + * Adds a global property change listener to the accessible component. + * + * @param l the listener to add + * @see #ACCESSIBLE_NAME_PROPERTY + * @see #ACCESSIBLE_DESCRIPTION_PROPERTY + * @see #ACCESSIBLE_STATE_PROPERTY + * @see #ACCESSIBLE_VALUE_PROPERTY + * @see #ACCESSIBLE_SELECTION_PROPERTY + * @see #ACCESSIBLE_TEXT_PROPERTY + * @see #ACCESSIBLE_VISIBLE_DATA_PROPERTY + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + Component.this.addPropertyChangeListener(l); + super.addPropertyChangeListener(l); + } + + /** + * Removes a global property change listener from this accessible + * component. + * + * @param l the listener to remove + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + Component.this.removePropertyChangeListener(l); + super.removePropertyChangeListener(l); + } + + /** + * Returns the accessible name of this component. It is almost always + * wrong to return getName(), since it is not localized. In fact, for + * things like buttons, this should be the text of the button, not the + * name of the object. The tooltip text might also be appropriate. + * + * @return the name + * @see #setAccessibleName(String) + */ + public String getAccessibleName() + { + return accessibleName; + } + + /** + * Returns a brief description of this accessible context. This should + * be localized. + * + * @return a description of this component + * @see #setAccessibleDescription(String) + */ + public String getAccessibleDescription() + { + return accessibleDescription; + } + + /** + * Returns the role of this component. + * + * @return the accessible role + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.AWT_COMPONENT; + } + + /** + * Returns a state set describing this component's state. + * + * @return a new state set + * @see AccessibleState + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet s = new AccessibleStateSet(); + if (Component.this.isEnabled()) + s.add(AccessibleState.ENABLED); + if (isFocusable()) + s.add(AccessibleState.FOCUSABLE); + if (isFocusOwner()) + s.add(AccessibleState.FOCUSED); + // Note: While the java.awt.Component has an 'opaque' property, it + // seems that it is not added to the accessible state set here, even + // if this property is true. However, it is handled for + // javax.swing.JComponent, so we add it there. + if (Component.this.isShowing()) + s.add(AccessibleState.SHOWING); + if (Component.this.isVisible()) + s.add(AccessibleState.VISIBLE); + return s; + } + + /** + * Returns the parent of this component, if it is accessible. + * + * @return the accessible parent + */ + public Accessible getAccessibleParent() + { + if (accessibleParent == null) + { + Container parent = getParent(); + accessibleParent = parent instanceof Accessible + ? (Accessible) parent : null; + } + return accessibleParent; + } + + /** + * Returns the index of this component in its accessible parent. + * + * @return the index, or -1 if the parent is not accessible + * @see #getAccessibleParent() + */ + public int getAccessibleIndexInParent() + { + if (getAccessibleParent() == null) + return -1; + AccessibleContext context + = ((Component) accessibleParent).getAccessibleContext(); + if (context == null) + return -1; + for (int i = context.getAccessibleChildrenCount(); --i >= 0; ) + if (context.getAccessibleChild(i) == Component.this) + return i; + return -1; + } + + /** + * Returns the number of children of this component which implement + * Accessible. Subclasses must override this if they can have children. + * + * @return the number of accessible children, default 0 + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns the ith accessible child. Subclasses must override this if + * they can have children. + * + * @return the ith accessible child, or null + * @see #getAccessibleChildrenCount() + */ + public Accessible getAccessibleChild(int i) + { + return null; + } + + /** + * Returns the locale of this component. + * + * @return the locale + * @throws IllegalComponentStateException if the locale is unknown + */ + public Locale getLocale() + { + return Component.this.getLocale(); + } + + /** + * Returns this, since it is an accessible component. + * + * @return the accessible component + */ + public AccessibleComponent getAccessibleComponent() + { + return this; + } + + /** + * Gets the background color. + * + * @return the background color + * @see #setBackground(Color) + */ + public Color getBackground() + { + return Component.this.getBackground(); + } + + /** + * Sets the background color. + * + * @param c the background color + * @see #getBackground() + * @see #isOpaque() + */ + public void setBackground(Color c) + { + Component.this.setBackground(c); + } + + /** + * Gets the foreground color. + * + * @return the foreground color + * @see #setForeground(Color) + */ + public Color getForeground() + { + return Component.this.getForeground(); + } + + /** + * Sets the foreground color. + * + * @param c the foreground color + * @see #getForeground() + */ + public void setForeground(Color c) + { + Component.this.setForeground(c); + } + + /** + * Gets the cursor. + * + * @return the cursor + * @see #setCursor(Cursor) + */ + public Cursor getCursor() + { + return Component.this.getCursor(); + } + + /** + * Sets the cursor. + * + * @param cursor the cursor + * @see #getCursor() + */ + public void setCursor(Cursor cursor) + { + Component.this.setCursor(cursor); + } + + /** + * Gets the font. + * + * @return the font + * @see #setFont(Font) + */ + public Font getFont() + { + return Component.this.getFont(); + } + + /** + * Sets the font. + * + * @param f the font + * @see #getFont() + */ + public void setFont(Font f) + { + Component.this.setFont(f); + } + + /** + * Gets the font metrics for a font. + * + * @param f the font to look up + * @return its metrics + * @throws NullPointerException if f is null + * @see #getFont() + */ + public FontMetrics getFontMetrics(Font f) + { + return Component.this.getFontMetrics(f); + } + + /** + * Tests if the component is enabled. + * + * @return true if the component is enabled + * @see #setEnabled(boolean) + * @see #getAccessibleStateSet() + * @see AccessibleState#ENABLED + */ + public boolean isEnabled() + { + return Component.this.isEnabled(); + } + + /** + * Set whether the component is enabled. + * + * @param b the new enabled status + * @see #isEnabled() + */ + public void setEnabled(boolean b) + { + Component.this.setEnabled(b); + } + + /** + * Test whether the component is visible (not necesarily showing). + * + * @return true if it is visible + * @see #setVisible(boolean) + * @see #getAccessibleStateSet() + * @see AccessibleState#VISIBLE + */ + public boolean isVisible() + { + return Component.this.isVisible(); + } + + /** + * Sets the visibility of this component. + * + * @param b the desired visibility + * @see #isVisible() + */ + public void setVisible(boolean b) + { + Component.this.setVisible(b); + } + + /** + * Tests if the component is showing. + * + * @return true if this is showing + */ + public boolean isShowing() + { + return Component.this.isShowing(); + } + + /** + * Tests if the point is contained in this component. + * + * @param p the point to check + * @return true if it is contained + * @throws NullPointerException if p is null + */ + public boolean contains(Point p) + { + return Component.this.contains(p.x, p.y); + } + + /** + * Returns the location of this object on the screen, or null if it is + * not showing. + * + * @return the location relative to screen coordinates, if showing + * @see #getBounds() + * @see #getLocation() + */ + public Point getLocationOnScreen() + { + return Component.this.isShowing() ? Component.this.getLocationOnScreen() + : null; + } + + /** + * Returns the location of this object relative to its parent's coordinate + * system, or null if it is not showing. + * + * @return the location + * @see #getBounds() + * @see #getLocationOnScreen() + */ + public Point getLocation() + { + return Component.this.getLocation(); + } + + /** + * Sets the location of this relative to its parent's coordinate system. + * + * @param p the location + * @throws NullPointerException if p is null + * @see #getLocation() + */ + public void setLocation(Point p) + { + Component.this.setLocation(p.x, p.y); + } + + /** + * Gets the bounds of this component, or null if it is not on screen. + * + * @return the bounds + * @see #contains(Point) + * @see #setBounds(Rectangle) + */ + public Rectangle getBounds() + { + return Component.this.getBounds(); + } + + /** + * Sets the bounds of this component. + * + * @param r the bounds + * @throws NullPointerException if r is null + * @see #getBounds() + */ + public void setBounds(Rectangle r) + { + Component.this.setBounds(r.x, r.y, r.width, r.height); + } + + /** + * Gets the size of this component, or null if it is not showing. + * + * @return the size + * @see #setSize(Dimension) + */ + public Dimension getSize() + { + return Component.this.getSize(); + } + + /** + * Sets the size of this component. + * + * @param d the size + * @throws NullPointerException if d is null + * @see #getSize() + */ + public void setSize(Dimension d) + { + Component.this.setSize(d.width, d.height); + } + + /** + * Returns the Accessible child at a point relative to the coordinate + * system of this component, if one exists, or null. Since components + * have no children, subclasses must override this to get anything besides + * null. + * + * @param p the point to check + * @return the accessible child at that point + * @throws NullPointerException if p is null + */ + public Accessible getAccessibleAt(Point p) + { + return null; + } + + /** + * Tests whether this component can accept focus. + * + * @return true if this is focus traversable + * @see #getAccessibleStateSet () + * @see AccessibleState#FOCUSABLE + * @see AccessibleState#FOCUSED + */ + public boolean isFocusTraversable () + { + return Component.this.isFocusTraversable (); + } + + /** + * Requests focus for this component. + * + * @see #isFocusTraversable () + */ + public void requestFocus () + { + Component.this.requestFocus (); + } + + /** + * Adds a focus listener. + * + * @param l the listener to add + */ + public void addFocusListener(FocusListener l) + { + Component.this.addFocusListener(l); + } + + /** + * Removes a focus listener. + * + * @param l the listener to remove + */ + public void removeFocusListener(FocusListener l) + { + Component.this.removeFocusListener(l); + } + + /** + * Converts component changes into property changes. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ + protected class AccessibleAWTComponentHandler implements ComponentListener + { + /** + * Default constructor. + */ + protected AccessibleAWTComponentHandler() + { + // Nothing to do here. + } + + /** + * Convert a component hidden to a property change. + * + * @param e the event to convert + */ + public void componentHidden(ComponentEvent e) + { + AccessibleAWTComponent.this.firePropertyChange + (ACCESSIBLE_STATE_PROPERTY, AccessibleState.VISIBLE, null); + } + + /** + * Convert a component shown to a property change. + * + * @param e the event to convert + */ + public void componentShown(ComponentEvent e) + { + AccessibleAWTComponent.this.firePropertyChange + (ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.VISIBLE); + } + + /** + * Moving a component does not affect properties. + * + * @param e ignored + */ + public void componentMoved(ComponentEvent e) + { + // Nothing to do here. + } + + /** + * Resizing a component does not affect properties. + * + * @param e ignored + */ + public void componentResized(ComponentEvent e) + { + // Nothing to do here. + } + } // class AccessibleAWTComponentHandler + + /** + * Converts focus changes into property changes. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ + protected class AccessibleAWTFocusHandler implements FocusListener + { + /** + * Default constructor. + */ + protected AccessibleAWTFocusHandler() + { + // Nothing to do here. + } + + /** + * Convert a focus gained to a property change. + * + * @param e the event to convert + */ + public void focusGained(FocusEvent e) + { + AccessibleAWTComponent.this.firePropertyChange + (ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.FOCUSED); + } + + /** + * Convert a focus lost to a property change. + * + * @param e the event to convert + */ + public void focusLost(FocusEvent e) + { + AccessibleAWTComponent.this.firePropertyChange + (ACCESSIBLE_STATE_PROPERTY, AccessibleState.FOCUSED, null); + } + } // class AccessibleAWTComponentHandler + } // class AccessibleAWTComponent + + /** + * This class provides support for blitting offscreen surfaces to a + * component. + * + * @see BufferStrategy + * + * @since 1.4 + */ + protected class BltBufferStrategy extends BufferStrategy + { + /** + * The capabilities of the image buffer. + */ + protected BufferCapabilities caps; + + /** + * The back buffers used in this strategy. + */ + protected VolatileImage[] backBuffers; + + /** + * Whether or not the image buffer resources are allocated and + * ready to be drawn into. + */ + protected boolean validatedContents; + + /** + * The width of the back buffers. + */ + protected int width; + + /** + * The height of the back buffers. + */ + protected int height; + + /** + * The front buffer. + */ + private VolatileImage frontBuffer; + + /** + * Creates a blitting buffer strategy. + * + * @param numBuffers the number of buffers, including the front + * buffer + * @param caps the capabilities of this strategy + */ + protected BltBufferStrategy(int numBuffers, BufferCapabilities caps) + { + this.caps = caps; + createBackBuffers(numBuffers - 1); + width = getWidth(); + height = getHeight(); + } + + /** + * Initializes the backBuffers field with an array of numBuffers + * VolatileImages. + * + * @param numBuffers the number of backbuffers to create + */ + protected void createBackBuffers(int numBuffers) + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + backBuffers = new VolatileImage[numBuffers]; + + for (int i = 0; i < numBuffers; i++) + backBuffers[i] = c.createCompatibleVolatileImage(width, height); + } + + /** + * Retrieves the capabilities of this buffer strategy. + * + * @return the capabilities of this buffer strategy + */ + public BufferCapabilities getCapabilities() + { + return caps; + } + + /** + * Retrieves a graphics object that can be used to draw into this + * strategy's image buffer. + * + * @return a graphics object + */ + public Graphics getDrawGraphics() + { + // Return the backmost buffer's graphics. + return backBuffers[0].getGraphics(); + } + + /** + * Bring the contents of the back buffer to the front buffer. + */ + public void show() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + // draw the front buffer. + getGraphics().drawImage(backBuffers[backBuffers.length - 1], + width, height, null); + + BufferCapabilities.FlipContents f = getCapabilities().getFlipContents(); + + // blit the back buffers. + for (int i = backBuffers.length - 1; i > 0 ; i--) + backBuffers[i] = backBuffers[i - 1]; + + // create new backmost buffer. + if (f == BufferCapabilities.FlipContents.UNDEFINED) + backBuffers[0] = c.createCompatibleVolatileImage(width, height); + + // create new backmost buffer and clear it to the background + // color. + if (f == BufferCapabilities.FlipContents.BACKGROUND) + { + backBuffers[0] = c.createCompatibleVolatileImage(width, height); + backBuffers[0].getGraphics().clearRect(0, 0, width, height); + } + + // FIXME: set the backmost buffer to the prior contents of the + // front buffer. How do we retrieve the contents of the front + // buffer? + // + // if (f == BufferCapabilities.FlipContents.PRIOR) + + // set the backmost buffer to a copy of the new front buffer. + if (f == BufferCapabilities.FlipContents.COPIED) + backBuffers[0] = backBuffers[backBuffers.length - 1]; + } + + /** + * Re-create the image buffer resources if they've been lost. + */ + protected void revalidate() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + for (int i = 0; i < backBuffers.length; i++) + { + int result = backBuffers[i].validate(c); + if (result == VolatileImage.IMAGE_INCOMPATIBLE) + backBuffers[i] = c.createCompatibleVolatileImage(width, height); + } + validatedContents = true; + } + + /** + * Returns whether or not the image buffer resources have been + * lost. + * + * @return true if the resources have been lost, false otherwise + */ + public boolean contentsLost() + { + for (int i = 0; i < backBuffers.length; i++) + { + if (backBuffers[i].contentsLost()) + { + validatedContents = false; + return true; + } + } + // we know that the buffer resources are valid now because we + // just checked them + validatedContents = true; + return false; + } + + /** + * Returns whether or not the image buffer resources have been + * restored. + * + * @return true if the resources have been restored, false + * otherwise + */ + public boolean contentsRestored() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + boolean imageRestored = false; + + for (int i = 0; i < backBuffers.length; i++) + { + int result = backBuffers[i].validate(c); + if (result == VolatileImage.IMAGE_RESTORED) + imageRestored = true; + else if (result == VolatileImage.IMAGE_INCOMPATIBLE) + return false; + } + // we know that the buffer resources are valid now because we + // just checked them + validatedContents = true; + return imageRestored; + } + } + + /** + * This class provides support for flipping component buffers. It + * can only be used on Canvases and Windows. + * + * @since 1.4 + */ + protected class FlipBufferStrategy extends BufferStrategy + { + /** + * The number of buffers. + */ + protected int numBuffers; + + /** + * The capabilities of this buffering strategy. + */ + protected BufferCapabilities caps; + + /** + * An Image reference to the drawing buffer. + */ + protected Image drawBuffer; + + /** + * A VolatileImage reference to the drawing buffer. + */ + protected VolatileImage drawVBuffer; + + /** + * Whether or not the image buffer resources are allocated and + * ready to be drawn into. + */ + protected boolean validatedContents; + + /** + * The width of the back buffer. + */ + private int width; + + /** + * The height of the back buffer. + */ + private int height; + + /** + * Creates a flipping buffer strategy. The only supported + * strategy for FlipBufferStrategy itself is a double-buffer page + * flipping strategy. It forms the basis for more complex derived + * strategies. + * + * @param numBuffers the number of buffers + * @param caps the capabilities of this buffering strategy + * + * @throws AWTException if the requested + * number-of-buffers/capabilities combination is not supported + */ + protected FlipBufferStrategy(int numBuffers, BufferCapabilities caps) + throws AWTException + { + this.caps = caps; + width = getWidth(); + height = getHeight(); + + if (numBuffers > 1) + createBuffers(numBuffers, caps); + else + { + drawVBuffer = peer.createVolatileImage(width, height); + drawBuffer = drawVBuffer; + } + } + + /** + * Creates a multi-buffer flipping strategy. The number of + * buffers must be greater than one and the buffer capabilities + * must specify page flipping. + * + * @param numBuffers the number of flipping buffers; must be + * greater than one + * @param caps the buffering capabilities; caps.isPageFlipping() + * must return true + * + * @throws IllegalArgumentException if numBuffers is not greater + * than one or if the page flipping capability is not requested + * + * @throws AWTException if the requested flipping strategy is not + * supported + */ + protected void createBuffers(int numBuffers, BufferCapabilities caps) + throws AWTException + { + if (numBuffers <= 1) + throw new IllegalArgumentException("FlipBufferStrategy.createBuffers:" + + " numBuffers must be greater than" + + " one."); + + if (!caps.isPageFlipping()) + throw new IllegalArgumentException("FlipBufferStrategy.createBuffers:" + + " flipping must be a specified" + + " capability."); + + peer.createBuffers(numBuffers, caps); + } + + /** + * Return a direct reference to the back buffer image. + * + * @return a direct reference to the back buffer image. + */ + protected Image getBackBuffer() + { + return peer.getBackBuffer(); + } + + /** + * Perform a flip operation to transfer the contents of the back + * buffer to the front buffer. + */ + protected void flip(BufferCapabilities.FlipContents flipAction) + { + peer.flip(flipAction); + } + + /** + * Release the back buffer's resources. + */ + protected void destroyBuffers() + { + peer.destroyBuffers(); + } + + /** + * Retrieves the capabilities of this buffer strategy. + * + * @return the capabilities of this buffer strategy + */ + public BufferCapabilities getCapabilities() + { + return caps; + } + + /** + * Retrieves a graphics object that can be used to draw into this + * strategy's image buffer. + * + * @return a graphics object + */ + public Graphics getDrawGraphics() + { + return drawVBuffer.getGraphics(); + } + + /** + * Re-create the image buffer resources if they've been lost. + */ + protected void revalidate() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + if (drawVBuffer.validate(c) == VolatileImage.IMAGE_INCOMPATIBLE) + drawVBuffer = peer.createVolatileImage(width, height); + validatedContents = true; + } + + /** + * Returns whether or not the image buffer resources have been + * lost. + * + * @return true if the resources have been lost, false otherwise + */ + public boolean contentsLost() + { + if (drawVBuffer.contentsLost()) + { + validatedContents = false; + return true; + } + // we know that the buffer resources are valid now because we + // just checked them + validatedContents = true; + return false; + } + + /** + * Returns whether or not the image buffer resources have been + * restored. + * + * @return true if the resources have been restored, false + * otherwise + */ + public boolean contentsRestored() + { + GraphicsConfiguration c = + GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + + int result = drawVBuffer.validate(c); + + boolean imageRestored = false; + + if (result == VolatileImage.IMAGE_RESTORED) + imageRestored = true; + else if (result == VolatileImage.IMAGE_INCOMPATIBLE) + return false; + + // we know that the buffer resources are valid now because we + // just checked them + validatedContents = true; + return imageRestored; + } + + /** + * Bring the contents of the back buffer to the front buffer. + */ + public void show() + { + flip(caps.getFlipContents()); + } + } +} diff --git a/libjava/classpath/java/awt/ComponentOrientation.java b/libjava/classpath/java/awt/ComponentOrientation.java new file mode 100644 index 000000000..69b14c755 --- /dev/null +++ b/libjava/classpath/java/awt/ComponentOrientation.java @@ -0,0 +1,215 @@ +/* ComponentOrientation.java -- describes a component's orientation + Copyright (C) 2000, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * This class is used to differentiate different orientations for text layout. + * It controls whether text flows left-to-right or right-to-left, and whether + * lines are horizontal or vertical, as in this table:
+ *
+ * LT      RT      TL      TR
+ * A B C   C B A   A D G   G D A
+ * D E F   F E D   B E H   H E B
+ * G H I   I H G   C F I   I F C
+ * 
+ * LT languages are most common (left-to-right lines, top-to-bottom). + * This includes Western European languages, and optionally includes Japanese, + * Chinese, and Korean. RT languages (right-to-left lines, + * top-to-bottom) are mainly middle eastern, such as Hebrew and Arabic. + * TR languages flow top-to-bottom in a line, right-to-left, and are + * the basis of Japanese, Chinese, and Korean. Finally, TL languages + * flow top-to-bottom in a line, left-to-right, as in Mongolian. + * + *

This is a pretty poor excuse for a type-safe enum, since it is not + * guaranteed that orientation objects are unique (thanks to serialization), + * yet there is no equals() method. You would be wise to compare the output + * of isHorizontal() and isLeftToRight() rather than comparing objects with + * ==, especially since more constants may be added in the future. + * + * @author Bryce McKinlay (bryce@albatross.co.nz) + * @since 1.0 + * @status updated to 1.4 + */ +public final class ComponentOrientation implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4113291392143563828L; + + /** Constant for unknown orientation. */ + private static final int UNKNOWN_ID = 1; + + /** Constant for horizontal line orientation. */ + private static final int HORIZONTAL_ID = 2; + + /** Constant for left-to-right orientation. */ + private static final int LEFT_TO_RIGHT_ID = 4; + + /** + * Items run left to right, and lines flow top to bottom. Examples: English, + * French. + */ + public static final ComponentOrientation LEFT_TO_RIGHT + = new ComponentOrientation(HORIZONTAL_ID | LEFT_TO_RIGHT_ID); + + /** + * Items run right to left, and lines flow top to bottom. Examples: Arabic, + * Hebrew. + */ + public static final ComponentOrientation RIGHT_TO_LEFT + = new ComponentOrientation(HORIZONTAL_ID); + + /** + * The orientation is unknown for the locale. For backwards compatibility, + * this behaves like LEFT_TO_RIGHT in the instance methods. + */ + public static final ComponentOrientation UNKNOWN + = new ComponentOrientation(UNKNOWN_ID | HORIZONTAL_ID | LEFT_TO_RIGHT_ID); + + /** + * The orientation of this object; bitwise-or of unknown (1), horizontal (2), + * and left-to-right (4). + * + * @serial the orientation + */ + private final int orientation; + + /** + * Construct a given orientation. + * + * @param orientation the orientation + */ + private ComponentOrientation(int orientation) + { + this.orientation = orientation; + } + + /** + * Returns true if the lines are horizontal, in which case lines flow + * top-to-bottom. For example, English, Hebrew. Counterexamples: Japanese, + * Chinese, Korean, Mongolian. + * + * @return true if this orientation has horizontal lines + */ + public boolean isHorizontal() + { + return (orientation & HORIZONTAL_ID) != 0; + } + + /** + * If isHorizontal() returns true, then this determines whether items in + * the line flow left-to-right. If isHorizontal() returns false, items in + * a line flow top-to-bottom, and this determines if lines flow + * left-to-right. + * + * @return true if this orientation flows left-to-right + */ + public boolean isLeftToRight() + { + return (orientation & LEFT_TO_RIGHT_ID) != 0; + } + + /** + * Gets an orientation appropriate for the locale. + * + * @param locale the locale + * @return the orientation for that locale + * @throws NullPointerException if locale is null + */ + public static ComponentOrientation getOrientation(Locale locale) + { + // Based on iterating over all languages defined in JDK 1.4, this behavior + // matches Sun's. However, it makes me wonder if any non-horizontal + // orientations even exist, as it sure contradicts their documentation. + String language = locale.getLanguage(); + if ("ar".equals(language) || "fa".equals(language) || "iw".equals(language) + || "ur".equals(language)) + return RIGHT_TO_LEFT; + return LEFT_TO_RIGHT; + } + + /** + * Gets an orientation from a resource bundle. This tries the following: + * + *

    + *
  • Use the key "Orientation" to find an instance of ComponentOrientation + * in the bundle.
  • + *
  • Get the locale of the resource bundle, and get the orientation of + * that locale.
  • + *
  • Give up and get the orientation of the default locale.
  • + *
+ * + * @param bdl the bundle to use + * @return the orientation + * @throws NullPointerException if bdl is null + * @deprecated use {@link #getOrientation(Locale)} instead + */ + public static ComponentOrientation getOrientation(ResourceBundle bdl) + { + ComponentOrientation r; + try + { + r = (ComponentOrientation) bdl.getObject("Orientation"); + if (r != null) + return r; + } + catch (MissingResourceException ignored) + { + } + catch (ClassCastException ignored) + { + } + try + { + r = getOrientation(bdl.getLocale()); + if (r != null) + return r; + } + catch (Exception ignored) + { + } + return getOrientation(Locale.getDefault()); + } +} // class ComponentOrientation diff --git a/libjava/classpath/java/awt/Composite.java b/libjava/classpath/java/awt/Composite.java new file mode 100644 index 000000000..ca3abe4d1 --- /dev/null +++ b/libjava/classpath/java/awt/Composite.java @@ -0,0 +1,73 @@ +/* Composite.java -- graphics formed from composite layers + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ColorModel; + +/** + * This interface is for graphics which are formed as composites of others. + * It combines {@link Graphics2D} shapes according to defined rules to form + * the new image. Implementations of this interface must be immutable, because + * they are not cloned when a Graphics2D container is cloned. + * + *

Since this can expose pixels to untrusted code, there is a security + * check on custom objects, readDisplayPixels, to prevent leaking + * restricted information graphically. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see AlphaComposite + * @see CompositeContext + * @see Graphics2D#setComposite(Composite) + * @since 1.2 + * @status updated to 1.4 + */ +public interface Composite +{ + /** + * Create a context state for performing the compositing operation. Several + * contexts may exist for this object, in a multi-threaded environment. + * + * @param srcColorModel the color model of the source + * @param dstColorModel the color model of the destination + * @param hints hints for choosing between rendering alternatives + */ + CompositeContext createContext(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints); +} // interface Composite diff --git a/libjava/classpath/java/awt/CompositeContext.java b/libjava/classpath/java/awt/CompositeContext.java new file mode 100644 index 000000000..018a27071 --- /dev/null +++ b/libjava/classpath/java/awt/CompositeContext.java @@ -0,0 +1,71 @@ +/* Composite.java -- the context for compositing graphics layers + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.Raster; +import java.awt.image.WritableRaster; + +/** + * This interface provides an optimized environment for compositing graphics. + * Several such contexts may exist for a given Composite object. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Composite + * @since 1.2 + * @status updated to 1.4 + */ +public interface CompositeContext +{ + /** + * Release resources allocated for the compositing. + */ + void dispose(); + + /** + * Compose the two source images into the composite image. The destination + * can be the same as one of the two inputs, and the destination must be + * compatible with the ColorModel chosen in {@link Composite#createContext}. + * + * @param src the lower image source in compositing + * @param dstIn the upper image source in compositing + * @param dstOut the destination for the composite + * @see Composite + */ + void compose(Raster src, Raster dstIn, WritableRaster dstOut); +} // interface CompositeContext diff --git a/libjava/classpath/java/awt/Container.java b/libjava/classpath/java/awt/Container.java new file mode 100644 index 000000000..7e596f10a --- /dev/null +++ b/libjava/classpath/java/awt/Container.java @@ -0,0 +1,2370 @@ +/* Container.java -- parent container class in AWT + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.peer.ComponentPeer; +import java.awt.peer.ContainerPeer; +import java.awt.peer.LightweightPeer; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.util.Collections; +import java.util.EventListener; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.accessibility.Accessible; + +/** + * A generic window toolkit object that acts as a container for other objects. + * Components are tracked in a list, and new elements are at the end of the + * list or bottom of the stacking order. + * + * @author original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * + * @since 1.0 + * + * @status still missing 1.4 support, some generics from 1.5 + */ +public class Container extends Component +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4613797578919906343L; + + /* Serialized fields from the serialization spec. */ + int ncomponents; + Component[] component; + LayoutManager layoutMgr; + + /** + * @since 1.4 + */ + boolean focusCycleRoot; + + /** + * Indicates if this container provides a focus traversal policy. + * + * @since 1.5 + */ + private boolean focusTraversalPolicyProvider; + + int containerSerializedDataVersion; + + /* Anything else is non-serializable, and should be declared "transient". */ + transient ContainerListener containerListener; + + /** The focus traversal policy that determines how focus is + transferred between this Container and its children. */ + private FocusTraversalPolicy focusTraversalPolicy; + + /** + * The focus traversal keys, if not inherited from the parent or default + * keyboard manager. These sets will contain only AWTKeyStrokes that + * represent press and release events to use as focus control. + * + * @see #getFocusTraversalKeys(int) + * @see #setFocusTraversalKeys(int, Set) + * @since 1.4 + */ + transient Set[] focusTraversalKeys; + + /** + * Default constructor for subclasses. + */ + public Container() + { + // Nothing to do here. + } + + /** + * Returns the number of components in this container. + * + * @return The number of components in this container. + */ + public int getComponentCount() + { + return countComponents (); + } + + /** + * Returns the number of components in this container. + * + * @return The number of components in this container. + * + * @deprecated use {@link #getComponentCount()} instead + */ + public int countComponents() + { + return ncomponents; + } + + /** + * Returns the component at the specified index. + * + * @param n The index of the component to retrieve. + * + * @return The requested component. + * + * @throws ArrayIndexOutOfBoundsException If the specified index is invalid + */ + public Component getComponent(int n) + { + synchronized (getTreeLock ()) + { + if (n < 0 || n >= ncomponents) + throw new ArrayIndexOutOfBoundsException("no such component"); + + return component[n]; + } + } + + /** + * Returns an array of the components in this container. + * + * @return The components in this container. + */ + public Component[] getComponents() + { + synchronized (getTreeLock ()) + { + Component[] result = new Component[ncomponents]; + + if (ncomponents > 0) + System.arraycopy(component, 0, result, 0, ncomponents); + + return result; + } + } + + /** + * Returns the insets for this container, which is the space used for + * borders, the margin, etc. + * + * @return The insets for this container. + */ + public Insets getInsets() + { + return insets (); + } + + /** + * Returns the insets for this container, which is the space used for + * borders, the margin, etc. + * + * @return The insets for this container. + * @deprecated use {@link #getInsets()} instead + */ + public Insets insets() + { + Insets i; + if (peer == null || peer instanceof LightweightPeer) + i = new Insets (0, 0, 0, 0); + else + i = ((ContainerPeer) peer).getInsets (); + return i; + } + + /** + * Adds the specified component to this container at the end of the + * component list. + * + * @param comp The component to add to the container. + * + * @return The same component that was added. + */ + public Component add(Component comp) + { + addImpl(comp, null, -1); + return comp; + } + + /** + * Adds the specified component to the container at the end of the + * component list. This method should not be used. Instead, use + * add(Component, Object). + * + * @param name The name of the component to be added. + * @param comp The component to be added. + * + * @return The same component that was added. + * + * @see #add(Component,Object) + */ + public Component add(String name, Component comp) + { + addImpl(comp, name, -1); + return comp; + } + + /** + * Adds the specified component to this container at the specified index + * in the component list. + * + * @param comp The component to be added. + * @param index The index in the component list to insert this child + * at, or -1 to add at the end of the list. + * + * @return The same component that was added. + * + * @throws ArrayIndexOutOfBoundsException If the specified index is invalid. + */ + public Component add(Component comp, int index) + { + addImpl(comp, null, index); + return comp; + } + + /** + * Adds the specified component to this container at the end of the + * component list. The layout manager will use the specified constraints + * when laying out this component. + * + * @param comp The component to be added to this container. + * @param constraints The layout constraints for this component. + */ + public void add(Component comp, Object constraints) + { + addImpl(comp, constraints, -1); + } + + /** + * Adds the specified component to this container at the specified index + * in the component list. The layout manager will use the specified + * constraints when layout out this component. + * + * @param comp The component to be added. + * @param constraints The layout constraints for this component. + * @param index The index in the component list to insert this child + * at, or -1 to add at the end of the list. + * + * @throws ArrayIndexOutOfBoundsException If the specified index is invalid. + */ + public void add(Component comp, Object constraints, int index) + { + addImpl(comp, constraints, index); + } + + /** + * This method is called by all the add() methods to perform + * the actual adding of the component. Subclasses who wish to perform + * their own processing when a component is added should override this + * method. Any subclass doing this must call the superclass version of + * this method in order to ensure proper functioning of the container. + * + * @param comp The component to be added. + * @param constraints The layout constraints for this component, or + * null if there are no constraints. + * @param index The index in the component list to insert this child + * at, or -1 to add at the end of the list. + * + * @throws ArrayIndexOutOfBoundsException If the specified index is invalid. + */ + protected void addImpl(Component comp, Object constraints, int index) + { + synchronized (getTreeLock ()) + { + if (index > ncomponents + || (index < 0 && index != -1) + || comp instanceof Window + || (comp instanceof Container + && ((Container) comp).isAncestorOf(this))) + throw new IllegalArgumentException(); + + // Reparent component, and make sure component is instantiated if + // we are. + if (comp.parent != null) + comp.parent.remove(comp); + + if (component == null) + component = new Component[4]; // FIXME, better initial size? + + // This isn't the most efficient implementation. We could do less + // copying when growing the array. It probably doesn't matter. + if (ncomponents >= component.length) + { + int nl = component.length * 2; + Component[] c = new Component[nl]; + System.arraycopy(component, 0, c, 0, ncomponents); + component = c; + } + + if (index == -1) + component[ncomponents++] = comp; + else + { + System.arraycopy(component, index, component, index + 1, + ncomponents - index); + component[index] = comp; + ++ncomponents; + } + + // Give the new component a parent. + comp.parent = this; + + // Update the counter for Hierarchy(Bounds)Listeners. + int childHierarchyListeners = comp.numHierarchyListeners; + if (childHierarchyListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + childHierarchyListeners); + int childHierarchyBoundsListeners = comp.numHierarchyBoundsListeners; + if (childHierarchyBoundsListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + childHierarchyListeners); + + // Invalidate the layout of this container. + if (valid) + invalidate(); + + // Create the peer _after_ the component has been added, so that + // the peer gets to know about the component hierarchy. + if (peer != null) + { + // Notify the component that it has a new parent. + comp.addNotify(); + } + + // Notify the layout manager. + if (layoutMgr != null) + { + // If we have a LayoutManager2 the constraints are "real", + // otherwise they are the "name" of the Component to add. + if (layoutMgr instanceof LayoutManager2) + { + LayoutManager2 lm2 = (LayoutManager2) layoutMgr; + lm2.addLayoutComponent(comp, constraints); + } + else if (constraints instanceof String) + layoutMgr.addLayoutComponent((String) constraints, comp); + else + layoutMgr.addLayoutComponent("", comp); + } + + // We previously only sent an event when this container is showing. + // Also, the event was posted to the event queue. A Mauve test shows + // that this event is not delivered using the event queue and it is + // also sent when the container is not showing. + if (containerListener != null + || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0) + { + ContainerEvent ce = new ContainerEvent(this, + ContainerEvent.COMPONENT_ADDED, + comp); + dispatchEvent(ce); + } + + // Notify hierarchy listeners. + comp.fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, comp, + this, HierarchyEvent.PARENT_CHANGED); + } + } + + /** + * Removes the component at the specified index from this container. + * + * @param index The index of the component to remove. + */ + public void remove(int index) + { + synchronized (getTreeLock ()) + { + if (index < 0 || index >= ncomponents) + throw new ArrayIndexOutOfBoundsException(); + + Component r = component[index]; + if (peer != null) + r.removeNotify(); + + if (layoutMgr != null) + layoutMgr.removeLayoutComponent(r); + + // Update the counter for Hierarchy(Bounds)Listeners. + int childHierarchyListeners = r.numHierarchyListeners; + if (childHierarchyListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + -childHierarchyListeners); + int childHierarchyBoundsListeners = r.numHierarchyBoundsListeners; + if (childHierarchyBoundsListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + -childHierarchyListeners); + + r.parent = null; + + System.arraycopy(component, index + 1, component, index, + ncomponents - index - 1); + component[--ncomponents] = null; + + if (valid) + invalidate(); + + if (containerListener != null + || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0) + { + // Post event to notify of removing the component. + ContainerEvent ce = new ContainerEvent(this, + ContainerEvent.COMPONENT_REMOVED, + r); + dispatchEvent(ce); + } + + // Notify hierarchy listeners. + r.fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, r, + this, HierarchyEvent.PARENT_CHANGED); + } + } + + /** + * Removes the specified component from this container. + * + * @param comp The component to remove from this container. + */ + public void remove(Component comp) + { + synchronized (getTreeLock ()) + { + for (int i = 0; i < ncomponents; ++i) + { + if (component[i] == comp) + { + remove(i); + break; + } + } + } + } + + /** + * Removes all components from this container. + */ + public void removeAll() + { + synchronized (getTreeLock ()) + { + // In order to allow the same bad tricks to be used as in RI + // this code has to stay exactly that way: In a real-life app + // a Container subclass implemented its own vector for + // subcomponents, supplied additional addXYZ() methods + // and overrode remove(int) and removeAll (the latter calling + // super.removeAll() ). + // By doing it this way, user code cannot prevent the correct + // removal of components. + while (ncomponents > 0) + { + ncomponents--; + Component r = component[ncomponents]; + component[ncomponents] = null; + + if (peer != null) + r.removeNotify(); + + if (layoutMgr != null) + layoutMgr.removeLayoutComponent(r); + + r.parent = null; + + // Send ContainerEvent if necessary. + if (containerListener != null + || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0) + { + // Post event to notify of removing the component. + ContainerEvent ce + = new ContainerEvent(this, + ContainerEvent.COMPONENT_REMOVED, + r); + dispatchEvent(ce); + } + + // Update the counter for Hierarchy(Bounds)Listeners. + int childHierarchyListeners = r.numHierarchyListeners; + if (childHierarchyListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + -childHierarchyListeners); + int childHierarchyBoundsListeners = r.numHierarchyBoundsListeners; + if (childHierarchyBoundsListeners > 0) + updateHierarchyListenerCount(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + -childHierarchyListeners); + + + // Send HierarchyEvent if necessary. + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, r, this, + HierarchyEvent.PARENT_CHANGED); + + } + + if (valid) + invalidate(); + } + } + + /** + * Returns the current layout manager for this container. + * + * @return The layout manager for this container. + */ + public LayoutManager getLayout() + { + return layoutMgr; + } + + /** + * Sets the layout manager for this container to the specified layout + * manager. + * + * @param mgr The new layout manager for this container. + */ + public void setLayout(LayoutManager mgr) + { + layoutMgr = mgr; + if (valid) + invalidate(); + } + + /** + * Layout the components in this container. + */ + public void doLayout() + { + layout (); + } + + /** + * Layout the components in this container. + * + * @deprecated use {@link #doLayout()} instead + */ + public void layout() + { + if (layoutMgr != null) + layoutMgr.layoutContainer (this); + } + + /** + * Invalidates this container to indicate that it (and all parent + * containers) need to be laid out. + */ + public void invalidate() + { + super.invalidate(); + if (layoutMgr != null && layoutMgr instanceof LayoutManager2) + { + LayoutManager2 lm2 = (LayoutManager2) layoutMgr; + lm2.invalidateLayout(this); + } + } + + /** + * Re-lays out the components in this container. + */ + public void validate() + { + ComponentPeer p = peer; + if (! valid && p != null) + { + ContainerPeer cPeer = null; + if (p instanceof ContainerPeer) + cPeer = (ContainerPeer) peer; + synchronized (getTreeLock ()) + { + if (cPeer != null) + cPeer.beginValidate(); + validateTree(); + valid = true; + if (cPeer != null) + cPeer.endValidate(); + } + } + } + + /** + * Recursively invalidates the container tree. + */ + private final void invalidateTree() + { + synchronized (getTreeLock()) + { + for (int i = 0; i < ncomponents; i++) + { + Component comp = component[i]; + if (comp instanceof Container) + ((Container) comp).invalidateTree(); + else if (comp.valid) + comp.invalidate(); + } + if (valid) + invalidate(); + } + } + + /** + * Recursively validates the container tree, recomputing any invalid + * layouts. + */ + protected void validateTree() + { + if (!valid) + { + ContainerPeer cPeer = null; + if (peer instanceof ContainerPeer) + { + cPeer = (ContainerPeer) peer; + cPeer.beginLayout(); + } + + doLayout (); + for (int i = 0; i < ncomponents; ++i) + { + Component comp = component[i]; + + if (comp instanceof Container && ! (comp instanceof Window) + && ! comp.valid) + { + ((Container) comp).validateTree(); + } + else + { + comp.validate(); + } + } + + if (cPeer != null) + { + cPeer = (ContainerPeer) peer; + cPeer.endLayout(); + } + } + + /* 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; + + } + + public void setFont(Font f) + { + Font oldFont = getFont(); + super.setFont(f); + Font newFont = getFont(); + if (newFont != oldFont && (oldFont == null || ! oldFont.equals(newFont))) + { + invalidateTree(); + } + } + + /** + * Returns the preferred size of this container. + * + * @return The preferred size of this container. + */ + public Dimension getPreferredSize() + { + return preferredSize (); + } + + /** + * Returns the preferred size of this container. + * + * @return The preferred size of this container. + * + * @deprecated use {@link #getPreferredSize()} instead + */ + public Dimension preferredSize() + { + Dimension size = prefSize; + // Try to return cached value if possible. + if (size == null || !(prefSizeSet || valid)) + { + // Need to lock here. + synchronized (getTreeLock()) + { + LayoutManager l = layoutMgr; + if (l != null) + prefSize = l.preferredLayoutSize(this); + else + prefSize = super.preferredSizeImpl(); + size = prefSize; + } + } + if (size != null) + return new Dimension(size); + else + return size; + } + + /** + * Returns the minimum size of this container. + * + * @return The minimum size of this container. + */ + public Dimension getMinimumSize() + { + return minimumSize (); + } + + /** + * Returns the minimum size of this container. + * + * @return The minimum size of this container. + * + * @deprecated use {@link #getMinimumSize()} instead + */ + public Dimension minimumSize() + { + Dimension size = minSize; + // Try to return cached value if possible. + if (size == null || !(minSizeSet || valid)) + { + // Need to lock here. + synchronized (getTreeLock()) + { + LayoutManager l = layoutMgr; + if (l != null) + minSize = l.minimumLayoutSize(this); + else + minSize = super.minimumSizeImpl(); + size = minSize; + } + } + if (size != null) + return new Dimension(size); + else + return size; + } + + /** + * Returns the maximum size of this container. + * + * @return The maximum size of this container. + */ + public Dimension getMaximumSize() + { + Dimension size = maxSize; + // Try to return cached value if possible. + if (size == null || !(maxSizeSet || valid)) + { + // Need to lock here. + synchronized (getTreeLock()) + { + LayoutManager l = layoutMgr; + if (l instanceof LayoutManager2) + maxSize = ((LayoutManager2) l).maximumLayoutSize(this); + else { + maxSize = super.maximumSizeImpl(); + } + size = maxSize; + } + } + if (size != null) + return new Dimension(size); + else + return size; + } + + /** + * Returns the preferred alignment along the X axis. This is a value + * between 0 and 1 where 0 represents alignment flush left and + * 1 means alignment flush right, and 0.5 means centered. + * + * @return The preferred alignment along the X axis. + */ + public float getAlignmentX() + { + LayoutManager layout = getLayout(); + float alignmentX = 0.0F; + if (layout != null && layout instanceof LayoutManager2) + { + synchronized (getTreeLock()) + { + LayoutManager2 lm2 = (LayoutManager2) layout; + alignmentX = lm2.getLayoutAlignmentX(this); + } + } + else + alignmentX = super.getAlignmentX(); + return alignmentX; + } + + /** + * Returns the preferred alignment along the Y axis. This is a value + * between 0 and 1 where 0 represents alignment flush top and + * 1 means alignment flush bottom, and 0.5 means centered. + * + * @return The preferred alignment along the Y axis. + */ + public float getAlignmentY() + { + LayoutManager layout = getLayout(); + float alignmentY = 0.0F; + if (layout != null && layout instanceof LayoutManager2) + { + synchronized (getTreeLock()) + { + LayoutManager2 lm2 = (LayoutManager2) layout; + alignmentY = lm2.getLayoutAlignmentY(this); + } + } + else + alignmentY = super.getAlignmentY(); + return alignmentY; + } + + /** + * Paints this container. The implementation of this method in this + * class forwards to any lightweight components in this container. If + * this method is subclassed, this method should still be invoked as + * a superclass method so that lightweight components are properly + * drawn. + * + * @param g - The graphics context for this paint job. + */ + public void paint(Graphics g) + { + if (isShowing()) + { + visitChildren(g, GfxPaintVisitor.INSTANCE, true); + } + } + + /** + * Updates this container. The implementation of this method in this + * class forwards to any lightweight components in this container. If + * this method is subclassed, this method should still be invoked as + * a superclass method so that lightweight components are properly + * drawn. + * + * @param g The graphics context for this update. + * + * @specnote The specification suggests that this method forwards the + * update() call to all its lightweight children. Tests show + * that this is not done either in the JDK. The exact behaviour + * seems to be that the background is cleared in heavyweight + * Containers, and all other containers + * directly call paint(), causing the (lightweight) children to + * be painted. + */ + public void update(Graphics g) + { + // It seems that the JDK clears the background of containers like Panel + // and Window (within this method) but not of 'plain' Containers or + // JComponents. This could + // lead to the assumption that it only clears heavyweight containers. + // However that is not quite true. In a test with a custom Container + // that overrides isLightweight() to return false, the background is + // also not cleared. So we do a check on !(peer instanceof LightweightPeer) + // instead. + if (isShowing()) + { + ComponentPeer p = peer; + if (! (p instanceof LightweightPeer)) + { + g.clearRect(0, 0, getWidth(), getHeight()); + } + paint(g); + } + } + + /** + * Prints this container. The implementation of this method in this + * class forwards to any lightweight components in this container. If + * this method is subclassed, this method should still be invoked as + * a superclass method so that lightweight components are properly + * drawn. + * + * @param g The graphics context for this print job. + */ + public void print(Graphics g) + { + super.print(g); + visitChildren(g, GfxPrintVisitor.INSTANCE, true); + } + + /** + * Paints all of the components in this container. + * + * @param g The graphics context for this paint job. + */ + public void paintComponents(Graphics g) + { + if (isShowing()) + visitChildren(g, GfxPaintAllVisitor.INSTANCE, false); + } + + /** + * Prints all of the components in this container. + * + * @param g The graphics context for this print job. + */ + public void printComponents(Graphics g) + { + super.paint(g); + visitChildren(g, GfxPrintAllVisitor.INSTANCE, true); + } + + /** + * Adds the specified container listener to this object's list of + * container listeners. + * + * @param listener The listener to add. + */ + public synchronized void addContainerListener(ContainerListener listener) + { + if (listener != null) + { + containerListener = AWTEventMulticaster.add(containerListener, + listener); + newEventsOnly = true; + } + } + + /** + * Removes the specified container listener from this object's list of + * container listeners. + * + * @param listener The listener to remove. + */ + public synchronized void removeContainerListener(ContainerListener listener) + { + containerListener = AWTEventMulticaster.remove(containerListener, listener); + } + + /** + * @since 1.4 + */ + public synchronized ContainerListener[] getContainerListeners() + { + return (ContainerListener[]) + AWTEventMulticaster.getListeners(containerListener, + ContainerListener.class); + } + + /** + * Returns all registered {@link EventListener}s of the given + * listenerType. + * + * @param listenerType the class of listeners to filter (null + * not permitted). + * + * @return An array of registered listeners. + * + * @throws ClassCastException if listenerType does not implement + * the {@link EventListener} interface. + * @throws NullPointerException if listenerType is + * null. + * + * @see #getContainerListeners() + * + * @since 1.3 + */ + public T[] getListeners(Class listenerType) + { + if (listenerType == ContainerListener.class) + return (T[]) getContainerListeners(); + return super.getListeners(listenerType); + } + + /** + * Processes the specified event. This method calls + * processContainerEvent() if this method is a + * ContainerEvent, otherwise it calls the superclass + * method. + * + * @param e The event to be processed. + */ + protected void processEvent(AWTEvent e) + { + if (e instanceof ContainerEvent) + processContainerEvent((ContainerEvent) e); + else + super.processEvent(e); + } + + /** + * Called when a container event occurs if container events are enabled. + * This method calls any registered listeners. + * + * @param e The event that occurred. + */ + protected void processContainerEvent(ContainerEvent e) + { + if (containerListener == null) + return; + switch (e.id) + { + case ContainerEvent.COMPONENT_ADDED: + containerListener.componentAdded(e); + break; + + case ContainerEvent.COMPONENT_REMOVED: + containerListener.componentRemoved(e); + break; + } + } + + /** + * AWT 1.0 event processor. + * + * @param e The event that occurred. + * + * @deprecated use {@link #dispatchEvent(AWTEvent)} instead + */ + public void deliverEvent(Event e) + { + if (!handleEvent (e)) + { + synchronized (getTreeLock ()) + { + Component parent = getParent (); + + if (parent != null) + parent.deliverEvent (e); + } + } + } + + /** + * Returns the component located at the specified point. This is done + * by checking whether or not a child component claims to contain this + * point. The first child component that does is returned. If no + * child component claims the point, the container itself is returned, + * unless the point does not exist within this container, in which + * case null is returned. + * + * When components overlap, the first component is returned. The component + * that is closest to (x, y), containing that location, is returned. + * Heavyweight components take precedence of lightweight components. + * + * This function does not ignore invisible components. If there is an invisible + * component at (x,y), it will be returned. + * + * @param x The X coordinate of the point. + * @param y The Y coordinate of the point. + * + * @return The component containing the specified point, or + * null if there is no such point. + */ + public Component getComponentAt(int x, int y) + { + return locate (x, y); + } + + /** + * Returns the mouse pointer position relative to this Container's + * top-left corner. If allowChildren is false, the mouse pointer + * must be directly over this container. If allowChildren is true, + * the mouse pointer may be over this container or any of its + * descendents. + * + * @param allowChildren true to allow descendents, false if pointer + * must be directly over Container. + * + * @return relative mouse pointer position + * + * @throws HeadlessException if in a headless environment + */ + public Point getMousePosition(boolean allowChildren) throws HeadlessException + { + return super.getMousePositionHelper(allowChildren); + } + + boolean mouseOverComponent(Component component, boolean allowChildren) + { + if (allowChildren) + return isAncestorOf(component); + else + return component == this; + } + + /** + * Returns the component located at the specified point. This is done + * by checking whether or not a child component claims to contain this + * point. The first child component that does is returned. If no + * child component claims the point, the container itself is returned, + * unless the point does not exist within this container, in which + * case null is returned. + * + * When components overlap, the first component is returned. The component + * that is closest to (x, y), containing that location, is returned. + * Heavyweight components take precedence of lightweight components. + * + * This function does not ignore invisible components. If there is an invisible + * component at (x,y), it will be returned. + * + * @param x The x position of the point to return the component at. + * @param y The y position of the point to return the component at. + * + * @return The component containing the specified point, or null + * if there is no such point. + * + * @deprecated use {@link #getComponentAt(int, int)} instead + */ + public Component locate(int x, int y) + { + synchronized (getTreeLock ()) + { + if (!contains (x, y)) + return null; + + // First find the component closest to (x,y) that is a heavyweight. + for (int i = 0; i < ncomponents; ++i) + { + Component comp = component[i]; + int x2 = x - comp.x; + int y2 = y - comp.y; + if (comp.contains (x2, y2) && !comp.isLightweight()) + return comp; + } + + // if a heavyweight component is not found, look for a lightweight + // closest to (x,y). + for (int i = 0; i < ncomponents; ++i) + { + Component comp = component[i]; + int x2 = x - comp.x; + int y2 = y - comp.y; + if (comp.contains (x2, y2) && comp.isLightweight()) + return comp; + } + + return this; + } + } + + /** + * Returns the component located at the specified point. This is done + * by checking whether or not a child component claims to contain this + * point. The first child component that does is returned. If no + * child component claims the point, the container itself is returned, + * unless the point does not exist within this container, in which + * case null is returned. + * + * The top-most child component is returned in the case where components overlap. + * This is determined by finding the component closest to (x,y) and contains + * that location. Heavyweight components take precedence of lightweight components. + * + * This function does not ignore invisible components. If there is an invisible + * component at (x,y), it will be returned. + * + * @param p The point to return the component at. + * @return The component containing the specified point, or null + * if there is no such point. + */ + public Component getComponentAt(Point p) + { + return getComponentAt (p.x, p.y); + } + + /** + * Locates the visible child component that contains the specified position. + * The top-most child component is returned in the case where there is overlap + * in the components. If the containing child component is a Container, + * this method will continue searching for the deepest nested child + * component. Components which are not visible are ignored during the search. + * + * findComponentAt differs from getComponentAt, because it recursively + * searches a Container's children. + * + * @param x - x coordinate + * @param y - y coordinate + * @return null if the component does not contain the position. + * If there is no child component at the requested point and the point is + * within the bounds of the container the container itself is returned. + */ + public Component findComponentAt(int x, int y) + { + synchronized (getTreeLock ()) + { + if (! contains(x, y)) + return null; + + for (int i = 0; i < ncomponents; ++i) + { + // Ignore invisible children... + if (!component[i].isVisible()) + continue; + + int x2 = x - component[i].x; + int y2 = y - component[i].y; + // We don't do the contains() check right away because + // findComponentAt would redundantly do it first thing. + if (component[i] instanceof Container) + { + Container k = (Container) component[i]; + Component r = k.findComponentAt(x2, y2); + if (r != null) + return r; + } + else if (component[i].contains(x2, y2)) + return component[i]; + } + + return this; + } + } + + /** + * Locates the visible child component that contains the specified position. + * The top-most child component is returned in the case where there is overlap + * in the components. If the containing child component is a Container, + * this method will continue searching for the deepest nested child + * component. Components which are not visible are ignored during the search. + * + * findComponentAt differs from getComponentAt, because it recursively + * searches a Container's children. + * + * @param p - the component's location + * @return null if the component does not contain the position. + * If there is no child component at the requested point and the point is + * within the bounds of the container the container itself is returned. + */ + public Component findComponentAt(Point p) + { + return findComponentAt(p.x, p.y); + } + + /** + * Called when this container is added to another container to inform it + * to create its peer. Peers for any child components will also be + * created. + */ + public void addNotify() + { + synchronized (getTreeLock()) + { + super.addNotify(); + addNotifyContainerChildren(); + } + } + + /** + * Called when this container is removed from its parent container to + * inform it to destroy its peer. This causes the peers of all child + * component to be destroyed as well. + */ + public void removeNotify() + { + synchronized (getTreeLock ()) + { + int ncomps = ncomponents; + Component[] comps = component; + for (int i = ncomps - 1; i >= 0; --i) + { + Component comp = comps[i]; + if (comp != null) + comp.removeNotify(); + } + super.removeNotify(); + } + } + + /** + * Tests whether or not the specified component is contained within + * this components subtree. + * + * @param comp The component to test. + * + * @return true if this container is an ancestor of the + * specified component, false otherwise. + */ + public boolean isAncestorOf(Component comp) + { + synchronized (getTreeLock ()) + { + while (true) + { + if (comp == null) + return false; + comp = comp.getParent(); + if (comp == this) + return true; + } + } + } + + /** + * Returns a string representing the state of this container for + * debugging purposes. + * + * @return A string representing the state of this container. + */ + protected String paramString() + { + if (layoutMgr == null) + return super.paramString(); + + CPStringBuilder sb = new CPStringBuilder(); + sb.append(super.paramString()); + sb.append(",layout="); + sb.append(layoutMgr.getClass().getName()); + return sb.toString(); + } + + /** + * Writes a listing of this container to the specified stream starting + * at the specified indentation point. + * + * @param out The PrintStream to write to. + * @param indent The indentation point. + */ + public void list(PrintStream out, int indent) + { + synchronized (getTreeLock ()) + { + super.list(out, indent); + for (int i = 0; i < ncomponents; ++i) + component[i].list(out, indent + 2); + } + } + + /** + * Writes a listing of this container to the specified stream starting + * at the specified indentation point. + * + * @param out The PrintWriter to write to. + * @param indent The indentation point. + */ + public void list(PrintWriter out, int indent) + { + synchronized (getTreeLock ()) + { + super.list(out, indent); + for (int i = 0; i < ncomponents; ++i) + component[i].list(out, indent + 2); + } + } + + /** + * Sets the focus traversal keys for a given traversal operation for this + * Container. + * + * @exception IllegalArgumentException If id is not one of + * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, + * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS, + * or if keystrokes contains null, or if any Object in keystrokes is not an + * AWTKeyStroke, or if any keystroke represents a KEY_TYPED event, or if any + * keystroke already maps to another focus traversal operation for this + * Container. + * + * @since 1.4 + */ + public void setFocusTraversalKeys(int id, + Set keystrokes) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + if (keystrokes == null) + { + Container parent = getParent (); + + while (parent != null) + { + if (parent.areFocusTraversalKeysSet (id)) + { + keystrokes = parent.getFocusTraversalKeys (id); + break; + } + parent = parent.getParent (); + } + + if (keystrokes == null) + keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager (). + getDefaultFocusTraversalKeys (id); + } + + Set sa; + Set sb; + Set sc; + String name; + switch (id) + { + case KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "forwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "backwardFocusTraversalKeys"; + break; + case KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + name = "upCycleFocusTraversalKeys"; + break; + case KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS: + sa = getFocusTraversalKeys + (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + sb = getFocusTraversalKeys + (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + sc = getFocusTraversalKeys + (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + name = "downCycleFocusTraversalKeys"; + break; + default: + throw new IllegalArgumentException (); + } + + int i = keystrokes.size (); + Iterator iter = keystrokes.iterator (); + + while (--i >= 0) + { + Object o = iter.next (); + if (!(o instanceof AWTKeyStroke) + || sa.contains (o) || sb.contains (o) || sc.contains (o) + || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) + throw new IllegalArgumentException (); + } + + if (focusTraversalKeys == null) + focusTraversalKeys = new Set[4]; + + keystrokes = + Collections.unmodifiableSet(new HashSet(keystrokes)); + firePropertyChange (name, focusTraversalKeys[id], keystrokes); + + focusTraversalKeys[id] = keystrokes; + } + + /** + * Returns the Set of focus traversal keys for a given traversal operation for + * this Container. + * + * @exception IllegalArgumentException If id is not one of + * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, + * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS. + * + * @since 1.4 + */ + public Set getFocusTraversalKeys (int id) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + Set s = null; + + if (focusTraversalKeys != null) + s = focusTraversalKeys[id]; + + if (s == null && parent != null) + s = parent.getFocusTraversalKeys (id); + + return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager() + .getDefaultFocusTraversalKeys(id)) : s; + } + + /** + * Returns whether the Set of focus traversal keys for the given focus + * traversal operation has been explicitly defined for this Container. + * If this method returns false, this Container is inheriting the Set from + * an ancestor, or from the current KeyboardFocusManager. + * + * @exception IllegalArgumentException If id is not one of + * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, + * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, + * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS. + * + * @since 1.4 + */ + public boolean areFocusTraversalKeysSet (int id) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + return focusTraversalKeys != null && focusTraversalKeys[id] != null; + } + + /** + * Check whether the given Container is the focus cycle root of this + * Container's focus traversal cycle. If this Container is a focus + * cycle root itself, then it will be in two different focus cycles + * -- it's own, and that of its ancestor focus cycle root's. In + * that case, if c is either of those containers, this + * method will return true. + * + * @param c the candidate Container + * + * @return true if c is the focus cycle root of the focus traversal + * cycle to which this Container belongs, false otherwise + * + * @since 1.4 + */ + public boolean isFocusCycleRoot (Container c) + { + if (this == c + && isFocusCycleRoot ()) + return true; + + Container ancestor = getFocusCycleRootAncestor (); + + if (c == ancestor) + return true; + + return false; + } + + /** + * If this Container is a focus cycle root, set the focus traversal + * policy that determines the focus traversal order for its + * children. If non-null, this policy will be inherited by all + * inferior focus cycle roots. If policy is null, this + * Container will inherit its policy from the closest ancestor focus + * cycle root that's had its policy set. + * + * @param policy the new focus traversal policy for this Container or null + * + * @since 1.4 + */ + public void setFocusTraversalPolicy (FocusTraversalPolicy policy) + { + focusTraversalPolicy = policy; + } + + /** + * Return the focus traversal policy that determines the focus + * traversal order for this Container's children. This method + * returns null if this Container is not a focus cycle root. If the + * focus traversal policy has not been set explicitly, then this + * method will return an ancestor focus cycle root's policy instead. + * + * @return this Container's focus traversal policy or null + * + * @since 1.4 + */ + public FocusTraversalPolicy getFocusTraversalPolicy () + { + if (!isFocusCycleRoot ()) + return null; + + if (focusTraversalPolicy == null) + { + Container ancestor = getFocusCycleRootAncestor (); + + if (ancestor != this && ancestor != null) + return ancestor.getFocusTraversalPolicy (); + else + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + return manager.getDefaultFocusTraversalPolicy (); + } + } + else + return focusTraversalPolicy; + } + + /** + * Check whether this Container's focus traversal policy has been + * explicitly set. If it has not, then this Container will inherit + * its focus traversal policy from one of its ancestor focus cycle + * roots. + * + * @return true if focus traversal policy is set, false otherwise + */ + public boolean isFocusTraversalPolicySet () + { + return focusTraversalPolicy == null; + } + + /** + * Set whether or not this Container is the root of a focus + * traversal cycle. This Container's focus traversal policy + * determines the order of focus traversal. Some policies prevent + * the focus from being transferred between two traversal cycles + * until an up or down traversal operation is performed. In that + * case, normal traversal (not up or down) is limited to this + * Container and all of this Container's descendents that are not + * descendents of inferior focus cycle roots. In the default case + * however, ContainerOrderFocusTraversalPolicy is in effect, and it + * supports implicit down-cycle traversal operations. + * + * @param focusCycleRoot true if this is a focus cycle root, false otherwise + * + * @since 1.4 + */ + public void setFocusCycleRoot (boolean focusCycleRoot) + { + this.focusCycleRoot = focusCycleRoot; + } + + /** + * Set to true if this container provides a focus traversal + * policy, false when the root container's focus + * traversal policy should be used. + * + * @return true if this container provides a focus traversal + * policy, false when the root container's focus + * traversal policy should be used + * + * @see #setFocusTraversalPolicyProvider(boolean) + * + * @since 1.5 + */ + public final boolean isFocusTraversalPolicyProvider() + { + return focusTraversalPolicyProvider; + } + + /** + * Set to true if this container provides a focus traversal + * policy, false when the root container's focus + * traversal policy should be used. + * + * @param b true if this container provides a focus traversal + * policy, false when the root container's focus + * traversal policy should be used + * + * @see #isFocusTraversalPolicyProvider() + * + * @since 1.5 + */ + public final void setFocusTraversalPolicyProvider(boolean b) + { + focusTraversalPolicyProvider = b; + } + + /** + * Check whether this Container is a focus cycle root. + * + * @return true if this is a focus cycle root, false otherwise + * + * @since 1.4 + */ + public boolean isFocusCycleRoot () + { + return focusCycleRoot; + } + + /** + * Transfer focus down one focus traversal cycle. If this Container + * is a focus cycle root, then its default component becomes the + * focus owner, and this Container becomes the current focus cycle + * root. No traversal will occur if this Container is not a focus + * cycle root. + * + * @since 1.4 + */ + public void transferFocusDownCycle () + { + if (isFocusCycleRoot()) + { + KeyboardFocusManager fm = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + fm.setGlobalCurrentFocusCycleRoot(this); + FocusTraversalPolicy policy = getFocusTraversalPolicy(); + Component defaultComponent = policy.getDefaultComponent(this); + if (defaultComponent != null) + defaultComponent.requestFocus(); + } + } + + /** + * Sets the ComponentOrientation property of this container and all components + * contained within it. + * + * @exception NullPointerException If orientation is null + * + * @since 1.4 + */ + public void applyComponentOrientation (ComponentOrientation orientation) + { + if (orientation == null) + throw new NullPointerException(); + + setComponentOrientation(orientation); + for (int i = 0; i < ncomponents; i++) + { + if (component[i] instanceof Container) + ((Container) component[i]).applyComponentOrientation(orientation); + else + component[i].setComponentOrientation(orientation); + } + } + + public void addPropertyChangeListener (PropertyChangeListener listener) + { + // TODO: Why is this overridden? + super.addPropertyChangeListener(listener); + } + + public void addPropertyChangeListener (String propertyName, + PropertyChangeListener listener) + { + // TODO: Why is this overridden? + super.addPropertyChangeListener(propertyName, listener); + } + + + /** + * Sets the Z ordering for the component comp to + * index. Components with lower Z order paint above components + * with higher Z order. + * + * @param comp the component for which to change the Z ordering + * @param index the index to set + * + * @throws NullPointerException if comp == null + * @throws IllegalArgumentException if comp is an ancestor of this container + * @throws IllegalArgumentException if index is not in + * [0, getComponentCount()] for moving between + * containers or [0, getComponentCount() - 1] for moving + * inside this container + * @throws IllegalArgumentException if comp == this + * @throws IllegalArgumentException if comp is a + * Window + * + * @see #getComponentZOrder(Component) + * + * @since 1.5 + */ + public final void setComponentZOrder(Component comp, int index) + { + if (comp == null) + throw new NullPointerException("comp must not be null"); + if (comp instanceof Container && ((Container) comp).isAncestorOf(this)) + throw new IllegalArgumentException("comp must not be an ancestor of " + + "this"); + if (comp instanceof Window) + throw new IllegalArgumentException("comp must not be a Window"); + + if (comp == this) + throw new IllegalArgumentException("cannot add component to itself"); + + synchronized (getTreeLock()) + { + // FIXME: Implement reparenting. + if ( comp.getParent() != this) + throw new AssertionError("Reparenting is not implemented yet"); + else + { + // Find current component index. + int currentIndex = getComponentZOrder(comp); + if (currentIndex < index) + { + System.arraycopy(component, currentIndex + 1, component, + currentIndex, index - currentIndex); + } + else + { + System.arraycopy(component, index, component, index + 1, + currentIndex - index); + } + component[index] = comp; + } + } + } + + /** + * Returns the Z ordering index of comp. If comp + * is not a child component of this Container, this returns -1. + * + * @param comp the component for which to query the Z ordering + * + * @return the Z ordering index of comp or -1 if + * comp is not a child of this Container + * + * @see #setComponentZOrder(Component, int) + * + * @since 1.5 + */ + public final int getComponentZOrder(Component comp) + { + synchronized (getTreeLock()) + { + int index = -1; + if (component != null) + { + for (int i = 0; i < ncomponents; i++) + { + if (component[i] == comp) + { + index = i; + break; + } + } + } + return index; + } + } + + // Hidden helper methods. + + /** + * Perform a graphics operation on the children of this container. + * For each applicable child, the visitChild() method will be called + * to perform the graphics operation. + * + * @param gfx The graphics object that will be used to derive new + * graphics objects for the children. + * + * @param visitor Object encapsulating the graphics operation that + * should be performed. + * + * @param lightweightOnly If true, only lightweight components will + * be visited. + */ + private void visitChildren(Graphics gfx, GfxVisitor visitor, + boolean lightweightOnly) + { + synchronized (getTreeLock()) + { + for (int i = ncomponents - 1; i >= 0; --i) + { + Component comp = component[i]; + boolean applicable = comp.isVisible() + && (comp.isLightweight() || ! lightweightOnly); + + if (applicable) + visitChild(gfx, visitor, comp); + } + } + } + + /** + * Perform a graphics operation on a child. A translated and clipped + * graphics object will be created, and the visit() method of the + * visitor will be called to perform the operation. + * + * @param gfx The graphics object that will be used to derive new + * graphics objects for the child. + * + * @param visitor Object encapsulating the graphics operation that + * should be performed. + * + * @param comp The child component that should be visited. + */ + private void visitChild(Graphics gfx, GfxVisitor visitor, + Component comp) + { + Rectangle bounds = comp.getBounds(); + + if(!gfx.hitClip(bounds.x,bounds.y, bounds.width, bounds.height)) + return; + Graphics g2 = gfx.create(bounds.x, bounds.y, bounds.width, + bounds.height); + try + { + g2.setFont(comp.getFont()); + visitor.visit(comp, g2); + } + finally + { + g2.dispose(); + } + } + + /** + * Overridden to dispatch events to lightweight descendents. + * + * @param e the event to dispatch. + */ + void dispatchEventImpl(AWTEvent e) + { + LightweightDispatcher dispatcher = LightweightDispatcher.getInstance(); + if (! isLightweight() && dispatcher.dispatchEvent(e)) + { + // Some lightweight descendent got this event dispatched. Consume + // it and let the peer handle it. + e.consume(); + ComponentPeer p = peer; + if (p != null) + p.handleEvent(e); + } + else + { + super.dispatchEventImpl(e); + } + } + + /** + * This is called by the lightweight dispatcher to avoid recursivly + * calling into the lightweight dispatcher. + * + * @param e the event to dispatch + * + * @see LightweightDispatcher#redispatch(MouseEvent, Component, int) + */ + void dispatchNoLightweight(AWTEvent e) + { + super.dispatchEventImpl(e); + } + + /** + * Tests if this container has an interest in the given event id. + * + * @param eventId The event id to check. + * + * @return true if a listener for the event id exists or + * if the eventMask is set for the event id. + * + * @see java.awt.Component#eventTypeEnabled(int) + */ + boolean eventTypeEnabled(int eventId) + { + if(eventId <= ContainerEvent.CONTAINER_LAST + && eventId >= ContainerEvent.CONTAINER_FIRST) + return containerListener != null + || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0; + else + return super.eventTypeEnabled(eventId); + } + + // This is used to implement Component.transferFocus. + Component findNextFocusComponent(Component child) + { + synchronized (getTreeLock ()) + { + int start, end; + if (child != null) + { + for (start = 0; start < ncomponents; ++start) + { + if (component[start] == child) + break; + } + end = start; + // This special case lets us be sure to terminate. + if (end == 0) + end = ncomponents; + ++start; + } + else + { + start = 0; + end = ncomponents; + } + + for (int j = start; j != end; ++j) + { + if (j >= ncomponents) + { + // The JCL says that we should wrap here. However, that + // seems wrong. To me it seems that focus order should be + // global within in given window. So instead if we reach + // the end we try to look in our parent, if we have one. + if (parent != null) + return parent.findNextFocusComponent(this); + j -= ncomponents; + } + if (component[j] instanceof Container) + { + Component c = component[j]; + c = c.findNextFocusComponent(null); + if (c != null) + return c; + } + else if (component[j].isFocusTraversable()) + return component[j]; + } + + return null; + } + } + + /** + * Fires hierarchy events to the children of this container and this + * container itself. This overrides {@link Component#fireHierarchyEvent} + * in order to forward this event to all children. + */ + void fireHierarchyEvent(int id, Component changed, Container parent, + long flags) + { + // Only propagate event if there is actually a listener waiting for it. + if ((id == HierarchyEvent.HIERARCHY_CHANGED && numHierarchyListeners > 0) + || ((id == HierarchyEvent.ANCESTOR_MOVED + || id == HierarchyEvent.ANCESTOR_RESIZED) + && numHierarchyBoundsListeners > 0)) + { + for (int i = 0; i < ncomponents; i++) + component[i].fireHierarchyEvent(id, changed, parent, flags); + super.fireHierarchyEvent(id, changed, parent, flags); + } + } + + /** + * Adjusts the number of hierarchy listeners of this container and all of + * its parents. This is called by the add/remove listener methods and + * structure changing methods in Container. + * + * @param type the type, either {@link AWTEvent#HIERARCHY_BOUNDS_EVENT_MASK} + * or {@link AWTEvent#HIERARCHY_EVENT_MASK} + * @param delta the number of listeners added or removed + */ + void updateHierarchyListenerCount(long type, int delta) + { + if (type == AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) + numHierarchyBoundsListeners += delta; + else if (type == AWTEvent.HIERARCHY_EVENT_MASK) + numHierarchyListeners += delta; + else + assert false : "Should not reach here"; + + if (parent != null) + parent.updateHierarchyListenerCount(type, delta); + } + + /** + * Notifies interested listeners about resizing or moving the container. + * This performs the super behaviour (sending component events) and + * additionally notifies any hierarchy bounds listeners on child components. + * + * @param resized true if the component has been resized, false otherwise + * @param moved true if the component has been moved, false otherwise + */ + void notifyReshape(boolean resized, boolean moved) + { + // Notify component listeners. + super.notifyReshape(resized, moved); + + if (ncomponents > 0) + { + // Notify hierarchy bounds listeners. + if (resized) + { + for (int i = 0; i < getComponentCount(); i++) + { + Component child = getComponent(i); + child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_RESIZED, + this, parent, 0); + } + } + if (moved) + { + for (int i = 0; i < getComponentCount(); i++) + { + Component child = getComponent(i); + child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_MOVED, + this, parent, 0); + } + } + } + } + + private void addNotifyContainerChildren() + { + synchronized (getTreeLock ()) + { + for (int i = ncomponents; --i >= 0; ) + { + component[i].addNotify(); + } + } + } + + /** + * Deserialize this Container: + *

    + *
  1. Read from the stream the default serializable fields.
  2. + *
  3. Read a list of serializable ContainerListeners as optional + * data. If the list is null, no listeners will be registered.
  4. + *
  5. Read this Container's FocusTraversalPolicy as optional data. + * If this is null, then this Container will use a + * DefaultFocusTraversalPolicy.
  6. + *
+ * + * @param s the stream to read from + * @throws ClassNotFoundException if deserialization fails + * @throws IOException if the stream fails + */ + private void readObject (ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject (); + String key = (String) s.readObject (); + while (key != null) + { + Object object = s.readObject (); + if ("containerL".equals (key)) + addContainerListener((ContainerListener) object); + // FIXME: under what key is the focus traversal policy stored? + else if ("focusTraversalPolicy".equals (key)) + setFocusTraversalPolicy ((FocusTraversalPolicy) object); + + key = (String) s.readObject(); + } + } + + /** + * Serialize this Container: + *
    + *
  1. Write to the stream the default serializable fields.
  2. + *
  3. Write the list of serializable ContainerListeners as optional + * data.
  4. + *
  5. Write this Container's FocusTraversalPolicy as optional data.
  6. + *
+ * + * @param s the stream to write to + * @throws IOException if the stream fails + */ + private void writeObject (ObjectOutputStream s) throws IOException + { + s.defaultWriteObject (); + AWTEventMulticaster.save (s, "containerL", containerListener); + if (focusTraversalPolicy instanceof Serializable) + s.writeObject (focusTraversalPolicy); + else + s.writeObject (null); + } + + // Nested classes. + + /* The following classes are used in concert with the + visitChildren() method to implement all the graphics operations + that requires traversal of the containment hierarchy. */ + + abstract static class GfxVisitor + { + public abstract void visit(Component c, Graphics gfx); + } + + static class GfxPaintVisitor extends GfxVisitor + { + public static final GfxVisitor INSTANCE = new GfxPaintVisitor(); + + public void visit(Component c, Graphics gfx) + { + c.paint(gfx); + } + } + + static class GfxPrintVisitor extends GfxVisitor + { + public static final GfxVisitor INSTANCE = new GfxPrintVisitor(); + + public void visit(Component c, Graphics gfx) + { + c.print(gfx); + } + } + + static class GfxPaintAllVisitor extends GfxVisitor + { + public static final GfxVisitor INSTANCE = new GfxPaintAllVisitor(); + + public void visit(Component c, Graphics gfx) + { + c.paintAll(gfx); + } + } + + static class GfxPrintAllVisitor extends GfxVisitor + { + public static final GfxVisitor INSTANCE = new GfxPrintAllVisitor(); + + public void visit(Component c, Graphics gfx) + { + c.printAll(gfx); + } + } + + /** + * This class provides accessibility support for subclasses of container. + * + * @author Eric Blake (ebb9@email.byu.edu) + * + * @since 1.3 + */ + protected class AccessibleAWTContainer extends AccessibleAWTComponent + { + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 5081320404842566097L; + + /** + * The handler to fire PropertyChange when children are added or removed. + * + * @serial the handler for property changes + */ + protected ContainerListener accessibleContainerHandler + = new AccessibleContainerHandler(); + + /** + * The default constructor. + */ + protected AccessibleAWTContainer() + { + Container.this.addContainerListener(accessibleContainerHandler); + } + + /** + * Return the number of accessible children of the containing accessible + * object (at most the total number of its children). + * + * @return the number of accessible children + */ + public int getAccessibleChildrenCount() + { + synchronized (getTreeLock ()) + { + int count = 0; + int i = component == null ? 0 : component.length; + while (--i >= 0) + if (component[i] instanceof Accessible) + count++; + return count; + } + } + + /** + * Return the nth accessible child of the containing accessible object. + * + * @param i the child to grab, zero-based + * @return the accessible child, or null + */ + public Accessible getAccessibleChild(int i) + { + synchronized (getTreeLock ()) + { + if (component == null) + return null; + int index = -1; + while (i >= 0 && ++index < component.length) + if (component[index] instanceof Accessible) + i--; + if (i < 0) + return (Accessible) component[index]; + return null; + } + } + + /** + * Return the accessible child located at point (in the parent's + * coordinates), if one exists. + * + * @param p the point to look at + * + * @return an accessible object at that point, or null + * + * @throws NullPointerException if p is null + */ + public Accessible getAccessibleAt(Point p) + { + Component c = getComponentAt(p.x, p.y); + return c != Container.this && c instanceof Accessible ? (Accessible) c + : null; + } + + /** + * This class fires a PropertyChange listener, if registered, + * when children are added or removed from the enclosing accessible object. + * + * @author Eric Blake (ebb9@email.byu.edu) + * + * @since 1.3 + */ + protected class AccessibleContainerHandler implements ContainerListener + { + /** + * Default constructor. + */ + protected AccessibleContainerHandler() + { + // Nothing to do here. + } + + /** + * Fired when a component is added; forwards to the PropertyChange + * listener. + * + * @param e the container event for adding + */ + public void componentAdded(ContainerEvent e) + { + AccessibleAWTContainer.this.firePropertyChange + (ACCESSIBLE_CHILD_PROPERTY, null, e.getChild()); + } + + /** + * Fired when a component is removed; forwards to the PropertyChange + * listener. + * + * @param e the container event for removing + */ + public void componentRemoved(ContainerEvent e) + { + AccessibleAWTContainer.this.firePropertyChange + (ACCESSIBLE_CHILD_PROPERTY, e.getChild(), null); + } + } // class AccessibleContainerHandler + } // class AccessibleAWTContainer +} // class Container diff --git a/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java b/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java new file mode 100644 index 000000000..3fa437db7 --- /dev/null +++ b/libjava/classpath/java/awt/ContainerOrderFocusTraversalPolicy.java @@ -0,0 +1,482 @@ +/* ContainerOrderFocusTraversalPolicy.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * ContainerOrderFocusTraversalPolicy defines a focus traversal order + * based on the order in which Components were packed in a Container. + * This policy performs a pre-order traversal of the Component + * hierarchy starting from a given focus cycle root. Portions of the + * hierarchy that are not visible and displayable are skipped. + * + * By default, this policy transfers focus down-cycle implicitly. + * That is, if a forward traversal is requested on a focus cycle root + * and the focus cycle root has focusable children, the focus will + * automatically be transfered down to the lower focus cycle. + * + * The default implementation of accept accepts only Components that + * are visible, displayable, enabled and focusable. Derived classes + * can override these acceptance criteria by overriding accept. + * + * @author Michael Koch + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + * @since 1.4 + */ +public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy + implements Serializable +{ + /** + * Compatible to JDK 1.4+ + */ + static final long serialVersionUID = 486933713763926351L; + + /** + * True if implicit down cycling is enabled. + */ + private boolean implicitDownCycleTraversal = true; + + /** + * Creates the ContainerOrderFocusTraversalPolicy object. + */ + public ContainerOrderFocusTraversalPolicy () + { + // Nothing to do here + } + + /** + * Returns the Component that should receive the focus after current. + * root must be a focus cycle root of current. + * + * @param root a focus cycle root of current + * @param current a (possibly indirect) child of root, or root itself + * + * @return the next Component in the focus traversal order for root, + * or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is not a focus cycle + * root of current, or if either root or current is null. + */ + public Component getComponentAfter (Container root, Component current) + { + if (root == null) + throw new IllegalArgumentException ("focus cycle root is null"); + if (current == null) + throw new IllegalArgumentException ("current component is null"); + + if (!root.isFocusCycleRoot ()) + throw new IllegalArgumentException ("root is not a focus cycle root"); + + Container ancestor = current.getFocusCycleRootAncestor (); + Container prevAncestor = ancestor; + while (ancestor != root) + { + ancestor = current.getFocusCycleRootAncestor (); + if (ancestor == prevAncestor) + { + // We've reached the top focus cycle root ancestor. Check + // if it is root. + if (ancestor == null) + ancestor = root; + else if (ancestor != root) + throw new IllegalArgumentException ("the given container is not" + + " a focus cycle root of the" + + " current component"); + else + break; + } + prevAncestor = ancestor; + } + + // FIXME: is this the right thing to do here? It moves the context + // for traversal up one focus traversal cycle. We'll need a test + // for this. + if ((Component) root == current) + root = current.getFocusCycleRootAncestor (); + + // Check if we've reached the top of the component hierarchy. If + // so then we want to loop around to the first component in the + // focus traversal cycle. + if (current instanceof Window) + return getFirstComponent ((Container) current); + + Container parent = current.getParent (); + synchronized (parent.getTreeLock ()) + { + Component[] components = parent.getComponents (); + int componentIndex = 0; + int numComponents = parent.getComponentCount (); + + // Find component's index. + for (int i = 0; i < numComponents; i++) + { + if (components[i].equals(current)) + componentIndex = i; + } + + // Search forward for the next acceptable component. + // Search through all components at least one time + // i.e. start at componentIndex + 1 --> nComponents -1 --> 0 ---> componentIndex + int i = componentIndex + 1; + int end = numComponents - 1; + Component next = getNextAvailableComponent(components, i, end); + if (next != null) + return next; + + // Now check remainder of components from 0 to componentIndex + i = 0; + end = componentIndex; + next = getNextAvailableComponent(components, i, end); + if (next != null) + return next; + + // No focusable components after current in its Container. So go + // to the next Component after current's Container (parent). + Component result = getComponentAfter (root, parent); + return result; + } + } + + /** + * Gets the next available component in the array between the given range. + * + * @param components - the array of components. + * @param start - where to start + * @param end - where to end + * @return next component if found + */ + private Component getNextAvailableComponent(Component[] components, int start, int end) + { + while (start <= end) + { + Component c = components[start]; + + if (c.visible && c.isDisplayable() && c.enabled && c.focusable) + return c; + + if (c instanceof Container) + { + Component result = getFirstComponent((Container) c); + + if (result != null && implicitDownCycleTraversal && result.visible + && result.isDisplayable() && result.enabled && result.focusable) + return result; + } + start++; + } + + return null; + } + + /** + * Gets the previous available component in the array between the given range. + * + * @param components - the array of components. + * @param start - where to start + * @param end - where to end + * @return previous component if found + */ + Component getPrevAvailableComponent(Component[] components, int start, int end) + { + while (start >= end) + { + Component c = components[start]; + if (c.visible && c.isDisplayable() && c.enabled && c.focusable) + return c; + + if (c instanceof Container) + { + Component result = getLastComponent((Container) c); + + if (result != null + && (result.visible && result.isDisplayable() && result.enabled && result.focusable)) + return result; + } + start--; + } + return null; + } + + /** + * Returns the Component that should receive the focus before + * current. root must be a focus cycle root of + * current. + * + * @param root a focus cycle root of current + * @param current a (possibly indirect) child of root, or root itself + * @return the previous Component in the focus traversal order for root, or + * null if no acceptable Component exists. + * @exception IllegalArgumentException If root is not a focus cycle root of + * current, or if either root or current is null. + */ + public Component getComponentBefore (Container root, Component current) + { + if (root == null) + throw new IllegalArgumentException ("focus cycle root is null"); + if (current == null) + throw new IllegalArgumentException ("current component is null"); + + if (!root.isFocusCycleRoot ()) + throw new IllegalArgumentException ("root is not a focus cycle root"); + + Container ancestor = current.getFocusCycleRootAncestor (); + Container prevAncestor = ancestor; + while (ancestor != root) + { + ancestor = current.getFocusCycleRootAncestor (); + if (ancestor == prevAncestor) + { + // We've reached the top focus cycle root ancestor. Check + // if it is root. + if (ancestor == null) + ancestor = root; + else if (ancestor != root) + throw new IllegalArgumentException ("the given container is not" + + " a focus cycle root of the" + + " current component"); + else + break; + } + prevAncestor = ancestor; + } + + // FIXME: is this the right thing to do here? It moves the context + // for traversal up one focus traversal cycle. We'll need a test + // for this. + if ((Component) root == current) + root = current.getFocusCycleRootAncestor (); + + // Check if we've reached the top of the component hierarchy. If + // so then we want to loop around to the last component in the + // focus traversal cycle. + if (current instanceof Window) + return getLastComponent ((Container) current); + + Container parent = current.getParent (); + + synchronized (parent.getTreeLock ()) + { + Component[] components = parent.getComponents (); + int componentIndex = 0; + int numComponents = parent.getComponentCount (); + + // Find component's index. + for (int i = 0; i < numComponents; i++) + { + if (components[i] == current) + componentIndex = i; + } + + // Search through all components at least one time + // i.e. start at componentIndex - 1 --> 0 --> numComponents -1 ---> componentIndex + int i = componentIndex - 1; + int end = 0; + Component prev = getPrevAvailableComponent(components, i, end); + if (prev != null) + return prev; + + // Now check remainder of components + i = numComponents -1; + end = componentIndex; + prev = getPrevAvailableComponent(components, i, end); + if (prev != null) + return prev; + + // No focusable components before current in its Container. So go + // to the previous Component before current's Container (parent). + Component result = getComponentBefore (root, parent); + + return result; + } + } + + /** + * Returns the first Component of root that should receive the focus. + * + * @param root a focus cycle root + * + * @return the first Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is null. + */ + public Component getFirstComponent(Container root) + { + if (root == null) + throw new IllegalArgumentException (); + + if (!root.isVisible () + || !root.isDisplayable ()) + return null; + + if (accept(root)) + return root; + + int ncomponents = root.getComponentCount(); + for (int i = 0; i < ncomponents; i++) + { + Component component = root.getComponent(i); + if (component instanceof Container + && !((Container) component).isFocusCycleRoot()) + { + Component first = null; + Container cont = (Container) component; + if (cont.isFocusTraversalPolicyProvider()) + { + FocusTraversalPolicy childPol = cont.getFocusTraversalPolicy(); + first = childPol.getFirstComponent(cont); + } + else + first = getFirstComponent(cont); + if (first != null) + return first; + } + else if (accept(component)) + return component; + } + + return null; + } + + /** + * Returns the last Component of root that should receive the focus. + * + * @param root a focus cycle root + * + * @return the last Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is null. + */ + public Component getLastComponent (Container root) + { + if (root == null) + throw new IllegalArgumentException (); + + if (!root.isVisible () + || !root.isDisplayable ()) + return null; + + if (root.visible && root.isDisplayable() && root.enabled + && root.focusable) + return root; + + Component[] componentArray = root.getComponents (); + + for (int i = componentArray.length - 1; i >= 0; i--) + { + Component component = componentArray [i]; + + if (component.visible && component.isDisplayable() && component.enabled + && component.focusable) + return component; + + if (component instanceof Container) + { + Component result = getLastComponent ((Container) component); + + if (result != null && + result.visible && result.isDisplayable() && result.enabled + && result.focusable) + return result; + } + } + + return null; + } + + /** + * Returns the default Component of root that should receive the focus. + * + * @param root a focus cycle root + * + * @return the default Component in the focus traversal order for + * root, or null if no acceptable Component exists. + * + * @exception IllegalArgumentException If root is null. + */ + public Component getDefaultComponent (Container root) + { + return getFirstComponent (root); + } + + /** + * Set whether or not implicit down cycling is enabled. If it is, + * then initiating a forward focus traversal operation onto a focus + * cycle root, the focus will be implicitly transferred into the + * root container's focus cycle. + * + * @param value the setting for implicit down cycling + */ + public void setImplicitDownCycleTraversal (boolean value) + { + implicitDownCycleTraversal = value; + } + + /** + * Check whether or not implicit down cycling is enabled. If it is, + * then initiating a forward focus traversal operation onto a focus + * cycle root, the focus will be implicitly transferred into the + * root container's focus cycle. + * + * @return true if the focus will be transferred down-cycle + * implicitly + */ + public boolean getImplicitDownCycleTraversal () + { + return implicitDownCycleTraversal; + } + + /** + * Check whether the given Component is an acceptable target for the + * keyboard input focus. + * + * @param current the Component to check + * + * @return true if current is acceptable, false otherwise + */ + protected boolean accept (Component current) + { + return (current.visible + && current.isDisplayable () + && current.enabled + && current.focusable); + } +} diff --git a/libjava/classpath/java/awt/Cursor.java b/libjava/classpath/java/awt/Cursor.java new file mode 100644 index 000000000..69d0e87f5 --- /dev/null +++ b/libjava/classpath/java/awt/Cursor.java @@ -0,0 +1,239 @@ +/* Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class represents various predefined cursor types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Cursor implements java.io.Serializable +{ + static final long serialVersionUID = 8028237497568985504L; + + /** + * Constant for the system default cursor type + */ + public static final int DEFAULT_CURSOR = 0; + + /** + * Constant for a cross-hair cursor. + */ + public static final int CROSSHAIR_CURSOR = 1; + + /** + * Constant for a cursor over a text field. + */ + public static final int TEXT_CURSOR = 2; + + /** + * Constant for a cursor to display while waiting for an action to complete. + */ + public static final int WAIT_CURSOR = 3; + + /** + * Cursor used over SW corner of window decorations. + */ + public static final int SW_RESIZE_CURSOR = 4; + + /** + * Cursor used over SE corner of window decorations. + */ + public static final int SE_RESIZE_CURSOR = 5; + + /** + * Cursor used over NW corner of window decorations. + */ + public static final int NW_RESIZE_CURSOR = 6; + + /** + * Cursor used over NE corner of window decorations. + */ + public static final int NE_RESIZE_CURSOR = 7; + + /** + * Cursor used over N edge of window decorations. + */ + public static final int N_RESIZE_CURSOR = 8; + + /** + * Cursor used over S edge of window decorations. + */ + public static final int S_RESIZE_CURSOR = 9; + + /** + * Cursor used over W edge of window decorations. + */ + public static final int W_RESIZE_CURSOR = 10; + + /** + * Cursor used over E edge of window decorations. + */ + public static final int E_RESIZE_CURSOR = 11; + + /** + * Constant for a hand cursor. + */ + public static final int HAND_CURSOR = 12; + + /** + * Constant for a cursor used during window move operations. + */ + public static final int MOVE_CURSOR = 13; + + private static String[] NAMES = { "Default Cursor", "Crosshair Cursor", + "Text Cursor", "Wait Cursor", + "Southwest Resize Cursor", + "Southeast Resize Cursor", + "Northwest Resize Cursor", + "Northeast Resize Cursor", + "North Resize Cursor", "South Resize Cursor", + "West Resize Cursor", "East Resize Cursor", + "Hand Cursor", "Move Cursor" }; + + public static final int CUSTOM_CURSOR = 0xFFFFFFFF; + + private static final int PREDEFINED_COUNT = 14; + + protected static Cursor[] predefined = new Cursor[PREDEFINED_COUNT]; + protected String name; + + /** + * @serial The numeric id of this cursor. + */ + int type; + + /** + * Initializes a new instance of Cursor with the specified + * type. + * + * @param type The cursor type. + * + * @exception IllegalArgumentException If the specified cursor type is invalid + */ + public Cursor(int type) + { + if (type < 0 || type >= PREDEFINED_COUNT) + throw new IllegalArgumentException ("invalid cursor " + type); + + this.type = type; + + name = NAMES[type]; + + // FIXME: lookup? + } + + /** This constructor is used internally only. + * Application code should call Toolkit.createCustomCursor(). + */ + protected Cursor(String name) + { + this.name = name; + this.type = CUSTOM_CURSOR; + } + + /** + * Returns an instance of Cursor for one of the specified + * predetermined types. + * + * @param type The type contant from this class. + * + * @return The requested predefined cursor. + * + * @exception IllegalArgumentException If the constant is not one of the + * predefined cursor type constants from this class. + */ + public static Cursor getPredefinedCursor(int type) + { + if (type < 0 || type >= PREDEFINED_COUNT) + throw new IllegalArgumentException ("invalid cursor " + type); + if (predefined[type] == null) + predefined[type] = new Cursor(type); + return predefined[type]; + } + + /** + * Retrieves the system specific custom Cursor named Cursor names are, + * for example: "Invalid.16x16". + * + * @exception AWTException + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public static Cursor getSystemCustomCursor(String name) + throws AWTException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); + + // FIXME + return null; + } + + /** + * Returns an instance of the system default cursor type. + * + * @return The system default cursor. + */ + public static Cursor getDefaultCursor() + { + return getPredefinedCursor(DEFAULT_CURSOR); + } + + /** + * Returns the numeric type identifier for this cursor. + * + * @return The cursor id. + */ + public int getType() + { + return type; + } + + public String getName() + { + return name; + } + + public String toString() + { + return (this.getClass() + + "[type=" + getType() + + ",name=" + getName() + "]"); + } +} diff --git a/libjava/classpath/java/awt/DefaultFocusTraversalPolicy.java b/libjava/classpath/java/awt/DefaultFocusTraversalPolicy.java new file mode 100644 index 000000000..3f017c5a5 --- /dev/null +++ b/libjava/classpath/java/awt/DefaultFocusTraversalPolicy.java @@ -0,0 +1,111 @@ +/* DefaultFocusTraversalPolicy.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * DefaultFocusTraversalPolicy is the default focus traversal policy + * used by Containers. + * + * This policy sharpens ContainerOrderFocusTraversalPolicy's + * acceptance criteria, to reject those Components that have + * unfocusable peers. Despite this extra strictness, this policy will + * always accept a Component that has explicitly been set focusable by + * any means. + * + * This AWT implementation assumes that the peers of the following + * Components are not focusable: Canvas, Panel, Label, ScrollPane, + * Scrollbar, Window, and any lightweight Component. + * + * A Component's focusability is independent of the focusability of + * its peer. + * + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + * @since 1.4 + */ +public class DefaultFocusTraversalPolicy + extends ContainerOrderFocusTraversalPolicy +{ + private static final long serialVersionUID = 8876966522510157497L; + + /** + * Construct a default focus traversal policy. + */ + public DefaultFocusTraversalPolicy () + { + } + + /** + * Check whether a given Component would be acceptable as a focus + * owner. The Component must be displayable, visible and enabled to + * be acceptable. If the Component's focus traversability has been + * overridden, by overriding Component.isFocusTraversable or + * Component.isFocusable, or by calling Component.setFocusable, then + * the Component will be accepted if it is focusable. If the + * Component uses the default focus traversable behaviour, then + * comp will always be rejected if it is a Canvas, + * Panel, Label, ScrollPane, Scrollbar, Window or lightweight + * Component. + * + * @param comp the Component to check + * + * @return true if the Component is an acceptable target for + * keyboard input focus, false otherwise + */ + protected boolean accept (Component comp) + { + if (comp.visible + && comp.isDisplayable () + && comp.enabled) + { + if (comp.isFocusTraversableOverridden != 0 + && (comp.isFocusTraversable () || comp.isFocusable())) + return true; + + if (!(comp instanceof Canvas + || comp instanceof Panel + || comp instanceof Label + || comp instanceof ScrollPane + || comp instanceof Scrollbar + || comp instanceof Window + || comp.isLightweight ())) + return true; + } + return false; + } +} diff --git a/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java b/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java new file mode 100644 index 000000000..f62e6e53e --- /dev/null +++ b/libjava/classpath/java/awt/DefaultKeyboardFocusManager.java @@ -0,0 +1,566 @@ +/* DefaultKeyboardFocusManager.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +// FIXME: finish documentation +public class DefaultKeyboardFocusManager extends KeyboardFocusManager +{ + /** + * This class models a request to delay the dispatch of events that + * arrive after a certain time, until a certain component becomes + * the focus owner. + */ + private class EventDelayRequest implements Comparable + { + /** A {@link java.util.List} of {@link java.awt.event.KeyEvent}s + that are being delayed, pending this request's {@link + Component} receiving the keyboard focus. */ + private LinkedList enqueuedKeyEvents = new LinkedList (); + + /** An event timestamp. All events that arrive after this time + should be queued in the {@link #enqueuedKeyEvents} {@link + java.util.List}. */ + public long timestamp; + /** When this {@link Component} becomes focused, all events + between this EventDelayRequest and the next one in will be + dispatched from {@link #enqueuedKeyEvents}. */ + public Component focusedComp; + + /** + * Construct a new EventDelayRequest. + * + * @param timestamp events that arrive after this time will be + * delayed + * @param focusedComp the Component that needs to receive focus + * before events are dispatched + */ + public EventDelayRequest (long timestamp, Component focusedComp) + { + this.timestamp = timestamp; + this.focusedComp = focusedComp; + } + + public int compareTo (Object o) + { + if (!(o instanceof EventDelayRequest)) + throw new ClassCastException (); + + EventDelayRequest request = (EventDelayRequest) o; + + if (request.timestamp < timestamp) + return -1; + else if (request.timestamp == timestamp) + return 0; + else + return 1; + } + + public boolean equals (Object o) + { + if (!(o instanceof EventDelayRequest) || o == null) + return false; + + EventDelayRequest request = (EventDelayRequest) o; + + return (request.timestamp == timestamp + && request.focusedComp == focusedComp); + } + + public void enqueueEvent (KeyEvent e) + { + KeyEvent last = (KeyEvent) enqueuedKeyEvents.getLast (); + if (last != null && e.getWhen () < last.getWhen ()) + throw new RuntimeException ("KeyEvents enqueued out-of-order"); + + if (e.getWhen () <= timestamp) + throw new RuntimeException ("KeyEvents enqueued before starting timestamp"); + + enqueuedKeyEvents.add (e); + } + + public void dispatchEvents () + { + int size = enqueuedKeyEvents.size (); + for (int i = 0; i < size; i++) + { + KeyEvent e = (KeyEvent) enqueuedKeyEvents.remove (0); + dispatchKeyEvent (e); + } + } + + public void discardEvents () + { + enqueuedKeyEvents.clear (); + } + } + + /** + * This flag indicates for which focus traversal key release event we + * possibly wait, before letting any more KEY_TYPED events through. + */ + private AWTKeyStroke waitForKeyStroke = null; + + /** The {@link java.util.SortedSet} of current + * {@link EventDelayRequest}s. */ + private SortedSet delayRequests = new TreeSet (); + + public DefaultKeyboardFocusManager () + { + } + + public boolean dispatchEvent (AWTEvent e) + { + if (e instanceof WindowEvent) + { + Window target = (Window) e.getSource (); + + if (e.id == WindowEvent.WINDOW_ACTIVATED) + setGlobalActiveWindow (target); + else if (e.id == WindowEvent.WINDOW_GAINED_FOCUS) + { + setGlobalFocusedWindow (target); + FocusTraversalPolicy p = target.getFocusTraversalPolicy(); + Component toFocus = p.getInitialComponent(target); + if (toFocus != null) + toFocus.requestFocusInWindow(); + } + else if (e.id != WindowEvent.WINDOW_LOST_FOCUS + && e.id != WindowEvent.WINDOW_DEACTIVATED) + return false; + + redispatchEvent(target, e); + return true; + } + else if (e instanceof FocusEvent) + { + FocusEvent fe = (FocusEvent) e; + Component target = fe.getComponent (); + + boolean retval = false; + if (e.id == FocusEvent.FOCUS_GAINED) + { + retval = handleFocusGained(fe); + } + else if (e.id == FocusEvent.FOCUS_LOST) + { + retval = handleFocusLost(fe); + } + return true; + } + else if (e instanceof KeyEvent) + { + // Loop through all registered KeyEventDispatchers, giving + // each a chance to handle this event. + Iterator i = getKeyEventDispatchers().iterator(); + + while (i.hasNext ()) + { + KeyEventDispatcher dispatcher = (KeyEventDispatcher) i.next (); + if (dispatcher.dispatchKeyEvent ((KeyEvent) e)) + return true; + } + + // processKeyEvent checks if this event represents a focus + // traversal key stroke. + Component focusOwner = getGlobalPermanentFocusOwner (); + + if (focusOwner != null) + processKeyEvent (focusOwner, (KeyEvent) e); + + if (e.isConsumed ()) + return true; + + if (enqueueKeyEvent ((KeyEvent) e)) + // This event was enqueued for dispatch at a later time. + return true; + else + // This event wasn't handled by any of the registered + // KeyEventDispatchers, and wasn't enqueued for dispatch + // later, so send it to the default dispatcher. + return dispatchKeyEvent ((KeyEvent) e); + } + + return false; + } + + /** + * Handles FOCUS_GAINED events in {@link #dispatchEvent(AWTEvent)}. + * + * @param fe the focus event + */ + private boolean handleFocusGained(FocusEvent fe) + { + Component target = fe.getComponent (); + + // If old focus owner != new focus owner, notify old focus + // owner that it has lost focus. + Component oldFocusOwner = getGlobalFocusOwner(); + if (oldFocusOwner != null && oldFocusOwner != target) + { + FocusEvent lost = new FocusEvent(oldFocusOwner, + FocusEvent.FOCUS_LOST, + fe.isTemporary(), target); + oldFocusOwner.dispatchEvent(lost); + } + + setGlobalFocusOwner (target); + if (target != getGlobalFocusOwner()) + { + // Focus transfer was rejected, like when the target is not + // focusable. + dequeueKeyEvents(-1, target); + // FIXME: Restore focus somehow. + } + else + { + if (! fe.isTemporary()) + { + setGlobalPermanentFocusOwner (target); + if (target != getGlobalPermanentFocusOwner()) + { + // Focus transfer was rejected, like when the target is not + // focusable. + dequeueKeyEvents(-1, target); + // FIXME: Restore focus somehow. + } + else + { + redispatchEvent(target, fe); + } + } + } + + return true; + } + + /** + * Handles FOCUS_LOST events for {@link #dispatchEvent(AWTEvent)}. + * + * @param fe the focus event + * + * @return if the event has been handled + */ + private boolean handleFocusLost(FocusEvent fe) + { + Component currentFocus = getGlobalFocusOwner(); + if (currentFocus != fe.getOppositeComponent()) + { + setGlobalFocusOwner(null); + if (getGlobalFocusOwner() != null) + { + // TODO: Is this possible? If so, then we should try to restore + // the focus. + } + else + { + if (! fe.isTemporary()) + { + setGlobalPermanentFocusOwner(null); + if (getGlobalPermanentFocusOwner() != null) + { + // TODO: Is this possible? If so, then we should try to + // restore the focus. + } + else + { + fe.setSource(currentFocus); + redispatchEvent(currentFocus, fe); + } + } + } + } + return true; + } + + private boolean enqueueKeyEvent (KeyEvent e) + { + Iterator i = delayRequests.iterator (); + boolean oneEnqueued = false; + while (i.hasNext ()) + { + EventDelayRequest request = (EventDelayRequest) i.next (); + if (e.getWhen () > request.timestamp) + { + request.enqueueEvent (e); + oneEnqueued = true; + } + } + return oneEnqueued; + } + + public boolean dispatchKeyEvent (KeyEvent e) + { + Component focusOwner = getFocusOwner(); + if (focusOwner == null) + focusOwner = getFocusedWindow(); + + if (focusOwner != null) + redispatchEvent(focusOwner, e); + + // Loop through all registered KeyEventPostProcessors, giving + // each a chance to process this event. + Iterator i = getKeyEventPostProcessors().iterator(); + + while (i.hasNext ()) + { + KeyEventPostProcessor processor = (KeyEventPostProcessor) i.next (); + if (processor.postProcessKeyEvent (e)) + return true; + } + + // The event hasn't been consumed yet. Check if it is an + // MenuShortcut. + if (postProcessKeyEvent (e)) + return true; + + // Always return true. + return true; + } + + public boolean postProcessKeyEvent (KeyEvent e) + { + // Check if this event represents a menu shortcut. + + // MenuShortcuts are activated by Ctrl- KeyEvents, only on KEY_PRESSED. + int modifiers = e.getModifiersEx (); + if (e.getID() == KeyEvent.KEY_PRESSED + && (modifiers & KeyEvent.CTRL_DOWN_MASK) != 0) + { + Window focusedWindow = getGlobalFocusedWindow (); + if (focusedWindow instanceof Frame) + { + MenuBar menubar = ((Frame) focusedWindow).getMenuBar (); + + if (menubar != null) + { + // If there's a menubar, loop through all menu items, + // checking whether each one has a shortcut, and if + // so, whether this key event should activate it. + int numMenus = menubar.getMenuCount (); + + for (int i = 0; i < numMenus; i++) + { + Menu menu = menubar.getMenu (i); + int numItems = menu.getItemCount (); + + for (int j = 0; j < numItems; j++) + { + MenuItem item = menu.getItem (j); + MenuShortcut shortcut = item.getShortcut (); + + if (item.isEnabled() && shortcut != null) + { + // Dispatch a new ActionEvent if: + // + // a) this is a Shift- KeyEvent, and the + // shortcut requires the Shift modifier + // + // or, b) this is not a Shift- KeyEvent, and the + // shortcut does not require the Shift + // modifier. + if (shortcut.getKey () == e.getKeyCode () + && ((shortcut.usesShiftModifier () + && (modifiers & KeyEvent.SHIFT_DOWN_MASK) != 0) + || (! shortcut.usesShiftModifier () + && (modifiers & KeyEvent.SHIFT_DOWN_MASK) == 0))) + { + item.dispatchEvent (new ActionEvent (item, + ActionEvent.ACTION_PERFORMED, + item.getActionCommand (), + modifiers)); + // The event was dispatched. + return true; + } + } + } + } + } + } + } + return false; + } + + public void processKeyEvent (Component comp, KeyEvent e) + { + AWTKeyStroke eventKeystroke = AWTKeyStroke.getAWTKeyStrokeForEvent (e); + // For every focus traversal keystroke, we need to also consume + // the other two key event types for the same key (e.g. if + // KEY_PRESSED TAB is a focus traversal keystroke, we also need to + // consume KEY_RELEASED and KEY_TYPED TAB key events). + // consuming KEY_RELEASED is easy, because their keyCodes matches + // the KEY_PRESSED event. Consuming the intermediate KEY_TYPED is + // very difficult because their is no clean way that we can know + // which KEY_TYPED belongs to a focusTraversalKey and which not. + // To address this problem we swallow every KEY_TYPE between the + // KEY_PRESSED event that matches a focusTraversalKey and the + // corresponding KEY_RELEASED. + AWTKeyStroke oppositeKeystroke = AWTKeyStroke.getAWTKeyStroke (e.getKeyCode (), + e.getModifiersEx (), + !(e.id == KeyEvent.KEY_RELEASED)); + + // Here we check if we are currently waiting for a KEY_RELEASED and + // swallow all KeyEvents that are to be delivered in between. This + // should only be the KEY_TYPED events that correspond to the + // focusTraversalKey's KEY_PRESSED event + if (waitForKeyStroke != null) + { + if (eventKeystroke.equals(waitForKeyStroke)) + // release this lock + waitForKeyStroke = null; + + // as long as we are waiting for the KEY_RELEASED, we swallow every + // KeyEvent, including the KEY_RELEASED + e.consume(); + return; + } + + Set forwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); + Set backwardKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); + Set upKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); + Set downKeystrokes = null; + if (comp instanceof Container) + downKeystrokes = comp.getFocusTraversalKeys (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); + + if (forwardKeystrokes.contains (eventKeystroke)) + { + waitForKeyStroke = oppositeKeystroke; + focusNextComponent (comp); + e.consume (); + } + else if (backwardKeystrokes.contains (eventKeystroke)) + { + waitForKeyStroke = oppositeKeystroke; + focusPreviousComponent (comp); + e.consume (); + } + else if (upKeystrokes.contains (eventKeystroke)) + { + waitForKeyStroke = oppositeKeystroke; + upFocusCycle (comp); + e.consume (); + } + else if (comp instanceof Container + && downKeystrokes.contains (eventKeystroke)) + { + waitForKeyStroke = oppositeKeystroke; + downFocusCycle ((Container) comp); + e.consume (); + } + } + + protected void enqueueKeyEvents (long after, Component untilFocused) + { + delayRequests.add (new EventDelayRequest (after, untilFocused)); + } + + protected void dequeueKeyEvents (long after, Component untilFocused) + { + // FIXME: need synchronization on delayRequests and enqueuedKeyEvents. + + // Remove the KeyEvent with the oldest timestamp, which should be + // the first element in the SortedSet. + if (after < 0) + { + int size = delayRequests.size (); + if (size > 0) + delayRequests.remove (delayRequests.first ()); + } + else + { + EventDelayRequest template = new EventDelayRequest (after, untilFocused); + if (delayRequests.contains (template)) + { + EventDelayRequest actual = (EventDelayRequest) delayRequests.tailSet (template).first (); + delayRequests.remove (actual); + actual.dispatchEvents (); + } + } + } + + protected void discardKeyEvents (Component comp) + { + // FIXME: need synchronization on delayRequests and enqueuedKeyEvents. + + Iterator i = delayRequests.iterator (); + + while (i.hasNext ()) + { + EventDelayRequest request = (EventDelayRequest) i.next (); + + if (request.focusedComp == comp + || (comp instanceof Container + && ((Container) comp).isAncestorOf (request.focusedComp))) + request.discardEvents (); + } + } + + public void focusPreviousComponent (Component comp) + { + if (comp != null) + comp.transferFocusBackward(); + } + + public void focusNextComponent (Component comp) + { + if (comp != null) + comp.transferFocus(); + } + + public void upFocusCycle (Component comp) + { + if (comp != null) + comp.transferFocusUpCycle(); + } + + public void downFocusCycle (Container cont) + { + if (cont != null) + cont.transferFocusDownCycle(); + } +} // class DefaultKeyboardFocusManager diff --git a/libjava/classpath/java/awt/Desktop.java b/libjava/classpath/java/awt/Desktop.java new file mode 100644 index 000000000..1b4078823 --- /dev/null +++ b/libjava/classpath/java/awt/Desktop.java @@ -0,0 +1,268 @@ +/* Desktop.java -- enable desktop integration between java programs and system + programs. + Copyright (C) 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.DesktopPeer; +import java.io.File; +import java.io.IOException; +import java.net.URI; + +/** + * This class enables Java application to access system commands to perform + * desktop oriented operations, like writing and sending emails, or surfing + * webpages with the system browser or editing/printing files with a default + * editor. Methods are provided to handle these common operations, plus an + * open command selects a default registered application for the + * specified file type. For example, opening an odf file results in launching + * OpenOffice. If an operation is not supported, or the application fails to + * launch, an exception is generated. + * + * Implementation note: As this class is used to manage Desktop + * integration, we provide some extension to configure the behaviour of this + * class depending on the type of dektop that is detected.
+ * + * First of all, we support 5 system properties that can be used to define + * the application to launch in any given case. These properties are:
+ *
+ * gnu.java.awt.peer.Desktop.html.command
+ * gnu.java.awt.peer.Desktop.mail.command
+ * gnu.java.awt.peer.Desktop.edit.command
+ * gnu.java.awt.peer.Desktop.print.command
+ * gnu.java.awt.peer.Desktop.open.command
+ *
+ *
+ * These can be specified from the command line and have priority on every + * other setting.
+ *
+ * The second method supported is defining a Java preference.
+ * The main preference node is a user node relative to the + * class gnu.java.awt.peer.ClasspathDesktopPeer. This node + * contains a child for each supported operation. The key for each type is + * always command: + *

+ * gnu.java.awt.peer.Desktop.html.command
+ * gnu.java.awt.peer.Desktop.mail.command
+ * gnu.java.awt.peer.Desktop.edit.command
+ * gnu.java.awt.peer.Desktop.print.command
+ * gnu.java.awt.peer.Desktop.open.command
+ *
+ *
+ * The access to these keys is done with the Preference API or, if outside + * of the java scope, is done in a backend dependent way. For example, + * with the GConf backend, you can access these properties + * with (may not be accurate on your system): + *

+ * + * gconftool-2 -g /apps/classpath/gnu/java/awt/peer/Desktop/html/command + * + * + * @author Mario Torre + * @since 1.6 + */ +public class Desktop +{ + /** + * Represents an action type supported by a platform. + * + * To determine if a given action is supported by the platform, + * use the Desktop.isSupported(java.awt.Desktop.Action) + * method. + * + * @author Mario Torre + */ + public enum Action + { + BROWSE, EDIT, MAIL, OPEN, PRINT + } + + private DesktopPeer peer; + + private Desktop() + { + /* nothing to be done */ + } + + /** + * Returns an instance of the Desktop Class. + * + * If this implementation does not support Desktop, an + * UnsupportedOperationException will be thrown. + * Also, an HeadlessException will be generated if + * GraphicsEnvironment.isHeadless() returns true. + * + * @throws UnsupportedOperationException + * @throws HeadlessException + */ + public static Desktop getDesktop() throws UnsupportedOperationException, + HeadlessException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + + if (!Desktop.isDesktopSupported()) + throw new UnsupportedOperationException(); + + Desktop desktop = new Desktop(); + desktop.peer = Toolkit.getDefaultToolkit().createDesktopPeer(desktop); + + return desktop; + } + + /** + * Check if this implementation supports Desktop. + * If true, use getDesktop to get an instance of this class. + * + * This implementation will return false when GraphicsEnvironment.isHeadless + * returns true. + * + * @return true if this class is supported on the current platform; + * false otherwise + */ + public static boolean isDesktopSupported() + { + if (GraphicsEnvironment.isHeadless()) + return false; + + return true; + } + + /** + * Check if the given Action is supported by this implementation. + * + * @param action + * @return + */ + public boolean isSupported(Desktop.Action action) + { + return peer.isSupported(action); + } + + /** + * Launches the Desktop default browser to open the given uri. + * + * If a security manager exists and denies + * AWTPermission("showWindowWithoutWarningBanner"),a SecurityException will + * be generated. + * + * @param uri + * @throws IOException + */ + public void browse(URI uri) + throws IOException + { + peer.browse(uri); + } + + /** + * Launch the edit command to edit this file. + * File should already exist before the editing starts. + * + * If a security manager exists and + * SecurityManager.checkRead(java.lang.String) method denies read access to + * the file, or SecurityManager.checkPrintJobAccess() method denies the + * permission to print the file, a SecurityException will be generated. + * + * @param file + * @throws IOException + */ + public void edit(File file) + throws IOException + { + peer.edit(file); + } + + /** + * Launches the Desktop default mailer. + * + * If a security manager exists and denies + * AWTPermission("showWindowWithoutWarningBanner"), a SecurityException will + * be generated. + * + * @throws IOException + */ + public void mail() + throws IOException + { + peer.mail(); + } + + /** + * Launches the Desktop default mailer, with the given mailtoURI + * as agrument. The mailtoURI must conform to the + * {@link http://www.ietf.org/rfc/rfc2368.txt The mailto URL scheme (RFC 2368)} + * + * If a security manager exists and denies + * AWTPermission("showWindowWithoutWarningBanner"), a SecurityException will + * be generated. + * + * @param mailtoURI + * @throws IOException + */ + public void mail(URI mailtoURI) + throws IOException + { + peer.mail(mailtoURI); + } + + /** + * Launches the Desktop default application to open the given File. + * If file is a directory, a file manager is launched. + * + * @param file + * @throws IOException + */ + public void open(File file) + throws IOException + { + peer.open(file); + } + + /** + * Launch the print program to print this file. + * + * @param file + * @throws IOException + */ + public void print(File file) + throws IOException + { + peer.print(file); + } +} diff --git a/libjava/classpath/java/awt/Dialog.java b/libjava/classpath/java/awt/Dialog.java new file mode 100644 index 000000000..d12e1eb12 --- /dev/null +++ b/libjava/classpath/java/awt/Dialog.java @@ -0,0 +1,570 @@ +/* Dialog.java -- An AWT dialog box + Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006 + Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.DialogPeer; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * Dialog provides a top-level window normally used to receive + * user input in applications. + *

+ * A dialog always has another top-level window as owner and is only visible + * if this owner is visible to the user. The default layout of dialogs is the + * BorderLayout. Dialogs can be modal (blocks user input to other + * components) or non-modal (user input in other components are allowed). + *

+ * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class Dialog extends Window +{ + public enum ModalExclusionType + { + APPLICATION_EXCLUDE, + NO_EXCLUDE, + TOOLKIT_EXCLUDE + } + + public enum ModalityType + { + APPLICATION_MODAL, + DOCUMENT_MODAL, + MODELESS, + TOOLKIT_MODAL + } + + // Serialization constant + private static final long serialVersionUID = 5920926903803293709L; + + /** + * @serial Indicates whether or not this dialog box is modal. + */ + private boolean modal; + + /** + * @serial Indicates whether or not this dialog box is resizable. + */ + private boolean resizable = true; + + /** + * @serial The title string for this dialog box, which can be + * null. + */ + private String title; + + /** + * This field indicates whether the dialog is undecorated or not. + */ + private boolean undecorated = false; + + /** + * Indicates that we are blocked for modality in show + */ + private boolean blocked = false; + + /** + * Secondary EventQueue to handle AWT events while we are blocked for + * modality in show. + */ + private EventQueue eq2 = null; + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_dialog_number; + + /** + * Initializes a new instance of Dialog with the specified + * parent, that is resizable and not modal, and which has no title. + * + * @param parent The parent frame of this dialog box. + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null. This exception is + * always thrown when GraphicsEnvironment.isHeadless() returns true. + */ + public Dialog(Frame parent) + { + this(parent, "", false); + } + + /** + * Initializes a new instance of Dialog with the specified + * parent and modality, that is resizable and which has no title. + * + * @param parent The parent frame of this dialog box. + * @param modal true if this dialog box is modal, + * false otherwise. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null. This exception is + * always thrown when GraphicsEnvironment.isHeadless() returns true. + */ + public Dialog(Frame parent, boolean modal) + { + this(parent, "", modal); + } + + /** + * Initializes a new instance of Dialog with the specified + * parent, that is resizable and not modal, and which has the specified + * title. + * + * @param parent The parent frame of this dialog box. + * @param title The title string for this dialog box. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null. This exceptionnis + * always thrown when GraphicsEnvironment.isHeadless() returns true. + */ + public Dialog(Frame parent, String title) + { + this(parent, title, false); + } + + /** + * Initializes a new instance of Dialog with the specified, + * parent, title, and modality, that is resizable. + * + * @param parent The parent frame of this dialog box. + * @param title The title string for this dialog box. + * @param modal true if this dialog box is modal, + * false otherwise. + * + * @exception IllegalArgumentException If owner is null or + * GraphicsEnvironment.isHeadless() returns true. + */ + public Dialog(Frame parent, String title, boolean modal) + { + this(parent, title, modal, parent.getGraphicsConfiguration()); + } + + /** + * Initializes a new instance of Dialog with the specified, + * parent, title, modality and GraphicsConfiguration, that is + * resizable. + * + * @param parent The parent frame of this dialog box. + * @param title The title string for this dialog box. + * @param modal true if this dialog box is modal, + * false otherwise. + * @param gc The GraphicsConfiguration object to use. If + * null the GraphicsConfiguration of the target + * frame is used. + * + * @exception IllegalArgumentException If owner is null, the + * GraphicsConfiguration is not a screen device or + * GraphicsEnvironment.isHeadless() returns true. + * @since 1.4 + */ + public Dialog(Frame parent, String title, boolean modal, + GraphicsConfiguration gc) + { + super(parent, (gc == null) ? parent.getGraphicsConfiguration() : gc); + + // A null title is equivalent to an empty title + this.title = (title != null) ? title : ""; + this.modal = modal; + visible = false; + + setLayout(new BorderLayout()); + setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + + /** + * Initializes a new instance of Dialog with the specified, + * parent, that is resizable. + * + * @param owner The parent frame of this dialog box. + * + * @exception IllegalArgumentException If parent is null. This exception is + * always thrown when GraphicsEnvironment.isHeadless() returns true. + * + * @since 1.2 + */ + public Dialog(Dialog owner) + { + this(owner, "", false, owner.getGraphicsConfiguration()); + } + + /** + * Initializes a new instance of Dialog with the specified, + * parent and title, that is resizable. + * + * @param owner The parent frame of this dialog box. + * @param title The title string for this dialog box. + * + * @exception IllegalArgumentException If parent is null. This exception is + * always thrown when GraphicsEnvironment.isHeadless() returns + * true. + * @since 1.2 + */ + public Dialog(Dialog owner, String title) + { + this(owner, title, false, owner.getGraphicsConfiguration()); + } + + /** + * Initializes a new instance of Dialog with the specified, + * parent, title and modality, that is resizable. + * + * @param owner The parent frame of this dialog box. + * @param title The title string for this dialog box. + * @param modal true if this dialog box is modal, + * false otherwise. + * + * @exception IllegalArgumentException If parent is null. This exception is + * always thrown when GraphicsEnvironment.isHeadless() returns true. + * @since 1.2 + */ + public Dialog(Dialog owner, String title, boolean modal) + { + this(owner, title, modal, owner.getGraphicsConfiguration()); + } + + /** + * Initializes a new instance of Dialog with the specified, + * parent, title, modality and GraphicsConfiguration, that is + * resizable. + * + * @param parent The parent frame of this dialog box. + * @param title The title string for this dialog box. + * @param modal true if this dialog box is modal, + * false otherwise. + * @param gc The GraphicsConfiguration object to use. If + * null the GraphicsConfiguration of the target + * frame is used. + * + * @exception IllegalArgumentException If parent is null, the + * GraphicsConfiguration is not a screen device or + * GraphicsEnvironment.isHeadless() returns true. + * + * @since 1.4 + */ + public Dialog(Dialog parent, String title, boolean modal, + GraphicsConfiguration gc) + { + super(parent, (gc == null) ? parent.getGraphicsConfiguration() : gc); + + // A null title is equivalent to an empty title + this.title = (title != null) ? title : ""; + this.modal = modal; + visible = false; + + setLayout(new BorderLayout()); + setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + + /** + * Returns the title of this dialog box. + * + * @return The title of this dialog box. + */ + public String getTitle() + { + return title; + } + + /** + * Sets the title of this dialog box to the specified string. + * + * @param title the new title. If null an empty + * title will be set. + */ + public synchronized void setTitle(String title) + { + // A null title is equivalent to an empty title + this.title = (title != null) ? title : ""; + + if (peer != null) + { + DialogPeer d = (DialogPeer) peer; + d.setTitle(title); + } + } + + /** + * Tests whether or not this dialog box is modal. + * + * @return true if this dialog box is modal, false + * otherwise. + */ + public boolean isModal() + { + return modal; + } + + /** + * Changes the modality of this dialog box. This can only be done before the + * peer is created. + * + * @param modal true to make this dialog box modal, + * false to make it non-modal. + */ + public void setModal(boolean modal) + { + this.modal = modal; + } + + /** + * Tests whether or not this dialog box is resizable. + * + * @return true if this dialog is resizable, + * false otherwise. + */ + public boolean isResizable() + { + return resizable; + } + + /** + * Changes the resizability of this dialog box. + * + * @param resizable true to make this dialog resizable, + * false to make it non-resizable. + */ + public synchronized void setResizable(boolean resizable) + { + this.resizable = resizable; + if (peer != null) + { + DialogPeer d = (DialogPeer) peer; + d.setResizable(resizable); + } + } + + /** + * Creates this object's native peer. + */ + public synchronized void addNotify() + { + if (peer == null) + peer = getToolkit().createDialog(this); + super.addNotify(); + } + + /** + * Makes this dialog visible and brings it to the front. If the dialog is + * modal and is not already visible, this call will not return until the + * dialog is hidden by someone calling hide or dispose. If this is the event + * dispatching thread we must ensure that another event thread runs while the + * one which invoked this method is blocked. + * + * @deprecated Use {@link Component#setVisible(boolean)} instead. + */ + public synchronized void show() + { + super.show(); + + if (isModal()) + { + // If already shown (and blocked) just return + if (blocked) + return; + + /* + * If show is called in the dispatch thread for a modal dialog it will + * block so we must run another thread so the events keep being + * dispatched. + */ + if (EventQueue.isDispatchThread()) + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + eq2 = new EventQueue(); + eq.push(eq2); + } + + try + { + blocked = true; + wait(); + blocked = false; + } + catch (InterruptedException e) + { + blocked = false; + } + + if (eq2 != null) + { + eq2.pop(); + eq2 = null; + } + } + } + + /** + * Hides the Dialog and then causes show() to return if it is currently + * blocked. + * + * @deprecated Use {@link Component#setVisible(boolean)} instead. + */ + public synchronized void hide() + { + if (blocked) + { + notifyAll(); + } + + super.hide(); + } + + /** + * Disposes the Dialog and then causes show() to return if it is currently + * blocked. + */ + public synchronized void dispose() + { + if (blocked) + { + notifyAll(); + } + + super.dispose(); + } + + /** + * Returns a debugging string for this component. + * + * @return A debugging string for this component. + */ + protected String paramString() + { + return "title+" + title + ",modal=" + modal + ",resizable=" + resizable + + "," + super.paramString(); + } + + /** + * Returns whether this frame is undecorated or not. + * + * @return true if this dialog is undecorated, + * false otherwise. + * + * @since 1.4 + */ + public boolean isUndecorated() + { + return undecorated; + } + + /** + * Disables or enables decorations for this frame. This method can only be + * called while the frame is not displayable. + * + * @param undecorated true to disable dialog decorations, + * false otherwise. + * + * @exception IllegalComponentStateException If this frame is displayable. + * @since 1.4 + */ + public void setUndecorated(boolean undecorated) + { + if (isDisplayable()) + throw new IllegalComponentStateException(); + + this.undecorated = undecorated; + } + + /** + * Accessibility support for Dialog. + */ + protected class AccessibleAWTDialog + extends AccessibleAWTWindow + { + private static final long serialVersionUID = 4837230331833941201L; + + /** + * Gets the role of this object. + * @return AccessibleRole.DIALOG + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.DIALOG; + } + + /** + * Gets the state set of this object. + * @return The current state of this dialog. + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (isResizable()) + states.add(AccessibleState.RESIZABLE); + if (isModal()) + states.add(AccessibleState.MODAL); + return states; + } + } + + /** + * Gets the AccessibleContext associated with this Dialog. The + * context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTDialog(); + return accessibleContext; + } + + /** + * Generate a unique name for this Dialog. + * + * @return A unique name for this Dialog. + */ + String generateName() + { + return "dialog" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_dialog_number++; + } +} diff --git a/libjava/classpath/java/awt/Dimension.java b/libjava/classpath/java/awt/Dimension.java new file mode 100644 index 000000000..4c1a07bf9 --- /dev/null +++ b/libjava/classpath/java/awt/Dimension.java @@ -0,0 +1,234 @@ +/* Dimension.java -- represents a 2-dimensional span + Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.Dimension2D; +import java.io.Serializable; + +/** + * This class holds a width and height value pair. This is used in plenty + * of windowing classes, but also has geometric meaning. + * + *

It is valid for a dimension to have negative width or height; but it + * is considered to have no area. Therefore, the behavior in various methods + * is undefined in such a case. + * + *

There are some public fields; if you mess with them in an inconsistent + * manner, it is your own fault when you get invalid results. Also, this + * class is not threadsafe. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Component + * @see LayoutManager + * @since 1.0 + * @status updated to 1.14 + */ +public class Dimension extends Dimension2D implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4723952579491349524L; + + /** + * The width of this object. + * + * @see #getSize() + * @see #setSize(double, double) + * @serial the width + */ + public int width; + + /** + * The height of this object. + * + * @see #getSize() + * @see #setSize(double, double) + * @serial the height + */ + public int height; + + /** + * Create a new Dimension with a width and height of zero. + */ + public Dimension() + { + } + + /** + * Create a new Dimension with width and height identical to that of the + * specified dimension. + * + * @param d the Dimension to copy + * @throws NullPointerException if d is null + */ + public Dimension(Dimension d) + { + width = d.width; + height = d.height; + } + + /** + * Create a new Dimension with the specified width and height. + * + * @param w the width of this object + * @param h the height of this object + */ + public Dimension(int w, int h) + { + width = w; + height = h; + } + + /** + * Gets the width of this dimension. + * + * @return the width, as a double + */ + public double getWidth() + { + return width; + } + + /** + * Gets the height of this dimension. + * + * @return the height, as a double + */ + public double getHeight() + { + return height; + } + + /** + * Sets the size of this dimension. The values are rounded to int. + * + * @param w the new width + * @param h the new height + * @since 1.2 + */ + public void setSize(double w, double h) + { + width = (int) w; + height = (int) h; + } + + /** + * Returns the size of this dimension. A pretty useless method, as this is + * already a dimension. + * + * @return a copy of this dimension + * @see #setSize(Dimension) + * @since 1.1 + */ + public Dimension getSize() + { + return new Dimension(width, height); + } + + /** + * Sets the width and height of this object to match that of the + * specified object. + * + * @param d the Dimension to get the new width and height from + * @throws NullPointerException if d is null + * @see #getSize() + * @since 1.1 + */ + public void setSize(Dimension d) + { + width = d.width; + height = d.height; + } + + /** + * Sets the width and height of this object to the specified values. + * + * @param w the new width value + * @param h the new height value + */ + public void setSize(int w, int h) + { + width = w; + height = h; + } + + /** + * Tests this object for equality against the specified object. This will + * be true if and only if the specified object is an instance of + * Dimension2D, and has the same width and height. + * + * @param obj the object to test against + * @return true if the object is equal to this + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Dimension)) + return false; + Dimension dim = (Dimension) obj; + return height == dim.height && width == dim.width; + } + + /** + * Return the hashcode for this object. It is not documented, but appears + * to be ((width + height) * (width + height + 1) / 2) + width. + * + * @return the hashcode + */ + public int hashCode() + { + // Reverse engineering this was fun! + return (width + height) * (width + height + 1) / 2 + width; + } + + /** + * Returns a string representation of this object. The format is: + * getClass().getName() + "[width=" + width + ",height=" + height + * + ']'. + * + * @return a string representation of this object + */ + public String toString() + { + return getClass().getName() + + "[width=" + width + ",height=" + height + ']'; + } +} // class Dimension diff --git a/libjava/classpath/java/awt/DisplayMode.java b/libjava/classpath/java/awt/DisplayMode.java new file mode 100644 index 000000000..be2a1d118 --- /dev/null +++ b/libjava/classpath/java/awt/DisplayMode.java @@ -0,0 +1,164 @@ +/* DisplayMode.java -- a description of display mode configurations + Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This encapsulates information about the display mode for a graphics + * device configuration. They are device dependent, and may not always be + * available. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see GraphicsDevice + * @since 1.4 + * @status updated to 1.4 + */ +public final class DisplayMode +{ + /** + * Value of the bit depth if multiple depths are supported. + * + * @see #getBitDepth() + */ + public static final int BIT_DEPTH_MULTI = -1; + + /** + * Value of an unknown refresh rate. + * + * @see #getRefreshRate() + */ + public static final int REFRESH_RATE_UNKNOWN = 0; + + /** The width. */ + private final int width; + + /** The height. */ + private final int height; + + /** The bit depth. */ + private final int bitDepth; + + /** The refresh rate. */ + private final int refreshRate; + + /** + * Create a mode with the given parameters. + * + * @param width the width + * @param height the height + * @param bitDepth the bitDepth + * @param refreshRate the refreshRate + * @see #BIT_DEPTH_MULTI + * @see #REFRESH_RATE_UNKNOWN + */ + public DisplayMode(int width, int height, int bitDepth, int refreshRate) + { + this.width = width; + this.height = height; + this.bitDepth = bitDepth; + this.refreshRate = refreshRate; + } + + /** + * Returns the height, in pixels. + * + * @return the height + */ + public int getHeight() + { + return height; + } + + /** + * Returns the width, in pixels. + * + * @return the width + */ + public int getWidth() + { + return width; + } + + /** + * Returns the bit depth, in bits per pixel. This may be BIT_DEPTH_MULTI. + * + * @return the bit depth + * @see #BIT_DEPTH_MULTI + */ + public int getBitDepth() + { + return bitDepth; + } + + /** + * Returns the refresh rate, in hertz. This may be REFRESH_RATE_UNKNOWN. + * + * @return the refresh rate + * @see #REFRESH_RATE_UNKNOWN + */ + public int getRefreshRate() + { + return refreshRate; + } + + /** + * Test for equality. This returns true for two modes with identical + * parameters. + * + * @param dm The display mode to compare to + * + * @return true if it is equal + */ + public boolean equals (DisplayMode dm) + { + return (width == dm.width + && height == dm.height + && bitDepth == dm.bitDepth + && refreshRate == dm.refreshRate); + } + + /** + * Returns a hash code for the display mode. + * + * @return the hash code + */ + public int hashCode() + { + return width + height + bitDepth + refreshRate; + } +} // class DisplayMode diff --git a/libjava/classpath/java/awt/Event.java b/libjava/classpath/java/awt/Event.java new file mode 100644 index 000000000..f2c98b9b0 --- /dev/null +++ b/libjava/classpath/java/awt/Event.java @@ -0,0 +1,185 @@ +/* Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +public class Event implements java.io.Serializable +{ + static final long serialVersionUID = 5488922509400504703L; + + public static final int SHIFT_MASK = 1; + public static final int CTRL_MASK = 2; + public static final int META_MASK = 4; + public static final int ALT_MASK = 8; + + public static final int ACTION_EVENT = 1001; + public static final int BACK_SPACE = 8; + public static final int CAPS_LOCK = 1022; + public static final int DELETE = 127; + public static final int DOWN = 1005; + public static final int END = 1001; + public static final int ENTER = 10; + public static final int ESCAPE = 27; + public static final int F1 = 1008; + public static final int F10 = 1017; + public static final int F11 = 1018; + public static final int F12 = 1019; + public static final int F2 = 1009; + public static final int F3 = 1010; + public static final int F4 = 1011; + public static final int F5 = 1012; + public static final int F6 = 1013; + public static final int F7 = 1014; + public static final int F8 = 1015; + public static final int F9 = 1016; + public static final int GOT_FOCUS = 1004; + public static final int HOME = 1000; + public static final int INSERT = 1025; + public static final int KEY_ACTION = 403; + public static final int KEY_ACTION_RELEASE = 404; + public static final int KEY_PRESS = 401; + public static final int KEY_RELEASE = 402; + public static final int LEFT = 1006; + public static final int LIST_DESELECT = 702; + public static final int LIST_SELECT = 701; + public static final int LOAD_FILE = 1002; + public static final int LOST_FOCUS = 1005; + public static final int MOUSE_DOWN = 501; + public static final int MOUSE_DRAG = 506; + public static final int MOUSE_ENTER = 504; + public static final int MOUSE_EXIT = 505; + public static final int MOUSE_MOVE = 503; + public static final int MOUSE_UP = 502; + public static final int NUM_LOCK = 1023; + public static final int PAUSE = 1024; + public static final int PGDN = 1003; + public static final int PGUP = 1002; + public static final int PRINT_SCREEN = 1020; + public static final int RIGHT = 1007; + public static final int SAVE_FILE = 1003; + public static final int SCROLL_ABSOLUTE = 605; + public static final int SCROLL_BEGIN = 606; + public static final int SCROLL_END = 607; + public static final int SCROLL_LINE_DOWN = 602; + public static final int SCROLL_LINE_UP = 601; + public static final int SCROLL_LOCK = 1021; + public static final int SCROLL_PAGE_DOWN = 604; + public static final int SCROLL_PAGE_UP = 603; + public static final int TAB = 9; + public static final int UP = 1004; + public static final int WINDOW_DEICONIFY = 204; + public static final int WINDOW_DESTROY = 201; + public static final int WINDOW_EXPOSE = 202; + public static final int WINDOW_ICONIFY = 203; + public static final int WINDOW_MOVED = 205; + + public Object arg; + public int clickCount; + boolean consumed; // Required by serialization spec. + public Event evt; + public int id; + public int key; + public int modifiers; + public Object target; + public long when; + public int x; + public int y; + + public Event (Object target, int id, Object arg) + { + this.id = id; + this.target = target; + this.arg = arg; + } + + public Event (Object target, long when, int id, int x, int y, int key, + int modifiers) + { + this.target = target; + this.when = when; + this.id = id; + this.x = x; + this.y = y; + this.key = key; + this.modifiers = modifiers; + } + + public Event (Object target, long when, int id, int x, int y, int key, + int modifiers, Object arg) + { + this (target, when, id, x, y, key, modifiers); + this.arg = arg; + } + + public boolean controlDown () + { + return ((modifiers & CTRL_MASK) == 0 ? false : true); + } + + public boolean metaDown () + { + return ((modifiers & META_MASK) == 0 ? false : true); + } + + protected String paramString () + { + return "id=" + id + ",x=" + x + ",y=" + y + + ",target=" + target + ",arg=" + arg; + } + + public boolean shiftDown() + { + return ((modifiers & SHIFT_MASK) == 0 ? false : true); + } + + public String toString() + { + return getClass().getName() + "[" + paramString() + "]"; + } + + public void translate (int x, int y) + { + this.x += x; + this.y += y; + } +} diff --git a/libjava/classpath/java/awt/EventDispatchThread.java b/libjava/classpath/java/awt/EventDispatchThread.java new file mode 100644 index 000000000..c2147f4c0 --- /dev/null +++ b/libjava/classpath/java/awt/EventDispatchThread.java @@ -0,0 +1,107 @@ +/* EventDispatchThread.java - + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt; + +/** + * @author Bryce McKinlay + * @status believed complete, but untested. + */ +class EventDispatchThread extends Thread +{ + /** + * The default priority when no property has been set. + */ + private static final int DEFAULT_PRIORITY = NORM_PRIORITY + 1; + + private static int dispatchThreadNum; + + private EventQueue queue; + + EventDispatchThread(EventQueue queue) + { + super(); + setName("AWT-EventQueue-" + ++dispatchThreadNum); + this.queue = queue; + + int priority = DEFAULT_PRIORITY; + try + { + String priorityString = + System.getProperty("gnu.awt.dispatchthread.priority"); + if (priorityString != null) + { + priority = Integer.parseInt(priorityString); + } + } + catch (NumberFormatException ex) + { + // Ignore and use default. + } + setPriority(priority); + + // Make sure that an event dispatch thread is never a daemon thread. + setDaemon(false); + } + + public void run() + { + while (true) + { + try + { + AWTEvent evt = queue.getNextEvent(); + queue.dispatchEvent(evt); + } + catch (ThreadDeath death) + { + // If someone wants to kill us, let them. + return; + } + catch (InterruptedException ie) + { + // We are interrupted when we should finish executing + return; + } + catch (Throwable x) + { + System.err.println("Exception during event dispatch:"); + x.printStackTrace(System.err); + } + } + } +} diff --git a/libjava/classpath/java/awt/EventQueue.java b/libjava/classpath/java/awt/EventQueue.java new file mode 100644 index 000000000..cb52ae013 --- /dev/null +++ b/libjava/classpath/java/awt/EventQueue.java @@ -0,0 +1,658 @@ +/* EventQueue.java -- + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.LowPriorityEvent; +import gnu.java.awt.peer.NativeEventLoopRunningEvent; + +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.InputMethodEvent; +import java.awt.event.InvocationEvent; +import java.awt.event.PaintEvent; +import java.awt.peer.ComponentPeer; +import java.awt.peer.LightweightPeer; +import java.lang.reflect.InvocationTargetException; +import java.util.EmptyStackException; + +/* Written using on-line Java 2 Platform Standard Edition v1.3 API + * Specification, as well as "The Java Class Libraries", 2nd edition + * (Addison-Wesley, 1998). + * Status: Believed complete, but untested. + */ + +/** + * This class manages a queue of AWTEvent objects that + * are posted to it. The AWT system uses only one event queue for all + * events. + * + * @author Bryce McKinlay + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class EventQueue +{ + /** + * Indicates events that are processed with normal priority. This is normally + * all events except PaintEvents. + */ + private static final int NORM_PRIORITY = 0; + + /** + * Indicates events that are processed with lowes priority. This is normally + * all PaintEvents and LowPriorityEvents. + */ + private static final int LOW_PRIORITY = 1; + + /** + * Implements the actual queue. EventQueue has 2 internal queues for + * different priorities: + * 1 PaintEvents are always dispatched with low priority. + * 2. All other events are dispatched with normal priority. + * + * This makes sure that the actual painting (output) is performed _after_ all + * available input has been processed and that the paint regions are + * coalesced as much as possible. + */ + private class Queue + { + /** + * The first item in the queue. This is where events are popped from. + */ + AWTEvent queueHead; + + /** + * The last item. This is where events are posted to. + */ + AWTEvent queueTail; + } + + /** + * The three internal event queues. + * + * @see Queue + */ + private Queue[] queues; + + private EventQueue next; + private EventQueue prev; + private AWTEvent currentEvent; + private long lastWhen = System.currentTimeMillis(); + + private EventDispatchThread dispatchThread = new EventDispatchThread(this); + private boolean nativeLoopRunning = false; + + private boolean isShutdown () + { + // This is the exact self-shutdown condition specified in J2SE: + // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html + + if (nativeLoopRunning) + return false; + + if (peekEvent() != null) + return false; + + if (Frame.hasDisplayableFrames()) + return false; + + return true; + } + + /** + * Initializes a new instance of EventQueue. + */ + public EventQueue() + { + queues = new Queue[2]; + queues[NORM_PRIORITY] = new Queue(); + queues[LOW_PRIORITY] = new Queue(); + } + + /** + * Returns the next event in the queue. This method will block until + * an event is available or until the thread is interrupted. + * + * @return The next event in the queue. + * + * @exception InterruptedException If this thread is interrupted while + * waiting for an event to be posted to the queue. + */ + public synchronized AWTEvent getNextEvent() + throws InterruptedException + { + if (next != null) + return next.getNextEvent(); + + AWTEvent res = getNextEventImpl(true); + + while (res == null) + { + if (isShutdown()) + { + // Explicitly set dispathThread to null. If we don't do + // this, there is a race condition where dispatchThread + // can be != null even after the event dispatch thread has + // stopped running. If that happens, then the + // dispatchThread == null check in postEventImpl will + // fail, and a new event dispatch thread will not be + // created, leaving invokeAndWaits waiting indefinitely. + dispatchThread = null; + + // Interrupt the event dispatch thread. + throw new InterruptedException(); + } + + wait(); + res = getNextEventImpl(true); + } + + return res; + } + + /** + * Fetches and possibly removes the next event from the internal queues. + * This method returns immediately. When all queues are empty, this returns + * null: + * + * @param remove when the event should be removed from the queue, + * false otherwise + * + * @return the next event or null when all internal queues + * are empty + */ + private AWTEvent getNextEventImpl(boolean remove) + { + AWTEvent next = null; + for (int i = 0; i < queues.length && next == null; i++) + { + Queue q = queues[i]; + if (q.queueHead != null) + { + // Got an event, remove it. + next = q.queueHead; + if (remove) + { + // Unlink event from the queue. + q.queueHead = next.queueNext; + if (q.queueHead == null) + q.queueTail = null; + next.queueNext = null; + } + } + } + return next; + } + + /** + * Returns the next event in the queue without removing it from the queue. + * This method will block until an event is available or until the thread + * is interrupted. + * + * @return The next event in the queue. + * @specnote Does not block. Returns null if there are no events on the + * queue. + */ + public synchronized AWTEvent peekEvent() + { + if (next != null) + return next.peekEvent(); + + return getNextEventImpl(false); + } + + /** + * Returns the next event in the queue that has the specified id + * without removing it from the queue. + * This method will block until an event is available or until the thread + * is interrupted. + * + * @param id The event id to return. + * + * @return The next event in the queue. + * + * @specnote Does not block. Returns null if there are no matching events + * on the queue. + */ + public synchronized AWTEvent peekEvent(int id) + { + if (next != null) + return next.peekEvent(id); + + AWTEvent evt = null; + for (int i = 0; i < queues.length && evt == null; i++) + { + Queue q = queues[i]; + evt = q.queueHead; + while (evt != null && evt.id != id) + evt = evt.queueNext; + // At this point we either have found an event (evt != null -> exit + // for loop), or we have found no event (evt == null -> search next + // internal queue). + } + return evt; + } + + /** + * Posts a new event to the queue. + * + * @param evt The event to post to the queue. + * + * @exception NullPointerException If event is null. + */ + public void postEvent(AWTEvent evt) + { + postEventImpl(evt); + } + + /** + * Sorts events to their priority and calls + * {@link #postEventImpl(AWTEvent, int)}. + * + * @param evt the event to post + */ + private synchronized final void postEventImpl(AWTEvent evt) + { + int priority = NORM_PRIORITY; + if (evt instanceof PaintEvent || evt instanceof LowPriorityEvent) + priority = LOW_PRIORITY; + // TODO: Maybe let Swing RepaintManager events also be processed with + // low priority. + if (evt instanceof NativeEventLoopRunningEvent) + { + nativeLoopRunning = ((NativeEventLoopRunningEvent) evt).isRunning(); + notify(); + return; + } + postEventImpl(evt, priority); + } + + /** + * Actually performs the event posting. This is needed because the + * RI doesn't use the public postEvent() method when transferring events + * between event queues in push() and pop(). + * + * @param evt the event to post + * @param priority the priority of the event + */ + private final void postEventImpl(AWTEvent evt, int priority) + { + if (evt == null) + throw new NullPointerException(); + + if (next != null) + { + next.postEvent(evt); + return; + } + + Object source = evt.getSource(); + + Queue q = queues[priority]; + if (source instanceof Component) + { + // For PaintEvents, ask the ComponentPeer to coalesce the event + // when the component is heavyweight. + Component comp = (Component) source; + ComponentPeer peer = comp.peer; + if (peer != null && evt instanceof PaintEvent + && ! (peer instanceof LightweightPeer)) + peer.coalescePaintEvent((PaintEvent) evt); + + // Check for any events already on the queue with the same source + // and ID. + AWTEvent previous = null; + for (AWTEvent qevt = q.queueHead; qevt != null; qevt = qevt.queueNext) + { + Object src = qevt.getSource(); + if (qevt.id == evt.id && src == comp) + { + // If there are, call coalesceEvents on the source component + // to see if they can be combined. + Component srccmp = (Component) src; + AWTEvent coalescedEvt = srccmp.coalesceEvents(qevt, evt); + if (coalescedEvt != null) + { + // Yes. Replace the existing event with the combined event. + if (qevt != coalescedEvt) + { + if (previous != null) + { + assert previous.queueNext == qevt; + previous.queueNext = coalescedEvt; + } + else + { + assert q.queueHead == qevt; + q.queueHead = coalescedEvt; + } + coalescedEvt.queueNext = qevt.queueNext; + if (q.queueTail == qevt) + q.queueTail = coalescedEvt; + qevt.queueNext = null; + } + return; + } + } + previous = qevt; + } + } + + if (q.queueHead == null) + { + // We have an empty queue. Set this event both as head and as tail. + q.queueHead = evt; + q.queueTail = evt; + } + else + { + // Note: queueTail should not be null here. + q.queueTail.queueNext = evt; + q.queueTail = evt; + } + + if (dispatchThread == null || !dispatchThread.isAlive()) + { + dispatchThread = new EventDispatchThread(this); + dispatchThread.start(); + } + + notify(); + } + + /** + * Causes runnable to have its run method called in the dispatch thread of the + * EventQueue. This will happen after all pending events are processed. The + * call blocks until this has happened. This method will throw an Error if + * called from the event dispatcher thread. + * + * @exception InterruptedException If another thread has interrupted + * this thread. + * @exception InvocationTargetException If an exception is thrown when running + * runnable. + * + * @since 1.2 + */ + public static void invokeAndWait(Runnable runnable) + throws InterruptedException, InvocationTargetException + { + if (isDispatchThread ()) + throw new Error("Can't call invokeAndWait from event dispatch thread"); + + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + Object notifyObject = new Object(); + + InvocationEvent ie = + new InvocationEvent(eq, runnable, notifyObject, true); + + synchronized (notifyObject) + { + eq.postEvent(ie); + notifyObject.wait(); + } + + Exception exception; + + if ((exception = ie.getException()) != null) + throw new InvocationTargetException(exception); + } + + /** + * This arranges for runnable to have its run method called in the + * dispatch thread of the EventQueue. This will happen after all + * pending events are processed. + * + * @since 1.2 + */ + public static void invokeLater(Runnable runnable) + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + + InvocationEvent ie = + new InvocationEvent(eq, runnable, null, false); + + eq.postEvent(ie); + } + + /** + * Return true if the current thread is the current AWT event dispatch + * thread. + */ + public static boolean isDispatchThread() + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + + /* Find last EventQueue in chain */ + while (eq.next != null) + eq = eq.next; + + return (Thread.currentThread() == eq.dispatchThread); + } + + /** + * Return the event currently being dispatched by the event + * dispatch thread. If the current thread is not the event + * dispatch thread, this method returns null. + * + * @since 1.4 + */ + public static AWTEvent getCurrentEvent() + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + Thread ct = Thread.currentThread(); + + /* Find out if this thread is the dispatch thread for any of the + EventQueues in the chain */ + while (ct != eq.dispatchThread) + { + // Try next EventQueue, if any + if (eq.next == null) + return null; // Not an event dispatch thread + eq = eq.next; + } + + return eq.currentEvent; + } + + /** + * Allows a custom EventQueue implementation to replace this one. + * All pending events are transferred to the new queue. Calls to postEvent, + * getNextEvent, and peekEvent and others are forwarded to the pushed queue + * until it is removed with a pop(). + * + * @exception NullPointerException if newEventQueue is null. + */ + public synchronized void push(EventQueue newEventQueue) + { + if (newEventQueue == null) + throw new NullPointerException (); + + /* Make sure we are at the top of the stack because callers can + only get a reference to the one at the bottom using + Toolkit.getDefaultToolkit().getSystemEventQueue() */ + if (next != null) + { + next.push (newEventQueue); + return; + } + + /* Make sure we have a live dispatch thread to drive the queue */ + if (dispatchThread == null) + dispatchThread = new EventDispatchThread(this); + + synchronized (newEventQueue) + { + // The RI transfers the events without calling the new eventqueue's + // push(), but using getNextEvent(). + while (peekEvent() != null) + { + try + { + newEventQueue.postEventImpl(getNextEvent()); + } + catch (InterruptedException ex) + { + // What should we do with this? + ex.printStackTrace(); + } + } + newEventQueue.prev = this; + } + + next = newEventQueue; + } + + /** Transfer any pending events from this queue back to the parent queue that + * was previously push()ed. Event dispatch from this queue is suspended. + * + * @exception EmptyStackException If no previous push was made on this + * EventQueue. + */ + protected void pop() throws EmptyStackException + { + /* The order is important here, we must get the prev lock first, + or deadlock could occur as callers usually get here following + prev's next pointer, and thus obtain prev's lock before trying + to get this lock. */ + EventQueue previous = prev; + if (previous == null) + throw new EmptyStackException(); + synchronized (previous) + { + synchronized (this) + { + EventQueue nextQueue = next; + if (nextQueue != null) + { + nextQueue.pop(); + } + else + { + previous.next = null; + + // The RI transfers the events without calling the new eventqueue's + // push(), so this should be OK and most effective. + while (peekEvent() != null) + { + try + { + previous.postEventImpl(getNextEvent()); + } + catch (InterruptedException ex) + { + // What should we do with this? + ex.printStackTrace(); + } + } + prev = null; + // Tell our EventDispatchThread that it can end + // execution. + if (dispatchThread != null) + { + dispatchThread.interrupt(); + dispatchThread = null; + } + } + } + } + } + + /** + * Dispatches an event. The manner in which the event is dispatched depends + * upon the type of the event and the type of the event's source object. + * + * @exception NullPointerException If event is null. + */ + protected void dispatchEvent(AWTEvent evt) + { + currentEvent = evt; + + if (evt instanceof InputEvent) + lastWhen = ((InputEvent) evt).getWhen(); + else if (evt instanceof ActionEvent) + lastWhen = ((ActionEvent) evt).getWhen(); + else if (evt instanceof InvocationEvent) + lastWhen = ((InvocationEvent) evt).getWhen(); + + if (evt instanceof ActiveEvent) + { + ActiveEvent active_evt = (ActiveEvent) evt; + active_evt.dispatch(); + } + else + { + Object source = evt.getSource(); + + if (source instanceof Component) + { + Component srccmp = (Component) source; + srccmp.dispatchEvent(evt); + } + else if (source instanceof MenuComponent) + { + MenuComponent srccmp = (MenuComponent) source; + srccmp.dispatchEvent(evt); + } + } + } + + /** + * Returns the timestamp of the most recent event that had a timestamp, or + * the initialization time of the event queue if no events have been fired. + * At present, only InputEvents, ActionEvents, + * InputMethodEvents, and InvocationEvents have + * timestamps, but this may be added to other events in future versions. + * If this is called by the event dispatching thread, it can be any + * (sequential) value, but to other threads, the safest bet is to return + * System.currentTimeMillis(). + * + * @return the most recent timestamp + * @see InputEvent#getWhen() + * @see ActionEvent#getWhen() + * @see InvocationEvent#getWhen() + * @see InputMethodEvent#getWhen() + * @since 1.4 + */ + public static long getMostRecentEventTime() + { + EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); + if (Thread.currentThread() != eq.dispatchThread) + return System.currentTimeMillis(); + return eq.lastWhen; + } +} diff --git a/libjava/classpath/java/awt/FileDialog.java b/libjava/classpath/java/awt/FileDialog.java new file mode 100644 index 000000000..c1bd08264 --- /dev/null +++ b/libjava/classpath/java/awt/FileDialog.java @@ -0,0 +1,395 @@ +/* FileDialog.java -- A filename selection dialog box + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.FileDialogPeer; +import java.io.FilenameFilter; +import java.io.Serializable; + +/** + * This class implements a file selection dialog box widget. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + */ +public class FileDialog extends Dialog implements Serializable +{ + +/* + * Static Variables + */ + +/** + * Indicates that the purpose of the dialog is for opening a file. + */ +public static final int LOAD = 0; + +/** + * Indicates that the purpose of the dialog is for saving a file. + */ +public static final int SAVE = 1; + +// Serialization constant +private static final long serialVersionUID = 5035145889651310422L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The directory for this file dialog. + */ +private String dir; + +/** + * @serial The filename for this file dialog + */ +private String file; + +/** + * @serial The filter for selecting filenames to display + */ +private FilenameFilter filter; + +/** + * @serial The mode of this dialog, either LOAD or + * SAVE. + */ +private int mode; + +/** + * The number used to generate the name returned by getName. + */ +private static transient long next_file_dialog_number; + +/*************************************************************************/ + +/* + * Constructors + */ + + /** + * Initializes a new instance of FileDialog with the specified + * parent. This dialog will have no title and will be for loading a file. + * + * @param parent The parent dialog for this. + * + * @since 1.5 + */ + public FileDialog(Dialog parent) + { + this(parent, "", LOAD); + } + + /** + * Initialized a new instance of FileDialog with the + * specified parent and title. This dialog will be for opening a file. + * + * @param parent The parent dialog for this. + * @param title The title for this dialog. + * + * @since 1.5 + */ + public FileDialog(Dialog parent, String title) + { + this(parent, title, LOAD); + } + + /** + * Initialized a new instance of FileDialog with the specified + * parent, title, and mode. + * + * @param parent The parent dialog for this. + * @param title The title for this dialog. + * @param mode The mode of the dialog, either LOAD or + * SAVE. + * @throws IllegalArgumentException - if illegal mode, if + * GraphicsEnvironment.isHeadless or if parent is null. + * + * @since 1.5 + */ + public FileDialog(Dialog parent, String title, int mode) + { + super(parent, title, true); + + // Other IllegalArgumentException cases are taken care of in Window.java + if (mode != LOAD && mode != SAVE) + throw new IllegalArgumentException ( + "Mode argument must be either LOAD or SAVE"); + + setMode(mode); + } + +/** + * Initializes a new instance of FileDialog with the + * specified parent. This dialog will have no title and will be for + * loading a file. + * + * @param parent The parent frame for this dialog. + */ +public +FileDialog(Frame parent) +{ + this(parent, "", LOAD); +} + +/*************************************************************************/ + +/** + * Initialized a new instance of FileDialog with the + * specified parent and title. This dialog will be for opening a file. + * + * @param parent The parent frame for this dialog. + * @param title The title for this dialog. + */ +public +FileDialog(Frame parent, String title) +{ + this(parent, title, LOAD); +} + +/*************************************************************************/ + +/** + * Initialized a new instance of FileDialog with the + * specified parent, title, and mode. + * + * @param parent The parent frame for this dialog. + * @param title The title for this dialog. + * @param mode The mode of the dialog, either LOAD or + * SAVE. + * + * @exception IllegalArgumentException If an illegal file dialog mode + * is supplied. + */ +public +FileDialog(Frame parent, String title, int mode) +{ + super(parent, title, true); + + if ((mode != LOAD) && (mode != SAVE)) + throw new IllegalArgumentException ( + "Mode argument must be either LOAD or SAVE"); + + setMode (mode); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the mode of this dialog, either LOAD or + * SAVE. + * + * @return The mode of this dialog. + */ +public int +getMode() +{ + return(mode); +} + +/*************************************************************************/ + +/** + * Sets the mode of this dialog to either LOAD or + * SAVE. This method is only effective before the native + * peer is created. + * + * @param mode The new mode of this file dialog. + * + * @exception IllegalArgumentException If an illegal file dialog mode + * is supplied. + */ +public void +setMode(int mode) +{ + if ((mode != LOAD) && (mode != SAVE)) + throw new IllegalArgumentException("Bad mode: " + mode); + + this.mode = mode; +} + +/*************************************************************************/ + +/** + * Returns the directory for this file dialog. + * + * @return The directory for this file dialog. + */ +public String +getDirectory() +{ + return(dir); +} + +/*************************************************************************/ + +/** + * Sets the directory for this file dialog. + * + * @param dir The new directory for this file dialog. + */ +public synchronized void +setDirectory(String dir) +{ + this.dir = dir; + if (peer != null) + { + FileDialogPeer f = (FileDialogPeer) peer; + f.setDirectory (dir); + } +} + +/*************************************************************************/ + +/** + * Returns the file that is selected in this dialog. + * + * @return The file that is selected in this dialog. + */ +public String +getFile() +{ + return(file); +} + +/*************************************************************************/ + +/** + * Sets the selected file for this dialog. + * + * @param file The selected file for this dialog. + */ +public synchronized void +setFile(String file) +{ + if ("".equals(file)) + this.file = null; + else + this.file = file; + + if (peer != null) + { + FileDialogPeer f = (FileDialogPeer) peer; + f.setFile (file); + } +} + +/*************************************************************************/ + +/** + * Returns the filename filter being used by this dialog. + * + * @return The filename filter being used by this dialog. + */ +public FilenameFilter +getFilenameFilter() +{ + return(filter); +} + +/*************************************************************************/ + +/** + * Sets the filename filter used by this dialog. + * + * @param filter The new filename filter for this file dialog box. + */ +public synchronized void +setFilenameFilter(FilenameFilter filter) +{ + this.filter = filter; + if (peer != null) + { + FileDialogPeer f = (FileDialogPeer) peer; + f.setFilenameFilter (filter); + } +} + +/*************************************************************************/ + +/** + * Creates the native peer for this file dialog box. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createFileDialog (this); + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +protected String +paramString() +{ + return ("dir=" + dir + ",file=" + file + + ",mode=" + mode + "," + super.paramString()); +} + +/** + * Generate a unique name for this FileDialog. + * + * @return A unique name for this FileDialog. + */ +String +generateName() +{ + return "filedlg" + getUniqueLong(); +} + +private static synchronized long +getUniqueLong() +{ + return next_file_dialog_number++; +} + +} // class FileDialog diff --git a/libjava/classpath/java/awt/FlowLayout.java b/libjava/classpath/java/awt/FlowLayout.java new file mode 100644 index 000000000..231d041b1 --- /dev/null +++ b/libjava/classpath/java/awt/FlowLayout.java @@ -0,0 +1,365 @@ +/* FlowLayout.java -- Grid-based layout engine + Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** This class implements a flow-based layout. Components are laid + * out in order from left to right. When a component cannot be placed + * without horizontal clipping, a new row is started. This class + * supports horizontal and vertical gaps. These are used for spacing + * between components. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class FlowLayout implements LayoutManager, Serializable +{ + /** Constant that specifies left alignment. */ + public static final int LEFT = 0; + /** Constant that specifies center alignment. */ + public static final int CENTER = 1; + /** Constant that specifies right alignment. */ + public static final int RIGHT = 2; + + /** Constant that specifies alignment to leading edge of container's + * orientation. */ + public static final int LEADING = 3; + /** Constant that specifies alignment to trailing edge of container's + * orientation. */ + public static final int TRAILING = 4; + + // Serialization constant + private static final long serialVersionUID = -7262534875583282631L; + + /** + * Add a new component to the layout. This particular implementation + * does nothing. + * + * @param name the name + * @param comp the component + */ + public void addLayoutComponent (String name, Component comp) + { + // Nothing. + } + + /** + * Returns the current justification value for this object. + * + * @return The current justification value for this object. + */ + public int getAlignment () + { + return align; + } + + /** + * Returns the horizontal gap between components. + * + * @return The horizontal gap between components. + */ + public int getHgap () + { + return hgap; + } + + /** + * Returns the vertical gap between lines of components. + * + * @return The vertical gap between lines of components. + */ + public int getVgap () + { + return vgap; + } + + /** + * Initializes a new instance of FlowLayout with a center + * justification and a default horizontal and vertical gap of 5. + */ + public FlowLayout () + { + this (CENTER, 5, 5); + } + + /** + * Initializes a new instance of FlowLayout with the specified + * justification and a default horizontal and vertical gap of 5. + * + * @param align The justification setting, which should be one of the + * contants in this class. + */ + public FlowLayout (int align) + { + this (align, 5, 5); + } + + /** + * Initializes a new instance of FlowLayout with the specified + * justification and gap values + * @param align Alignment + * @param hgap The horizontal gap + * @param vgap The vertical gap + * @exception IllegalArgumentException If either gap is negative + */ + public FlowLayout (int align, int hgap, int vgap) + { + // Use methods to set fields so that we can have all the checking + // in one place. + setVgap (vgap); + setHgap (hgap); + setAlignment (align); + } + + /** Lay out the container's components based on current settings. + * @param parent The parent container + */ + public void layoutContainer (Container parent) + { + synchronized (parent.getTreeLock ()) + { + int num = parent.getComponentCount (); + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + Dimension d = parent.getSize (); + Insets ins = parent.getInsets (); + + ComponentOrientation orient = parent.getComponentOrientation (); + boolean left_to_right = orient.isLeftToRight (); + + int y = ins.top + vgap; + int i = 0; + while (i < num) + { + // Find the components which go in the current row. + int new_w = ins.left + hgap + ins.right; + int new_h = 0; + int j; + boolean found_one = false; + for (j = i; j < num; ++j) + { + // Skip invisible items. + if (! comps[j].visible) + continue; + + Dimension c = comps[j].getPreferredSize (); + + int next_w = new_w + hgap + c.width; + if (next_w <= d.width || ! found_one) + { + new_w = next_w; + new_h = Math.max (new_h, c.height); + found_one = true; + } + else + { + // Must start a new row, and we already found an item + break; + } + } + + // Set the location of each component for this row. + int x; + + int myalign = align; + if (align == LEADING) + myalign = left_to_right ? LEFT : RIGHT; + else if (align == TRAILING) + myalign = left_to_right ? RIGHT : LEFT; + + if (myalign == RIGHT) + x = ins.left + (d.width - new_w) + hgap; + else if (myalign == CENTER) + x = ins.left + (d.width - new_w) / 2 + hgap; + else // LEFT and all other values of align. + x = ins.left + hgap; + + for (int k = i; k < j; ++k) + { + if (comps[k].visible) + { + Dimension c = comps[k].getPreferredSize (); + comps[k].setBounds (x, y + (new_h - c.height) / 2, + c.width, c.height); + x += c.width + hgap; + } + } + + // Advance to next row. + i = j; + y += new_h + vgap; + } + } + } + + /** + * Returns the minimum layout size for the specified container using + * this layout. + * @param cont The parent container + * @return The minimum layout size. + */ + public Dimension minimumLayoutSize (Container cont) + { + return getSize (cont, true); + } + + /** + * Returns the preferred layout size for the specified container using + * this layout. + * @param cont The parent container + * @return The preferred layout size. + */ + public Dimension preferredLayoutSize (Container cont) + { + return getSize (cont, false); + } + + /** Remove the indicated component from this layout manager. + * This particular implementation does nothing. + * @param comp The component to remove + */ + public void removeLayoutComponent (Component comp) + { + // Nothing. + } + + /** + * Sets the justification value for this object to the specified value. + * + * @param align The new justification value for this object, which must + * be one of the constants in this class. + */ + public void setAlignment (int align) + { + // The JDK accepts invalid values and treats them as + // LEFT during layout, so do we. The invalid value is even stored, + // getAlignment() returns the same invalid value. + this.align = align; + } + + /** + * Sets the horizontal gap between lines of components to the specified value. + * No Exception is thrown if hgap < 0. + * + * @param hgap The new horizontal gap between components. + */ + public void setHgap (int hgap) + { + this.hgap = hgap; + } + + /** + * Sets the vertical gap between lines of components to the specified value. + * No Exception is thrown if vgap < 0. + * + * @param vgap The new vertical gap. + */ + public void setVgap (int vgap) + { + this.vgap = vgap; + } + + /** Return String description of this object. + * @return A string representation of this object. + */ + public String toString () + { + return ("[" + getClass ().getName () + ",hgap=" + hgap + ",vgap=" + vgap + + ",align=" + align + "]"); + } + + // This method is used to compute the various sizes. + private Dimension getSize (Container parent, boolean is_min) + { + synchronized (parent.getTreeLock ()) + { + int w, h, num = parent.getComponentCount (); + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + w = 0; + h = 0; + for (int i = 0; i < num; ++i) + { + if (! comps[i].visible) + continue; + + // FIXME: can we just directly read the fields in Component? + // Or will that not work with subclassing? + Dimension d; + + if (is_min) + d = comps[i].getMinimumSize (); + else + d = comps[i].getPreferredSize (); + + w += d.width; + h = Math.max (d.height, h); + } + + Insets ins = parent.getInsets (); + + if (num == 0) + w += 2 * hgap + ins.left + ins.right; + else + w += (num + 1) * hgap + ins.left + ins.right; + h += 2 * vgap + ins.top + ins.bottom; + + return new Dimension (w, h); + } + } + + /** + * @serial The justification alignment of the lines of components, which + * will be one of the constants defined in this class. + */ + private int align; + + /** + * @serial The horizontal gap between components. + */ + private int hgap; + + /** + * @serial The vertical gap between lines of components. + */ + private int vgap; +} diff --git a/libjava/classpath/java/awt/FocusTraversalPolicy.java b/libjava/classpath/java/awt/FocusTraversalPolicy.java new file mode 100644 index 000000000..d9374eae8 --- /dev/null +++ b/libjava/classpath/java/awt/FocusTraversalPolicy.java @@ -0,0 +1,103 @@ +/* FocusTraversalPolicy.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * @since 1.4 + */ +public abstract class FocusTraversalPolicy +{ + /** + * Creates a FocusTraversalPolicy object. + */ + public FocusTraversalPolicy() + { + // Do nothing in here. + } + + /** + * Returns the Component that should receive the focus after a Component. + * + * @exception IllegalArgumentException If root or current is null, + * or if root is not a focus cycle root of current. + */ + public abstract Component getComponentAfter(Container root, + Component current); + + /** + * Returns the Component that should receive the focus before a Component. + * + * @exception IllegalArgumentException If root or current is null, + * or if root is not a focus cycle root of current. + */ + public abstract Component getComponentBefore(Container root, + Component current); + + /** + * Returns the first Component in the traversal cycle. + * + * @exception IllegalArgumentException If root is null. + */ + public abstract Component getFirstComponent(Container root); + + /** + * Returns the last Component in the traversal cycle. + * + * @exception IllegalArgumentException If root is null. + */ + public abstract Component getLastComponent(Container root); + + /** + * Returns the default Component to focus. + * + * @exception IllegalArgumentException If root is null. + */ + public abstract Component getDefaultComponent(Container root); + + /** + * Returns the Component that should receive the focus when a Window is made + * visible for the first time. + * + * @exception IllegalArgumentException If window is null. + */ + public Component getInitialComponent(Window window) + { + return getDefaultComponent(window); + } +} // class FocusTraversalPolicy diff --git a/libjava/classpath/java/awt/Font.java b/libjava/classpath/java/awt/Font.java new file mode 100644 index 000000000..89ff1f52a --- /dev/null +++ b/libjava/classpath/java/awt/Font.java @@ -0,0 +1,1451 @@ +/* Font.java -- Font object + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; +import gnu.java.awt.peer.ClasspathFontPeer; + +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.LineMetrics; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.peer.FontPeer; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * This class represents a windowing system font. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @author Graydon Hoare (graydon@redhat.com) + */ +public class Font implements Serializable +{ + + /** + * Constant indicating a "plain" font. + */ + public static final int PLAIN = 0; + + /** + * Constant indicating a "bold" font. + */ + public static final int BOLD = 1; + + /** + * Constant indicating an "italic" font. + */ + public static final int ITALIC = 2; + + /** + * Constant indicating the baseline mode characteristic of Roman. + */ + public static final int ROMAN_BASELINE = 0; + + /** + * Constant indicating the baseline mode characteristic of Chinese. + */ + public static final int CENTER_BASELINE = 1; + + /** + * Constant indicating the baseline mode characteristic of Devanigri. + */ + public static final int HANGING_BASELINE = 2; + + + /** + * Indicates to createFont that the supplied font data + * is in TrueType format. + * + *

Specification Note: The Sun JavaDoc for J2SE 1.4 does + * not indicate whether this value also subsumes OpenType. OpenType + * is essentially the same format as TrueType, but allows to define + * glyph shapes in the same way as PostScript, using cubic bezier + * curves. + * + * @since 1.3 + */ + public static final int TRUETYPE_FONT = 0; + + /** + * Indicates to createFont that the supplied font data + * is in Type1 format. + * + * @since 1.5 + */ + public static final int TYPE1_FONT = 1; + + /** + * A flag for layoutGlyphVector, indicating that the + * orientation of a text run is from left to right. + * + * @since 1.4 + */ + public static final int LAYOUT_LEFT_TO_RIGHT = 0; + + + /** + * A flag for layoutGlyphVector, indicating that the + * orientation of a text run is from right to left. + * + * @since 1.4 + */ + public static final int LAYOUT_RIGHT_TO_LEFT = 1; + + + /** + * A flag for layoutGlyphVector, indicating that the + * text does not contain valid characters before the + * start position. If this flag is set, + * layoutGlyphVector does not examine the text before + * start, even if this would be necessary to select the + * correct glyphs (e.g., for Arabic text). + * + * @since 1.4 + */ + public static final int LAYOUT_NO_START_CONTEXT = 2; + + + /** + * A flag for layoutGlyphVector, indicating that the + * text does not contain valid characters after the + * limit position. If this flag is set, + * layoutGlyphVector does not examine the text after + * limit, even if this would be necessary to select the + * correct glyphs (e.g., for Arabic text). + * + * @since 1.4 + */ + public static final int LAYOUT_NO_LIMIT_CONTEXT = 4; + + /** + * @since 1.6 + */ + public static final String DIALOG = "Dialog"; + + /** + * @since 1.6 + */ + public static final String DIALOG_INPUT = "DialogInput"; + + /** + * @since 1.6 + */ + public static final String MONOSPACED = "Monospaced"; + + /** + * @since 1.6 + */ + public static final String SANS_SERIF = "SansSerif"; + + /** + * @since 1.6 + */ + public static final String SERIF = "Serif"; + + /** + * The logical name of this font. + * + * @since 1.0 + */ + protected String name; + + /** + * The size of this font in points, rounded. + * + * @since 1.0 + */ + protected int size; + + /** + * The size of this font in points. + * + * @since 1.0 + */ + protected float pointSize; + + /** + * The style of this font -- PLAIN, BOLD, ITALIC or BOLD+ITALIC. + * + * @since 1.0 + */ + protected int style; + +//Serialization constant + private static final long serialVersionUID = -4206021311591459213L; + + + // The ClasspathToolkit-provided peer which implements this font + private transient ClasspathFontPeer peer; + + /** + * The cached hashcode. A value of 0 (default initialized) means that the + * hashcode is not computed yet. + */ + private transient int hashCode; + + /** + * Creates a Font object from the specified string, which + * is in one of the following formats: + *

+ *

    + *
  • fontname-style-pointsize + *
  • fontname-style + *
  • fontname-pointsize + *
  • fontname + *
+ *

+ * The style should be one of BOLD, ITALIC, or BOLDITALIC. The default + * style if none is specified is PLAIN. The default size if none + * is specified is 12. + * + * @param fontspec a string specifying the required font (null + * permitted, interpreted as 'Dialog-PLAIN-12'). + * + * @return A font. + */ + public static Font decode(String fontspec) + { + if (fontspec == null) + fontspec = "Dialog-PLAIN-12"; + String name = null; + int style = PLAIN; + int size = 12; + + StringTokenizer st = new StringTokenizer(fontspec, "- "); + while (st.hasMoreTokens()) + { + String token = st.nextToken(); + if (name == null) + { + name = token; + continue; + } + + if (token.toUpperCase().equals("BOLD")) + { + style = BOLD; + continue; + } + if (token.toUpperCase().equals("ITALIC")) + { + style = ITALIC; + continue; + } + if (token.toUpperCase().equals("BOLDITALIC")) + { + style = BOLD | ITALIC; + continue; + } + + int tokenval = 0; + try + { + tokenval = Integer.parseInt(token); + } + catch (NumberFormatException e) + { + // Ignored. + } + + if (tokenval != 0) + size = tokenval; + } + + HashMap attrs = new HashMap(); + ClasspathFontPeer.copyStyleToAttrs(style, attrs); + ClasspathFontPeer.copySizeToAttrs(size, attrs); + + return getFontFromToolkit(name, attrs); + } + + /* These methods delegate to the toolkit. */ + + static ClasspathToolkit tk() + { + return (ClasspathToolkit) Toolkit.getDefaultToolkit(); + } + + /* Every factory method in Font should eventually call this. */ + static Font getFontFromToolkit(String name, Map attribs) + { + return tk().getFont(name, attribs); + } + + /* Every Font constructor should eventually call this. */ + static ClasspathFontPeer getPeerFromToolkit(String name, Map attrs) + { + return tk().getClasspathFontPeer(name, attrs); + } + + + /** + * Returns a Font object from the passed property name. + * + * @param propname The name of the system property. + * @param defval Value to use if the property is not found. + * + * @return The requested font, or default if the property + * not exist or is malformed. + */ + public static Font getFont(String propname, Font defval) + { + String propval = System.getProperty(propname); + if (propval != null) + return decode(propval); + return defval; + } + + /** + * Returns a Font object from the passed property name. + * + * @param propname The name of the system property. + * + * @return The requested font, or null if the property + * not exist or is malformed. + */ + public static Font getFont(String propname) + { + return getFont(propname, (Font) null); + } + + protected Font(Font font) + { + this(font.getName(), font.getAttributes()); + } + + /** + * Initializes a new instance of Font with the specified + * attributes. + * + * @param name The name of the font. + * @param style The font style. + * @param size The font point size. + */ + public Font(String name, int style, int size) + { + HashMap attrs = new HashMap(); + ClasspathFontPeer.copyStyleToAttrs(style, attrs); + ClasspathFontPeer.copySizeToAttrs(size, attrs); + this.peer = getPeerFromToolkit(name, attrs); + this.size = size; + this.pointSize = (float) size; + if (name != null) + this.name = name; + else + this.name = peer.getName(this); + } + + public Font(Map attrs) + { + this(null, attrs); + } + + /* This extra constructor is here to permit ClasspathToolkit and to + build a font with a "logical name" as well as attrs. + ClasspathToolkit.getFont(String,Map) uses reflection to call this + package-private constructor. */ + Font(String name, Map attrs) + { + // If attrs is null, setting it to an empty HashMap will give this + // Font default attributes. + if (attrs == null) + attrs = new HashMap(); + peer = getPeerFromToolkit(name, attrs); + size = (int) peer.getSize(this); + pointSize = peer.getSize(this); + if (name != null) + this.name = name; + else + this.name = peer.getName(this); + } + + /** + * Returns the logical name of the font. A logical name is the name the + * font was constructed with. It may be the name of a logical font (one + * of 6 required names in all java environments) or it may be a face + * name. + * + * @return The logical name of the font. + * + * @see #getFamily() + * @see #getFontName() + */ + public String getName () + { + return peer.getName(this); + } + + /** + * Returns the size of the font, in typographics points (1/72 of an inch), + * rounded to an integer. + * + * @return The font size + */ + public int getSize() + { + return size; + } + + /** + * Returns the size of the font, in typographics points (1/72 of an inch). + * + * @return The font size + */ + public float getSize2D() + { + return pointSize; + } + + /** + * Tests whether or not this is a plain font. This will be true if + * and only if neither the bold nor the italics style is set. + * + * @return true if this is a plain font, false + * otherwise. + */ + public boolean isPlain() + { + return peer.isPlain(this); + } + + /** + * Tests whether or not this font is bold. + * + * @return true if this font is bold, false + * otherwise. + */ + public boolean isBold() + { + return peer.isBold(this); + } + + /** + * Tests whether or not this font is italic. + * + * @return true if this font is italic, false + * otherwise. + */ + public boolean isItalic() + { + return peer.isItalic(this); + } + + /** + * Returns the family name of this font. A family name describes a design + * or "brand name" (such as Helvetica or Palatino). It is less specific + * than a font face name (such as Helvetica Bold). + * + * @return A string containing the font family name. + * + * @since 1.2 + * + * @see #getName() + * @see #getFontName() + * @see GraphicsEnvironment#getAvailableFontFamilyNames() + */ + public String getFamily() + { + return peer.getFamily(this); + } + + /** + * Returns integer code representing the sum of style flags of this font, a + * combination of either {@link #PLAIN}, {@link #BOLD}, or {@link #ITALIC}. + * + * @return code representing the style of this font. + * + * @see #isPlain() + * @see #isBold() + * @see #isItalic() + */ + public int getStyle() + { + return peer.getStyle(this); + } + + /** + * Checks if specified character maps to a glyph in this font. + * + * @param c The character to check. + * + * @return Whether the character has a corresponding glyph in this font. + * + * @since 1.2 + */ + public boolean canDisplay(char c) + { + return canDisplay((int) c); + } + + public boolean canDisplay(int codePoint) + { + return peer.canDisplay(this, codePoint); + } + + /** + * Checks how much of a given string can be mapped to glyphs in + * this font. + * + * @param s The string to check. + * + * @return The index of the first character in s which cannot + * be converted to a glyph by this font, or -1 if all + * characters can be mapped to glyphs. + * + * @since 1.2 + */ + public int canDisplayUpTo(String s) + { + return peer.canDisplayUpTo(this, new StringCharacterIterator(s), + 0, s.length() - 1); + } + + /** + * Checks how much of a given sequence of text can be mapped to glyphs in + * this font. + * + * @param text Array containing the text to check. + * @param start Position of first character to check in text. + * @param limit Position of last character to check in text. + * + * @return The index of the first character in the indicated range which + * cannot be converted to a glyph by this font, or -1 if all + * characters can be mapped to glyphs. + * + * @since 1.2 + * + * @throws IndexOutOfBoundsException if the range [start, limit] is + * invalid in text. + */ + public int canDisplayUpTo (char[] text, int start, int limit) + { + return peer.canDisplayUpTo(this, + new StringCharacterIterator(new String (text)), + start, limit); + } + + /** + * Checks how much of a given sequence of text can be mapped to glyphs in + * this font. + * + * @param i Iterator over the text to check. + * @param start Position of first character to check in i. + * @param limit Position of last character to check in i. + * + * @return The index of the first character in the indicated range which + * cannot be converted to a glyph by this font, or -1 if all + * characters can be mapped to glyphs. + * + * @since 1.2 + * + * @throws IndexOutOfBoundsException if the range [start, limit] is + * invalid in i. + */ + public int canDisplayUpTo(CharacterIterator i, int start, int limit) + { + return peer.canDisplayUpTo(this, i, start, limit); + } + + /** + * Creates a new font with point size 1 and {@link #PLAIN} style, + * reading font data from the provided input stream. The resulting font + * can have further fonts derived from it using its + * deriveFont method. + * + * @param fontFormat Integer code indicating the format the font data is + * in.Currently this can only be {@link #TRUETYPE_FONT}. + * @param is {@link InputStream} from which font data will be read. This + * stream is not closed after font data is extracted. + * + * @return A new {@link Font} of the format indicated. + * + * @throws IllegalArgumentException if fontType is not + * recognized. + * @throws FontFormatException if data in InputStream is not of format + * indicated. + * @throws IOException if insufficient data is present on InputStream. + * + * @since 1.3 + */ + public static Font createFont (int fontFormat, InputStream is) + throws FontFormatException, IOException + { + return tk().createFont(fontFormat, is); + } + + /** + * Creates a new font from a File object. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + * + * @param fontFormat - Integer code indicating the format the font data is + * in.Currently this can only be {@link #TRUETYPE_FONT}. + * @param file - a {@link File} from which font data will be read. + * + * @return A new {@link Font} of the format indicated. + * + * @throws IllegalArgumentException if fontType is not + * recognized. + * @throws NullPointerException if file is null. + * @throws FontFormatException if data in the file is invalid or cannot be read.. + * @throws SecurityException if the caller has no read permission for the file. + * @throws IOException if the file cannot be read + * + * @since 1.5 + */ + public static Font createFont (int fontFormat, File file) + throws FontFormatException, IOException + { + if( file == null ) + throw new NullPointerException("Null file argument"); + return tk().createFont(fontFormat, new FileInputStream( file )); + } + + /** + * Maps characters to glyphs in a one-to-one relationship, returning a new + * {@link GlyphVector} with a mapped glyph for each input character. This + * sort of mapping is often sufficient for some scripts such as Roman, but + * is inappropriate for scripts with special shaping or contextual layout + * requirements such as Arabic, Indic, Hebrew or Thai. + * + * @param ctx The rendering context used for precise glyph placement. + * @param str The string to convert to Glyphs. + * + * @return A new {@link GlyphVector} containing glyphs mapped from str, + * through the font's cmap table. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + */ + public GlyphVector createGlyphVector(FontRenderContext ctx, String str) + { + return peer.createGlyphVector(this, ctx, new StringCharacterIterator(str)); + } + + /** + * Maps characters to glyphs in a one-to-one relationship, returning a new + * {@link GlyphVector} with a mapped glyph for each input character. This + * sort of mapping is often sufficient for some scripts such as Roman, but + * is inappropriate for scripts with special shaping or contextual layout + * requirements such as Arabic, Indic, Hebrew or Thai. + * + * @param ctx The rendering context used for precise glyph placement. + * @param i Iterator over the text to convert to glyphs. + * + * @return A new {@link GlyphVector} containing glyphs mapped from str, + * through the font's cmap table. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + */ + public GlyphVector createGlyphVector(FontRenderContext ctx, + CharacterIterator i) + { + return peer.createGlyphVector(this, ctx, i); + } + + /** + * Maps characters to glyphs in a one-to-one relationship, returning a new + * {@link GlyphVector} with a mapped glyph for each input character. This + * sort of mapping is often sufficient for some scripts such as Roman, but + * is inappropriate for scripts with special shaping or contextual layout + * requirements such as Arabic, Indic, Hebrew or Thai. + * + * @param ctx The rendering context used for precise glyph placement. + * @param chars Array of characters to convert to glyphs. + * + * @return A new {@link GlyphVector} containing glyphs mapped from str, + * through the font's cmap table. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + */ + public GlyphVector createGlyphVector(FontRenderContext ctx, char[] chars) + { + return peer.createGlyphVector(this, ctx, + new StringCharacterIterator(new String(chars))); + } + + /** + * Extracts a sequence of glyphs from a font, returning a new {@link + * GlyphVector} with a mapped glyph for each input glyph code. + * + * @param ctx The rendering context used for precise glyph placement. + * @param glyphCodes Array of characters to convert to glyphs. + * + * @return A new {@link GlyphVector} containing glyphs mapped from str, + * through the font's cmap table. + * + * @see #layoutGlyphVector(FontRenderContext, char[], int, int, int) + * + * @specnote This method is documented to perform character-to-glyph + * conversions, in the Sun documentation, but its second parameter name is + * "glyphCodes" and it is not clear to me why it would exist if its + * purpose was to transport character codes inside integers. I assume it + * is mis-documented in the Sun documentation. + */ + public GlyphVector createGlyphVector(FontRenderContext ctx, int[] glyphCodes) + { + return peer.createGlyphVector(this, ctx, glyphCodes); + } + + /** + * Produces a new {@link Font} based on the current font, adjusted to a + * new size and style. + * + * @param style The style of the newly created font. + * @param size The size of the newly created font. + * + * @return A clone of the current font, with the specified size and style. + * + * @since 1.2 + */ + public Font deriveFont(int style, float size) + { + return peer.deriveFont(this, style, size); + } + + /** + * Produces a new {@link Font} based on the current font, adjusted to a + * new size. + * + * @param size The size of the newly created font. + * + * @return A clone of the current font, with the specified size. + * + * @since 1.2 + */ + public Font deriveFont(float size) + { + return peer.deriveFont(this, size); + } + + /** + * Produces a new {@link Font} based on the current font, adjusted to a + * new style. + * + * @param style The style of the newly created font. + * + * @return A clone of the current font, with the specified style. + * + * @since 1.2 + */ + public Font deriveFont(int style) + { + return peer.deriveFont(this, style); + } + + /** + * Produces a new {@link Font} based on the current font, adjusted to a + * new style and subjected to a new affine transformation. + * + * @param style The style of the newly created font. + * @param a The transformation to apply. + * + * @return A clone of the current font, with the specified style and + * transform. + * + * @throws IllegalArgumentException If transformation is + * null. + * + * @since 1.2 + */ + public Font deriveFont(int style, AffineTransform a) + { + if (a == null) + throw new IllegalArgumentException("Affine transformation is null"); + + return peer.deriveFont(this, style, a); + } + + /** + * Produces a new {@link Font} based on the current font, subjected + * to a new affine transformation. + * + * @param a The transformation to apply. + * + * @return A clone of the current font, with the specified transform. + * + * @throws IllegalArgumentException If transformation is + * null. + * + * @since 1.2 + */ + public Font deriveFont(AffineTransform a) + { + if (a == null) + throw new IllegalArgumentException("Affine transformation is null"); + + return peer.deriveFont(this, a); + } + + /** + * Produces a new {@link Font} based on the current font, adjusted to a + * new set of attributes. + * + * @param attributes Attributes of the newly created font. + * + * @return A clone of the current font, with the specified attributes. + * + * @since 1.2 + */ + public Font deriveFont(Map attributes) + { + return peer.deriveFont(this, attributes); + } + + /** + * Returns a map of chracter attributes which this font currently has set. + * + * @return A map of chracter attributes which this font currently has set. + * + * @see #getAvailableAttributes() + * @see java.text.AttributedCharacterIterator.Attribute + * @see java.awt.font.TextAttribute + */ + public Map getAttributes() + { + return peer.getAttributes(this); + } + + /** + * Returns an array of chracter attribute keys which this font understands. + * + * @return An array of chracter attribute keys which this font understands. + * + * @see #getAttributes() + * @see java.text.AttributedCharacterIterator.Attribute + * @see java.awt.font.TextAttribute + */ + public AttributedCharacterIterator.Attribute[] getAvailableAttributes() + { + return peer.getAvailableAttributes(this); + } + + /** + * Returns a baseline code (one of {@link #ROMAN_BASELINE}, {@link + * #CENTER_BASELINE} or {@link #HANGING_BASELINE}) indicating which baseline + * this font will measure baseline offsets for, when presenting glyph + * metrics for a given character. + * + * Baseline offsets describe the position of a glyph relative to an + * invisible line drawn under, through the center of, or over a line of + * rendered text, respectively. Different scripts use different baseline + * modes, so clients should not assume all baseline offsets in a glyph + * vector are from a common baseline. + * + * @param c The character code to select a baseline mode for. + * + * @return The baseline mode which would be used in a glyph associated + * with the provided character. + * + * @since 1.2 + * + * @see LineMetrics#getBaselineOffsets() + */ + public byte getBaselineFor(char c) + { + return peer.getBaselineFor(this, c); + } + + /** + * Returns the family name of this font. A family name describes a + * typographic style (such as Helvetica or Palatino). It is more specific + * than a logical font name (such as Sans Serif) but less specific than a + * font face name (such as Helvetica Bold). + * + * @param lc The locale in which to describe the name of the font family. + * + * @return A string containing the font family name, localized for the + * provided locale. + * + * @since 1.2 + * + * @see #getName() + * @see #getFontName() + * @see GraphicsEnvironment#getAvailableFontFamilyNames() + * @see Locale + */ + public String getFamily(Locale lc) + { + return peer.getFamily(this, lc); + } + + /** + * Returns a font appropriate for the given attribute set. + * + * @param attributes The attributes required for the new font. + * + * @return A new Font with the given attributes. + * + * @since 1.2 + * + * @see java.awt.font.TextAttribute + */ + public static Font getFont(Map attributes) + { + return getFontFromToolkit(null, attributes); + } + + /** + * Returns the font face name of the font. A font face name describes a + * specific variant of a font family (such as Helvetica Bold). It is more + * specific than both a font family name (such as Helvetica) and a logical + * font name (such as Sans Serif). + * + * @return The font face name of the font. + * + * @since 1.2 + * + * @see #getName() + * @see #getFamily() + */ + public String getFontName() + { + return peer.getFontName(this); + } + + /** + * Returns the font face name of the font. A font face name describes a + * specific variant of a font family (such as Helvetica Bold). It is more + * specific than both a font family name (such as Helvetica). + * + * @param lc The locale in which to describe the name of the font face. + * + * @return A string containing the font face name, localized for the + * provided locale. + * + * @since 1.2 + * + * @see #getName() + * @see #getFamily() + */ + public String getFontName(Locale lc) + { + return peer.getFontName(this, lc); + } + + /** + * Returns the italic angle of this font, a measurement of its slant when + * style is {@link #ITALIC}. The precise meaning is the inverse slope of a + * caret line which "best measures" the font's italic posture. + * + * @return The italic angle. + * + * @see java.awt.font.TextAttribute#POSTURE + */ + public float getItalicAngle() + { + return peer.getItalicAngle(this); + } + + /** + * Returns a {@link LineMetrics} object constructed with the specified + * text and {@link FontRenderContext}. + * + * @param text The string to calculate metrics from. + * @param begin Index of first character in text to measure. + * @param limit Index of last character in text to measure. + * @param rc Context for calculating precise glyph placement and hints. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in text. + */ + public LineMetrics getLineMetrics(String text, int begin, + int limit, FontRenderContext rc) + { + return peer.getLineMetrics(this, new StringCharacterIterator(text), + begin, limit, rc); + } + + /** + * Returns a {@link LineMetrics} object constructed with the specified + * text and {@link FontRenderContext}. + * + * @param chars The string to calculate metrics from. + * @param begin Index of first character in text to measure. + * @param limit Index of last character in text to measure. + * @param rc Context for calculating precise glyph placement and hints. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in chars. + */ + public LineMetrics getLineMetrics(char[] chars, int begin, + int limit, FontRenderContext rc) + { + return peer.getLineMetrics(this, + new StringCharacterIterator(new String(chars)), + begin, limit, rc); + } + + /** + * Returns a {@link LineMetrics} object constructed with the specified + * text and {@link FontRenderContext}. + * + * @param ci The string to calculate metrics from. + * @param begin Index of first character in text to measure. + * @param limit Index of last character in text to measure. + * @param rc Context for calculating precise glyph placement and hints. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in ci. + */ + public LineMetrics getLineMetrics(CharacterIterator ci, int begin, + int limit, FontRenderContext rc) + { + return peer.getLineMetrics(this, ci, begin, limit, rc); + } + + /** + * Returns the maximal bounding box of all the bounding boxes in this + * font, when the font's bounding boxes are evaluated in a given {@link + * FontRenderContext} + * + * @param rc Context in which to evaluate bounding boxes. + * + * @return The maximal bounding box. + */ + public Rectangle2D getMaxCharBounds(FontRenderContext rc) + { + return peer.getMaxCharBounds(this, rc); + } + + /** + * Returns the glyph code this font uses to represent missing glyphs. This + * code will be present in glyph vectors when the font was unable to + * locate a glyph to represent a particular character code. + * + * @return The missing glyph code. + * + * @since 1.2 + */ + public int getMissingGlyphCode() + { + return peer.getMissingGlyphCode(this); + } + + /** + * Returns the overall number of glyphs in this font. This number is one + * more than the greatest glyph code used in any glyph vectors this font + * produces. In other words, glyph codes are taken from the range + * [ 0, getNumGlyphs() - 1 ]. + * + * @return The number of glyphs in this font. + * + * @since 1.2 + */ + public int getNumGlyphs() + { + return peer.getNumGlyphs(this); + } + + /** + * Returns the PostScript Name of this font. + * + * @return The PostScript Name of this font. + * + * @since 1.2 + * + * @see #getName() + * @see #getFamily() + * @see #getFontName() + */ + public String getPSName() + { + return peer.getPostScriptName(this); + } + + /** + * Returns the logical bounds of the specified string when rendered with this + * font in the specified {@link FontRenderContext}. This box will include the + * glyph origin, ascent, advance, height, and leading, but may not include all + * diacritics or accents. To get the complete visual bounding box of all the + * glyphs in a run of text, use the {@link TextLayout#getBounds} method of + * {@link TextLayout}. + * + * @param str The string to measure. + * @param frc The context in which to make the precise glyph measurements. + * + * @return A bounding box covering the logical bounds of the specified text. + * + * @see #createGlyphVector(FontRenderContext, String) + */ + public Rectangle2D getStringBounds(String str, FontRenderContext frc) + { + char[] chars = str.toCharArray(); + return getStringBounds(chars, 0, chars.length, frc); + } + + /** + * Returns the logical bounds of the specified string when rendered with this + * font in the specified {@link FontRenderContext}. This box will include the + * glyph origin, ascent, advance, height, and leading, but may not include all + * diacritics or accents. To get the complete visual bounding box of all the + * glyphs in a run of text, use the {@link TextLayout#getBounds} method of + * {@link TextLayout}. + * + * @param str The string to measure. + * @param begin Index of the first character in str to measure. + * @param limit Index of the last character in str to measure. + * @param frc The context in which to make the precise glyph measurements. + * + * @return A bounding box covering the logical bounds of the specified text. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in str. + * + * @since 1.2 + * + * @see #createGlyphVector(FontRenderContext, String) + */ + public Rectangle2D getStringBounds(String str, int begin, + int limit, FontRenderContext frc) + { + String sub = str.substring(begin, limit); + return getStringBounds(sub, frc); + } + + /** + * Returns the logical bounds of the specified string when rendered with this + * font in the specified {@link FontRenderContext}. This box will include the + * glyph origin, ascent, advance, height, and leading, but may not include all + * diacritics or accents. To get the complete visual bounding box of all the + * glyphs in a run of text, use the {@link TextLayout#getBounds} method of + * {@link TextLayout}. + * + * @param ci The text to measure. + * @param begin Index of the first character in ci to measure. + * @param limit Index of the last character in ci to measure. + * @param frc The context in which to make the precise glyph measurements. + * + * @return A bounding box covering the logical bounds of the specified text. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in ci. + * + * @since 1.2 + * + * @see #createGlyphVector(FontRenderContext, CharacterIterator) + */ + public Rectangle2D getStringBounds(CharacterIterator ci, int begin, + int limit, FontRenderContext frc) + { + int start = ci.getBeginIndex(); + int end = ci.getEndIndex(); + char[] chars = new char[limit - start]; + ci.setIndex(start); + for (int index = 0; index < chars.length; index++) + { + chars[index] = ci.current(); + ci.next(); + } + return getStringBounds(chars, 0, chars.length, frc); + } + + /** + * Returns the logical bounds of the specified string when rendered with this + * font in the specified {@link FontRenderContext}. This box will include the + * glyph origin, ascent, advance, height, and leading, but may not include all + * diacritics or accents. To get the complete visual bounding box of all the + * glyphs in a run of text, use the {@link TextLayout#getBounds} method of + * {@link TextLayout}. + * + * @param chars The text to measure. + * @param begin Index of the first character in ci to measure. + * @param limit Index of the last character in ci to measure. + * @param frc The context in which to make the precise glyph measurements. + * + * @return A bounding box covering the logical bounds of the specified text. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in chars. + * + * @since 1.2 + * + * @see #createGlyphVector(FontRenderContext, char[]) + */ + public Rectangle2D getStringBounds(char[] chars, int begin, + int limit, FontRenderContext frc) + { + String str = new String(chars, begin, limit - begin); + TextLayout layout = new TextLayout(str, this, frc); + return new Rectangle2D.Float(0, -layout.getAscent(), layout.getAdvance(), + layout.getDescent() + layout.getLeading()); + } + + /** + * Returns a copy of the affine transformation this font is currently + * subject to, if any. + * + * @return The current transformation. + */ + public AffineTransform getTransform() + { + return peer.getTransform(this); + } + + /** + * Indicates whether this font's line metrics are uniform. A font may be + * composed of several "subfonts", each covering a different code range, + * and each with their own line metrics. A font with no subfonts, or + * subfonts with identical line metrics, is said to have "uniform" line + * metrics. + * + * @return Whether this font has uniform line metrics. + * + * @see LineMetrics + * @see #getLineMetrics(String, FontRenderContext) + */ + public boolean hasUniformLineMetrics() + { + return peer.hasUniformLineMetrics(this); + } + + /** + * Indicates whether this font is subject to a non-identity affine + * transformation. + * + * @return true iff the font has a non-identity affine + * transformation applied to it. + */ + public boolean isTransformed() + { + return peer.isTransformed(this); + } + + /** + * Produces a glyph vector representing a full layout fo the specified + * text in this font. Full layouts may include complex shaping and + * reordering operations, for scripts such as Arabic or Hindi. + * + * Bidirectional (bidi) layout is not performed in this method; text + * should have its bidi direction specified with one of the flags {@link + * #LAYOUT_LEFT_TO_RIGHT} or {@link #LAYOUT_RIGHT_TO_LEFT}. + * + * Some types of layout (notably Arabic glyph shaping) may examine context + * characters beyond the bounds of the indicated range, in order to select + * an appropriate shape. The flags {@link #LAYOUT_NO_START_CONTEXT} and + * {@link #LAYOUT_NO_LIMIT_CONTEXT} can be provided to prevent these extra + * context areas from being examined, for instance if they contain invalid + * characters. + * + * @param frc Context in which to perform the layout. + * @param chars Text to perform layout on. + * @param start Index of first character to perform layout on. + * @param limit Index of last character to perform layout on. + * @param flags Combination of flags controlling layout. + * + * @return A new {@link GlyphVector} representing the specified text. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in chars. + */ + public GlyphVector layoutGlyphVector(FontRenderContext frc, + char[] chars, int start, + int limit, int flags) + { + return peer.layoutGlyphVector(this, frc, chars, start, limit, flags); + } + + + /** + * Returns a native peer object for this font. + * + * @return A native peer object for this font. + * + * @deprecated + */ + public FontPeer getPeer() + { + return peer; + } + + + /** + * Returns a hash value for this font. + * + * @return A hash for this font. + */ + public int hashCode() + { + // We cache the hashcode. This makes sense, because the font wouldn't + // change the relevant properties. + if (hashCode == 0) + { + hashCode = getName().hashCode() ^ getTransform().hashCode() ^ getSize() + ^ getStyle(); + // In the rare case when the above yields 0, we set this to some other + // value to avoid recomputing over and over again. This is still + // conform to the specification of hashCode(). + if (hashCode == 0) + { + hashCode = -1; + } + } + return hashCode; + } + + + /** + * Tests whether or not the specified object is equal to this font. This + * will be true if and only if: + *

+ *

    + *
  • The object is not null. + *
  • The object is an instance of Font. + *
  • The object has the same names, style, size, and transform as this object. + *
+ * + * @return true if the specified object is equal to this + * object, false otherwise. + */ + public boolean equals(Object obj) + { + if (obj == null) + return false; + + if (! (obj instanceof Font)) + return false; + + Font f = (Font) obj; + + return (f.getName().equals(this.getName()) + && f.getFamily().equals(this.getFamily()) + && f.getFontName().equals(this.getFontName()) + && f.getTransform().equals(this.getTransform ()) + && f.getSize() == this.getSize() + && f.getStyle() == this.getStyle()); + } + + /** + * Returns a string representation of this font. + * + * @return A string representation of this font. + */ + public String toString() + { + String styleString = ""; + + switch (getStyle()) + { + case 0: + styleString = "plain"; + break; + case 1: + styleString = "bold"; + break; + case 2: + styleString = "italic"; + break; + default: + styleString = "unknown"; + } + + return getClass().getName() + + "[family=" + getFamily () + + ",name=" + getFontName () + + ",style=" + styleString + + ",size=" + getSize () + "]"; + } + + + /** + * Determines the line metrics for a run of text. + * + * @param str the text run to be measured. + * + * @param frc the font rendering parameters that are used for the + * measurement. The exact placement and size of text slightly + * depends on device-specific characteristics, for instance + * the device resolution or anti-aliasing. For this reason, + * the returned measurement will only be accurate if the + * passed FontRenderContext correctly reflects + * the relevant parameters. Hence, frc should be + * obtained from the same Graphics2D that will + * be used for drawing, and any rendering hints should be set + * to the desired values before obtaining frc. + * + * @see java.awt.Graphics2D#getFontRenderContext() + */ + public LineMetrics getLineMetrics(String str, FontRenderContext frc) + { + return getLineMetrics(str, 0, str.length() - 1, frc); + } + + public boolean hasLayoutAttributes() + { + // TODO: Implement properly. + return false; + } + + /** + * Reads the normal fields from the stream and then constructs the + * peer from the style and size through getPeerFromToolkit(). + */ + private void readObject(ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + + HashMap attrs = new HashMap(); + ClasspathFontPeer.copyStyleToAttrs(style, attrs); + ClasspathFontPeer.copySizeToAttrs(size, attrs); + peer = getPeerFromToolkit(name, attrs); + + } +} diff --git a/libjava/classpath/java/awt/FontFormatException.java b/libjava/classpath/java/awt/FontFormatException.java new file mode 100644 index 000000000..6ec8757fb --- /dev/null +++ b/libjava/classpath/java/awt/FontFormatException.java @@ -0,0 +1,65 @@ +/* FontFormatException.java -- the specified font is bad + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * Thrown when a specified font is bad. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Font + * @since 1.3 + * @status updated to 1.4 + */ +public class FontFormatException extends Exception +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -4481290147811361272L; + + /** + * Create a new instance with the specified detailed error message. + * + * @param message the detailed error message + */ + public FontFormatException(String message) + { + super(message); + } +} // class FontFormatException diff --git a/libjava/classpath/java/awt/FontMetrics.java b/libjava/classpath/java/awt/FontMetrics.java new file mode 100644 index 000000000..e483d28fc --- /dev/null +++ b/libjava/classpath/java/awt/FontMetrics.java @@ -0,0 +1,448 @@ +/* FontMetrics.java -- Information about about a fonts display characteristics + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.font.FontRenderContext; +import java.awt.font.LineMetrics; +import java.awt.geom.Rectangle2D; +import java.text.CharacterIterator; + +// FIXME: I leave many methods basically unimplemented. This +// should be reviewed. + +/** + * This class returns information about the display characteristics of + * a font. It is abstract, and concrete subclasses should implement at + * least the following methods: + * + *
    + *
  • getAscent()
  • + *
  • getDescent()
  • + *
  • getLeading()
  • + *
  • getMaxAdvance()
  • + *
  • charWidth(char)
  • + *
  • charsWidth(char[], int, int)
  • + *
+ * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class FontMetrics implements java.io.Serializable +{ + // Serialization constant. + private static final long serialVersionUID = 1681126225205050147L; + + /** + * This is the font for which metrics will be returned. + */ + protected Font font; + + /** + * Initializes a new instance of FontMetrics for the + * specified font. + * + * @param font The font to return metric information for. + */ + protected FontMetrics(Font font) + { + this.font = font; + } + + /** + * Returns the font that this object is creating metric information fo. + * + * @return The font for this object. + */ + public Font getFont() + { + return font; + } + + /** + * Returns the leading, or spacing between lines, for this font. + * + * @return The font leading. + */ + public int getLeading() + { + return 0; + } + + /** + * Returns the ascent of the font, which is the distance from the base + * to the top of the majority of characters in the set. Some characters + * can exceed this value however. + * + * @return The font ascent. + */ + public int getAscent() + { + return 1; + } + + /** + * Returns the descent of the font, which is the distance from the base + * to the bottom of the majority of characters in the set. Some characters + * can exceed this value however. + * + * @return The font descent. + */ + public int getDescent() + { + return 1; + } + + /** + * Returns the height of a line in this font. This will be the sum + * of the leading, the ascent, and the descent. + * + * @return The height of the font. + */ + public int getHeight() + { + return getAscent() + getDescent() + getLeading(); + } + + /** + * Returns the maximum ascent value. This is the maximum distance any + * character in the font rised above the baseline. + * + * @return The maximum ascent for this font. + */ + public int getMaxAscent() + { + return getAscent(); + } + + /** + * Returns the maximum descent value. This is the maximum distance any + * character in the font extends below the baseline. + * + * @return The maximum descent for this font. + */ + public int getMaxDescent() + { + return getMaxDecent(); + } + + /** + * Returns the maximum descent value. This is the maximum distance any + * character in the font extends below the baseline. + * + * @return The maximum descent for this font. + * + * @deprecated This method is deprecated in favor of + * getMaxDescent(). + */ + public int getMaxDecent() + { + return getDescent(); + } + + /** + * Returns the width of the widest character in the font. + * + * @return The width of the widest character in the font. + */ + public int getMaxAdvance() + { + return -1; + } + + /** + * Returns the width of the specified character. + * + * @param ch The character to return the width of. + * + * @return The width of the specified character. + */ + public int charWidth(int ch) + { + char[] chars = Character.toChars(ch); + return charsWidth(chars, 0, chars.length); + } + + /** + * Returns the width of the specified character. + * + * @param ch The character to return the width of. + * + * @return The width of the specified character. + */ + public int charWidth(char ch) + { + return 1; + } + + /** + * Returns the total width of the specified string + * + * @param str The string to return the width of. + * + * @return The width of the string. + */ + public int stringWidth(String str) + { + char[] buf = new char[str.length()]; + str.getChars(0, str.length(), buf, 0); + + return charsWidth(buf, 0, buf.length); + } + + /** + * Returns the total width of the specified character array. + * + * @param buf The character array containing the data. + * @param offset The offset into the array to start calculating from. + * @param len The total number of bytes to process. + * + * @return The width of the requested characters. + */ + public int charsWidth(char[] buf, int offset, int len) + { + int total_width = 0; + int endOffset = offset + len; + for (int i = offset; i < endOffset; i++) + total_width += charWidth(buf[i]); + return total_width; + } + + /** + * Returns the total width of the specified byte array. + * + * @param buf The byte array containing the data. + * @param offset The offset into the array to start calculating from. + * @param len The total number of bytes to process. + * + * @return The width of the requested characters. + */ + public int bytesWidth(byte[] buf, int offset, int len) + { + int total_width = 0; + for (int i = offset; i < len; i++) + total_width = charWidth((char) buf[i]); + + return total_width; + } + + /** + * Returns the widths of the first 256 characters in the font. + * + * @return The widths of the first 256 characters in the font. + */ + public int[] getWidths() + { + int[] result = new int[256]; + for (char i = 0; i < 256; i++) + result[i] = charWidth(i); + return result; + } + + /** + * Returns a string representation of this object. + * + * @return A string representation of this object. + */ + public String toString() + { + return (this.getClass() + "[font=" + font + ",ascent=" + getAscent() + + ",descent=" + getDescent() + ",height=" + getHeight() + "]"); + } + + // Generic FontRenderContext used when getLineMetrics is called with a + // plain Graphics object. + private static final FontRenderContext gRC = new FontRenderContext(null, + false, + false); + + /** + * Returns a {@link LineMetrics} object constructed with the + * specified text and the {@link FontRenderContext} of the Graphics + * object when it is an instance of Graphics2D or a generic + * FontRenderContext with a null transform, not anti-aliased and not + * using fractional metrics. + * + * @param text The string to calculate metrics from. + * @param g The Graphics object that will be used. + * + * @return A new {@link LineMetrics} object. + */ + public LineMetrics getLineMetrics(String text, Graphics g) + { + return getLineMetrics(text, 0, text.length(), g); + } + + /** + * Returns a {@link LineMetrics} object constructed with the + * specified text and the {@link FontRenderContext} of the Graphics + * object when it is an instance of Graphics2D or a generic + * FontRenderContext with a null transform, not anti-aliased and not + * using fractional metrics. + * + * @param text The string to calculate metrics from. + * @param begin Index of first character in text to measure. + * @param limit Index of last character in text to measure. + * @param g The Graphics object that will be used. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in text. + */ + public LineMetrics getLineMetrics(String text, int begin, int limit, + Graphics g) + { + FontRenderContext rc; + if (g instanceof Graphics2D) + rc = ((Graphics2D) g).getFontRenderContext(); + else + rc = gRC; + return font.getLineMetrics(text, begin, limit, rc); + } + + /** + * Returns a {@link LineMetrics} object constructed with the + * specified text and the {@link FontRenderContext} of the Graphics + * object when it is an instance of Graphics2D or a generic + * FontRenderContext with a null transform, not anti-aliased and not + * using fractional metrics. + * + * @param chars The string to calculate metrics from. + * @param begin Index of first character in text to measure. + * @param limit Index of last character in text to measure. + * @param g The Graphics object that will be used. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in text. + */ + public LineMetrics getLineMetrics(char[] chars, int begin, int limit, + Graphics g) + { + FontRenderContext rc; + if (g instanceof Graphics2D) + rc = ((Graphics2D) g).getFontRenderContext(); + else + rc = gRC; + return font.getLineMetrics(chars, begin, limit, rc); + } + + /** + * Returns the bounds of the largest character in a Graphics context. + * @param context the Graphics context object. + * @return a Rectangle2D representing the bounds + */ + public Rectangle2D getMaxCharBounds(Graphics context) + { + if( context instanceof Graphics2D ) + return font.getMaxCharBounds(((Graphics2D)context).getFontRenderContext()); + return font.getMaxCharBounds( gRC ); + } + + /** + * Returns a {@link LineMetrics} object constructed with the + * specified text and the {@link FontRenderContext} of the Graphics + * object when it is an instance of Graphics2D or a generic + * FontRenderContext with a null transform, not anti-aliased and not + * using fractional metrics. + * + * @param ci An iterator over the string to calculate metrics from. + * @param begin Index of first character in text to measure. + * @param limit Index of last character in text to measure. + * @param g The Graphics object that will be used. + * + * @return A new {@link LineMetrics} object. + * + * @throws IndexOutOfBoundsException if the range [begin, limit] is + * invalid in text. + */ + public LineMetrics getLineMetrics(CharacterIterator ci, int begin, + int limit, Graphics g) + { + FontRenderContext rc; + if (g instanceof Graphics2D) + rc = ((Graphics2D) g).getFontRenderContext(); + else + rc = gRC; + return font.getLineMetrics(ci, begin, limit, rc); + } + + public Rectangle2D getStringBounds(String str, Graphics context) + { + return font.getStringBounds(str, getFontRenderContext(context)); + } + + public Rectangle2D getStringBounds(String str, int beginIndex, int limit, + Graphics context) + { + return font.getStringBounds(str, beginIndex, limit, + getFontRenderContext(context)); + } + + public Rectangle2D getStringBounds(char[] chars, int beginIndex, int limit, + Graphics context) + { + return font.getStringBounds(chars, beginIndex, limit, + getFontRenderContext(context)); + } + + public Rectangle2D getStringBounds(CharacterIterator ci, int beginIndex, + int limit, Graphics context) + { + return font.getStringBounds(ci, beginIndex, limit, + getFontRenderContext(context)); + } + + private FontRenderContext getFontRenderContext(Graphics context) + { + if (context instanceof Graphics2D) + return ((Graphics2D) context).getFontRenderContext(); + + return gRC; + } + + /** + * Returns if the font has uniform line metrics. + * @see Font#hasUniformLineMetrics() + */ + public boolean hasUniformLineMetrics() + { + return font.hasUniformLineMetrics(); + } +} diff --git a/libjava/classpath/java/awt/Frame.java b/libjava/classpath/java/awt/Frame.java new file mode 100644 index 000000000..83bdb0947 --- /dev/null +++ b/libjava/classpath/java/awt/Frame.java @@ -0,0 +1,704 @@ +/* Frame.java -- AWT toplevel window + Copyright (C) 1999, 2000, 2002, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.FramePeer; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Vector; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * This class is a top-level window with a title bar and window + * decorations. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Frame extends Window implements MenuContainer +{ + + /** + * Constant for the default cursor. + * + * @deprecated Replaced by Cursor.DEFAULT_CURSOR instead. + */ + public static final int DEFAULT_CURSOR = Cursor.DEFAULT_CURSOR; + + /** + * Constant for a cross-hair cursor. + * + * @deprecated Use Cursor.CROSSHAIR_CURSOR instead. + */ + public static final int CROSSHAIR_CURSOR = Cursor.CROSSHAIR_CURSOR; + + /** + * Constant for a cursor over a text field. + * + * @deprecated Use Cursor.TEXT_CURSOR instead. + */ + public static final int TEXT_CURSOR = Cursor.TEXT_CURSOR; + + /** + * Constant for a cursor to display while waiting for an action to complete. + * + * @deprecated Use Cursor.WAIT_CURSOR. + */ + public static final int WAIT_CURSOR = Cursor.WAIT_CURSOR; + + /** + * Cursor used over SW corner of window decorations. + * + * @deprecated Use Cursor.SW_RESIZE_CURSOR instead. + */ + public static final int SW_RESIZE_CURSOR = Cursor.SW_RESIZE_CURSOR; + + /** + * Cursor used over SE corner of window decorations. + * @deprecated Use Cursor.SE_RESIZE_CURSOR instead. + */ + public static final int SE_RESIZE_CURSOR = Cursor.SE_RESIZE_CURSOR; + + /** + * Cursor used over NW corner of window decorations. + * + * @deprecated Use Cursor.NW_RESIZE_CURSOR instead. + */ + public static final int NW_RESIZE_CURSOR = Cursor.NW_RESIZE_CURSOR; + + /** + * Cursor used over NE corner of window decorations. + * + * @deprecated Use Cursor.NE_RESIZE_CURSOR instead. + */ + public static final int NE_RESIZE_CURSOR = Cursor.NE_RESIZE_CURSOR; + + /** + * Cursor used over N edge of window decorations. + * + * @deprecated Use Cursor.N_RESIZE_CURSOR instead. + */ + public static final int N_RESIZE_CURSOR = Cursor.N_RESIZE_CURSOR; + + /** + * Cursor used over S edge of window decorations. + * + * @deprecated Use Cursor.S_RESIZE_CURSOR instead. + */ + public static final int S_RESIZE_CURSOR = Cursor.S_RESIZE_CURSOR; + + /** + * Cursor used over E edge of window decorations. + * + * @deprecated Use Cursor.E_RESIZE_CURSOR instead. + */ + public static final int E_RESIZE_CURSOR = Cursor.E_RESIZE_CURSOR; + + /** + * Cursor used over W edge of window decorations. + * + * @deprecated Use Cursor.W_RESIZE_CURSOR instead. + */ + public static final int W_RESIZE_CURSOR = Cursor.W_RESIZE_CURSOR; + + /** + * Constant for a hand cursor. + * + * @deprecated Use Cursor.HAND_CURSOR instead. + */ + public static final int HAND_CURSOR = Cursor.HAND_CURSOR; + + /** + * Constant for a cursor used during window move operations. + * + * @deprecated Use Cursor.MOVE_CURSOR instead. + */ + public static final int MOVE_CURSOR = Cursor.MOVE_CURSOR; + + public static final int ICONIFIED = 1; + public static final int MAXIMIZED_BOTH = 6; + public static final int MAXIMIZED_HORIZ = 2; + public static final int MAXIMIZED_VERT = 4; + public static final int NORMAL = 0; + +//Serialization version constant + private static final long serialVersionUID = 2673458971256075116L; + + /** + * @serial The version of the class data being serialized + * FIXME: what is this value? + */ + private int frameSerializedDataVersion; + + /** + * @serial Image used as the icon when this frame is minimized. + */ + private Image icon; + + /** + * @serial Constant used by the JDK Motif peer set. Not used in + * this implementation. + */ + private boolean mbManagement; + + /** + * @serial The menu bar for this frame. + */ + private MenuBar menuBar; + + /** + * @serial A list of other top-level windows owned by this window. + */ + Vector ownedWindows = new Vector(); + + /** + * @serial Indicates whether or not this frame is resizable. + */ + private boolean resizable = true; + + /** + * @serial The state of this frame. + * // FIXME: What are the values here? + * This is package-private to avoid an accessor method. + */ + int state; + + /** + * @serial The title of the frame. + */ + private String title = ""; + + /** + * Maximized bounds for this frame. + */ + private Rectangle maximizedBounds; + + /** + * This field indicates whether the frame is undecorated or not. + */ + private boolean undecorated = false; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_frame_number; + + /** + * Initializes a new instance of Frame that is not visible + * and has no title. + */ + public Frame() + { + this(""); + noteFrame(this); + } + + /** + * Initializes a new instance of Frame that is not visible + * and has the specified title. + * + * @param title the title of this frame + */ + public Frame(String title) + { + super(); + this.title = title; + // Top-level frames are initially invisible. + visible = false; + noteFrame(this); + } + + public Frame(GraphicsConfiguration gc) + { + super(gc); + visible = false; + noteFrame(this); + } + + public Frame(String title, GraphicsConfiguration gc) + { + super(gc); + setTitle(title); + visible = false; + noteFrame(this); + } + + /** + * Returns this frame's title string. + * + * @return this frame's title string + */ + public String getTitle() + { + return title; + } + + /** + * Sets this frame's title to the specified value. + * + * @param title the new frame title + */ + public synchronized void setTitle(String title) + { + this.title = title; + if (peer != null) + ((FramePeer) peer).setTitle(title); + } + + /** + * Returns this frame's icon. + * + * @return this frame's icon, or null if this frame does not + * have an icon + */ + public Image getIconImage() + { + return icon; + } + + /** + * Sets this frame's icon to the specified value. + * + * @icon the new icon for this frame + */ + public synchronized void setIconImage(Image icon) + { + this.icon = icon; + if (peer != null) + ((FramePeer) peer).setIconImage(icon); + } + + /** + * Returns this frame's menu bar. + * + * @return this frame's menu bar, or null if this frame + * does not have a menu bar + */ + public MenuBar getMenuBar() + { + return menuBar; + } + + /** + * Sets this frame's menu bar. Removes any existing menu bar. If the + * given menu bar is part of another frame it will be removed from + * that frame. + * + * @param menuBar the new menu bar for this frame + */ + public synchronized void setMenuBar(MenuBar menuBar) + { + if (this.menuBar != null) + remove(this.menuBar); + + this.menuBar = menuBar; + if (menuBar != null) + { + MenuContainer parent = menuBar.getParent(); + if (parent != null) + parent.remove(menuBar); + menuBar.setParent(this); + + // Create local copy for thread safety. + FramePeer p = (FramePeer) peer; + if (p != null) + { + if (menuBar != null) + menuBar.addNotify(); + if (valid) + invalidate(); + p.setMenuBar(menuBar); + } + } + } + + /** + * Tests whether or not this frame is resizable. This will be + * true by default. + * + * @return true if this frame is resizable, false + * otherwise + */ + public boolean isResizable() + { + return resizable; + } + + /** + * Sets the resizability of this frame to the specified value. + * + * @param resizable true to make the frame resizable, + * false to make it non-resizable + */ + public synchronized void setResizable(boolean resizable) + { + this.resizable = resizable; + if (peer != null) + ((FramePeer) peer).setResizable(resizable); + } + + /** + * Returns the cursor type of the cursor for this window. This will + * be one of the constants in this class. + * + * @return the cursor type for this frame + * + * @deprecated Use Component.getCursor() instead. + */ + public int getCursorType() + { + return getCursor().getType(); + } + + /** + * Sets the cursor for this window to the specified type. The specified + * type should be one of the constants in this class. + * + * @param type the cursor type + * + * @deprecated Use Component.setCursor(Cursor) instead. + */ + public void setCursor(int type) + { + setCursor(new Cursor(type)); + } + + /** + * Removes the specified menu component from this frame. If it is + * the current MenuBar it is removed from the frame. If it is a + * Popup it is removed from this component. If it is any other menu + * component it is ignored. + * + * @param menu the menu component to remove + */ + public void remove(MenuComponent menu) + { + if (menu == menuBar) + { + if (menuBar != null) + { + if (peer != null) + { + ((FramePeer) peer).setMenuBar(null); + menuBar.removeNotify(); + } + menuBar.setParent(null); + } + menuBar = null; + } + else + super.remove(menu); + } + + public void addNotify() + { + if (menuBar != null) + menuBar.addNotify(); + if (peer == null) + peer = getToolkit ().createFrame (this); + + super.addNotify(); + } + + public void removeNotify() + { + if (menuBar != null) + menuBar.removeNotify(); + super.removeNotify(); + } + + /** + * Returns a debugging string describing this window. + * + * @return a debugging string describing this window + */ + protected String paramString() + { + String title = getTitle(); + + String resizable = ""; + if (isResizable ()) + resizable = ",resizable"; + + String state = ""; + switch (getState ()) + { + case NORMAL: + state = ",normal"; + break; + case ICONIFIED: + state = ",iconified"; + break; + case MAXIMIZED_BOTH: + state = ",maximized-both"; + break; + case MAXIMIZED_HORIZ: + state = ",maximized-horiz"; + break; + case MAXIMIZED_VERT: + state = ",maximized-vert"; + break; + } + + return super.paramString () + ",title=" + title + resizable + state; + } + + /** + * The list of active frames. GC'ed frames get removed in noteFrame(). + */ + private static ArrayList> weakFrames = + new ArrayList>(); + + /** + * The death queue for all frames. + */ + private static ReferenceQueue weakFramesQueue = + new ReferenceQueue(); + + private static void noteFrame(Frame f) + { + synchronized (weakFrames) + { + // Remove GCed frames from the list. + Reference ref = weakFramesQueue.poll(); + while (ref != null) + { + weakFrames.remove(ref); + ref = weakFramesQueue.poll(); + } + // Add new frame. + weakFrames.add(new WeakReference(f)); + } + } + + /** + * Returns true when there are any displayable frames, + * false otherwise. + * + * @return true when there are any displayable frames, + * false otherwise + */ + static boolean hasDisplayableFrames() + { + synchronized (weakFrames) + { + for (WeakReference r : Frame.weakFrames) + { + Frame f = (Frame) r.get(); + if (f != null && f.isDisplayable()) + return true; + } + } + return false; + } + + public static Frame[] getFrames() + { + synchronized (weakFrames) + { + ArrayList existingFrames = new ArrayList(); + for (WeakReference ref : weakFrames) + { + Frame f = ref.get(); + if (f != null) + { + existingFrames.add(f); + } + } + Frame[] frames = new Frame[existingFrames.size()]; + frames = existingFrames.toArray(frames); + return frames; + } + } + + public void setState(int state) + { + int current_state = getExtendedState (); + + if (state == NORMAL + && (current_state & ICONIFIED) != 0) + setExtendedState(current_state | ICONIFIED); + + if (state == ICONIFIED + && (current_state & ~ICONIFIED) == 0) + setExtendedState(current_state & ~ICONIFIED); + } + + public int getState() + { + return (getExtendedState() & ICONIFIED) != 0 ? ICONIFIED : NORMAL; + } + + /** + * @since 1.4 + */ + public void setExtendedState(int state) + { + if (getToolkit().isFrameStateSupported(state)) + { + this.state = state; + FramePeer p = (FramePeer) peer; + if (p != null) + p.setState(state); + } + } + + /** + * @since 1.4 + */ + public int getExtendedState() + { + FramePeer p = (FramePeer) peer; + if (p != null) + state = p.getState(); + return state; + } + + /** + * @since 1.4 + */ + public void setMaximizedBounds(Rectangle maximizedBounds) + { + this.maximizedBounds = maximizedBounds; + } + + /** + * Returns the maximized bounds of this frame. + * + * @return the maximized rectangle, may be null + * + * @since 1.4 + */ + public Rectangle getMaximizedBounds() + { + return maximizedBounds; + } + + /** + * Returns whether this frame is undecorated or not. + * + * @since 1.4 + */ + public boolean isUndecorated() + { + return undecorated; + } + + /** + * Disables or enables decorations for this frame. This method can only be + * called while the frame is not displayable. + * + * @throws IllegalComponentStateException if this frame is displayable + * + * @since 1.4 + */ + public void setUndecorated(boolean undecorated) + { + if (isDisplayable()) + throw new IllegalComponentStateException(); + + this.undecorated = undecorated; + } + + /** + * Generate a unique name for this frame. + * + * @return a unique name for this frame + */ + String generateName() + { + return "frame" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_frame_number++; + } + + /** + * Accessibility support for Frame. + */ + protected class AccessibleAWTFrame extends AccessibleAWTWindow + { + private static final long serialVersionUID = -6172960752956030250L; + + /** + * Gets the role of this object. + * @return AccessibleRole.FRAME + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.FRAME; + } + + /** + * Gets the state set of this object. + * @return The current state of this frame. + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (isResizable()) + states.add(AccessibleState.RESIZABLE); + if ((state & ICONIFIED) != 0) + states.add(AccessibleState.ICONIFIED); + return states; + } + } + + /** + * Gets the AccessibleContext associated with this Frame. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + // Create the context if this is the first request. + if (accessibleContext == null) + accessibleContext = new AccessibleAWTFrame(); + return accessibleContext; + } +} diff --git a/libjava/classpath/java/awt/GradientPaint.java b/libjava/classpath/java/awt/GradientPaint.java new file mode 100644 index 000000000..0bb840755 --- /dev/null +++ b/libjava/classpath/java/awt/GradientPaint.java @@ -0,0 +1,229 @@ +/* GradientPaint.java -- + Copyright (C) 2002, 2005, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import gnu.java.awt.GradientPaintContext; + +/** + * A paint object that can be used to color a region by blending two colors. + * Instances of this class are immutable. + */ +public class GradientPaint implements Paint +{ + private final float x1; + private final float y1; + private final Color c1; + private final float x2; + private final float y2; + private final Color c2; + private final boolean cyclic; + + /** + * Creates a new acyclic GradientPaint. + * + * @param x1 the x-coordinate of the anchor point for color 1. + * @param y1 the y-coordinate of the anchor point for color 1. + * @param c1 color 1 (null not permitted). + * @param x2 the x-coordinate of the anchor point for color 2. + * @param y2 the y-coordinate of the anchor point for color 2. + * @param c2 the second color (null not permitted). + */ + public GradientPaint(float x1, float y1, Color c1, + float x2, float y2, Color c2) + { + this(x1, y1, c1, x2, y2, c2, false); + } + + /** + * Creates a new acyclic GradientPaint. + * + * @param p1 anchor point 1 (null not permitted). + * @param c1 color 1 (null not permitted). + * @param p2 anchor point 2 (null not permitted). + * @param c2 color 2 (null not permitted). + */ + public GradientPaint(Point2D p1, Color c1, Point2D p2, Color c2) + { + this((float) p1.getX(), (float) p1.getY(), c1, + (float) p2.getX(), (float) p2.getY(), c2, false); + } + + /** + * Creates a new cyclic or acyclic GradientPaint. + * + * @param x1 the x-coordinate of the anchor point for color 1. + * @param y1 the y-coordinate of the anchor point for color 1. + * @param c1 color 1 (null not permitted). + * @param x2 the x-coordinate of the anchor point for color 2. + * @param y2 the y-coordinate of the anchor point for color 2. + * @param c2 the second color (null not permitted). + * @param cyclic a flag that controls whether the gradient is cyclic or + * acyclic. + */ + public GradientPaint(float x1, float y1, Color c1, + float x2, float y2, Color c2, boolean cyclic) + { + if (c1 == null || c2 == null) + throw new NullPointerException(); + this.x1 = x1; + this.y1 = y1; + this.c1 = c1; + this.x2 = x2; + this.y2 = y2; + this.c2 = c2; + this.cyclic = cyclic; + } + + /** + * Creates a new cyclic or acyclic GradientPaint. + * + * @param p1 anchor point 1 (null not permitted). + * @param c1 color 1 (null not permitted). + * @param p2 anchor point 2 (null not permitted). + * @param c2 color 2 (null not permitted). + * @param cyclic a flag that controls whether the gradient is cyclic or + * acyclic. + */ + public GradientPaint(Point2D p1, Color c1, Point2D p2, Color c2, + boolean cyclic) + { + this((float) p1.getX(), (float) p1.getY(), c1, + (float) p2.getX(), (float) p2.getY(), c2, cyclic); + } + + /** + * Returns a point with the same coordinates as the anchor point for color 1. + * Note that if you modify this point, the GradientPaint remains + * unchanged. + * + * @return A point with the same coordinates as the anchor point for color 1. + */ + public Point2D getPoint1() + { + return new Point2D.Float(x1, y1); + } + + /** + * Returns the first color. + * + * @return The color (never null). + */ + public Color getColor1() + { + return c1; + } + + /** + * Returns a point with the same coordinates as the anchor point for color 2. + * Note that if you modify this point, the GradientPaint remains + * unchanged. + * + * @return A point with the same coordinates as the anchor point for color 2. + */ + public Point2D getPoint2() + { + return new Point2D.Float(x2, y2); + } + + /** + * Returns the second color. + * + * @return The color (never null). + */ + public Color getColor2() + { + return c2; + } + + /** + * Returns true if this GradientPaint instance is + * cyclic, and false otherwise. + * + * @return A boolean. + */ + public boolean isCyclic() + { + return cyclic; + } + + /** + * Returns the {@link PaintContext} used to generate the color pattern. + * + * @param cm the color model, used as a hint (ignored in this + * implementation). + * @param deviceBounds the device space bounding box of the painted area. + * @param userBounds the user space bounding box of the painted area. + * @param xform the transformation from user space to device space. + * @param hints any hints for choosing between rendering alternatives. + * + * @return The context for performing the paint + */ + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + Point2D xp1 = xform.transform(getPoint1(), null); + Point2D xp2 = xform.transform(getPoint2(), null); + return new GradientPaintContext((float) xp1.getX(), (float) xp1.getY(), c1, + (float) xp2.getX(), (float) xp2.getY(), c2, cyclic); + } + + /** + * Returns the transparency code for this GradientPaint instance. + * This is derived from the two {@link Color} objects used in creating this + * object: if both colors are opaque, this method returns + * {@link Transparency#OPAQUE}, otherwise it returns + * {@link Transparency#TRANSLUCENT}. + * + * @return {@link Transparency#OPAQUE} or {@link Transparency#TRANSLUCENT}. + */ + public int getTransparency() + { + if (c1.getAlpha() == 255 && c2.getAlpha() == 255) + return Transparency.OPAQUE; + else + return Transparency.TRANSLUCENT; + } + +} // class GradientPaint diff --git a/libjava/classpath/java/awt/Graphics.java b/libjava/classpath/java/awt/Graphics.java new file mode 100644 index 000000000..dfc772207 --- /dev/null +++ b/libjava/classpath/java/awt/Graphics.java @@ -0,0 +1,639 @@ +/* Graphics.java -- Abstract Java drawing class + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ImageObserver; +import java.text.AttributedCharacterIterator; + +/** + * This is the abstract superclass of classes for drawing to graphics + * devices such as the screen or printers. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class Graphics +{ + + /** + * Default constructor for subclasses. + */ + protected + Graphics() + { + } + + /** + * Returns a copy of this Graphics object. + * + * @return A copy of this object. + */ + public abstract Graphics create(); + + /** + * Returns a copy of this Graphics object. The origin point + * will be translated to the point (x, y) and the cliping rectangle set + * to the intersection of the clipping rectangle in this object and the + * rectangle specified by the parameters to this method. + * + * @param x The new X coordinate of the clipping region rect. + * @param y The new Y coordinate of the clipping region rect. + * @param width The width of the clipping region intersect rectangle. + * @param height The height of the clipping region intersect rectangle. + * + * @return A copy of this object, modified as specified. + */ + public Graphics create(int x, int y, int width, int height) + { + Graphics g = create(); + + g.translate(x, y); + // FIXME: I'm not sure if this will work. Are the old clip rect bounds + // translated above? + g.clipRect(0, 0, width, height); + + return(g); + } + + /** + * Translates this context so that its new origin point is the point + * (x, y). + * + * @param x The new X coordinate of the origin. + * @param y The new Y coordinate of the origin. + */ + public abstract void translate(int x, int y); + + /** + * Returns the current color for this object. + * + * @return The color for this object. + */ + public abstract Color getColor(); + + /** + * Sets the current color for this object. + * + * @param color The new color. + */ + public abstract void setColor(Color color); + + /** + * Sets this context into "paint" mode, where the target pixels are + * completely overwritten when drawn on. + */ + public abstract void setPaintMode(); + + /** + * Sets this context info "XOR" mode, where the targe pixles are + * XOR-ed when drawn on. + * + * @param color The color to XOR against. + */ + public abstract void setXORMode(Color color); + + /** + * Returns the current font for this graphics context. + * + * @return The current font. + */ + public abstract Font getFont(); + + /** + * Sets the font for this graphics context to the specified value. + * + * @param font The new font. + */ + public abstract void setFont(Font font); + + /** + * Returns the font metrics for the current font. + * + * @return The font metrics for the current font. + */ + public FontMetrics getFontMetrics() + { + return getFontMetrics(getFont()); + } + + /** + * Returns the font metrics for the specified font. + * + * @param font The font to return metrics for. + * + * @return The requested font metrics. + */ + public abstract FontMetrics getFontMetrics(Font font); + + /** + * Returns the bounding rectangle of the clipping region for this + * graphics context. + * + * @return The bounding rectangle for the clipping region. + */ + public abstract Rectangle getClipBounds(); + + /** + * Returns the bounding rectangle of the clipping region for this + * graphics context. + * + * @return The bounding rectangle for the clipping region. + * + * @deprecated This method is deprecated in favor of + * getClipBounds(). + */ + public Rectangle getClipRect() + { + return getClipBounds(); + } + + /** + * Sets the clipping region to the intersection of the current clipping + * region and the rectangle determined by the specified parameters. + * + * @param x The X coordinate of the upper left corner of the intersect rect. + * @param y The Y coordinate of the upper left corner of the intersect rect. + * @param width The width of the intersect rect. + * @param height The height of the intersect rect. + */ + public abstract void clipRect(int x, int y, int width, int height); + + /** + * Sets the clipping region to the rectangle determined by the specified + * parameters. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + */ + public abstract void setClip(int x, int y, int width, int height); + + /** + * Returns the current clipping region as a Shape object. + * + * @return The clipping region as a Shape. + */ + public abstract Shape getClip(); + + /** + * Sets the clipping region to the specified Shape. + * + * @param clip The new clipping region. + */ + public abstract void setClip(Shape clip); + + /** + * Copies the specified rectangle to the specified offset location. + * + * @param x The X coordinate of the upper left corner of the copy rect. + * @param y The Y coordinate of the upper left corner of the copy rect. + * @param width The width of the copy rect. + * @param height The height of the copy rect. + * @param dx The offset from the X value to start drawing. + * @param dy The offset from the Y value to start drawing. + */ + public abstract void copyArea(int x, int y, int width, int height, int dx, + int dy); + + /** + * Draws a line between the two specified points. + * + * @param x1 The X coordinate of the first point. + * @param y1 The Y coordinate of the first point. + * @param x2 The X coordinate of the second point. + * @param y2 The Y coordinate of the second point. + */ + public abstract void drawLine(int x1, int y1, int x2, int y2); + + /** + * Fills the area bounded by the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the fill rect. + * @param y The Y coordinate of the upper left corner of the fill rect. + * @param width The width of the fill rect. + * @param height The height of the fill rect. + */ + public abstract void fillRect(int x, int y, int width, int height); + + /** + * Draws the outline of the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the draw rect. + * @param y The Y coordinate of the upper left corner of the draw rect. + * @param width The width of the draw rect. + * @param height The height of the draw rect. + */ + public void drawRect(int x, int y, int width, int height) + { + int x1 = x; + int y1 = y; + int x2 = x + width; + int y2 = y + height; + drawLine(x1, y1, x2, y1); + drawLine(x2, y1, x2, y2); + drawLine(x2, y2, x1, y2); + drawLine(x1, y2, x1, y1); + } + + /** + * Clears the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the clear rect. + * @param y The Y coordinate of the upper left corner of the clear rect. + * @param width The width of the clear rect. + * @param height The height of the clear rect. + */ + public abstract void clearRect(int x, int y, int width, int height); + + /** + * Draws the outline of the specified rectangle with rounded cornders. + * + * @param x The X coordinate of the upper left corner of the draw rect. + * @param y The Y coordinate of the upper left corner of the draw rect. + * @param width The width of the draw rect. + * @param height The height of the draw rect. + * @param arcWidth The width of the corner arcs. + * @param arcHeight The height of the corner arcs. + */ + public abstract void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight); + + /** + * Fills the specified rectangle with rounded cornders. + * + * @param x The X coordinate of the upper left corner of the fill rect. + * @param y The Y coordinate of the upper left corner of the fill rect. + * @param width The width of the fill rect. + * @param height The height of the fill rect. + * @param arcWidth The width of the corner arcs. + * @param arcHeight The height of the corner arcs. + */ + public abstract void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight); + + public void draw3DRect(int x, int y, int width, int height, boolean raised) + { + Color color = getColor(); + Color tl = color.brighter(); + Color br = color.darker(); + + if (!raised) + { + Color tmp = tl; + tl = br; + br = tmp; + } + + int x1 = x; + int y1 = y; + int x2 = x + width; + int y2 = y + height; + + setColor(tl); + drawLine(x1, y1, x2, y1); + drawLine(x1, y2, x1, y1); + setColor(br); + drawLine(x2, y1, x2, y2); + drawLine(x2, y2, x1, y2); + setColor(color); + } + + /** + * Fills the specified rectangle with a 3D effect + * + * @param x The X coordinate of the upper left corner of the fill rect. + * @param y The Y coordinate of the upper left corner of the fill rect. + * @param width The width of the fill rect. + * @param height The height of the fill rect. + * @param raised true if the rectangle appears raised, + * false if it should appear etched. + */ + public void fill3DRect(int x, int y, int width, int height, boolean raised) + { + fillRect(x, y, width, height); + draw3DRect(x, y, width-1, height-1, raised); + } + + /** + * Draws an oval that just fits within the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + */ + public abstract void drawOval(int x, int y, int width, int height); + + /** + * Fills an oval that just fits within the specified rectangle. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + */ + public abstract void fillOval(int x, int y, int width, int height); + + /** + * Draws an arc using the specified bounding rectangle and the specified + * angle parameter. The arc is centered at the center of the rectangle. + * The arc starts at the arcAngle position and extend for arcAngle + * degrees. The degree origin is at the 3 o'clock position. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + * @param arcStart The beginning angle of the arc. + * @param arcAngle The extent of the arc. + */ + public abstract void drawArc(int x, int y, int width, int height, + int arcStart, int arcAngle); + + /** + * Fills the arc define by the specified bounding rectangle and the specified + * angle parameter. The arc is centered at the center of the rectangle. + * The arc starts at the arcAngle position and extend for arcAngle + * degrees. The degree origin is at the 3 o'clock position. + * + * @param x The X coordinate of the upper left corner of the rect. + * @param y The Y coordinate of the upper left corner of the rect. + * @param width The width of the rect. + * @param height The height of the rect. + * @param arcStart The beginning angle of the arc. + * @param arcAngle The extent of the arc. + */ + public abstract void fillArc(int x, int y, int width, int height, + int arcStart, int arcAngle); + + /** + * Draws a series of interconnected lines determined by the arrays + * of corresponding x and y coordinates. + * + * @param xPoints The X coordinate array. + * @param yPoints The Y coordinate array. + * @param npoints The number of points to draw. + */ + public abstract void drawPolyline(int xPoints[], int yPoints[], int npoints); + + /** + * Draws a series of interconnected lines determined by the arrays + * of corresponding x and y coordinates. The figure is closed if necessary + * by connecting the first and last points. + * + * @param xPoints The X coordinate array. + * @param yPoints The Y coordinate array. + * @param npoints The number of points to draw. + */ + public abstract void drawPolygon(int xPoints[], int yPoints[], int npoints); + + /** + * Draws the specified polygon. + * + * @param polygon The polygon to draw. + */ + public void drawPolygon(Polygon polygon) + { + drawPolygon(polygon.xpoints, polygon.ypoints, polygon.npoints); + } + + /** + * Fills the polygon determined by the arrays + * of corresponding x and y coordinates. + * + * @param xPoints The X coordinate array. + * @param yPoints The Y coordinate array. + * @param npoints The number of points to draw. + */ + public abstract void fillPolygon(int xPoints[], int yPoints[], int npoints); + + /** + * Fills the specified polygon + * + * @param polygon The polygon to fill. + */ + public void fillPolygon(Polygon polygon) + { + fillPolygon(polygon.xpoints, polygon.ypoints, polygon.npoints); + } + + /** + * Draws the specified string starting at the specified point. + * + * @param string The string to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + */ + public abstract void drawString(String string, int x, int y); + + public abstract void drawString (AttributedCharacterIterator ci, int x, + int y); + + /** + * Draws the specified characters starting at the specified point. + * + * @param data The array of characters to draw. + * @param offset The offset into the array to start drawing characters from. + * @param length The number of characters to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + */ + public void drawChars(char data[], int offset, int length, int x, int y) + { + drawString(new String(data, offset, length), x, y); + } + + public void drawBytes(byte[] data, int offset, int length, int x, int y) + { + String str = new String(data, offset, length); + drawString(str, x, y); + } + + /** + * Draws all of the image that is available and returns. If the image + * is not completely loaded, false is returned and + * the specified iamge observer is notified as more data becomes + * available. + * + * @param image The image to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + * @param observer The image observer to notify as data becomes available. + * + * @return true if all the image data is available, + * false otherwise. + */ + public abstract boolean drawImage(Image image, int x, int y, + ImageObserver observer); + + /** + * Draws all of the image that is available and returns. The image + * is scaled to fit in the specified rectangle. If the image + * is not completely loaded, false is returned and + * the specified iamge observer is notified as more data becomes + * available. + * + * @param image The image to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + * @param width The width of the rectangle to draw in. + * @param height The height of the rectangle to draw in. + * @param observer The image observer to notify as data becomes available. + * + * @return true if all the image data is available, + * false otherwise. + */ + public abstract boolean drawImage(Image image, int x, int y, int width, + int height, ImageObserver observer); + + /** + * Draws all of the image that is available and returns. If the image + * is not completely loaded, false is returned and + * the specified iamge observer is notified as more data becomes + * available. + * + * @param image The image to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + * @param bgcolor The background color to use for the image. + * @param observer The image observer to notify as data becomes available. + * + * @return true if all the image data is available, + * false otherwise. + */ + public abstract boolean drawImage(Image image, int x, int y, Color bgcolor, + ImageObserver observer); + + /** + * Draws all of the image that is available and returns. The image + * is scaled to fit in the specified rectangle. If the image + * is not completely loaded, false is returned and + * the specified iamge observer is notified as more data becomes + * available. + * + * @param image The image to draw. + * @param x The X coordinate of the point to draw at. + * @param y The Y coordinate of the point to draw at. + * @param width The width of the rectangle to draw in. + * @param height The height of the rectangle to draw in. + * @param bgcolor The background color to use for the image. + * @param observer The image observer to notify as data becomes available. + * + * @return true if all the image data is available, + * false otherwise. + */ + public abstract boolean drawImage(Image image, int x, int y, int width, + int height, Color bgcolor, + ImageObserver observer); + + /** + * FIXME: Write Javadocs for this when you understand it. + */ + public abstract boolean drawImage(Image image, int dx1, int dy1, int dx2, + int dy2, int sx1, int sy1, int sx2, + int sy2, ImageObserver observer); + + /** + * FIXME: Write Javadocs for this when you understand it. + */ + public abstract boolean drawImage(Image image, int dx1, int dy1, int dx2, + int dy2, int sx1, int sy1, int sx2, + int sy2, Color bgcolor, + ImageObserver observer); + + /** + * Free any resources held by this graphics context immediately instead + * of waiting for the object to be garbage collected and finalized. + */ + public abstract void dispose(); + + /** + * Frees the resources held by this graphics context when it is + * garbage collected. + */ + public void finalize() + { + dispose(); + } + + /** + * Returns a string representation of this object. + * + * @return A string representation of this object. + */ + public String toString() + { + return getClass ().getName () + "[font=" + getFont () + ",color=" + + getColor () + "]"; + } + + /** + * Returns true if the specified rectangle intersects with the + * current clip, false otherwise. + * + * @param x the X coordinate of the upper left corner of the test rectangle + * @param y the Y coordinate of the upper left corner of the test rectangle + * @param width the width of the upper left corner of the test rectangle + * @param height the height of the upper left corner of the test rectangle + * @return true if the specified rectangle intersects with the + * current clip, false otherwise + */ + public boolean hitClip(int x, int y, int width, int height) + { + Shape clip = getClip(); + if (clip == null) + return true; + return getClip().intersects(x, y, width, height); + } + + public Rectangle getClipBounds(Rectangle r) + { + Rectangle clipBounds = getClipBounds(); + + if (r == null) + return clipBounds; + + r.x = clipBounds.x; + r.y = clipBounds.y; + r.width = clipBounds.width; + r.height = clipBounds.height; + return r; + } +} diff --git a/libjava/classpath/java/awt/Graphics2D.java b/libjava/classpath/java/awt/Graphics2D.java new file mode 100644 index 000000000..7c9622aef --- /dev/null +++ b/libjava/classpath/java/awt/Graphics2D.java @@ -0,0 +1,358 @@ +/* Copyright (C) 2000, 2002, 2004, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * An abstract class defining a device independent two-dimensional vector + * graphics API. Concrete subclasses implement this API for output of + * vector graphics to: + *

+ *

    + *
  • a {@link javax.swing.JComponent} - in the + * {@link javax.swing.JComponent#paint(Graphics)} method, the incoming + * {@link Graphics} should always be an instance of + * Graphics2D;
  • + *
  • a {@link BufferedImage} - see + * {@link BufferedImage#createGraphics()};
  • + *
  • a {@link java.awt.print.PrinterJob} - in the + * {@link Printable#print(Graphics, PageFormat, int)} method, the incoming + * {@link Graphics} should always be an instance of + * Graphics2D.
  • + *
+ *

+ * Third party libraries provide support for output to other formats via this + * API, including encapsulated postscript (EPS), portable document format (PDF), + * and scalable vector graphics (SVG). + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public abstract class Graphics2D extends Graphics +{ + + protected Graphics2D() + { + } + + public void draw3DRect(int x, int y, int width, int height, + boolean raised) + { + super.draw3DRect(x, y, width, height, raised); + } + + public void fill3DRect(int x, int y, int width, int height, + boolean raised) + { + super.fill3DRect(x, y, width, height, raised); + } + + /** + * Draws an outline around a shape using the current stroke and paint. + * + * @param shape the shape (null not permitted). + * + * @see #getStroke() + * @see #getPaint() + */ + public abstract void draw(Shape shape); + + public abstract boolean drawImage(Image image, AffineTransform xform, + ImageObserver obs); + + public abstract void drawImage(BufferedImage image, + BufferedImageOp op, + int x, + int y); + + public abstract void drawRenderedImage(RenderedImage image, + AffineTransform xform); + + public abstract void drawRenderableImage(RenderableImage image, + AffineTransform xform); + + /** + * Draws a string at the specified location, using the current font. + * + * @param text the string to draw. + * @param x the x-coordinate. + * @param y the y-coordinate. + * + * @see Graphics#setFont(Font) + */ + public abstract void drawString(String text, int x, int y); + + /** + * Draws a string at the specified location, using the current font. + * + * @param text the string to draw. + * @param x the x-coordinate. + * @param y the y-coordinate. + * + * @see Graphics#setFont(Font) + */ + public abstract void drawString(String text, float x, float y); + + /** + * Draws an attributed string at the specified location. + * + * @param iterator the source of the attributed text. + * @param x the x-coordinate. + * @param y the y-coordinate. + */ + public abstract void drawString(AttributedCharacterIterator iterator, + int x, int y); + + /** + * Draws an attributed string at the specified location. + * + * @param iterator the source of the attributed text. + * @param x the x-coordinate. + * @param y the y-coordinate. + */ + public abstract void drawString(AttributedCharacterIterator iterator, + float x, float y); + + /** + * Fills the interior of the specified shape using the current + * paint. + * + * @param shape the shape to fill (null not permitted). + * + * @see #draw(Shape) + * @see #getPaint() + */ + public abstract void fill(Shape shape); + + public abstract boolean hit(Rectangle rect, Shape text, + boolean onStroke); + + public abstract GraphicsConfiguration getDeviceConfiguration(); + + /** + * Sets the current compositing rule. + * + * @param comp the composite. + * + * @see #getComposite() + */ + public abstract void setComposite(Composite comp); + + /** + * Sets the paint to be used for subsequent drawing operations. + * + * @param paint the paint (null not permitted). + * + * @see #getPaint() + */ + public abstract void setPaint(Paint paint); + + /** + * Sets the stroke to be used for subsequent drawing operations. + * + * @param stroke the stroke (null not permitted). + * + * @see #getStroke() + */ + public abstract void setStroke(Stroke stroke); + + /** + * Adds or updates a hint in the current rendering hints table. + * + * @param hintKey the hint key. + * @param hintValue the hint value. + */ + public abstract void setRenderingHint(RenderingHints.Key hintKey, + Object hintValue); + + /** + * Returns the current value of a rendering hint. + * + * @param hintKey the key for the hint. + * + * @return The value for the specified hint. + */ + public abstract Object getRenderingHint(RenderingHints.Key hintKey); + + /** + * Replaces the current rendering hints with the supplied hints. + * + * @param hints the hints. + * + * @see #addRenderingHints(Map) + */ + public abstract void setRenderingHints(Map hints); + + /** + * Adds/updates the rendering hint. + * + * @param hints the hints to add or update. + */ + public abstract void addRenderingHints(Map hints); + + /** + * Returns the current rendering hints. + * + * @return The current rendering hints. + */ + public abstract RenderingHints getRenderingHints(); + + public abstract void translate(int x, int y); + + public abstract void translate(double tx, double ty); + + public abstract void rotate(double theta); + + public abstract void rotate(double theta, double x, double y); + + public abstract void scale(double scaleX, double scaleY); + + public abstract void shear(double shearX, double shearY); + + /** + * Sets the current transform to a concatenation of transform + * and the existing transform. + * + * @param transform the transform. + */ + public abstract void transform(AffineTransform transform); + + /** + * Sets the current transform. If the caller specifies a null + * transform, this method should set the current transform to the + * identity transform. + * + * @param transform the transform (null permitted). + * + * @see #getTransform() + */ + public abstract void setTransform(AffineTransform transform); + + /** + * Returns the current transform. + * + * @return The current transform. + * + * @see #setTransform(AffineTransform) + */ + public abstract AffineTransform getTransform(); + + /** + * Returns the current paint. + * + * @return The current paint. + * + * @see #setPaint(Paint) + */ + public abstract Paint getPaint(); + + /** + * Returns the current compositing rule. + * + * @return The current compositing rule. + * + * @see #setComposite(Composite) + */ + public abstract Composite getComposite(); + + /** + * Sets the background color (used by the + * {@link Graphics#clearRect(int, int, int, int)} method). + * + * @param color the color. + * + * @see #getBackground() + */ + public abstract void setBackground(Color color); + + /** + * Returns the color used by the + * {@link Graphics#clearRect(int, int, int, int)} method. + * + * @return The background color. + * + * @see #setBackground(Color) + */ + public abstract Color getBackground(); + + /** + * Returns the current stroke. + * + * @return The current stroke. + * + * @see #setStroke(Stroke) + */ + public abstract Stroke getStroke(); + + /** + * Sets the clip region to the intersection of the current clipping region + * and s. + * + * @param s the shape to intersect with the current clipping region. + * + * @see Graphics#setClip(Shape) + */ + public abstract void clip(Shape s); + + /** + * Returns the font render context. + * + * @return The font render context. + */ + public abstract FontRenderContext getFontRenderContext(); + + /** + * Draws a glyph vector at the specified location. + * + * @param g the glyph vector. + * @param x the x-coordinate. + * @param y the y-coordinate. + */ + public abstract void drawGlyphVector(GlyphVector g, float x, float y); +} diff --git a/libjava/classpath/java/awt/GraphicsConfigTemplate.java b/libjava/classpath/java/awt/GraphicsConfigTemplate.java new file mode 100644 index 000000000..e46888378 --- /dev/null +++ b/libjava/classpath/java/awt/GraphicsConfigTemplate.java @@ -0,0 +1,106 @@ +/* GraphicsConfigTemplate.java -- a template for selecting configurations + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * This allows filtering an array of GraphicsConfigurations for the best + * one based on various requirements. The resulting configuration has had + * all non-default attributes set as required to meet or exceed the request. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see GraphicsConfiguration + * @see GraphicsDevice + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class GraphicsConfigTemplate implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -8061369279557787079L; + + /** States that a feature is required to select a configuration. */ + public static final int REQUIRED = 1; + + /** + * States that a feature is preferred, but not required, to select a + * configuration. In the case of multiple valid configurations, the tie + * breaks in favor of the one with the feature. + */ + public static final int PREFERRED = 2; + + /** + * States that a feature is not necessary in the configuration. In the case + * of multiple valid configurations, the tie breaks in favor of the one + * without the feature, to reduce overhead. + */ + public static final int UNNECESSARY = 3; + + /** + * The default constructor. + */ + public GraphicsConfigTemplate() + { + } + + /** + * Returns the "best" match among the array of possible configurations, given + * the criteria of this template. + * + * @param array the array to choose from + * @return the best match + * @throws NullPointerException if array is null + */ + public abstract GraphicsConfiguration getBestConfiguration + (GraphicsConfiguration[] array); + + /** + * Returns true if the given configuration supports all the features required + * by this template. + * + * @param config the configuration to test + * @return true if it is a match + * @throws NullPointerException if config is null + */ + public abstract boolean isGraphicsConfigSupported + (GraphicsConfiguration config); +} // class GraphicsConfigTemplate diff --git a/libjava/classpath/java/awt/GraphicsConfiguration.java b/libjava/classpath/java/awt/GraphicsConfiguration.java new file mode 100644 index 000000000..bbd9820c9 --- /dev/null +++ b/libjava/classpath/java/awt/GraphicsConfiguration.java @@ -0,0 +1,275 @@ +/* GraphicsConfiguration.java -- describes characteristics of graphics + Copyright (C) 2000, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.VolatileImage; + +/** + * This class describes the configuration of various graphics devices, such + * as a monitor or printer. Different configurations may exist for the same + * device, according to the different native modes supported. + * + *

Virtual devices are supported (for example, in a multiple screen + * environment, a virtual device covers all screens simultaneously); the + * configuration will have a non-zero relative coordinate system in such + * a case. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Window + * @see Frame + * @see GraphicsEnvironment + * @see GraphicsDevice + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class GraphicsConfiguration +{ + + /** The cached image capabilities. */ + private ImageCapabilities imageCapabilities; + + /** The cached buffer capabilities. */ + private BufferCapabilities bufferCapabilities; + + /** + * The default constructor. + * + * @see GraphicsDevice#getConfigurations() + * @see GraphicsDevice#getDefaultConfiguration() + * @see GraphicsDevice#getBestConfiguration(GraphicsConfigTemplate) + * @see Graphics2D#getDeviceConfiguration() + */ + protected GraphicsConfiguration () + { + } + + /** + * Gets the associated device that this configuration describes. + * + * @return the device + */ + public abstract GraphicsDevice getDevice(); + + /** + * Returns a buffered image optimized to this device, so that blitting can + * be supported in the buffered image. + * + * @param w the width of the buffer + * @param h the height of the buffer + * @return the buffered image, or null if none is supported + */ + public abstract BufferedImage createCompatibleImage(int w, int h); + + /** + * Returns a buffered volatile image optimized to this device, so that + * blitting can be supported in the buffered image. Because the buffer is + * volatile, it can be optimized by native graphics accelerators. + * + * @param w the width of the buffer + * @param h the height of the buffer + * @return the buffered image, or null if none is supported + * @see Component#createVolatileImage(int, int) + * @since 1.4 + */ + public abstract VolatileImage createCompatibleVolatileImage(int w, int h); + + /** + * Returns a buffered volatile image optimized to this device, and with the + * given capabilities, so that blitting can be supported in the buffered + * image. Because the buffer is volatile, it can be optimized by native + * graphics accelerators. + * + * @param w the width of the buffer + * @param h the height of the buffer + * @param caps the desired capabilities of the image buffer + * @return the buffered image, or null if none is supported + * @throws AWTException if the capabilities cannot be met + * @since 1.4 + */ + public VolatileImage createCompatibleVolatileImage(int w, int h, + ImageCapabilities caps) + throws AWTException + { + // Must be overridden by implementations to check caps. + return createCompatibleVolatileImage(w, h); + } + + /** + * Returns a buffered volatile image optimized to this device, and + * with the given transparency. Because the buffer is volatile, it + * can be optimized by native graphics accelerators. + * + * @param width the width of the buffer + * @param height the height of the buffer + * @param transparency the transparency value for the buffer + * @return the buffered image, or null if none is supported + * @since 1.5 + */ + public abstract VolatileImage createCompatibleVolatileImage(int width, + int height, + int transparency); + + /** + * Creates a volatile image with the specified capabilities and transparency. + * If the backend cannot meet the requested capabilities and transparency, + * an AWTException is thrown. + * + * @param width the width of the image + * @param height the height of the image + * @param caps the requested capabilities + * @param transparency the required transparency + * + * @return a volatile image with the specified capabilites and transparency + * + * @throws AWTException if the required capabilities and transparency cannot + * be met + * + * @since 1.5 + */ + public VolatileImage createCompatibleVolatileImage(int width, int height, + ImageCapabilities caps, + int transparency) + throws AWTException + { + // Must be overridden by implementations to check caps. + return createCompatibleVolatileImage(width, height, transparency); + } + + /** + * Returns a buffered image optimized to this device, and with the specified + * transparency, so that blitting can be supported in the buffered image. + * + * @param w the width of the buffer + * @param h the height of the buffer + * @param transparency the transparency of the buffer + * @return the buffered image, or null if none is supported + * @see Transparency#OPAQUE + * @see Transparency#BITMASK + * @see Transparency#TRANSLUCENT + */ + public abstract BufferedImage createCompatibleImage(int w, int h, + int transparency); + + /** + * Gets the color model of the corresponding device. + * + * @return the color model + */ + public abstract ColorModel getColorModel(); + + /** + * Gets a color model for the corresponding device which supports the desired + * transparency level. + * + * @param transparency the transparency of the model + * @return the color model, with transparency + * @see Transparency#OPAQUE + * @see Transparency#BITMASK + * @see Transparency#TRANSLUCENT + */ + public abstract ColorModel getColorModel(int transparency); + + /** + * Returns a transform that maps user coordinates to device coordinates. The + * preferred mapping is about 72 user units to 1 inch (2.54 cm) of physical + * space. This is often the identity transform. The device coordinates have + * the origin at the upper left, with increasing x to the right, and + * increasing y to the bottom. + * + * @return the transformation from user space to device space + * @see #getNormalizingTransform() + */ + public abstract AffineTransform getDefaultTransform(); + + /** + * Returns a transform that maps user coordinates to device coordinates. The + * exact mapping is 72 user units to 1 inch (2.54 cm) of physical space. + * This is often the identity transform. The device coordinates have the + * origin at the upper left, with increasing x to the right, and increasing + * y to the bottom. Note that this is more accurate (and thus, sometimes more + * costly) than the default transform. + * + * @return the normalized transformation from user space to device space + * @see #getDefaultTransform() + */ + public abstract AffineTransform getNormalizingTransform(); + + /** + * Returns the bounds of the configuration, in device coordinates. If this + * is a virtual device (for example, encompassing several screens), the + * bounds may have a non-zero origin. + * + * @return the device bounds + * @since 1.3 + */ + public abstract Rectangle getBounds(); + + /** + * Returns the buffering capabilities of this configuration. + * + * @return the buffer capabilities + * @since 1.4 + */ + public BufferCapabilities getBufferCapabilities() + { + if (imageCapabilities == null) + getImageCapabilities(); + + if (bufferCapabilities == null) + bufferCapabilities = new BufferCapabilities(imageCapabilities, + imageCapabilities, null); + return bufferCapabilities; + } + + /** + * Returns the imaging capabilities of this configuration. + * + * @return the image capabilities + * @since 1.4 + */ + public ImageCapabilities getImageCapabilities() + { + if (imageCapabilities == null) + imageCapabilities = new ImageCapabilities(false); + return imageCapabilities; + } +} // class GraphicsConfiguration diff --git a/libjava/classpath/java/awt/GraphicsDevice.java b/libjava/classpath/java/awt/GraphicsDevice.java new file mode 100644 index 000000000..c99c14b1b --- /dev/null +++ b/libjava/classpath/java/awt/GraphicsDevice.java @@ -0,0 +1,292 @@ +/* GraphicsDevice.java -- information about a graphics device + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.VolatileImage; + +/** + * This describes a graphics device available to the given environment. This + * includes screen and printer devices, and the different configurations for + * each device. Also, this allows you to create virtual devices which operate + * over a multi-screen environment. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see GraphicsEnvironment + * @see GraphicsConfiguration + * @since 1.3 + * @status updated to 1.4 + */ +public abstract class GraphicsDevice +{ + /** Device is a raster screen. */ + public static final int TYPE_RASTER_SCREEN = 0; + + /** Device is a printer. */ + public static final int TYPE_PRINTER = 1; + + /** Device is an image buffer not visible to the user. */ + public static final int TYPE_IMAGE_BUFFER = 2; + + /** The current full-screen window, or null if there is none. */ + private Window full_screen; + + /** + * The bounds of the fullscreen window before it has been switched to full + * screen. + */ + private Rectangle fullScreenOldBounds; + + /** The current display mode, or null if unknown. */ + private DisplayMode mode; + + /** + * The default constructor. + * + * @see GraphicsEnvironment#getScreenDevices() + * @see GraphicsEnvironment#getDefaultScreenDevice() + * @see GraphicsConfiguration#getDevice() + */ + protected GraphicsDevice() + { + } + + /** + * Returns the type of the device. + * + * @return the device type + * @see #TYPE_RASTER_SCREEN + * @see #TYPE_PRINTER + * @see #TYPE_IMAGE_BUFFER + */ + public abstract int getType(); + + /** + * Returns an identification string for the device. This can be + * vendor-specific, and may be useful for debugging. + * + * @return the identification + */ + public abstract String getIDstring(); + + /** + * Return all configurations valid for this device. + * + * @return an array of configurations + */ + public abstract GraphicsConfiguration[] getConfigurations(); + + /** + * Return the default configuration for this device. + * + * @return the default configuration + */ + public abstract GraphicsConfiguration getDefaultConfiguration(); + + /** + * Return the best configuration, according to the criteria in the given + * template. + * + * @param template the template to adjust by + * @return the best configuration + * @throws NullPointerException if template is null + */ + public GraphicsConfiguration getBestConfiguration + (GraphicsConfigTemplate template) + { + return template.getBestConfiguration(getConfigurations()); + } + + /** + * Returns true if the device supports full-screen exclusive mode. The + * default implementation returns true; subclass it if this is not the case. + * + * @return true if full screen support is available + * @since 1.4 + */ + public boolean isFullScreenSupported() + { + return true; + } + + /** + * Toggle the given window between full screen and normal mode. The previous + * full-screen window, if different, is restored; if the given window is + * null, no window will be full screen. If + * isFullScreenSupported() returns true, full screen mode is + * considered to be exclusive, which implies:

    + *
  • Windows cannot overlap the full-screen window. All other application + * windows will always appear beneath the full-screen window in the + * Z-order.
  • + *
  • Input method windows are disabled. It is advisable to call + * Component.enableInputMethods(false) to make a component + * a non-client of the input method framework.
  • + *

+ * If isFullScreenSupported() returns false, full-screen + * exclusive mode is simulated by resizing the window to the size of the + * screen and positioning it at (0,0). This is also what this method does. + * If a device supports real fullscreen mode then it should override this + * method as well as #isFullScreenSupported and #getFullScreenWindow. + * + * @param w the window to toggle + * @see #isFullScreenSupported() + * @see #getFullScreenWindow() + * @see #setDisplayMode(DisplayMode) + * @see Component#enableInputMethods(boolean) + * @since 1.4 + */ + public synchronized void setFullScreenWindow(Window w) + { + // Restore the previous window to normal mode and release the reference. + if (full_screen != null) + { + full_screen.setBounds(fullScreenOldBounds); + } + + full_screen = null; + + // If w != null, make it full-screen. + if (w != null) + { + fullScreenOldBounds = w.getBounds(); + full_screen = w; + DisplayMode dMode = getDisplayMode(); + full_screen.setBounds(0, 0, dMode.getWidth(), dMode.getHeight()); + full_screen.requestFocus(); + full_screen.setLocationRelativeTo(null); + } + } + + /** + * Returns the current full-screen window of the device, or null if no + * window is full-screen. + * + * @return the full-screen window + * @see #setFullScreenWindow(Window) + * @since 1.4 + */ + public Window getFullScreenWindow() + { + return full_screen; + } + + /** + * Returns whether this device supports low-level display changes. This may + * depend on whether full-screen exclusive mode is available. + * + * XXX The default implementation returns false for now. + * + * @return true if display changes are supported + * @see #setDisplayMode(DisplayMode) + * @since 1.4 + */ + public boolean isDisplayChangeSupported() + { + return false; + } + + /** + * Sets the display mode. This may be dependent on the availability of + * full-screen exclusive mode. + * + * @param mode the new mode + * @throws IllegalArgumentException if the new mode is not in getDisplayModes + * @throws UnsupportedOperationException if ! isDisplayChangeSupported() + * @see #getDisplayMode() + * @see #getDisplayModes() + * @see #isDisplayChangeSupported() + * @since 1.4 + */ + public void setDisplayMode(DisplayMode mode) + { + DisplayMode[] array = getDisplayModes(); + if (! isDisplayChangeSupported()) + throw new UnsupportedOperationException(); + int i = array == null ? 0 : array.length; + while (--i >= 0) + if (array[i].equals(mode)) + break; + if (i < 0) + throw new IllegalArgumentException(); + this.mode = mode; + } + + /** + * Returns the current display mode of this device, or null if unknown. + * + * @return the current display mode + * @see #setDisplayMode(DisplayMode) + * @see #getDisplayModes() + * @since 1.4 + */ + public DisplayMode getDisplayMode() + { + return mode; + } + + /** + * Return an array of all available display modes. This implementation + * returns a 0-length array, so subclasses must override this. + * + * @return the array of available modes + * @since 1.4 + */ + public DisplayMode[] getDisplayModes() + { + return new DisplayMode[0]; + } + + /** + * Return the number of bytes available in accelerated memory on this + * device. The device may support creation or caching on a first-come, + * first-served basis, depending on the operating system and driver. + * Memory may be a finite resource, and because of multi-threading, you + * are not guaranteed that the result of this method ensures your image + * will successfully be put in accelerated memory. A negative result means + * the memory is unlimited. The default implementation assumes no special + * memory is available, and returns 0. + * + * @return the size of accelerated memory available + * @see VolatileImage#flush() + * @see ImageCapabilities#isAccelerated() + */ + public int getAvailableAcceleratedMemory() + { + return 0; + } +} // class GraphicsDevice diff --git a/libjava/classpath/java/awt/GraphicsEnvironment.java b/libjava/classpath/java/awt/GraphicsEnvironment.java new file mode 100644 index 000000000..a82e7a357 --- /dev/null +++ b/libjava/classpath/java/awt/GraphicsEnvironment.java @@ -0,0 +1,244 @@ +/* GraphicsEnvironment.java -- information about the graphics environment + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; +import gnu.classpath.SystemProperties; +import java.awt.image.BufferedImage; +import java.util.Locale; + +/** + * This descibes the collection of GraphicsDevice and Font objects available + * on a given platform. The resources might be local or remote, and specify + * the valid configurations for displaying graphics. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see GraphicsDevice + * @see GraphicsConfiguration + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class GraphicsEnvironment +{ + private static GraphicsEnvironment localGraphicsEnvironment; + + /** + * The environment must be obtained from a factory or query method, hence + * this constructor is protected. + */ + protected GraphicsEnvironment() + { + } + + /** + * Returns the local graphics environment. If the java.awt.graphicsenv + * system property is set, it instantiates the specified class, + * otherwise it assume that the awt toolkit is a ClasspathToolkit + * and delegates to it to create the instance. + * + * @return the local environment + */ + public static GraphicsEnvironment getLocalGraphicsEnvironment() + { + if (localGraphicsEnvironment != null) + return localGraphicsEnvironment; + + String graphicsenv = SystemProperties.getProperty("java.awt.graphicsenv", + null); + if (graphicsenv != null) + { + try + { + // We intentionally use the bootstrap class loader. + localGraphicsEnvironment = (GraphicsEnvironment) + Class.forName(graphicsenv).newInstance(); + return localGraphicsEnvironment; + } + catch (Exception x) + { + throw (InternalError) + new InternalError("Unable to instantiate java.awt.graphicsenv") + .initCause(x); + } + } + else + { + ClasspathToolkit tk; + tk = ((ClasspathToolkit) Toolkit.getDefaultToolkit()); + localGraphicsEnvironment = tk.getLocalGraphicsEnvironment(); + return localGraphicsEnvironment; + } + } + + /** + * Check if the local environment is headless, meaning that it does not + * support a display, keyboard, or mouse. Many methods in the Abstract + * Windows Toolkit (java.awt) throw a {@link HeadlessException} if this + * returns true. + * + * This method returns true if the java.awt.headless property is set + * to "true". + * + * @return true if the environment is headless, meaning that graphics are + * unsupported + * @since 1.4 + */ + public static boolean isHeadless() + { + String headless = SystemProperties.getProperty("java.awt.headless", null); + return "true".equalsIgnoreCase(headless); + } + + /** + * Check if the given environment is headless, meaning that it does not + * support a display, keyboard, or mouse. Many methods in the Abstract + * Windows Toolkit (java.awt) throw a {@link HeadlessException} if this + * returns true. This default implementation returns isHeadless(), so + * subclasses need only override it if they differ. + * + * @return true if the environment is headless, meaning that graphics are + * unsupported + * @since 1.4 + */ + public boolean isHeadlessInstance() + { + return isHeadless(); + } + + /** + * Get an array of all the GraphicsDevice objects. + * + * @return the available graphics devices, may be 0 length + * @throws HeadlessException if the environment is headless + */ + public abstract GraphicsDevice[] getScreenDevices(); + + /** + * Get the default screen GraphicsDevice object. + * + * @return the default screen device + * @throws HeadlessException if the environment is headless + */ + public abstract GraphicsDevice getDefaultScreenDevice(); + + /** + * Return a Graphics2D object which will render into the specified image. + * + * @param image the image to render into + * @return the object that renders into the image + */ + public abstract Graphics2D createGraphics(BufferedImage image); + + /** + * Returns an array of the one-point size fonts available in this + * environment. From there, the user can select the font and derive the + * correct one of proper size and attributes, using deriveFont. + * Only one master version of each font appears in this array; if a font + * can be derived from another, it must be created in that way. + * + * @return the array of available fonts + * @see #getAvailableFontFamilyNames() + * @see Font#deriveFont(int, float) + * @since 1.2 + */ + public abstract Font[] getAllFonts(); + + /** + * Returns an array of the font family names available in this environment. + * This allows flexibility in choosing the style of font, while still letting + * the Font class decide its best match. + * + * @return the array of available font families + * @see #getAllFonts() + * @see Font#getFamily() + * @since 1.2 + */ + public abstract String[] getAvailableFontFamilyNames(); + + /** + * Returns an array of the font family names available in this environment, + * localized to the current Locale if l is non-null. This allows + * flexibility in choosing the style of font, while still letting the Font + * class decide its best match. + * + * @param l the locale to use + * @return the array of available font families, localized + * @see #getAllFonts() + * @see Font#getFamily() + * @since 1.2 + */ + public abstract String[] getAvailableFontFamilyNames(Locale l); + + /** + * Returns the point where a window should be centered. You should probably + * also check that the window fits within the screen bounds. The default + * simply returns the center of the maximum window bounds; subclasses should + * override this if native objects (like scrollbars) make that off-centered. + * + * @return the centering point + * @throws HeadlessException if the environment is headless + * @see #getMaximumWindowBounds() + * @since 1.4 + */ + public Point getCenterPoint() + { + Rectangle r = getMaximumWindowBounds(); + return new Point(r.x + r.width / 2, r.y + r.height / 2); + } + + /** + * Returns the maximum bounds for a centered window object. The default + * implementation simply returns the bounds of the default configuration + * of the default screen; subclasses should override this to if native + * objects (like scrollbars) reduce what is truly available. Also, + * subclasses should override this if the window should be centered across + * a multi-screen display. + * + * @return the maximum window bounds + * @throws HeadlessException if the environment is headless + * @see #getCenterPoint() + * @see GraphicsConfiguration#getBounds() + * @see Toolkit#getScreenInsets(GraphicsConfiguration) + * @since 1.4 + */ + public Rectangle getMaximumWindowBounds() + { + return getDefaultScreenDevice().getDefaultConfiguration().getBounds(); + } +} // class GraphicsEnvironment diff --git a/libjava/classpath/java/awt/GridBagConstraints.java b/libjava/classpath/java/awt/GridBagConstraints.java new file mode 100644 index 000000000..823130058 --- /dev/null +++ b/libjava/classpath/java/awt/GridBagConstraints.java @@ -0,0 +1,246 @@ +/* GridBagConstraints.java -- Constraints for GridBag layout manager + Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * This specifies the constraints for a component managed by the + * GridBagLayout layout manager. + */ +public class GridBagConstraints implements Cloneable, Serializable +{ + static final long serialVersionUID = -1000070633030801713L; + + // Fill values. + /** + * Don't fill. + */ + public static final int NONE = 0; + + /** + * Fill in both directions. + */ + public static final int BOTH = 1; + + /** + * Fill horizontally. + */ + public static final int HORIZONTAL = 2; + + /** + * Fill vertically. + */ + public static final int VERTICAL = 3; + + // Anchor values. + /** + * Position in the center. + */ + public static final int CENTER = 10; + + /** + * Position to the north. + */ + public static final int NORTH = 11; + + /** + * Position to the northeast. + */ + public static final int NORTHEAST = 12; + + /** + * Position to the east. + */ + public static final int EAST = 13; + + /** + * Position to the southeast. + */ + public static final int SOUTHEAST = 14; + + /** + * Position to the south. + */ + public static final int SOUTH = 15; + + /** + * Position to the southwest. + */ + public static final int SOUTHWEST = 16; + + /** + * Position to the west. + */ + public static final int WEST = 17; + + /** + * Position to the northwest. + */ + public static final int NORTHWEST = 18; + + // gridx and gridy values. + /** + * Occupy all remaining cells except last cell. + */ + public static final int RELATIVE = -1; + + /** + * Occupy all remaining cells. + */ + public static final int REMAINDER = 0; + + /** + * Position to where a page starts. Equals NORTH for horizontal orientations. + */ + public static final int PAGE_START = 19; + + /** + * Position to where a page ends. Equals SOUTH for horizontal orientations. + */ + public static final int PAGE_END = 20; + + /** + * Position to where a text line would start. Equals to WEST for + * left-to-right orientations. + */ + public static final int LINE_START = 21; + + /** + * Position to where a text line would end. Equals to EAST for + * left-to-right orientations. + */ + public static final int LINE_END = 22; + + /** + * Position to where the first text line would start. Equals to NORTHWEST for + * horizontal left-to-right orientations. + */ + public static final int FIRST_LINE_START = 23; + + /** + * Position to where the first text line would end. Equals to NORTHEAST for + * horizontal left-to-right orientations. + */ + public static final int FIRST_LINE_END = 24; + + /** + * Position to where the last text line would start. Equals to SOUTHWEST for + * horizontal left-to-right orientations. + */ + public static final int LAST_LINE_START = 25; + + /** + * Position to where the last text line would end. Equals to SOUTHEAST for + * horizontal left-to-right orientations. + */ + public static final int LAST_LINE_END = 26; + + public int anchor; + public int fill; + public int gridheight; + public int gridwidth; + public int gridx; + public int gridy; + public Insets insets; + public int ipadx; + public int ipady; + public double weightx; + public double weighty; + + /** + * Create a copy of this object. + */ + public Object clone () + { + try + { + GridBagConstraints g = (GridBagConstraints) super.clone (); + g.insets = (Insets) insets.clone (); + return g; + } + catch (CloneNotSupportedException _) + { + // Can't happen. + return null; + } + } + + /** + * Create a new GridBagConstraints object with the default + * parameters. + */ + public GridBagConstraints () + { + this.anchor = CENTER; + this.fill = NONE; + this.gridx = RELATIVE; + this.gridy = RELATIVE; + this.gridwidth = 1; + this.gridheight = 1; + this.ipadx = 0; + this.ipady = 0; + this.insets = new Insets (0, 0, 0, 0); + this.weightx = 0; + this.weighty = 0; + } + + /** + * Create a new GridBagConstraints object with the indicated + * parameters. + */ + public GridBagConstraints (int gridx, int gridy, + int gridwidth, int gridheight, + double weightx, double weighty, + int anchor, int fill, + Insets insets, int ipadx, int ipady) + { + this.anchor = anchor; + this.fill = fill; + this.gridx = gridx; + this.gridy = gridy; + this.gridwidth = gridwidth; + this.gridheight = gridheight; + this.ipadx = ipadx; + this.ipady = ipady; + this.insets = insets; + this.weightx = weightx; + this.weighty = weighty; + } +} diff --git a/libjava/classpath/java/awt/GridBagLayout.java b/libjava/classpath/java/awt/GridBagLayout.java new file mode 100644 index 000000000..224d53ff2 --- /dev/null +++ b/libjava/classpath/java/awt/GridBagLayout.java @@ -0,0 +1,1114 @@ +/* GridBagLayout - Layout manager for components according to GridBagConstraints + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class GridBagLayout + implements Serializable, LayoutManager2 +{ + private static final long serialVersionUID = 8838754796412211005L; + + protected static final int MINSIZE = 1; + protected static final int PREFERREDSIZE = 2; + protected static final int MAXGRIDSIZE = 512; + + // comptable remembers the original contraints given to us. + // internalcomptable is used to keep track of modified constraint values + // that we calculate, particularly when we are given RELATIVE and + // REMAINDER constraints. + // Constraints kept in comptable are never modified, and constraints + // kept in internalcomptable can be modified internally only. + protected Hashtable comptable; + private Hashtable internalcomptable; + protected GridBagLayoutInfo layoutInfo; + protected GridBagConstraints defaultConstraints; + + public double[] columnWeights; + public int[] columnWidths; + public double[] rowWeights; + public int[] rowHeights; + + public GridBagLayout () + { + this.comptable = new Hashtable(); + this.internalcomptable = new Hashtable(); + this.defaultConstraints= new GridBagConstraints(); + } + + /** + * Helper method to calc the sum of a range of elements in an int array. + */ + private int sumIntArray (int[] array, int upto) + { + int result = 0; + + for (int i = 0; i < upto; i++) + result += array [i]; + + return result; + } + + /** + * Helper method to calc the sum of all elements in an int array. + */ + private int sumIntArray (int[] array) + { + return sumIntArray(array, array.length); + } + + /** + * Helper method to calc the sum of all elements in an double array. + */ + private double sumDoubleArray (double[] array) + { + double result = 0; + + for (int i = 0; i < array.length; i++) + result += array [i]; + + return result; + } + + public void addLayoutComponent (String name, Component component) + { + // do nothing here. + } + + public void removeLayoutComponent (Component component) + { + // do nothing here + } + + public void addLayoutComponent (Component component, Object constraints) + { + if (constraints == null) + return; + + if (!(constraints instanceof GridBagConstraints)) + throw new IllegalArgumentException("constraints " + + constraints + + " are not an instance of GridBagConstraints"); + + setConstraints (component, (GridBagConstraints) constraints); + } + + public Dimension preferredLayoutSize (Container parent) + { + if (parent == null) + return new Dimension (0, 0); + + GridBagLayoutInfo li = getLayoutInfo (parent, PREFERREDSIZE); + return getMinSize (parent, li); + } + + public Dimension minimumLayoutSize (Container parent) + { + if (parent == null) + return new Dimension (0, 0); + + GridBagLayoutInfo li = getLayoutInfo (parent, MINSIZE); + return getMinSize (parent, li); + } + + public Dimension maximumLayoutSize (Container target) + { + return new Dimension (Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + public void layoutContainer (Container parent) + { + arrangeGrid (parent); + } + + public float getLayoutAlignmentX (Container target) + { + return Component.CENTER_ALIGNMENT; + } + + public float getLayoutAlignmentY (Container target) + { + return Component.CENTER_ALIGNMENT; + } + + public void invalidateLayout (Container target) + { + this.layoutInfo = null; + } + + public void setConstraints (Component component, + GridBagConstraints constraints) + { + GridBagConstraints clone = (GridBagConstraints) constraints.clone(); + + if (clone.gridx < 0) + clone.gridx = GridBagConstraints.RELATIVE; + + if (clone.gridy < 0) + clone.gridy = GridBagConstraints.RELATIVE; + + if (clone.gridwidth == 0) + clone.gridwidth = GridBagConstraints.REMAINDER; + else if (clone.gridwidth < 0) + clone.gridwidth = 1; + + if (clone.gridheight == 0) + clone.gridheight = GridBagConstraints.REMAINDER; + else if (clone.gridheight < 0) + clone.gridheight = 1; + + comptable.put (component, clone); + } + + public GridBagConstraints getConstraints (Component component) + { + return (GridBagConstraints) (lookupConstraints (component).clone()); + } + + protected GridBagConstraints lookupConstraints (Component component) + { + GridBagConstraints result = comptable.get (component); + + if (result == null) + { + setConstraints (component, defaultConstraints); + result = comptable.get (component); + } + + return result; + } + + private GridBagConstraints lookupInternalConstraints (Component component) + { + GridBagConstraints result = internalcomptable.get (component); + + if (result == null) + { + result = (GridBagConstraints) lookupConstraints(component).clone(); + internalcomptable.put (component, result); + } + + return result; + } + + /** + * @since 1.1 + */ + public Point getLayoutOrigin () + { + if (layoutInfo == null) + return new Point (0, 0); + + return new Point (layoutInfo.pos_x, layoutInfo.pos_y); + } + + /** + * @since 1.1 + */ + public int[][] getLayoutDimensions () + { + int[][] result = new int [2][]; + if (layoutInfo == null) + { + result[0] = new int[0]; + result[1] = new int[0]; + + return result; + } + + result [0] = new int [layoutInfo.cols]; + System.arraycopy (layoutInfo.colWidths, 0, result [0], 0, layoutInfo.cols); + result [1] = new int [layoutInfo.rows]; + System.arraycopy (layoutInfo.rowHeights, 0, result [1], 0, layoutInfo.rows); + return result; + } + + public double[][] getLayoutWeights () + { + double[][] result = new double [2][]; + if (layoutInfo == null) + { + result[0] = new double[0]; + result[1] = new double[0]; + + return result; + } + + result [0] = new double [layoutInfo.cols]; + System.arraycopy (layoutInfo.colWeights, 0, result [0], 0, layoutInfo.cols); + result [1] = new double [layoutInfo.rows]; + System.arraycopy (layoutInfo.rowWeights, 0, result [1], 0, layoutInfo.rows); + return result; + } + + /** + * @since 1.1 + */ + public Point location (int x, int y) + { + if (layoutInfo == null) + return new Point (0, 0); + + int col; + int row; + int pixel_x = layoutInfo.pos_x; + int pixel_y = layoutInfo.pos_y; + + for (col = 0; col < layoutInfo.cols; col++) + { + int w = layoutInfo.colWidths [col]; + if (x < pixel_x + w) + break; + + pixel_x += w; + } + + for (row = 0; row < layoutInfo.rows; row++) + { + int h = layoutInfo.rowHeights [row]; + if (y < pixel_y + h) + break; + + pixel_y += h; + } + + return new Point (col, row); + } + + /** + * Return a string representation of this GridBagLayout. + * + * @return a string representation + */ + public String toString() + { + return getClass().getName(); + } + + /** + * Move and resize a rectangle according to a set of grid bag + * constraints. The x, y, width and height fields of the + * rectangle argument are adjusted to the new values. + * + * @param constraints position and size constraints + * @param r rectangle to be moved and resized + */ + protected void AdjustForGravity (GridBagConstraints constraints, + Rectangle r) + { + Insets insets = constraints.insets; + if (insets != null) + { + r.x += insets.left; + r.y += insets.top; + r.width -= insets.left + insets.right; + r.height -= insets.top + insets.bottom; + } + } + + /** + * Obsolete. + */ + protected void ArrangeGrid (Container parent) + { + Component[] components = parent.getComponents(); + + if (components.length == 0) + return; + + GridBagLayoutInfo info = getLayoutInfo (parent, PREFERREDSIZE); + if (info.cols == 0 && info.rows == 0) + return; + + // DEBUG + //dumpLayoutInfo (info); + + // Calling setBounds on these components causes this layout to + // be invalidated, clearing the layout information cache, + // layoutInfo. So we wait until after this for loop to set + // layoutInfo. + Component lastComp = null; + + Rectangle cell = new Rectangle(); + + for (int i = 0; i < components.length; i++) + { + Component component = components[i]; + + // If component is not visible we dont have to care about it. + if (! component.isVisible()) + continue; + + Dimension dim = component.getPreferredSize(); + GridBagConstraints constraints = lookupInternalConstraints(component); + + if (lastComp != null + && constraints.gridheight == GridBagConstraints.REMAINDER) + cell.y += cell.height; + else + cell.y = sumIntArray(info.rowHeights, constraints.gridy); + + if (lastComp != null + && constraints.gridwidth == GridBagConstraints.REMAINDER) + cell.x += cell.width; + else + cell.x = sumIntArray(info.colWidths, constraints.gridx); + + cell.width = sumIntArray(info.colWidths, constraints.gridx + + constraints.gridwidth) - cell.x; + cell.height = sumIntArray(info.rowHeights, constraints.gridy + + constraints.gridheight) - cell.y; + + // Adjust for insets. + AdjustForGravity( constraints, cell ); + + // Note: Documentation says that padding is added on both sides, but + // visual inspection shows that the Sun implementation only adds it + // once, so we do the same. + dim.width += constraints.ipadx; + dim.height += constraints.ipady; + + switch (constraints.fill) + { + case GridBagConstraints.HORIZONTAL: + dim.width = cell.width; + break; + case GridBagConstraints.VERTICAL: + dim.height = cell.height; + break; + case GridBagConstraints.BOTH: + dim.width = cell.width; + dim.height = cell.height; + break; + } + + int x = 0; + int y = 0; + + switch (constraints.anchor) + { + case GridBagConstraints.NORTH: + x = cell.x + (cell.width - dim.width) / 2; + y = cell.y; + break; + case GridBagConstraints.SOUTH: + x = cell.x + (cell.width - dim.width) / 2; + y = cell.y + cell.height - dim.height; + break; + case GridBagConstraints.WEST: + x = cell.x; + y = cell.y + (cell.height - dim.height) / 2; + break; + case GridBagConstraints.EAST: + x = cell.x + cell.width - dim.width; + y = cell.y + (cell.height - dim.height) / 2; + break; + case GridBagConstraints.NORTHEAST: + x = cell.x + cell.width - dim.width; + y = cell.y; + break; + case GridBagConstraints.NORTHWEST: + x = cell.x; + y = cell.y; + break; + case GridBagConstraints.SOUTHEAST: + x = cell.x + cell.width - dim.width; + y = cell.y + cell.height - dim.height; + break; + case GridBagConstraints.SOUTHWEST: + x = cell.x; + y = cell.y + cell.height - dim.height; + break; + default: + x = cell.x + (cell.width - dim.width) / 2; + y = cell.y + (cell.height - dim.height) / 2; + break; + } + component.setBounds(info.pos_x + x, info.pos_y + y, dim.width, + dim.height); + lastComp = component; + } + + // DEBUG + //dumpLayoutInfo(info); + + // Cache layout information. + layoutInfo = getLayoutInfo(parent, PREFERREDSIZE); + } + + /** + * Obsolete. + */ + protected GridBagLayoutInfo GetLayoutInfo (Container parent, int sizeflag) + { + if (sizeflag != MINSIZE && sizeflag != PREFERREDSIZE) + throw new IllegalArgumentException(); + + Dimension parentDim = parent.getSize (); + Insets parentInsets = parent.getInsets (); + parentDim.width -= parentInsets.left + parentInsets.right; + parentDim.height -= parentInsets.top + parentInsets.bottom; + + int current_y = 0; + int max_x = 0; + int max_y = 0; + + // Guaranteed to contain the last component added to the given row + // or column, whose gridwidth/height is not REMAINDER. + HashMap lastInRow = new HashMap(); + HashMap lastInCol = new HashMap(); + + Component[] components = parent.getComponents(); + + // Components sorted by gridwidths/heights, + // smallest to largest, with REMAINDER and RELATIVE at the end. + // These are useful when determining sizes and weights. + ArrayList sortedByWidth = + new ArrayList(components.length); + ArrayList sortedByHeight = + new ArrayList(components.length); + + // STEP 1: first we figure out how many rows/columns + for (int i = 0; i < components.length; i++) + { + Component component = components [i]; + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + // When looking up the constraint for the first time, check the + // original unmodified constraint. After the first time, always + // refer to the internal modified constraint. + GridBagConstraints originalConstraints = lookupConstraints (component); + GridBagConstraints constraints = (GridBagConstraints) originalConstraints.clone(); + internalcomptable.put(component, constraints); + + // Cases: + // + // 1. gridy == RELATIVE, gridx == RELATIVE + // + // use y as the row number; check for the next + // available slot at row y + // + // 2. only gridx == RELATIVE + // + // check for the next available slot at row gridy + // + // 3. only gridy == RELATIVE + // + // check for the next available slot at column gridx + // + // 4. neither gridx or gridy == RELATIVE + // + // nothing to check; just add it + + // cases 1 and 2 + if(constraints.gridx == GridBagConstraints.RELATIVE) + { + if (constraints.gridy == GridBagConstraints.RELATIVE) + constraints.gridy = current_y; + + int x; + + // Check the component that occupies the right-most spot in this + // row. We want to add this component after it. + // If this row is empty, add to the 0 position. + if (!lastInRow.containsKey(new Integer(constraints.gridy))) + x = 0; + else + { + Component lastComponent = lastInRow.get(new Integer(constraints.gridy)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + x = lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth); + } + + // Determine if this component will fit in the slot vertically. + // If not, bump it over to where it does fit. + for (int y = constraints.gridy + 1; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) + { + if (lastInRow.containsKey(new Integer(y))) + { + Component lastComponent = lastInRow.get(new Integer(y)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + x = Math.max (x, + lastConstraints.gridx + Math.max(1, lastConstraints.gridwidth)); + } + } + + constraints.gridx = x; + } + // case 3 + else if(constraints.gridy == GridBagConstraints.RELATIVE) + { + int y; + // Check the component that occupies the bottom-most spot in + // this column. We want to add this component below it. + // If this column is empty, add to the 0 position. + if (!lastInCol.containsKey(new Integer(constraints.gridx))) + { + y = current_y; + } + else + { + Component lastComponent = lastInCol.get(new Integer(constraints.gridx)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + y = lastConstraints.gridy + Math.max(1, lastConstraints.gridheight); + } + + // Determine if this component will fit in the slot horizontally. + // If not, bump it down to where it does fit. + for (int x = constraints.gridx + 1; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) + { + if (lastInCol.containsKey(new Integer(x))) + { + Component lastComponent = lastInCol.get(new Integer(x)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + y = Math.max (y, + lastConstraints.gridy + Math.max(1, lastConstraints.gridheight)); + } + } + + constraints.gridy = y; + } + // case 4: do nothing + + max_x = Math.max(max_x, + constraints.gridx + Math.max(1, constraints.gridwidth)); + max_y = Math.max(max_y, + constraints.gridy + Math.max(1, constraints.gridheight)); + + sortBySpan(component, constraints.gridwidth, sortedByWidth, true); + sortBySpan(component, constraints.gridheight, sortedByHeight, false); + + // Update our reference points for RELATIVE gridx and gridy. + if(constraints.gridwidth == GridBagConstraints.REMAINDER) + { + current_y = constraints.gridy + Math.max(1, constraints.gridheight); + } + else if (constraints.gridwidth != GridBagConstraints.REMAINDER) + { + for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) + { + if(lastInRow.containsKey(new Integer(y))) + { + Component lastComponent = lastInRow.get(new Integer(y)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + if (constraints.gridx > lastConstraints.gridx) + { + lastInRow.put(new Integer(y), component); + } + } + else + { + lastInRow.put(new Integer(y), component); + } + } + + for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) + { + if(lastInCol.containsKey(new Integer(x))) + { + Component lastComponent = lastInCol.get(new Integer(x)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + if (constraints.gridy > lastConstraints.gridy) + { + lastInCol.put(new Integer(x), component); + } + } + else + { + lastInCol.put(new Integer(x), component); + } + } + } + } // end of STEP 1 + + GridBagLayoutInfo info = new GridBagLayoutInfo(max_x, max_y); + + // Check if column widths and row heights are overridden. + + for (int x = 0; x < max_x; x++) + { + if(columnWidths != null && columnWidths.length > x) + info.colWidths[x] = columnWidths[x]; + if(columnWeights != null && columnWeights.length > x) + info.colWeights[x] = columnWeights[x]; + } + + for (int y = 0; y < max_y; y++) + { + if(rowHeights != null && rowHeights.length > y) + info.rowHeights[y] = rowHeights[y]; + if(rowWeights != null && rowWeights.length > y) + info.rowWeights[y] = rowWeights[y]; + } + + // STEP 2: Fix up any cells with width/height as REMAINDER/RELATIVE. + for (int i = 0; i < components.length; i++) + { + Component component = components [i]; + + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + GridBagConstraints constraints = lookupInternalConstraints (component); + + if(constraints.gridwidth == GridBagConstraints.REMAINDER || constraints.gridwidth == GridBagConstraints.RELATIVE) + { + if(constraints.gridwidth == GridBagConstraints.REMAINDER) + { + for (int y = constraints.gridy; y < constraints.gridy + Math.max(1, constraints.gridheight); y++) + { + if (lastInRow.containsKey(new Integer(y))) + { + Component lastComponent = lastInRow.get(new Integer(y)); + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + + if (lastConstraints.gridwidth == GridBagConstraints.RELATIVE) + { + constraints.gridx = max_x - 1; + break; + } + else + { + constraints.gridx = Math.max (constraints.gridx, + lastConstraints.gridx + Math.max (1, lastConstraints.gridwidth)); + } + } + } + constraints.gridwidth = max_x - constraints.gridx; + } + else if (constraints.gridwidth == GridBagConstraints.RELATIVE) + { + constraints.gridwidth = max_x - constraints.gridx - 1; + } + + // Re-sort + sortedByWidth.remove(sortedByWidth.indexOf(component)); + sortBySpan(component, constraints.gridwidth, sortedByWidth, true); + } + + if(constraints.gridheight == GridBagConstraints.REMAINDER || constraints.gridheight == GridBagConstraints.RELATIVE) + { + if(constraints.gridheight == GridBagConstraints.REMAINDER) + { + for (int x = constraints.gridx; x < constraints.gridx + Math.max(1, constraints.gridwidth); x++) + { + if (lastInCol.containsKey(new Integer(x))) + { + Component lastComponent = lastInRow.get(new Integer(x)); + if (lastComponent != null) + { + GridBagConstraints lastConstraints = lookupInternalConstraints(lastComponent); + + if (lastConstraints.gridheight == GridBagConstraints.RELATIVE) + { + constraints.gridy = max_y - 1; + break; + } + else + { + constraints.gridy = Math.max (constraints.gridy, + lastConstraints.gridy + Math.max (1, lastConstraints.gridheight)); + } + } + } + } + constraints.gridheight = max_y - constraints.gridy; + } + else if (constraints.gridheight == GridBagConstraints.RELATIVE) + { + constraints.gridheight = max_y - constraints.gridy - 1; + } + + // Re-sort + sortedByHeight.remove(sortedByHeight.indexOf(component)); + sortBySpan(component, constraints.gridheight, sortedByHeight, false); + } + } // end of STEP 2 + + // STEP 3: Determine sizes and weights for columns. + for (int i = 0; i < sortedByWidth.size(); i++) + { + Component component = sortedByWidth.get(i); + + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + GridBagConstraints constraints = lookupInternalConstraints (component); + + int width = (sizeflag == PREFERREDSIZE) ? + component.getPreferredSize().width : + component.getMinimumSize().width; + + if(constraints.insets != null) + width += constraints.insets.left + constraints.insets.right; + + width += constraints.ipadx; + + distributeSizeAndWeight(width, + constraints.weightx, + constraints.gridx, + constraints.gridwidth, + info.colWidths, + info.colWeights); + } // end of STEP 3 + + // STEP 4: Determine sizes and weights for rows. + for (int i = 0; i < sortedByHeight.size(); i++) + { + Component component = sortedByHeight.get(i); + + // If component is not visible we dont have to care about it. + if (!component.isVisible()) + continue; + + GridBagConstraints constraints = lookupInternalConstraints (component); + + int height = (sizeflag == PREFERREDSIZE) ? + component.getPreferredSize().height : + component.getMinimumSize().height; + + if(constraints.insets != null) + height += constraints.insets.top + constraints.insets.bottom; + + height += constraints.ipady; + + distributeSizeAndWeight(height, + constraints.weighty, + constraints.gridy, + constraints.gridheight, + info.rowHeights, + info.rowWeights); + } // end of STEP 4 + + // Adjust cell sizes iff parent size not zero. + if (parentDim.width > 0 && parentDim.height > 0) + { + calcCellSizes (info.colWidths, info.colWeights, parentDim.width); + calcCellSizes (info.rowHeights, info.rowWeights, parentDim.height); + } + + int totalWidth = sumIntArray(info.colWidths); + int totalHeight = sumIntArray(info.rowHeights); + + // Make sure pos_x and pos_y are never negative. + if (totalWidth >= parentDim.width) + info.pos_x = parentInsets.left; + else + info.pos_x = parentInsets.left + (parentDim.width - totalWidth) / 2; + + if (totalHeight >= parentDim.height) + info.pos_y = parentInsets.top; + else + info.pos_y = parentInsets.top + (parentDim.height - totalHeight) / 2; + + // DEBUG + //dumpLayoutInfo (info); + + return info; + } + + /** + * Obsolete. + */ + protected Dimension GetMinSize (Container parent, GridBagLayoutInfo info) + { + if (parent == null || info == null) + return new Dimension (0, 0); + + Insets insets = parent.getInsets(); + int width = sumIntArray (info.colWidths) + insets.left + insets.right; + int height = sumIntArray (info.rowHeights) + insets.top + insets.bottom; + return new Dimension (width, height); + } + + /** + * @since 1.4 + */ + protected Dimension getMinSize (Container parent, GridBagLayoutInfo info) + { + return GetMinSize (parent, info); + } + + /** + * Helper method used by GetLayoutInfo to keep components sorted, either + * by gridwidth or gridheight. + * + * @param component Component to add to the sorted list. + * @param span Either the component's gridwidth or gridheight. + * @param list ArrayList of components, sorted by + * their span. + * @param sortByWidth Flag indicating sorting index. If true, sort by + * width. Otherwise, sort by height. + * FIXME: Use a better sorting algorithm. + */ + private void sortBySpan (Component component, int span, + ArrayList list, boolean sortByWidth) + { + if (span == GridBagConstraints.REMAINDER + || span == GridBagConstraints.RELATIVE) + { + // Put all RELATIVE and REMAINDER components at the end. + list.add(component); + } + else + { + int i = 0; + if (list.size() > 0) + { + GridBagConstraints gbc = lookupInternalConstraints(list.get(i)); + int otherspan = sortByWidth ? + gbc.gridwidth : + gbc.gridheight; + while (otherspan != GridBagConstraints.REMAINDER + && otherspan != GridBagConstraints.RELATIVE + && span >= otherspan) + { + i++; + if (i < list.size()) + { + gbc = lookupInternalConstraints(list.get(i)); + otherspan = sortByWidth ? + gbc.gridwidth : + gbc.gridheight; + } + else + break; + } + } + list.add(i, component); + } + } + + /** + * Helper method used by GetLayoutInfo to distribute a component's size + * and weight. + * + * @param size Preferred size of component, with inset and padding + * already added. + * @param weight Weight of component. + * @param start Starting position of component. Either + * constraints.gridx or gridy. + * @param span Span of component. either contraints.gridwidth or + * gridheight. + * @param sizes Sizes of rows or columns. + * @param weights Weights of rows or columns. + */ + private void distributeSizeAndWeight (int size, double weight, + int start, int span, + int[] sizes, double[] weights) + { + if (span == 1) + { + sizes[start] = Math.max(sizes[start], size); + weights[start] = Math.max(weights[start], weight); + } + else + { + int numOccupied = span; + int lastOccupied = -1; + + for(int i = start; i < start + span; i++) + { + if (sizes[i] == 0.0) + numOccupied--; + else + { + size -= sizes[i]; + lastOccupied = i; + } + } + + // A component needs to occupy at least one row. + if(numOccupied == 0) + sizes[start + span - 1] = size; + else if (size > 0) + sizes[lastOccupied] += size; + + calcCellWeights(weight, weights, start, span); + } + } + + /** + * Helper method used by GetLayoutInfo to calculate weight distribution. + * @param weight Weight of component. + * @param weights Weights of rows/columns. + * @param start Starting position of component in grid (gridx/gridy). + * @param span Span of component (gridwidth/gridheight). + */ + private void calcCellWeights (double weight, double[] weights, int start, int span) + { + double totalWeight = 0.0; + for(int k = start; k < start + span; k++) + totalWeight += weights[k]; + + if(weight > totalWeight) + { + if (totalWeight == 0.0) + { + weights[start + span - 1] += weight; + } + else + { + double diff = weight - totalWeight ; + double remaining = diff; + + for(int k = start; k < start + span; k++) + { + double extraWeight = diff * weights[k] / totalWeight; + weights[k] += extraWeight; + remaining -= extraWeight; + } + + if (remaining > 0.0 && weights[start + span - 1] != 0.0) + { + weights[start + span - 1] += remaining; + } + } + } + } + + /** + * Helper method used by GetLayoutInfo to distribute extra space + * based on weight distribution. + * + * @param sizes Sizes of rows/columns. + * @param weights Weights of rows/columns. + * @param range Dimension of container. + */ + private void calcCellSizes (int[] sizes, double[] weights, int range) + { + int totalSize = sumIntArray (sizes); + double totalWeight = sumDoubleArray (weights); + + int diff = range - totalSize; + + if (diff == 0) + return; + + for (int i = 0; i < sizes.length; i++) + { + int newsize = (int) (sizes[i] + (((double) diff) * weights [i] / totalWeight )); + + if (newsize > 0) + sizes[i] = newsize; + } + } + + private void dumpLayoutInfo (GridBagLayoutInfo info) + { + System.out.println ("GridBagLayoutInfo:"); + System.out.println ("cols: " + info.cols + ", rows: " + info.rows); + System.out.print ("colWidths: "); + dumpArray(info.colWidths); + System.out.print ("rowHeights: "); + dumpArray(info.rowHeights); + System.out.print ("colWeights: "); + dumpArray(info.colWeights); + System.out.print ("rowWeights: "); + dumpArray(info.rowWeights); + } + + private void dumpArray(int[] array) + { + String sep = ""; + for(int i = 0; i < array.length; i++) + { + System.out.print(sep); + System.out.print(array[i]); + sep = ", "; + } + System.out.println(); + } + + private void dumpArray(double[] array) + { + String sep = ""; + for(int i = 0; i < array.length; i++) + { + System.out.print(sep); + System.out.print(array[i]); + sep = ", "; + } + System.out.println(); + } + + /** + * @since 1.4 + */ + protected void arrangeGrid (Container parent) + { + ArrangeGrid (parent); + } + + /** + * @since 1.4 + */ + protected GridBagLayoutInfo getLayoutInfo (Container parent, int sizeflag) + { + return GetLayoutInfo (parent, sizeflag); + } + + /** + * Move and resize a rectangle according to a set of grid bag + * constraints. The x, y, width and height fields of the + * rectangle argument are adjusted to the new values. + * + * @param constraints position and size constraints + * @param r rectangle to be moved and resized + * + * @since 1.4 + */ + protected void adjustForGravity (GridBagConstraints constraints, + Rectangle r) + { + AdjustForGravity (constraints, r); + } +} diff --git a/libjava/classpath/java/awt/GridBagLayoutInfo.java b/libjava/classpath/java/awt/GridBagLayoutInfo.java new file mode 100644 index 000000000..43ba09d72 --- /dev/null +++ b/libjava/classpath/java/awt/GridBagLayoutInfo.java @@ -0,0 +1,70 @@ +/* GridBagLayoutInfo - + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * @author Michael Koch (konqueror@gmx.de) + */ +class GridBagLayoutInfo implements Serializable +{ + private static final long serialVersionUID = -4899416460737170217L; + + int pos_x; + int pos_y; + int cols; + int rows; + int colWidths[]; + int rowHeights[]; + double colWeights[]; + double rowWeights[]; + + GridBagLayoutInfo (int cols, int rows) + { + this.pos_x = 0; + this.pos_y = 0; + this.cols = cols; + this.rows = rows; + this.colWidths = new int [cols]; + this.rowHeights = new int [rows]; + this.colWeights = new double [cols]; + this.rowWeights = new double [rows]; + } +} diff --git a/libjava/classpath/java/awt/GridLayout.java b/libjava/classpath/java/awt/GridLayout.java new file mode 100644 index 000000000..a8b0189de --- /dev/null +++ b/libjava/classpath/java/awt/GridLayout.java @@ -0,0 +1,354 @@ +/* GridLayout.java -- Grid-based layout engine + Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** This class implements a grid-based layout scheme. Components are + * all given the same size and are laid out from left to right and top + * to bottom. A GridLayout is configured with a number of rows and a + * number of columns. If both are specified, then the number of + * columns is ignored and is derived from the number of rows and the + * total number of components. If either is zero then that dimension + * is computed based on the actual size of the container. An + * exception is thrown if an attempt is made to set both the number of + * rows and the number of columns to 0. This class also supports + * horizontal and vertical gaps; these are used as spacing between + * cells. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class GridLayout implements LayoutManager, Serializable +{ + static final long serialVersionUID = -7411804673224730901L; + + /** Add a new component to the layout. This particular implementation + * does nothing. + * @param name The name of the component to add. + * @param comp The component to add. + */ + public void addLayoutComponent (String name, Component comp) + { + // Nothing. + } + + /** Return the number of columns in this layout. */ + public int getColumns () + { + return cols; + } + + /** Return the horizontal gap. */ + public int getHgap () + { + return hgap; + } + + /** Return the number of rows in this layout. */ + public int getRows () + { + return rows; + } + + /** Return the vertical gap. */ + public int getVgap () + { + return vgap; + } + + /** Create a new GridLayout with one row and any number + * of columns. Both gaps are set to 0. + */ + public GridLayout () + { + this (1, 0, 0, 0); + } + + /** Create a new GridLayout with the specified number + * of rows and columns. Both gaps are set to 0. Note that the row + * and column settings cannot both be zero. If both the row and + * column values are non-zero, the rows value takes precedence. + * @param rows Number of rows + * @param cols Number of columns + * @exception IllegalArgumentException If rows and columns are both + * 0, or if either are negative + */ + public GridLayout (int rows, int cols) + { + this (rows, cols, 0, 0); + } + + /** Create a new GridLayout with the specified number of rows and + * columns and the specified gaps. + * Note that the row and column settings cannot both be + * zero. If both the row and column values are non-zero, the rows value + * takes precedence. + * @param rows Number of rows + * @param cols Number of columns + * @param hgap The horizontal gap + * @param vgap The vertical gap + * @exception IllegalArgumentException If rows and columns are both + * 0, if either are negative, or if either gap is negative + */ + public GridLayout (int rows, int cols, int hgap, int vgap) + { + if (rows < 0) + throw new IllegalArgumentException ("number of rows cannot be negative"); + if (cols < 0) + throw new IllegalArgumentException ("number of columns cannot be negative"); + if (rows == 0 && cols == 0) + throw new IllegalArgumentException ("both rows and columns cannot be 0"); + if (hgap < 0) + throw new IllegalArgumentException ("horizontal gap must be nonnegative"); + if (vgap < 0) + throw new IllegalArgumentException ("vertical gap must be nonnegative"); + this.rows = rows; + this.cols = cols; + this.hgap = hgap; + this.vgap = vgap; + } + + /** Lay out the container's components based on current settings. + * The free space in the container is divided evenly into the specified + * number of rows and columns in this object. + * @param parent The container to lay out + */ + public void layoutContainer (Container parent) + { + synchronized (parent.getTreeLock ()) + { + int num = parent.ncomponents; + + // There's no point, and handling this would mean adding special + // cases. + if (num == 0) + return; + + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + int real_rows = rows; + int real_cols = cols; + if (real_rows == 0) + real_rows = (num + real_cols - 1) / real_cols; + else + real_cols = (num + real_rows - 1) / real_rows; + + // We might have less than a single row. In this case we expand + // to fill. + if (num < real_cols) + real_cols = num; + + Dimension d = parent.getSize (); + Insets ins = parent.getInsets (); + + // Compute width and height of each cell in the grid. + int tw = d.width - ins.left - ins.right; + tw = (tw - (real_cols - 1) * hgap) / real_cols; + int th = d.height - ins.top - ins.bottom; + th = (th - (real_rows - 1) * vgap) / real_rows; + + // If the cells are too small, still try to do something. + if (tw < 0) + tw = 1; + if (th < 0) + th = 1; + + int x = ins.left; + int y = ins.top; + int i = 0; + int recount = 0; + + while (i < num) + { + comps[i].setBounds (x, y, tw, th); + + ++i; + ++recount; + if (recount == real_cols) + { + recount = 0; + y += vgap + th; + x = ins.left; + } + else + x += hgap + tw; + } + } + } + + /** Get the minimum layout size of the container. + * @param cont The parent container + */ + public Dimension minimumLayoutSize (Container cont) + { + return getSize (cont, true); + } + + /** Get the preferred layout size of the container. + * @param cont The parent container + */ + public Dimension preferredLayoutSize (Container cont) + { + return getSize (cont, false); + } + + /** Remove the indicated component from this layout manager. + * This particular implementation does nothing. + * @param comp The component to remove + */ + public void removeLayoutComponent (Component comp) + { + // Nothing. + } + + /** Set the number of columns. + * @param newCols + * @exception IllegalArgumentException If the number of columns is + * negative, or if the number of columns is zero and the number + * of rows is already 0. + */ + public void setColumns (int newCols) + { + if (newCols < 0) + throw new IllegalArgumentException ("number of columns cannot be negative"); + if (newCols == 0 && rows == 0) + throw new IllegalArgumentException ("number of rows is already 0"); + this.cols = newCols; + } + + /** Set the horizontal gap. An Exception is not thrown if hgap < 0. + * @param hgap The horizontal gap + */ + public void setHgap (int hgap) + { + this.hgap = hgap; + } + + /** Set the number of rows + * @param newRows + * @exception IllegalArgumentException If the number of rows is + * negative, or if the number of rows is zero and the number + * of columns is already 0. + */ + public void setRows (int newRows) + { + if (newRows < 0) + throw new IllegalArgumentException ("number of rows cannot be negative"); + if (newRows == 0 && cols == 0) + throw new IllegalArgumentException ("number of columns is already 0"); + this.rows = newRows; + } + + /** Set the vertical gap. An Exception is not thrown if vgap < 0. + * @param vgap The vertical gap + */ + public void setVgap (int vgap) + { + this.vgap = vgap; + } + + /** Return String description of this object. */ + public String toString () + { + return (getClass ().getName () + "[" + + "hgap=" + hgap + ",vgap=" + vgap + + ",rows=" + rows + ",cols=" + cols + + "]"); + } + + // This method is used to compute the various sizes. + private Dimension getSize (Container parent, boolean is_min) + { + synchronized (parent.getTreeLock ()) + { + int w = 0, h = 0, num = parent.ncomponents; + // This is more efficient than calling getComponents(). + Component[] comps = parent.component; + + for (int i = 0; i < num; ++i) + { + Dimension d; + + if (is_min) + d = comps[i].getMinimumSize (); + else + d = comps[i].getPreferredSize (); + + w = Math.max (d.width, w); + h = Math.max (d.height, h); + } + + int real_rows = rows; + int real_cols = cols; + if (real_rows == 0) + real_rows = (num + real_cols - 1) / real_cols; + else + real_cols = (num + real_rows - 1) / real_rows; + + Insets ins = parent.getInsets (); + // We subtract out an extra gap here because the gaps are only + // between cells. + w = ins.left + ins.right + real_cols * (w + hgap) - hgap; + h = ins.top + ins.bottom + real_rows * (h + vgap) - vgap; + return new Dimension (w, h); + } + } + + /** + * @serial The number of columns in the grid. + */ + private int cols; + + /** + * @serial The number of rows in the grid. + */ + private int rows; + + /** + * @serial The horizontal gap between columns + */ + private int hgap; + + /** + * @serial The vertical gap between rows + */ + private int vgap; +} diff --git a/libjava/classpath/java/awt/HeadlessException.java b/libjava/classpath/java/awt/HeadlessException.java new file mode 100644 index 000000000..b180b1d86 --- /dev/null +++ b/libjava/classpath/java/awt/HeadlessException.java @@ -0,0 +1,72 @@ +/* HeadlessException.java -- operation not possible in headless environment + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This exception is thrown when code dependent on a keyboard, mouse, or + * display is executed in a headless environment. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class HeadlessException extends UnsupportedOperationException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 167183644944358563L; + + /** + * Create a new instance with no detailed error message. + */ + public HeadlessException() + { + } + + /** + * Create a new instance with the specified detailed error message. + * + * @param message the detailed error message + */ + public HeadlessException(String message) + { + super(message); + } +} // class HeadlessException diff --git a/libjava/classpath/java/awt/IllegalComponentStateException.java b/libjava/classpath/java/awt/IllegalComponentStateException.java new file mode 100644 index 000000000..4a47f1da9 --- /dev/null +++ b/libjava/classpath/java/awt/IllegalComponentStateException.java @@ -0,0 +1,71 @@ +/* IllegalComponentStateException.java -- bad component state + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This exception is thrown when the requested operation failed because + * a component was not in the proper state. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class IllegalComponentStateException extends IllegalStateException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -1889339587208144238L; + + /** + * Create a new instance with no detailed error message. + */ + public IllegalComponentStateException() + { + } + + /** + * Create a new instance with the specified detailed error message. + * + * @param message the detailed error message + */ + public IllegalComponentStateException(String message) + { + super(message); + } +} // class IllegalComponentStateException diff --git a/libjava/classpath/java/awt/Image.java b/libjava/classpath/java/awt/Image.java new file mode 100644 index 000000000..7b2cee7ae --- /dev/null +++ b/libjava/classpath/java/awt/Image.java @@ -0,0 +1,242 @@ +/* Image.java -- superclass for images + Copyright (C) 1999, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.AreaAveragingScaleFilter; +import java.awt.image.FilteredImageSource; +import java.awt.image.ImageFilter; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.ReplicateScaleFilter; + +/** + * This is the abstract superclass of all image objects in Java. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.5 + */ +public abstract class Image +{ + /** + * This variable is returned whenever a property that is not defined + * is requested. + */ + // For debug purposes, this might as well be a unique string. + public static final Object UndefinedProperty + = new String("undefined property"); + + /** + * Constant indicating that the default scaling algorithm should be used. + * + * @since 1.1 + */ + public static final int SCALE_DEFAULT = 1; + + /** + * Constant indicating that a fast scaling algorithm should be used. + * + * @since 1.1 + */ + public static final int SCALE_FAST = 2; + + /** + * Constant indicating that a smooth scaling algorithm should be used. + * + * @since 1.1 + */ + public static final int SCALE_SMOOTH = 4; + + /** + * Constant indicating that the ReplicateScaleFilter class + * algorithm should be used for scaling. + * + * @see ReplicateScaleFilter + * @since 1.1 + */ + public static final int SCALE_REPLICATE = 8; + + /** + * Constant indicating that the area averaging scaling algorithm should be + * used. + * + * @see java.awt.image.AreaAveragingScaleFilter + * @since 1.1 + */ + public static final int SCALE_AREA_AVERAGING = 16; + + /** + * The acceleration priority of the image + * @since 1.5 + */ + protected float accelerationPriority; + + /** + * A default constructor for subclasses. + */ + public Image() + { + } + + /** + * Returns the width of the image, or -1 if it is unknown. If the + * image width is unknown, the observer object will be notified when + * the value is known. + * + * @param observer the image observer for this object + * @return the width in pixels + * @see #getHeight(ImageObserver) + */ + public abstract int getWidth(ImageObserver observer); + + /** + * Returns the height of the image, or -1 if it is unknown. If the + * image height is unknown, the observer object will be notified when + * the value is known. + * + * @param observer the image observer for this object + * @return the height in pixels + * @see #getWidth(ImageObserver) + */ + public abstract int getHeight(ImageObserver observer); + + /** + * Returns the image producer object for this object. The producer is the + * object which generates pixels for this image. + * + * @return the image producer for this object + */ + public abstract ImageProducer getSource(); + + /** + * Returns a graphics context object for drawing an off-screen object. + * This method is only valid for off-screen objects. + * + * @return a graphics context object for an off-screen object + */ + public abstract Graphics getGraphics(); + + /** + * This method requests a named property for an object. The value of the + * property is returned. The value UndefinedProperty is + * returned if there is no property with the specified name. The value + * null is returned if the properties for the object are + * not yet known. In this case, the specified image observer is notified + * when the properties are known. + * + * @param name the requested property name + * @param observer the image observer for this object + * @return the named property, if available + * @see #UndefinedProperty + */ + public abstract Object getProperty(String name, ImageObserver observer); + + /** + * Scales the image to the requested dimension. A new Image with asynchronous + * loading will be produced according to the hints of the algorithm + * requested. If either the width or height is non-positive, it is adjusted + * to preserve the original aspect ratio. + * If an illegal value of flags is passed, + * the default algorithm is used. + * + * @param width the width of the scaled image + * @param height the height of the scaled image + * @param flags a value indicating the algorithm to use + * @return the scaled Image object + * @see #SCALE_DEFAULT + * @see #SCALE_FAST + * @see #SCALE_SMOOTH + * @see #SCALE_REPLICATE + * @see #SCALE_AREA_AVERAGING + * @since 1.1 + */ + public Image getScaledInstance(int width, int height, int flags) + { + ImageFilter filter; + switch (flags) + { + case SCALE_AREA_AVERAGING: + case SCALE_SMOOTH: + filter = new AreaAveragingScaleFilter(width, height); + break; + case SCALE_DEFAULT: + case SCALE_FAST: + case SCALE_REPLICATE: + default: + filter = new ReplicateScaleFilter(width, height); + } + + ImageProducer producer = new FilteredImageSource(getSource(), filter); + return Toolkit.getDefaultToolkit().createImage(producer); + } + + /** + * Flushes (that is, destroys) any resources used for this image. This + * includes the actual image data. + */ + public abstract void flush(); + + /** + * Sets the acceleration priority of the image. + * This is a value from 0 (lowest) to 1 (highest), which may + * be used as a hint for image acceleration. + * E.g. higher priority images may be stored in video memory. + * @param priority - the priority + * @throws IllegalArgumentException if priority is not >= 0 and <= 1. + * + * @since 1.5 + */ + public void setAccelerationPriority(float priority) + { + if( priority < 0f || priority > 1f) + throw new IllegalArgumentException("Invalid priority value."); + accelerationPriority = priority; + } + + /** + * Returns the acceleration priority of the image. + * + * @see #setAccelerationPriority(float) + * @since 1.5 + */ + public float getAccelerationPriority() + { + return accelerationPriority; + } +} // class Image diff --git a/libjava/classpath/java/awt/ImageCapabilities.java b/libjava/classpath/java/awt/ImageCapabilities.java new file mode 100644 index 000000000..2fe71d1e2 --- /dev/null +++ b/libjava/classpath/java/awt/ImageCapabilities.java @@ -0,0 +1,107 @@ +/* ImageCapabilities.java -- the capabilities of an image buffer + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class represents the capabilities of an image buffer. An + * image buffer may be backed by accelerated graphics resources. + * Those resources may or may not be volatile. This class is used to + * describe these image buffer characteristics. + */ +public class ImageCapabilities implements Cloneable +{ + /** + * Whether or not this the image buffer uses accelerated graphics + * resources. + */ + private final boolean accelerated; + + /** + * Create a new image capability descriptor. + * + * @param accelerated true if the image buffer uses accelerated + * graphics resources + */ + public ImageCapabilities(boolean accelerated) + { + this.accelerated = accelerated; + } + + /** + * Returns whether or not the image buffer uses accelerated graphics + * resources. + * + * @return true if the image buffer uses accelerated graphics + * resources; false otherwise + */ + public boolean isAccelerated() + { + return accelerated; + } + + /** + * Returns whether or not the image buffer's resources are volatile, + * meaning that they can be reclaimed by the graphics system at any + * time. + * + * @return true if the image buffer's resources are volatile; false + * otherwise + */ + public boolean isTrueVolatile() + { + return true; + } + + /** + * Clone this image capability descriptor. + * + * @return a clone of this image capability descriptor + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); + } + } +} diff --git a/libjava/classpath/java/awt/Insets.java b/libjava/classpath/java/awt/Insets.java new file mode 100644 index 000000000..1747cae6a --- /dev/null +++ b/libjava/classpath/java/awt/Insets.java @@ -0,0 +1,177 @@ +/* Insets.java -- information about a container border + Copyright (C) 1999, 2000, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.io.Serializable; + +/** + * This class represents the "margin" or space around a container. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status + */ +public class Insets implements Cloneable, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2272572637695466749L; + + /** + * The gap from the top. + * + * @serial the top inset + */ + public int top; + + /** + * The gap from the left. + * + * @serial the left inset + */ + public int left; + + /** + * The gap from the bottom. + * + * @serial the bottom inset + */ + public int bottom; + + /** + * The gap from the right. + * + * @serial the right inset + */ + public int right; + + /** + * Initializes a new instance of Inset with the specified + * inset values. + * + * @param top the top inset + * @param left the left inset + * @param bottom the bottom inset + * @param right the right inset + */ + public Insets(int top, int left, int bottom, int right) + { + this.top = top; + this.left = left; + this.bottom = bottom; + this.right = right; + } + + /** + * Set the contents of this Insets object to the specified values. + * + * @param top the top inset + * @param left the left inset + * @param bottom the bottom inset + * @param right the right inset + * + * @since 1.5 + */ + public void set(int top, int left, int bottom, int right) + { + this.top = top; + this.left = left; + this.bottom = bottom; + this.right = right; + } + + /** + * Tests whether this object is equal to the specified object. The other + * object must be an instance of Insets with identical field values. + * + * @param obj the object to test against + * @return true if the specified object is equal to this one + * + * @since 1.1 + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Insets)) + return false; + Insets i = (Insets) obj; + return top == i.top && bottom == i.bottom + && left == i.left && right == i.right; + } + + /** + * Returns a hashcode for this instance. The formula is unspecified, but + * appears to be XXX what is it? . + * + * @return the hashcode + */ + public int hashCode() + { + // This can't be right... + return top + bottom + left + right; + } + + /** + * Returns a string representation of this object, which will be non-null. + * + * @return a string representation of this object + */ + public String toString() + { + return getClass().getName() + "[top=" + top + ",left=" + left + + ",bottom=" + bottom + ",right=" + right + ']'; + } + + /** + * Returns a copy of this object. + * + * @return a copy of this object + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } +} // class Insets diff --git a/libjava/classpath/java/awt/ItemSelectable.java b/libjava/classpath/java/awt/ItemSelectable.java new file mode 100644 index 000000000..f155f723e --- /dev/null +++ b/libjava/classpath/java/awt/ItemSelectable.java @@ -0,0 +1,75 @@ +/* ItemSelectable.java -- items that can be selected + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ItemListener; + +/** + * This interface is for objects that can have one or more items selected. + * For example, radio buttons. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface ItemSelectable +{ + /** + * Returns the list of objects that are selected in this component. + * + * @return the list of selected objects, or null + */ + Object[] getSelectedObjects(); + + /** + * Adds an item listener to this object. It will receive selection events + * for this object by the user (but not programatically). If listener is + * null, it is ignored. + * + * @param listener the item listener to add + */ + void addItemListener(ItemListener listener); + + /** + * Removes an item listener from this object. + * + * @param listener the item listener to remove + */ + void removeItemListener(ItemListener listener); +} // interface ItemSelectable diff --git a/libjava/classpath/java/awt/JobAttributes.java b/libjava/classpath/java/awt/JobAttributes.java new file mode 100644 index 000000000..e84901d33 --- /dev/null +++ b/libjava/classpath/java/awt/JobAttributes.java @@ -0,0 +1,502 @@ +/* JobAttributes.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.lang.CPStringBuilder; + +/** + * Needs documentation... + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4, lacks documentation + */ +public final class JobAttributes implements Cloneable +{ + public static final class DefaultSelectionType extends AttributeValue + { + private static final String[] NAMES = { "all", "range", "selection" }; + public static final DefaultSelectionType ALL + = new DefaultSelectionType(0); + public static final DefaultSelectionType RANGE + = new DefaultSelectionType(1); + public static final DefaultSelectionType SELECTION + = new DefaultSelectionType(2); + private DefaultSelectionType(int value) + { + super(value, NAMES); + } + } // class DefaultSelectionType + + public static final class DestinationType extends AttributeValue + { + private static final String[] NAMES = { "file", "printer" }; + public static final DestinationType FILE = new DestinationType(0); + public static final DestinationType PRINTER = new DestinationType(1); + private DestinationType(int value) + { + super(value, NAMES); + } + } // class DestinationType + + public static final class DialogType extends AttributeValue + { + private static final String[] NAMES = { "common", "native", "none" }; + public static final DialogType COMMON = new DialogType(0); + public static final DialogType NATIVE = new DialogType(1); + public static final DialogType NONE = new DialogType(2); + private DialogType(int value) + { + super(value, NAMES); + } + } // class DialogType + + public static final class MultipleDocumentHandlingType + extends AttributeValue + { + private static final String[] NAMES = { + "separate-documents-collated-copies", + "separate-documents-uncollated-copies" + }; + public static final MultipleDocumentHandlingType + SEPARATE_DOCUMENTS_COLLATED_COPIES + = new MultipleDocumentHandlingType(0); + public static final MultipleDocumentHandlingType + SEPARATE_DOCUMENTS_UNCOLLATED_COPIES + = new MultipleDocumentHandlingType(1); + private MultipleDocumentHandlingType(int value) + { + super(value, NAMES); + } + } // class MultipleDocumentHandlingType + + public static final class SidesType extends AttributeValue + { + private static final String[] NAMES + = { "one-sided", "two-sided-long-edge", "two-sided-short-edge" }; + public static final SidesType ONE_SIDED = new SidesType(0); + public static final SidesType TWO_SIDED_LONG_EDGE = new SidesType(1); + public static final SidesType TWO_SIDED_SHORT_EDGE = new SidesType(2); + private SidesType(int value) + { + super(value, NAMES); + } + } // class SidesType + + private int copies; + private DefaultSelectionType selection; + private DestinationType destination; + private DialogType dialog; + private String filename; + private int maxPage; + private int minPage; + private MultipleDocumentHandlingType multiple; + private int[][] pageRanges; // null for default value + private int fromPage; // 0 for default value + private int toPage; // 0 for default value + private String printer; + private SidesType sides; + + public JobAttributes() + { + copies = 1; + selection = DefaultSelectionType.ALL; + destination = DestinationType.PRINTER; + dialog = DialogType.NATIVE; + maxPage = Integer.MAX_VALUE; + minPage = 1; + multiple + = MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES; + sides = SidesType.ONE_SIDED; + } + + public JobAttributes(JobAttributes attr) + { + set(attr); + } + + public JobAttributes(int copies, DefaultSelectionType selection, + DestinationType destination, DialogType dialog, + String filename, int max, int min, + MultipleDocumentHandlingType multiple, + int[][] pageRanges, String printer, SidesType sides) + { + if (copies <= 0 || selection == null || destination == null + || dialog == null || max < min || min <= 0 || multiple == null + || sides == null) + throw new IllegalArgumentException(); + this.copies = copies; + this.selection = selection; + this.destination = destination; + this.dialog = dialog; + this.filename = filename; + maxPage = max; + minPage = min; + this.multiple = multiple; + setPageRanges(pageRanges); + this.printer = printer; + this.sides = sides; + } + + public Object clone() + { + return new JobAttributes(this); + } + + public void set(JobAttributes attr) + { + copies = attr.copies; + selection = attr.selection; + destination = attr.destination; + dialog = attr.dialog; + filename = attr.filename; + maxPage = attr.maxPage; + minPage = attr.minPage; + multiple = attr.multiple; + pageRanges = (int[][]) attr.pageRanges.clone(); + printer = attr.printer; + sides = attr.sides; + fromPage = attr.fromPage; + toPage = attr.toPage; + } + + public int getCopies() + { + return copies; + } + + public void setCopies(int copies) + { + if (copies <= 0) + throw new IllegalArgumentException(); + this.copies = copies; + } + + public void setCopiesToDefault() + { + copies = 1; + } + + public DefaultSelectionType getDefaultSelection() + { + return selection; + } + + public void setDefaultSelection(DefaultSelectionType selection) + { + if (selection == null) + throw new IllegalArgumentException(); + this.selection = selection; + } + + public DestinationType getDestination() + { + return destination; + } + + public void setDestination(DestinationType destination) + { + if (destination == null) + throw new IllegalArgumentException(); + this.destination = destination; + } + + public DialogType getDialog() + { + return dialog; + } + + public void setDialog(DialogType dialog) + { + if (dialog == null) + throw new IllegalArgumentException(); + this.dialog = dialog; + } + + public String getFileName() + { + return filename; + } + + public void setFileName(String filename) + { + this.filename = filename; + } + + public int getFromPage() + { + return fromPage != 0 ? fromPage + : pageRanges != null ? pageRanges[0][0] + : toPage != 0 ? toPage : minPage; + } + + public void setFromPage(int fromPage) + { + if (fromPage < minPage || (fromPage > toPage && toPage != 0) + || fromPage > maxPage) + throw new IllegalArgumentException(); + if (pageRanges == null) + this.fromPage = fromPage; + } + + public int getMaxPage() + { + return maxPage; + } + + public void setMaxPage(int maxPage) + { + if (maxPage < minPage) + throw new IllegalArgumentException(); + this.maxPage = maxPage; + if (maxPage < fromPage) + fromPage = maxPage; + if (maxPage < toPage) + toPage = maxPage; + if (pageRanges != null) + { + int i = pageRanges.length - 1; + while (i >= 0 && maxPage < pageRanges[i][1]) + i--; + if (maxPage >= pageRanges[++i][0]) + pageRanges[i++][1] = maxPage; + if (i == 0) + pageRanges = null; + else if (i < pageRanges.length) + { + int[][] tmp = new int[i][]; + System.arraycopy(pageRanges, 0, tmp, 0, i); + pageRanges = tmp; + } + } + } + + public int getMinPage() + { + return minPage; + } + + public void setMinPage(int minPage) + { + if (minPage <= 0 || minPage > maxPage) + throw new IllegalArgumentException(); + this.minPage = minPage; + if (minPage > toPage) + toPage = minPage; + if (minPage > fromPage) + fromPage = minPage; + if (pageRanges != null) + { + int size = pageRanges.length; + int i = 0; + while (i < size && minPage > pageRanges[i][0]) + i++; + if (minPage <= pageRanges[i - 1][1]) + pageRanges[--i][0] = minPage; + if (i == size) + pageRanges = null; + else if (i > 0) + { + int[][] tmp = new int[size - i][]; + System.arraycopy(pageRanges, i, tmp, 0, size - i); + pageRanges = tmp; + } + } + } + + public MultipleDocumentHandlingType getMultipleDocumentHandling() + { + return multiple; + } + + public void setMultipleDocumentHandling + (MultipleDocumentHandlingType multiple) + { + if (multiple == null) + throw new IllegalArgumentException(); + this.multiple = multiple; + } + + public void setMultipleDocumentHandlingToDefault() + { + multiple + = MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES; + } + + public int[][] getPageRanges() + { + if (pageRanges == null) + return new int[][] { { getFromPage(), getToPage() } }; + // Perform a deep clone, so user code cannot affect original arrays. + int i = pageRanges.length; + int[][] result = new int[i][]; + while (--i >= 0) + result[i] = (int[]) pageRanges[i].clone(); + return result; + } + + public void setPageRanges(int[][] pageRanges) + { + int size = pageRanges == null ? 0 : pageRanges.length; + if (size == 0) + throw new IllegalArgumentException(); + while (--size >= 0) + { + int[] range = pageRanges[size]; + if (range == null || range.length != 2 + || range[0] < minPage || range[1] < range[0] || range[1] > maxPage + || (size != 0 && range[0] <= pageRanges[size - 1][1])) + throw new IllegalArgumentException(); + } + size = pageRanges.length; + if (fromPage > 0 && pageRanges[0][0] > fromPage) + fromPage = pageRanges[0][0]; + if (toPage > 0 && pageRanges[size - 1][1] < toPage) + toPage = pageRanges[size - 1][1]; + this.pageRanges = new int[size][]; + while (--size >= 0) + this.pageRanges[size] = (int[]) pageRanges[size].clone(); + } + + public String getPrinter() + { + return printer; + } + + public void setPrinter(String printer) + { + this.printer = printer; + } + + public SidesType getSides() + { + return sides; + } + + public void setSides(SidesType sides) + { + if (sides == null) + throw new IllegalArgumentException(); + this.sides = sides; + } + + public void setSidesToDefault() + { + sides = SidesType.ONE_SIDED; + } + + public int getToPage() + { + return toPage != 0 ? toPage + : pageRanges != null ? pageRanges[pageRanges.length - 1][1] + : fromPage != 0 ? fromPage : maxPage; + } + + public void setToPage(int toPage) + { + if (toPage < minPage || (fromPage > toPage && fromPage != 0) + || toPage > maxPage) + throw new IllegalArgumentException(); + if (pageRanges == null) + this.toPage = toPage; + } + + public boolean equals(Object o) + { + if (this == o) + return true; + if (! (o instanceof JobAttributes)) + return false; + JobAttributes ja = (JobAttributes) o; + if (copies != ja.copies || selection != ja.selection + || destination != ja.destination || dialog != ja.dialog + || ! filename.equals(ja.filename) || maxPage != ja.maxPage + || minPage != ja.minPage || multiple != ja.multiple + || fromPage != ja.fromPage || toPage != ja.toPage + || ! printer.equals(ja.printer) || sides != ja.sides + || (pageRanges == null) != (ja.pageRanges == null)) + return false; + if (pageRanges != ja.pageRanges) + for (int i = pageRanges.length; --i >= 0; ) + if (pageRanges[i][0] != ja.pageRanges[i][0] + || pageRanges[i][1] != ja.pageRanges[i][1]) + return false; + return true; + } + + public int hashCode() + { + int hash = (selection.value << 6) ^ (destination.value << 5) + ^ (dialog.value << 3) ^ (multiple.value << 2) ^ sides.value + ^ (filename == null ? 0 : filename.hashCode()) + ^ (printer == null ? 0 : printer.hashCode()); + // The effect of the above fields on the hashcode match the JDK. However, + // I am unable to reverse engineer the effect of the fields listed below, + // so I am using my own implementation. Note that this still satisfies + // the general contract of hashcode, it just doesn't match the JDK. + hash ^= (copies << 27) ^ (maxPage << 22) ^ (minPage << 17); + if (pageRanges == null) + hash ^= (getFromPage() << 13) ^ (getToPage() << 8); + else + for (int i = pageRanges.length; --i >= 0; ) + hash ^= (pageRanges[i][0] << 13) ^ (pageRanges[i][1] << 8); + return hash; + } + + public String toString() + { + CPStringBuilder s = new CPStringBuilder("copies=").append(copies) + .append(",defaultSelection=").append(selection).append(",destination=") + .append(destination).append(",dialog=").append(dialog) + .append(",fileName=").append(filename).append(",fromPage=") + .append(getFromPage()).append(",maxPage=").append(maxPage) + .append(",minPage=").append(minPage) + .append(",multiple-document-handling=").append(multiple) + .append(",page-ranges=["); + if (pageRanges == null) + s.append(minPage).append(':').append(minPage).append(']'); + else + for (int i = 0; i < pageRanges.length; i++) + s.append(pageRanges[i][0]).append(':').append(pageRanges[i][1]) + .append(','); + s.setLength(s.length() - 1); + return s.append("],printer=").append(printer).append(",sides=") + .append(sides).append(",toPage=").append(getToPage()).toString(); + } +} // class JobAttributes diff --git a/libjava/classpath/java/awt/KeyEventDispatcher.java b/libjava/classpath/java/awt/KeyEventDispatcher.java new file mode 100644 index 000000000..30997278e --- /dev/null +++ b/libjava/classpath/java/awt/KeyEventDispatcher.java @@ -0,0 +1,82 @@ +/* KeyEventDispatcher.java -- dispatches key events + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.KeyEvent; + +/** + * An instance of this interface coordinates with a KeyboardFocusManager to + * target and dispatch all key events. This allows retargeting, consuming, + * changing, or otherwise manipulating the key event before sending it on to + * a target. + * + *

By default, the KeyboardFocusManager is the sink for all key events not + * dispatched by other dispatchers. Therefore, it is unnecessary for the user + * to register the focus manager as a dispatcher. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see KeyboardFocusManager#addKeyEventDispatcher(KeyEventDispatcher) + * @see KeyboardFocusManager#removeKeyEventDispatcher(KeyEventDispatcher) + * @since 1.4 + * @status updated to 1.4 + */ +public interface KeyEventDispatcher +{ + /** + * Called by the KeyboardFocusManager to request that a key event be + * dispatched. The dispatcher is free to retarget the event, consume it, + * dispatch it, or make other changes. This is usually done to allow + * delivery of key events to objects other than the window in focus, such + * as for navigating non-focusable components. If this dispatcher chooses + * to dispatch the event itself, it should call redispatchEvent + * to avoid infinite recursion. + * + *

If the return value is false, the KeyEvent is passed to the next + * dispatcher in the chain, ending with the KeyboardFocusManager. If the + * return value is true, the event has been consumed (although it might + * have been ignored), and no further action will be taken on the event. Be + * sure to check whether the event was consumed before dispatching it + * further. + * + * @param e the key event + * @return true if the event has been consumed + * @see KeyboardFocusManager#redispatchEvent(Component, AWTEvent) + */ + boolean dispatchKeyEvent(KeyEvent e); +} // interface KeyEventDispatcher diff --git a/libjava/classpath/java/awt/KeyEventPostProcessor.java b/libjava/classpath/java/awt/KeyEventPostProcessor.java new file mode 100644 index 000000000..0b39dc250 --- /dev/null +++ b/libjava/classpath/java/awt/KeyEventPostProcessor.java @@ -0,0 +1,81 @@ +/* KeyEventPostProcessor.java -- performs actions after a key event dispatch + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.KeyEvent; + +/** + * An instance of this interface coordinates with a KeyboardFocusManager to + * target and dispatch all key events that are otherwise unconsumed. This + * allows events which take place when nothing has focus to still operate, + * such as menu keyboard shortcuts. + * + *

By default, the KeyboardFocusManager is the sink for all key events not + * post-processed elsewhere. Therefore, it is unnecessary for the user + * to register the focus manager as a dispatcher. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see KeyboardFocusManager#addKeyEventPostProcessor(KeyEventPostProcessor) + * @see KeyboardFocusManager#removeKeyEventPostProcessor(KeyEventPostProcessor) + * @since 1.4 + * @status updated to 1.4 + */ +public interface KeyEventPostProcessor +{ + /** + * Called by the KeyboardFocusManager to request that a key event be + * post-processed. Typically, the event has already been dispatched and + * handled, unless no object has focus. Thus, this allows global event + * handling for things like menu shortcuts. If this post-processor chooses + * to dispatch the event, it should call redispatchEvent + * to avoid infinite recursion. + * + *

If the return value is false, the KeyEvent is passed to the next + * dispatcher in the chain, ending with the KeyboardFocusManager. If the + * return value is true, the event has been consumed (although it might + * have been ignored), and no further action will be taken on the event. Be + * sure to check whether the event was consumed before dispatching it + * further. + * + * @param e the key event + * @return true if the event has been consumed + * @see KeyboardFocusManager#redispatchEvent(Component, AWTEvent) + */ + boolean postProcessKeyEvent(KeyEvent e); +} // interface KeyEventPostProcessor diff --git a/libjava/classpath/java/awt/KeyboardFocusManager.java b/libjava/classpath/java/awt/KeyboardFocusManager.java new file mode 100644 index 000000000..691351175 --- /dev/null +++ b/libjava/classpath/java/awt/KeyboardFocusManager.java @@ -0,0 +1,1479 @@ +/* KeyboardFocusManager.java -- manage component focusing via the keyboard + Copyright (C) 2002, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.applet.Applet; +import java.awt.FocusTraversalPolicy; +import java.awt.event.FocusEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * The KeyboardFocusManager handles the focusing of + * windows for receiving keyboard events. The manager handles + * the dispatch of all FocusEvents and + * KeyEvents, along with WindowEvents + * relating to the focused window. Users can use the manager + * to ascertain the current focus owner and fire events. + *
+ *
+ * The focus owner is the Component that receives + * key events. The focus owner is either the currently focused + * window or a component within this window. + *
+ *
+ * The underlying native windowing system may denote the active + * window or its children with special decorations (e.g. a highlighted + * title bar). The active window is always either a Frame + * or Dialog, and is either the currently focused + * window or its owner. + *
+ *
+ * Applets may be partitioned into different applet contexts, according + * to their code base. In this case, each context has its own + * KeyboardFocusManager, as opposed to the global + * manager maintained by applets which share the same context. + * Each context is insulated from the others, and they don't interact. + * The resulting behaviour, as with context division, depends on the browser + * supporting the applets. Regardless, there can only ever be + * one focused window, one active window and one focus owner + * per ClassLoader. + *
+ *
+ * To support this separation of focus managers, the manager instances + * and the internal state information is grouped by the + * ThreadGroup to which it pertains. With respect to + * applets, each code base has its own ThreadGroup, so the + * isolation of each context is enforced within the manager. + *
+ *
+ * By default, the manager defines TAB and Ctrl+TAB as the + * forward focus traversal keys and Shift+TAB and Ctrl+Shift+TAB + * as the backward focus traversal keys. No up or down cycle + * traversal keys are defined by default. Traversal takes effect + * on the firing of a relevant KEY_PRESSED event. + * However, all other key events related to the use of the + * defined focus traversal key sequence are consumed and not + * dispatched. + *
+ *
+ * These default traversal keys come into effect on all windows + * for which no alternative set of keys is defined. This also + * applies recursively to any child components of such a window, + * which define no traversal keys of their own. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.4 + */ +public abstract class KeyboardFocusManager + implements KeyEventDispatcher, KeyEventPostProcessor +{ + /** Identifies {@link AWTKeyStroke}s that move the focus forward in + the focus cycle. */ + public static final int FORWARD_TRAVERSAL_KEYS = 0; + + /** Identifies {@link AWTKeyStroke}s that move the focus backward in + the focus cycle. */ + public static final int BACKWARD_TRAVERSAL_KEYS = 1; + + /** Identifies {@link AWTKeyStroke}s that move the focus up to the + parent focus cycle root. */ + public static final int UP_CYCLE_TRAVERSAL_KEYS = 2; + + /** Identifies {@link AWTKeyStroke}s that move the focus down to the + child focus cycle root. */ + public static final int DOWN_CYCLE_TRAVERSAL_KEYS = 3; + + /** The set of {@link AWTKeyStroke}s that cause focus to be moved to + the next focusable Component in the focus cycle. */ + private static final Set DEFAULT_FORWARD_KEYS; + + /** The set of {@link AWTKeyStroke}s that cause focus to be moved to + the previous focusable Component in the focus cycle. */ + private static final Set DEFAULT_BACKWARD_KEYS; + + /** Populate the DEFAULT_FORWARD_KEYS and DEFAULT_BACKWARD_KEYS + {@link java.util.Set}s. */ + static + { + Set s = new HashSet(); + s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0)); + s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + KeyEvent.CTRL_DOWN_MASK)); + DEFAULT_FORWARD_KEYS = Collections.unmodifiableSet(s); + s = new HashSet(); + s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + KeyEvent.SHIFT_DOWN_MASK)); + s.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, + KeyEvent.SHIFT_DOWN_MASK + | KeyEvent.CTRL_DOWN_MASK)); + DEFAULT_BACKWARD_KEYS = Collections.unmodifiableSet(s); + } + + /** The global object {@link java.util.Map}s. */ + + /** For security reasons, {@link java.applet.Applet}s in different + codebases must be insulated from one another. Since {@link + KeyboardFocusManager}s have the ability to return {@link + Component}s from a given {@link java.applet.Applet}, each + codebase must have an independent {@link KeyboardFocusManager}. + Since each codebase has its own {@link ThreadGroup} in which its + {@link Applet}s run, it makes sense to partition {@link + KeyboardFocusManager}s according to {@link + java.lang.ThreadGroup}. Thus, currentKeyboardFocusManagers is a + {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}. */ + private static Map currentKeyboardFocusManagers = new HashMap (); + + /** {@link java.applet.Applet}s in one codebase must not be allowed + to access {@link Component}s in {@link java.applet.Applet}s in + other codebases. To enforce this restriction, we key the + following {@link java.util.Map}s on {@link java.lang.ThreadGroup}s (which + are per-codebase). For example, if {@link + java.lang.ThreadGroup} A calls {@link #setGlobalFocusOwner}, + passing {@link Component} C, currentFocusOwners[A] is assigned + C, and all other currentFocusOwners values are nullified. Then + if {@link java.lang.ThreadGroup} A subsequently calls {@link + #getGlobalFocusOwner}, it will return currentFocusOwners[A], + that is, {@link Component} C. If another {@link + java.lang.ThreadGroup} K calls {@link #getGlobalFocusOwner}, it + will return currentFocusOwners[K], that is, null. + + Since this is a static field, we ensure that there is only one + focused {@link Component} per class loader. */ + private static Map currentFocusOwners = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the {@link Component} that owns the permanent + keyboard focus. @see currentFocusOwners */ + private static Map currentPermanentFocusOwners = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the focused {@link Window}. @see + currentFocusOwners */ + private static Map currentFocusedWindows = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the active {@link Window}. @see + currentFocusOwners */ + private static Map currentActiveWindows = new HashMap (); + + /** A {@link java.util.Map} keyed on {@link java.lang.ThreadGroup}s + that stores the focus cycle root {@link Container}. @see + currentFocusOwners */ + private static Map currentFocusCycleRoots = new HashMap (); + + /** The default {@link FocusTraversalPolicy} that focus-managing + {@link Container}s will use to define their initial focus + traversal policy. */ + private FocusTraversalPolicy defaultPolicy; + + /** An array that stores the {@link #FORWARD_TRAVERSAL_KEYS}, {@link + #BACKWARD_TRAVERSAL_KEYS}, {@link #UP_CYCLE_TRAVERSAL_KEYS} and + {@link #DOWN_CYCLE_TRAVERSAL_KEYS} {@link AWTKeyStroke}s {@link + java.util.Set}s. */ + private Set[] defaultFocusKeys = new Set[] + { + DEFAULT_FORWARD_KEYS, DEFAULT_BACKWARD_KEYS, + Collections.EMPTY_SET, Collections.EMPTY_SET + }; + + /** + * A utility class to support the handling of events relating to property changes. + */ + private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport (this); + + /** + * A utility class to support the handling of events relating to vetoable changes. + */ + private final VetoableChangeSupport vetoableChangeSupport = new VetoableChangeSupport (this); + + /** A list of {@link KeyEventDispatcher}s that process {@link + KeyEvent}s before they are processed the default keyboard focus + manager. */ + private final ArrayList keyEventDispatchers = new ArrayList(); + + /** A list of {@link KeyEventPostProcessor}s that process unconsumed + {@link KeyEvent}s. */ + private final ArrayList keyEventPostProcessors = new ArrayList(); + + /** + * Construct a KeyboardFocusManager. + */ + public KeyboardFocusManager () + { + } + + /** + * Retrieve the keyboard focus manager associated with the {@link + * java.lang.ThreadGroup} to which the calling thread belongs. + * + * @return the keyboard focus manager associated with the current + * thread group + */ + public static KeyboardFocusManager getCurrentKeyboardFocusManager () + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + + if (currentKeyboardFocusManagers.get (currentGroup) == null) + setCurrentKeyboardFocusManager (null); + + return (KeyboardFocusManager) currentKeyboardFocusManagers.get (currentGroup); + } + + /** + * Set the keyboard focus manager associated with the {@link + * java.lang.ThreadGroup} to which the calling thread belongs. + * + * @param m the keyboard focus manager for the current thread group + */ + public static void setCurrentKeyboardFocusManager (KeyboardFocusManager m) + { + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + sm.checkPermission (new AWTPermission ("replaceKeyboardFocusManager")); + + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + KeyboardFocusManager manager; + + if (m == null) + manager = new DefaultKeyboardFocusManager(); + else + manager = m; + + currentKeyboardFocusManagers.put (currentGroup, manager); + } + + /** + * Retrieve the {@link Component} that has the keyboard focus, or + * null if the focus owner was not set by a thread in the current + * {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner or null + */ + public Component getFocusOwner () + { + return (Component) getObject (currentFocusOwners); + } + + /** + * Retrieve the {@link Component} that has the keyboard focus, + * regardless of whether or not it was set by a thread in the + * current {@link java.lang.ThreadGroup}. If there is no temporary + * focus owner in effect then this method will return the same value + * as {@link #getGlobalPermanentFocusOwner}. + * + * @return the keyboard focus owner + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Component getGlobalFocusOwner () + { + return (Component) getGlobalObject(currentFocusOwners, true); + } + + /** + * Set the {@link Component} that will be returned by {@link + * #getFocusOwner} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalFocusOwner}. This + * method does not actually transfer the keyboard focus. + * + * @param owner the Component to return from getFocusOwner and + * getGlobalFocusOwner + * + * @see Component#requestFocus() + * @see Component#requestFocusInWindow() + */ + protected void setGlobalFocusOwner (Component owner) + { + if (owner == null || owner.focusable) + setGlobalObject (currentFocusOwners, owner, "focusOwner"); + } + + /** + * Clear the global focus owner and deliver a FOCUS_LOST event to + * the previously-focused {@link Component}. Until another {@link + * Component} becomes the keyboard focus owner, key events will be + * discarded by top-level windows. + */ + public void clearGlobalFocusOwner () + { + synchronized (currentFocusOwners) + { + Component focusOwner = getGlobalFocusOwner (); + Component permanentFocusOwner = getGlobalPermanentFocusOwner (); + + setGlobalFocusOwner (null); + setGlobalPermanentFocusOwner (null); + + // Inform the old focus owner that it has lost permanent + // focus. + if (focusOwner != null) + { + // We can't cache the event queue, because of + // bootstrapping issues. We need to set the default + // KeyboardFocusManager in EventQueue before the event + // queue is started. + EventQueue q = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + if (focusOwner != permanentFocusOwner) + q.postEvent (new FocusEvent (focusOwner, FocusEvent.FOCUS_LOST, true)); + else + q.postEvent (new FocusEvent (focusOwner, FocusEvent.FOCUS_LOST, false)); + } + + if (focusOwner != permanentFocusOwner) + { + EventQueue q = Toolkit.getDefaultToolkit ().getSystemEventQueue (); + q.postEvent (new FocusEvent (permanentFocusOwner, FocusEvent.FOCUS_LOST, false)); + } + } + } + + /** + * Retrieve the {@link Component} that has the permanent keyboard + * focus, or null if the focus owner was not set by a thread in the + * current {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner or null + */ + public Component getPermanentFocusOwner () + { + return (Component) getObject (currentPermanentFocusOwners); + } + + /** + * Retrieve the {@link Component} that has the permanent keyboard + * focus, regardless of whether or not it was set by a thread in the + * current {@link java.lang.ThreadGroup}. + * + * @return the keyboard focus owner + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Component getGlobalPermanentFocusOwner () + { + return (Component) getGlobalObject (currentPermanentFocusOwners, true); + } + + /** + * Set the {@link Component} that will be returned by {@link + * #getPermanentFocusOwner} (when it is called from the current + * {@link java.lang.ThreadGroup}) and {@link + * #getGlobalPermanentFocusOwner}. This method does not actually + * transfer the keyboard focus. + * + * @param focusOwner the Component to return from + * getPermanentFocusOwner and getGlobalPermanentFocusOwner + * + * @see Component#requestFocus() + * @see Component#requestFocusInWindow() + */ + protected void setGlobalPermanentFocusOwner (Component focusOwner) + { + if (focusOwner == null || focusOwner.focusable) + setGlobalObject (currentPermanentFocusOwners, focusOwner, + "permanentFocusOwner"); + } + + /** + * Retrieve the {@link Window} that is or contains the keyboard + * focus owner, or null if the focused window was not set by a + * thread in the current {@link java.lang.ThreadGroup}. + * + * @return the focused window or null + */ + public Window getFocusedWindow () + { + return (Window) getObject (currentFocusedWindows); + } + + /** + * Retrieve the {@link Window} that is or contains the focus owner, + * regardless of whether or not the {@link Window} was set focused + * by a thread in the current {@link java.lang.ThreadGroup}. + * + * @return the focused window + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Window getGlobalFocusedWindow () + { + return (Window) getGlobalObject (currentFocusedWindows, true); + } + + /** + * Set the {@link Window} that will be returned by {@link + * #getFocusedWindow} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalFocusedWindow}. + * This method does not actually cause window to become + * the focused {@link Window}. + * + * @param window the Window to return from getFocusedWindow and + * getGlobalFocusedWindow + */ + protected void setGlobalFocusedWindow (Window window) + { + if (window == null || window.focusable) + setGlobalObject (currentFocusedWindows, window, "focusedWindow"); + } + + /** + * Retrieve the active {@link Window}, or null if the active window + * was not set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the active window or null + */ + public Window getActiveWindow() + { + return (Window) getObject (currentActiveWindows); + } + + /** + * Retrieve the active {@link Window}, regardless of whether or not + * the {@link Window} was made active by a thread in the current + * {@link java.lang.ThreadGroup}. + * + * @return the active window + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Window getGlobalActiveWindow() + { + return (Window) getGlobalObject (currentActiveWindows, true); + } + + /** + * Set the {@link Window} that will be returned by {@link + * #getActiveWindow} (when it is called from the current {@link + * java.lang.ThreadGroup}) and {@link #getGlobalActiveWindow}. This + * method does not actually cause window to be made + * active. + * + * @param window the Window to return from getActiveWindow and + * getGlobalActiveWindow + */ + protected void setGlobalActiveWindow(Window window) + { + setGlobalObject (currentActiveWindows, window, "activeWindow"); + } + + /** + * Retrieve the default {@link FocusTraversalPolicy}. + * Focus-managing {@link Container}s use the returned object to + * define their initial focus traversal policy. + * + * @return a non-null default FocusTraversalPolicy object + */ + public FocusTraversalPolicy getDefaultFocusTraversalPolicy () + { + if (defaultPolicy == null) + defaultPolicy = new DefaultFocusTraversalPolicy (); + return defaultPolicy; + } + + /** + * Set the {@link FocusTraversalPolicy} returned by {@link + * #getDefaultFocusTraversalPolicy}. Focus-managing {@link + * Container}s created after this call will use policy as their + * initial focus traversal policy. Existing {@link Container}s' + * focus traversal policies will not be affected by calls to this + * method. + * + * @param policy the FocusTraversalPolicy that will be returned by + * subsequent calls to getDefaultFocusTraversalPolicy + * @throws IllegalArgumentException if policy is null + */ + public void setDefaultFocusTraversalPolicy (FocusTraversalPolicy policy) + { + if (policy == null) + throw new IllegalArgumentException (); + firePropertyChange ("defaultFocusTraversalPolicy", defaultPolicy, policy); + defaultPolicy = policy; + } + + /** + * Set the default {@link java.util.Set} of focus traversal keys for + * one of the focus traversal directions. + * + * @param id focus traversal direction identifier + * @param keystrokes set of AWTKeyStrokes + * + * @see #FORWARD_TRAVERSAL_KEYS + * @see #BACKWARD_TRAVERSAL_KEYS + * @see #UP_CYCLE_TRAVERSAL_KEYS + * @see #DOWN_CYCLE_TRAVERSAL_KEYS + */ + public void setDefaultFocusTraversalKeys (int id, + Set + keystrokes) + { + if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && + id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && + id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + + if (keystrokes == null) + throw new IllegalArgumentException (); + + Set sa; + Set sb; + Set sc; + String type; + switch (id) + { + case FORWARD_TRAVERSAL_KEYS: + sa = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS]; + sb = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS]; + sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS]; + type = "forwardDefaultFocusTraversalKeys"; + break; + case BACKWARD_TRAVERSAL_KEYS: + sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS]; + sb = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS]; + sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS]; + type = "backwardDefaultFocusTraversalKeys"; + break; + case UP_CYCLE_TRAVERSAL_KEYS: + sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS]; + sb = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS]; + sc = defaultFocusKeys[DOWN_CYCLE_TRAVERSAL_KEYS]; + type = "upCycleDefaultFocusTraversalKeys"; + break; + case DOWN_CYCLE_TRAVERSAL_KEYS: + sa = defaultFocusKeys[FORWARD_TRAVERSAL_KEYS]; + sb = defaultFocusKeys[BACKWARD_TRAVERSAL_KEYS]; + sc = defaultFocusKeys[UP_CYCLE_TRAVERSAL_KEYS]; + type = "downCycleDefaultFocusTraversalKeys"; + break; + default: + throw new IllegalArgumentException (); + } + int i = keystrokes.size (); + Iterator iter = keystrokes.iterator (); + while (--i >= 0) + { + Object o = iter.next (); + if (!(o instanceof AWTKeyStroke) + || sa.contains (o) || sb.contains (o) || sc.contains (o) + || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) + throw new IllegalArgumentException (); + } + keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); + firePropertyChange (type, defaultFocusKeys[id], keystrokes); + defaultFocusKeys[id] = keystrokes; + } + + /** + * Retrieve the default {@link java.util.Set} of focus traversal + * keys for one of the focus traversal directions. + * + * @param id focus traversal direction identifier + * + * @return the default set of AWTKeyStrokes + * + * @see #FORWARD_TRAVERSAL_KEYS + * @see #BACKWARD_TRAVERSAL_KEYS + * @see #UP_CYCLE_TRAVERSAL_KEYS + * @see #DOWN_CYCLE_TRAVERSAL_KEYS + */ + public Set getDefaultFocusTraversalKeys (int id) + { + if (id < FORWARD_TRAVERSAL_KEYS || id > DOWN_CYCLE_TRAVERSAL_KEYS) + throw new IllegalArgumentException (); + return defaultFocusKeys[id]; + } + + /** + * Retrieve the current focus cycle root, or null if the focus owner + * was not set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the current focus cycle root or null + */ + public Container getCurrentFocusCycleRoot () + { + return (Container) getObject (currentFocusCycleRoots); + } + + /** + * Retrieve the current focus cycle root, regardless of whether or + * not it was made set by a thread in the current {@link + * java.lang.ThreadGroup}. + * + * @return the current focus cycle root + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + */ + protected Container getGlobalCurrentFocusCycleRoot () + { + return (Container) getGlobalObject (currentFocusCycleRoots, true); + } + + /** + * Set the {@link Container} that will be returned by {@link + * #getCurrentFocusCycleRoot} (when it is called from the current + * {@link java.lang.ThreadGroup}) and {@link + * #getGlobalCurrentFocusCycleRoot}. This method does not actually + * make cycleRoot the current focus cycle root. + * + * @param cycleRoot the focus cycle root to return from + * getCurrentFocusCycleRoot and getGlobalCurrentFocusCycleRoot + */ + public void setGlobalCurrentFocusCycleRoot (Container cycleRoot) + { + setGlobalObject (currentFocusCycleRoots, cycleRoot, "currentFocusCycleRoot"); + } + + /** + * Registers the supplied property change listener for receiving + * events caused by the following property changes: + * + *

    + *
  • the current focus owner ("focusOwner")
  • + *
  • the permanent focus owner ("permanentFocusOwner")
  • + *
  • the focused window ("focusedWindow")
  • + *
  • the active window ("activeWindow")
  • + *
  • the default focus traversal policy ("defaultFocusTraversalPolicy")
  • + *
  • the default set of forward traversal keys ("forwardDefaultFocusTraversalKeys")
  • + *
  • the default set of backward traversal keys ("backwardDefaultFocusTraversalKeys")
  • + *
  • the default set of up cycle traversal keys ("upCycleDefaultFocusTraversalKeys")
  • + *
  • the default set of down cycle traversal keys ("downCycleDefaultFocusTraversalKeys")
  • + *
  • the current focus cycle root ("currentFocusCycleRoot")
  • + *
+ * + * If the supplied listener is null, nothing occurs. + * + * @param l the new listener to register. + * @see KeyboardFocusManager#addPropertyChangeListener(String, java.beans.PropertyChangeListener) + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + if (l != null) + propertyChangeSupport.addPropertyChangeListener(l); + } + + /** + * Removes the supplied property change listener from the list + * of registered listeners. If the supplied listener is null, + * nothing occurs. + * + * @param l the listener to remove. + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + if (l != null) + propertyChangeSupport.removePropertyChangeListener(l); + } + + /** + * Returns the currently registered property change listeners + * in array form. The returned array is empty if no listeners are + * currently registered. + * + * @return an array of registered property change listeners. + */ + public PropertyChangeListener[] getPropertyChangeListeners() + { + return propertyChangeSupport.getPropertyChangeListeners(); + } + + /** + * Registers a property change listener for receiving events relating + * to a change to a specified property. The supplied property name can be + * either user-defined or one from the following list of properties + * relevant to this class: + * + *
    + *
  • the current focus owner ("focusOwner")
  • + *
  • the permanent focus owner ("permanentFocusOwner")
  • + *
  • the focused window ("focusedWindow")
  • + *
  • the active window ("activeWindow")
  • + *
  • the default focus traversal policy ("defaultFocusTraversalPolicy")
  • + *
  • the default set of forward traversal keys ("forwardDefaultFocusTraversalKeys")
  • + *
  • the default set of backward traversal keys ("backwardDefaultFocusTraversalKeys")
  • + *
  • the default set of up cycle traversal keys ("upCycleDefaultFocusTraversalKeys")
  • + *
  • the default set of down cycle traversal keys ("downCycleDefaultFocusTraversalKeys")
  • + *
  • the current focus cycle root ("currentFocusCycleRoot")
  • + *
+ * + * Nothing occurs if a null listener is supplied. null is regarded as a valid property name. + * + * @param name the name of the property to handle change events for. + * @param l the listener to register for changes to the specified property. + * @see KeyboardFocusManager#addPropertyChangeListener(java.beans.PropertyChangeListener) + */ + public void addPropertyChangeListener(String name, PropertyChangeListener l) + { + if (l != null) + propertyChangeSupport.addPropertyChangeListener(name, l); + } + + /** + * Removes the supplied property change listener registered for the + * specified property from the list of registered listeners. If the + * supplied listener is null, nothing occurs. + * + * @param name the name of the property the listener is + * monitoring changes to. + * @param l the listener to remove. + */ + public void removePropertyChangeListener(String name, + PropertyChangeListener l) + { + if (l != null) + propertyChangeSupport.removePropertyChangeListener(name, l); + } + + /** + * Returns the currently registered property change listeners + * in array form, which listen for changes to the supplied property. + * The returned array is empty, if no listeners are currently registered + * for events pertaining to the supplied property. + * + * @param name The property the returned listeners monitor for changes. + * @return an array of registered property change listeners which + * listen for changes to the supplied property. + */ + public PropertyChangeListener[] getPropertyChangeListeners(String name) + { + return propertyChangeSupport.getPropertyChangeListeners(name); + } + + /** + * Fires a property change event as a response to a change to + * to the specified property. The event is only fired if a + * change has actually occurred (i.e. o and n are different). + * + * @param name The name of the property to which a change occurred. + * @param o The old value of the property. + * @param n The new value of the property. + */ + protected void firePropertyChange(String name, Object o, Object n) + { + propertyChangeSupport.firePropertyChange(name, o, n); + } + + /** + * Registers a vetoable property change listener for receiving events + * relating to the following properties: + * + *
    + *
  • the current focus owner ("focusOwner")
  • + *
  • the permanent focus owner ("permanentFocusOwner")
  • + *
  • the focused window ("focusedWindow")
  • + *
  • the active window ("activeWindow")
  • + *
+ * + * Nothing occurs if a null listener is supplied. + * + * @param l the listener to register. + * @see KeyboardFocusManager#addVetoableChangeListener(String, java.beans.VetoableChangeListener) + */ + public void addVetoableChangeListener(VetoableChangeListener l) + { + if (l != null) + vetoableChangeSupport.addVetoableChangeListener(l); + } + + /** + * Removes the supplied vetoable property change listener from + * the list of registered listeners. If the supplied listener + * is null, nothing occurs. + * + * @param l the listener to remove. + */ + public void removeVetoableChangeListener(VetoableChangeListener l) + { + if (l != null) + vetoableChangeSupport.removeVetoableChangeListener(l); + } + + /** + * Returns the currently registered vetoable property change listeners + * in array form. The returned array is empty if no listeners are + * currently registered. + * + * @return an array of registered vetoable property change listeners. + * @since 1.4 + */ + public VetoableChangeListener[] getVetoableChangeListeners() + { + return vetoableChangeSupport.getVetoableChangeListeners(); + } + + /** + * Registers a vetoable property change listener for receiving events relating + * to a vetoable change to a specified property. The supplied property name can be + * either user-defined or one from the following list of properties + * relevant to this class: + * + *
    + *
  • the current focus owner ("focusOwner")
  • + *
  • the permanent focus owner ("permanentFocusOwner")
  • + *
  • the focused window ("focusedWindow")
  • + *
  • the active window ("activeWindow")
  • + *
+ * + * Nothing occurs if a null listener is supplied. null is regarded as a valid property name. + * + * @param name the name of the property to handle change events for. + * @param l the listener to register for changes to the specified property. + * @see KeyboardFocusManager#addVetoableChangeListener(java.beans.VetoableChangeListener) + */ + public void addVetoableChangeListener(String name, VetoableChangeListener l) + { + if (l != null) + vetoableChangeSupport.addVetoableChangeListener(name, l); + } + + /** + * Removes the supplied vetoable property change listener registered + * for the specified property from the list of registered listeners. + * If the supplied listener is null, nothing occurs. + * + * @param name the name of the vetoable property the listener is + * monitoring changes to. + * @param l the listener to remove. + */ + public void removeVetoableChangeListener(String name, + VetoableChangeListener l) + { + if (l != null) + vetoableChangeSupport.removeVetoableChangeListener(name, l); + } + + /** + * Returns the currently registered vetoable property change listeners + * in array form, which listen for changes to the supplied property. + * The returned array is empty, if no listeners are currently registered + * for events pertaining to the supplied property. + * + * @param name The property the returned listeners monitor for changes. + * @return an array of registered property change listeners which + * listen for changes to the supplied property. + * @since 1.4 + */ + public VetoableChangeListener[] getVetoableChangeListeners(String name) + { + return vetoableChangeSupport.getVetoableChangeListeners(name); + } + + /** + * Fires a property change event as a response to a vetoable change to + * to the specified property. The event is only fired if a + * change has actually occurred (i.e. o and n are different). + * In the event that the property change is vetoed, the following + * occurs: + * + *
    + *
  1. + * This method throws a PropertyVetoException to + * the proposed change. + *
  2. + *
  3. + * A new event is fired to reverse the previous change. + *
  4. + *
  5. + * This method again throws a PropertyVetoException + * in response to the reversion. + *
  6. + *
+ * + * @param name The name of the property to which a change occurred. + * @param o The old value of the property. + * @param n The new value of the property. + * @throws PropertyVetoException if one of the listeners vetos + * the change by throwing this exception. + */ + protected void fireVetoableChange(String name, Object o, Object n) + throws PropertyVetoException + { + vetoableChangeSupport.fireVetoableChange(name, o, n); + } + + /** + * Adds a key event dispatcher to the list of registered dispatchers. + * When a key event is fired, each dispatcher's dispatchKeyEvent + * method is called in the order that they were added, prior to the manager + * dispatching the event itself. Notifications halt when one of the + * dispatchers returns true. + *
+ *
+ * The same dispatcher can exist multiple times within the list + * of registered dispatchers, and there is no limit on the length + * of this list. A null dispatcher is simply ignored. + * + * @param dispatcher The dispatcher to register. + */ + public void addKeyEventDispatcher(KeyEventDispatcher dispatcher) + { + if (dispatcher != null) + keyEventDispatchers.add(dispatcher); + } + + /** + * Removes the specified key event dispatcher from the list of + * registered dispatchers. The manager always dispatches events, + * regardless of its existence within the list. The manager + * can be added and removed from the list, as with any other + * dispatcher, but this does not affect its ability to dispatch + * key events. Non-existent and null dispatchers are simply ignored + * by this method. + * + * @param dispatcher The dispatcher to remove. + */ + public void removeKeyEventDispatcher(KeyEventDispatcher dispatcher) + { + keyEventDispatchers.remove(dispatcher); + } + + /** + * Returns the currently registered key event dispatchers in List + * form. At present, this only includes dispatchers explicitly registered + * via the addKeyEventDispatcher() method, but this behaviour + * is subject to change and should not be depended on. The manager itself + * may be a member of the list, but only if explicitly registered. If no + * dispatchers have been registered, the list will be empty. + * + * @return A list of explicitly registered key event dispatchers. + * @see KeyboardFocusManager#addKeyEventDispatcher(java.awt.KeyEventDispatcher) + */ + protected List getKeyEventDispatchers () + { + return (List) keyEventDispatchers.clone (); + } + + /** + * Adds a key event post processor to the list of registered post processors. + * Post processors work in the same way as key event dispatchers, except + * that they are invoked after the manager has dispatched the key event, + * and not prior to this. Each post processor's postProcessKeyEvent + * method is called to see if any post processing needs to be performed. THe + * processors are called in the order in which they were added to the list, + * and notifications continue until one returns true. As with key event + * dispatchers, the manager is implicitly called following this process, + * regardless of whether or not it is present within the list. + *
+ *
+ * The same post processor can exist multiple times within the list + * of registered post processors, and there is no limit on the length + * of this list. A null post processor is simply ignored. + * + * @param postProcessor the post processor to register. + * @see KeyboardFocusManager#addKeyEventDispatcher(java.awt.KeyEventDispatcher) + */ + public void addKeyEventPostProcessor (KeyEventPostProcessor postProcessor) + { + if (postProcessor != null) + keyEventPostProcessors.add (postProcessor); + } + + /** + * Removes the specified key event post processor from the list of + * registered post processors. The manager always post processes events, + * regardless of its existence within the list. The manager + * can be added and removed from the list, as with any other + * post processor, but this does not affect its ability to post process + * key events. Non-existent and null post processors are simply ignored + * by this method. + * + * @param postProcessor the post processor to remove. + */ + public void removeKeyEventPostProcessor (KeyEventPostProcessor postProcessor) + { + keyEventPostProcessors.remove (postProcessor); + } + + /** + * Returns the currently registered key event post processors in List + * form. At present, this only includes post processors explicitly registered + * via the addKeyEventPostProcessor() method, but this behaviour + * is subject to change and should not be depended on. The manager itself + * may be a member of the list, but only if explicitly registered. If no + * post processors have been registered, the list will be empty. + * + * @return A list of explicitly registered key event post processors. + * @see KeyboardFocusManager#addKeyEventPostProcessor(java.awt.KeyEventPostProcessor) + */ + protected List getKeyEventPostProcessors () + { + return (List) keyEventPostProcessors.clone (); + } + + /** + * The AWT event dispatcher uses this method to request that the manager + * handle a particular event. If the manager fails or refuses to + * dispatch the supplied event (this method returns false), the + * AWT event dispatcher will try to dispatch the event itself. + *
+ *
+ * The manager is expected to handle all FocusEvents + * and KeyEvents, and WindowEvents + * relating to the focus. Dispatch is done with regard to the + * the focus owner and the currently focused and active windows. + * In handling the event, the source of the event may be overridden. + *
+ *
+ * The actual dispatching is performed by calling + * redispatchEvent(). This avoids the infinite recursion + * of dispatch requests which may occur if this method is called on + * the target component. + * + * @param e the event to dispatch. + * @return true if the event was dispatched. + * @see KeyboardFocusManager#redispatchEvent(java.awt.Component, java.awt.AWTEvent) + * @see KeyEvent + * @see FocusEvent + * @see WindowEvent + */ + public abstract boolean dispatchEvent (AWTEvent e); + + /** + * Handles redispatching of an event so that recursion of + * dispatch requests does not occur. Event dispatch methods + * within this manager (dispatchEvent()) and + * the key event dispatchers should use this method to handle + * dispatching rather than the dispatch method of the target + * component. + *
+ *
+ * + * This method is not intended for general consumption, and is + * only for the use of the aforementioned classes. + * + * + * @param target the target component to which the event is + * dispatched. + * @param e the event to dispatch. + */ + public final void redispatchEvent (Component target, AWTEvent e) + { + e.isFocusManagerEvent = true; + target.dispatchEvent (e); + e.isFocusManagerEvent = false; + } + + /** + * Attempts to dispatch key events for which no key event dispatcher + * has so far succeeded. This method is usually called by + * dispatchEvent() following the sending of the key + * event to any registered key event dispatchers. If the key + * event reaches this stage, none of the dispatchers returned + * true. This is, of course, always the case if there are no + * registered dispatchers. + *
+ *
+ * If this method also fails to handle the key event, then + * false is returned to the caller. In the case of + * dispatchEvent(), the calling method may try + * to handle the event itself or simply forward on the + * false result to its caller. When the event is dispatched + * by this method, a true result is propogated through the + * calling methods. + * + * @param e the key event to dispatch. + * @return true if the event was dispatched successfully. + */ + public abstract boolean dispatchKeyEvent (KeyEvent e); + + /** + * Handles the post processing of key events. By default, + * this method will map unhandled key events to appropriate + * MenuShortcuts. The event is consumed + * in the process and the shortcut is activated. This + * method is usually called by dispatchKeyEvent. + * + * @param e the key event to post process. + * @return true by default, as the event was handled. + */ + public abstract boolean postProcessKeyEvent (KeyEvent e); + + /** + * Handles focus traversal operations for key events which + * represent focus traversal keys in relation to the supplied + * component. The supplied component is assumed to have the + * focus, whether it does so or not, and the operation is + * carried out as appropriate, with this in mind. + * + * @param focused the component on which to perform focus traversal, + * on the assumption that this component has the focus. + * @param e the possible focus traversal key event. + */ + public abstract void processKeyEvent (Component focused, KeyEvent e); + + /** + * Delays all key events following the specified timestamp until the + * supplied component has focus. The AWT calls this method when it is + * determined that a focus change may occur within the native windowing + * system. Any key events which occur following the time specified by + * after are delayed until a FOCUS_GAINED event is received + * for the untilFocused component. The manager is responsible for ensuring + * this takes place. + * + * @param after the timestamp beyond which all key events are delayed until + * the supplied component gains focus. + * @param untilFocused the component to wait on gaining focus. + */ + protected abstract void enqueueKeyEvents (long after, Component untilFocused); + + /** + * Removes the key event block specified by the supplied timestamp and component. + * All delayed key events are released for normal dispatching following its + * removal and subsequent key events that would have been blocked are now + * immediately dispatched. If the specified timestamp is below 0, then + * the request with the oldest timestamp is removed. + * + * @param after the timestamp of the key event block to be removed, or a + * value smaller than 0 if the oldest is to be removed. + * @param untilFocused the component of the key event block to be removed. + */ + protected abstract void dequeueKeyEvents (long after, Component untilFocused); + + /** + * Discards all key event blocks relating to focus requirements for + * the supplied component, regardless of timestamp. + * + * @param comp the component of the key event block(s) to be removed. + */ + protected abstract void discardKeyEvents (Component comp); + + /** + * Moves the current focus to the next component following + * comp, based on the current focus traversal policy. By + * default, only visible, displayable, accepted components + * can receive focus. Canvases, Panels, + * Labels, ScrollPanes, Scrollbars, + * Windows and lightweight components are judged + * to be unacceptable by default. See the + * DefaultFocusTraversalPolicy for more details. + * + * @param comp the component prior to the one which will + * become the focus, following execution of this method. + * @see DefaultFocusTraversalPolicy + */ + public abstract void focusNextComponent(Component comp); + + /** + * Moves the current focus to the previous component, prior to + * comp, based on the current focus traversal policy. By + * default, only visible, displayable, accepted components + * can receive focus. Canvases, Panels, + * Labels, ScrollPanes, Scrollbars, + * Windows and lightweight components are judged + * to be unacceptable by default. See the + * DefaultFocusTraversalPolicy for more details. + * + * @param comp the component following the one which will + * become the focus, following execution of this method. + * @see DefaultFocusTraversalPolicy + */ + public abstract void focusPreviousComponent(Component comp); + + /** + * Moves the current focus upwards by one focus cycle. + * Both the current focus owner and current focus cycle root + * become the focus cycle root of the supplied component. + * However, in the case of a Window, the default + * focus component becomes the focus owner and the focus cycle + * root is not changed. + * + * @param comp the component used as part of the focus traversal. + */ + public abstract void upFocusCycle(Component comp); + + /** + * Moves the current focus downwards by one focus cycle. + * If the supplied container is a focus cycle root, then this + * becomes the current focus cycle root and the focus goes + * to the default component of the specified container. + * Nothing happens for non-focus cycle root containers. + * + * @param cont the container used as part of the focus traversal. + */ + public abstract void downFocusCycle(Container cont); + + /** + * Moves the current focus to the next component, based on the + * current focus traversal policy. By default, only visible, + * displayable, accepted component can receive focus. + * Canvases, Panels, + * Labels, ScrollPanes, Scrollbars, + * Windows and lightweight components are judged + * to be unacceptable by default. See the + * DefaultFocusTraversalPolicy for more details. + * + * @see DefaultFocusTraversalPolicy + */ + public final void focusNextComponent() + { + focusNextComponent (null); + } + + /** + * Moves the current focus to the previous component, based on the + * current focus traversal policy. By default, only visible, + * displayable, accepted component can receive focus. + * Canvases, Panels, + * Labels, ScrollPanes, Scrollbars, + * Windows and lightweight components are judged + * to be unacceptable by default. See the + * DefaultFocusTraversalPolicy for more details. + * + * @see DefaultFocusTraversalPolicy + */ + public final void focusPreviousComponent() + { + focusPreviousComponent (null); + } + + /** + * Moves the current focus upwards by one focus cycle, + * so that the new focus owner is the focus cycle root + * of the current owner. The current focus cycle root then + * becomes the focus cycle root of the new focus owner. + * However, in the case of the focus cycle root of the + * current focus owner being a Window, the default + * component of this window becomes the focus owner and the + * focus cycle root is not changed. + */ + public final void upFocusCycle() + { + upFocusCycle (null); + } + + /** + * Moves the current focus downwards by one focus cycle, + * iff the current focus cycle root is a Container. + * Usually, the new focus owner is set to the default component + * of the container and the current focus cycle root is set + * to the current focus owner. Nothing occurs if the current + * focus cycle root is not a container. + */ + public final void downFocusCycle() + { + Component focusOwner = getGlobalFocusOwner (); + if (focusOwner instanceof Container + && ((Container) focusOwner).isFocusCycleRoot ()) + downFocusCycle ((Container) focusOwner); + } + + /** + * Retrieve an object from one of the global object {@link + * java.util.Map}s, if the object was set by the a thread in the + * current {@link java.lang.ThreadGroup}. Otherwise, return null. + * + * @param globalMap one of the global object Maps + * + * @return a global object set by the current ThreadGroup, or null + * + * @see #getFocusOwner() + * @see #getPermanentFocusOwner() + * @see #getFocusedWindow() + * @see #getActiveWindow() + * @see #getCurrentFocusCycleRoot() + */ + private Object getObject (Map globalMap) + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + return globalMap.get (currentGroup); + } + + /** + * Retrieve an object from one of the global object {@link + * java.util.Map}s, regardless of whether or not the object was set + * by a thread in the current {@link java.lang.ThreadGroup}. + * + * @param globalMap one of the global object Maps + * + * @return a global object set by the current ThreadGroup, or null + * + * @throws SecurityException if this is not the keyboard focus + * manager associated with the current {@link java.lang.ThreadGroup} + * + * @see #getGlobalFocusOwner() + * @see #getGlobalPermanentFocusOwner() + * @see #getGlobalFocusedWindow() + * @see #getGlobalActiveWindow() + * @see #getGlobalCurrentFocusCycleRoot() + */ + private Object getGlobalObject (Map globalMap, boolean checkThread) + { + if (checkThread) + { + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + KeyboardFocusManager managerForCallingThread = + (KeyboardFocusManager) currentKeyboardFocusManagers.get(currentGroup); + + if (this != managerForCallingThread) + throw new SecurityException ("Attempted to retrieve an object from a " + + "keyboard focus manager that isn't " + + "associated with the current thread group."); + } + synchronized (globalMap) + { + Collection globalObjects = globalMap.values (); + Iterator i = globalObjects.iterator (); + Component globalObject; + + while (i.hasNext ()) + { + globalObject = (Component) i.next (); + if (globalObject != null) + return globalObject; + } + } + + // No Object was found. + return null; + } + + /** + * Set an object in one of the global object {@link java.util.Map}s, + * that will be returned by subsequent calls to getGlobalObject on + * the same {@link java.util.Map}. + * + * @param globalMap one of the global object Maps + * @param newObject the object to set + * @param property the property that will change + * + * @see #setGlobalFocusOwner(Component) + * @see #setGlobalPermanentFocusOwner(Component) + * @see #setGlobalFocusedWindow(Window) + * @see #setGlobalActiveWindow(Window) + * @see #setGlobalCurrentFocusCycleRoot(Container) + */ + private void setGlobalObject (Map globalMap, + Object newObject, + String property) + { + synchronized (globalMap) + { + // Save old object. + Object oldObject = getGlobalObject(globalMap, false); + + // Nullify old object. + Collection threadGroups = globalMap.keySet (); + Iterator i = threadGroups.iterator (); + while (i.hasNext ()) + { + ThreadGroup oldThreadGroup = (ThreadGroup) i.next (); + if (globalMap.get (oldThreadGroup) != null) + { + globalMap.put (oldThreadGroup, null); + // There should only be one object set at a time, so + // we can short circuit. + break; + } + } + + ThreadGroup currentGroup = Thread.currentThread ().getThreadGroup (); + firePropertyChange (property, oldObject, newObject); + try + { + fireVetoableChange (property, oldObject, newObject); + // Set new object. + globalMap.put (currentGroup, newObject); + } + catch (PropertyVetoException e) + { + } + } + } + + + /** + * Maps focus requests from heavyweight to lightweight components. + */ + private static HashMap focusRequests = new HashMap(); + + /** + * Retargets focus events that come from the peer (which only know about + * heavyweight components) to go to the correct lightweight component + * if appropriate. + * + * @param ev the event to check + * + * @return the retargetted event + */ + static AWTEvent retargetFocusEvent(AWTEvent ev) + { + if (ev instanceof FocusEvent) + { + FocusEvent fe = (FocusEvent) ev; + Component target = fe.getComponent(); + if (focusRequests.containsKey(target)) + { + Component lightweight = (Component) focusRequests.get(target); + ev = new FocusEvent(lightweight, fe.id, fe.isTemporary()); + focusRequests.remove(target); + } + } + return ev; + } + + /** + * Adds a lightweight focus request for a heavyweight component. + * + * @param heavyweight the heavyweight from which we will receive a focus + * event soon + * @param lightweight the lightweight that ultimately receives the request + */ + static void addLightweightFocusRequest(Component heavyweight, + Component lightweight) + { + focusRequests.put(heavyweight, lightweight); + } +} diff --git a/libjava/classpath/java/awt/Label.java b/libjava/classpath/java/awt/Label.java new file mode 100644 index 000000000..1522dac36 --- /dev/null +++ b/libjava/classpath/java/awt/Label.java @@ -0,0 +1,294 @@ +/* Label.java -- Java label widget + Copyright (C) 1999, 2000, 2002, 2004, 2005, 2006, Free Software + Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.LabelPeer; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This component is used for displaying simple text strings that cannot + * be edited by the user. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class Label extends Component implements Accessible +{ + + /** + * Alignment constant aligning the text to the left of its window. + */ + public static final int LEFT = 0; + + /** + * Alignment constant aligning the text in the center of its window. + */ + public static final int CENTER = 1; + + /** + * Alignment constant aligning the text to the right of its window. + */ + public static final int RIGHT = 2; + + // Serialization version constant: + private static final long serialVersionUID = 3094126758329070636L; + + /** + * @serial Indicates the alignment of the text within this label's window. + * This is one of the constants in this class. The default value is + * LEFT. + */ + private int alignment; + + /** + * @serial The text displayed in the label + */ + private String text; + + /** + * Initializes a new instance of Label with no text. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Label() + { + this("", LEFT); + } + + /** + * Initializes a new instance of Label with the specified + * text that is aligned to the left. + * + * @param text The text of the label. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Label(String text) + { + this(text, LEFT); + } + + /** + * Initializes a new instance of Label with the specified + * text and alignment. + * + * @param text The text of the label. + * @param alignment The desired alignment for the text in this label, + * which must be one of LEFT, CENTER, or + * RIGHT. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Label(String text, int alignment) + { + setAlignment(alignment); + setText(text); + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } + + /** + * Returns the constant indicating the alignment of the text in this + * label. The value returned will be one of the alignment constants + * from this class. + * + * @return The alignment of the text in the label. + */ + public int getAlignment() + { + return(alignment); + } + + /** + * Sets the text alignment of this label to the specified value. + * + * @param alignment The desired alignment for the text in this label, + * which must be one of LEFT, CENTER, or + * RIGHT. + */ + public synchronized void setAlignment(int alignment) + { + if (alignment != CENTER && alignment != LEFT && alignment != RIGHT) + throw new IllegalArgumentException("invalid alignment: " + alignment); + this.alignment = alignment; + if (peer != null) + { + LabelPeer lp = (LabelPeer) peer; + lp.setAlignment(alignment); + } + } + + /** + * Returns the text displayed in this label. + * + * @return The text for this label. + */ + public String getText() + { + return text; + } + + /** + * Sets the text in this label to the specified value. + * + * @param text The new text for this label. + */ + public synchronized void setText(String text) + { + if ((this.text == null && text != null) + || (this.text != null && ! this.text.equals(text))) + { + this.text = text; + + if (peer != null) + { + LabelPeer lp = (LabelPeer) peer; + lp.setText(text); + } + invalidate(); + } + } + + /** + * Notifies this label that it has been added to a container, causing + * the peer to be created. This method is called internally by the AWT + * system. + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit().createLabel(this); + super.addNotify(); + } + + /** + * Returns a parameter string useful for debugging. + * + * @return A debugging string. + */ + protected String paramString() + { + return ("text=" + getText() + ",alignment=" + + getAlignment() + "," + super.paramString()); + } + + /** + * This class provides accessibility support for the label. + */ + protected class AccessibleAWTLabel + extends AccessibleAWTComponent + { + /** + * For compatability with Sun's JDK 1.4.2 rev. 5 + */ + private static final long serialVersionUID = -3568967560160480438L; + + /** + * Constructor for the accessible label. + */ + public AccessibleAWTLabel() + { + } + + /** + * Returns the accessible name for the label. This is + * the text used in the label. + * + * @return a String containing the accessible + * name for this label. + */ + public String getAccessibleName() + { + return getText(); + } + + /** + * Returns the accessible role for the label. + * + * @return an instance of AccessibleRole, describing + * the role of the label. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LABEL; + } + + } + + /** + * Gets the AccessibleContext associated with this Label. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTLabel(); + return accessibleContext; + } + + /** + * Generate a unique name for this button. + * + * @return A unique name for this button. + */ + String generateName() + { + return "label" + getUniqueLong(); + } + + /** + * The number used to generate the name returned by getName. + */ + private static transient long nextLabelNumber; + + private static synchronized long getUniqueLong() + { + return nextLabelNumber++; + } + +} diff --git a/libjava/classpath/java/awt/LayoutManager.java b/libjava/classpath/java/awt/LayoutManager.java new file mode 100644 index 000000000..62ff8087e --- /dev/null +++ b/libjava/classpath/java/awt/LayoutManager.java @@ -0,0 +1,92 @@ +/* LayoutManager.java -- lay out elements in a Container + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This interface is for laying out containers in a particular sequence. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Container + * @since 1.0 + * @status updated to 1.4 + */ +public interface LayoutManager +{ + /** + * Adds the specified component to the layout group. + * + * @param name the name of the component to add + * @param component the component to add + */ + void addLayoutComponent(String name, Component component); + + /** + * Removes the specified component from the layout group. + * + * @param component the component to remove + */ + void removeLayoutComponent(Component component); + + /** + * Calculates the preferred size for this container, taking into account + * the components it contains. + * + * @param parent the parent container to lay out + * @return the preferred dimensions of this container + * @see #minimumLayoutSize(Container) + */ + Dimension preferredLayoutSize(Container parent); + + /** + * Calculates the minimum size for this container, taking into account + * the components it contains. + * + * @param parent the parent container to lay out + * @return the minimum dimensions of this container + * @see #preferredLayoutSize(Container) + */ + Dimension minimumLayoutSize(Container parent); + + /** + * Lays out the components in the given container. + * + * @param parent the container to lay out + */ + void layoutContainer(Container parent); +} // interface LayoutManager diff --git a/libjava/classpath/java/awt/LayoutManager2.java b/libjava/classpath/java/awt/LayoutManager2.java new file mode 100644 index 000000000..45fc5430f --- /dev/null +++ b/libjava/classpath/java/awt/LayoutManager2.java @@ -0,0 +1,100 @@ +/* LayoutManager2.java -- enhanced layout manager + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * Layout manager for laying out containers based on contraints. The + * constraints control how the layout will proceed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see LayoutManager + * @see Container + * @since 1.0 + * @status updated to 1.4 + */ +public interface LayoutManager2 extends LayoutManager +{ + /** + * Adds the specified component to the layout, with the specified + * constraints object. + * + * @param component the component to add + * @param constraints the constraints to satisfy + */ + void addLayoutComponent(Component component, Object constraints); + + /** + * Determines the maximum size of the specified target container. + * + * @param target the container to lay out + * @return the maximum size of the container + * @see Component#getMaximumSize() + */ + Dimension maximumLayoutSize(Container target); + + /** + * Returns the preferred X axis alignment for the specified target + * container. This value will range from 0 to 1 where 0 is alignment + * closest to the origin, 0.5 is centered, and 1 is aligned furthest + * from the origin. + * + * @param target the target container + * @return the x-axis alignment preference + */ + float getLayoutAlignmentX(Container target); + + /** + * Returns the preferred Y axis alignment for the specified target + * container. This value will range from 0 to 1 where 0 is alignment + * closest to the origin, 0.5 is centered, and 1 is aligned furthest + * from the origin. + * + * @param target the target container + * @return the y-axis alignment preference + */ + float getLayoutAlignmentY(Container target); + + /** + * Forces the layout manager to purge any cached information about the + * layout of the target container. This will force it to be recalculated. + * + * @param target the target container + */ + void invalidateLayout(Container target); +} // interface LayoutManager2 diff --git a/libjava/classpath/java/awt/LightweightDispatcher.java b/libjava/classpath/java/awt/LightweightDispatcher.java new file mode 100644 index 000000000..11b83d8bf --- /dev/null +++ b/libjava/classpath/java/awt/LightweightDispatcher.java @@ -0,0 +1,362 @@ +/* LightweightDispatcher.java -- Dispatches mouse events to lightweights + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.peer.LightweightPeer; +import java.util.WeakHashMap; + +/** + * Redispatches mouse events to lightweight components. The native peers know + * nothing about the lightweight components and thus mouse events are always + * targetted at Windows or heavyweight components. This class listenes directly + * on the eventqueue and dispatches mouse events to lightweight components. + * + * @author Roman Kennke (kennke@aicas.com) + */ +final class LightweightDispatcher +{ + + /** + * Maps thread groups to lightweight dispatcher instances. We need to + * have one instance per thread group so that 2 or more applets or otherwise + * separated applications (like in OSGI) do not interfer with each other. + */ + private static WeakHashMap instances = new WeakHashMap(); + + /** + * The last mouse event target. If the target changes, additional + * MOUSE_ENTERED and MOUSE_EXITED events must be dispatched. + */ + private Component lastTarget; + + /** + * The current mouseEventTarget. + */ + private Component mouseEventTarget; + + /** + * Returns an instance of LightweightDispatcher for the current thread's + * thread group. + * + * @return an instance of LightweightDispatcher for the current thread's + * thread group + */ + static LightweightDispatcher getInstance() + { + Thread t = Thread.currentThread(); + ThreadGroup tg = t.getThreadGroup(); + LightweightDispatcher instance = (LightweightDispatcher) instances.get(tg); + if (instance == null) + { + instance = new LightweightDispatcher(); + instances.put(tg, instance); + } + return instance; + } + + /** + * Creates a new LightweightDispatcher. This is private to prevent access + * from outside. Use {@link #getInstance()} instead. + */ + private LightweightDispatcher() + { + // Nothing to do here. + } + + /** + * Receives notification if a mouse event passes along the eventqueue. + * + * @param event the event + */ + public boolean dispatchEvent(final AWTEvent event) + { + if (event instanceof MouseEvent) + { + MouseEvent mouseEvent = (MouseEvent) event; + return handleMouseEvent(mouseEvent); + } + return false; + } + + /** + * Handles all mouse events that are targetted at toplevel containers + * (Window instances) and dispatches them to the correct lightweight child. + * + * @param ev the mouse event + * @return whether or not we found a lightweight that handled the event. + */ + private boolean handleMouseEvent(final MouseEvent ev) + { + Container container = (Container) ev.getSource(); + Component target = findTarget(container, ev.getX(), ev.getY()); + trackEnterExit(target, ev); + int id = ev.getID(); + + // Dont update the mouseEventTarget when dragging. Also, MOUSE_CLICKED + // must be dispatched to the original target of MOUSE_PRESSED, so don't + // update in this case either. + if (! isDragging(ev) && id != MouseEvent.MOUSE_CLICKED) + mouseEventTarget = (target != container) ? target : null; + + if (mouseEventTarget != null) + { + switch (id) + { + case MouseEvent.MOUSE_ENTERED: + case MouseEvent.MOUSE_EXITED: + // This is already handled in trackEnterExit(). + break; + case MouseEvent.MOUSE_PRESSED: + case MouseEvent.MOUSE_RELEASED: + case MouseEvent.MOUSE_MOVED: + redispatch(ev, mouseEventTarget, id); + break; + case MouseEvent.MOUSE_CLICKED: + // MOUSE_CLICKED must be dispatched to the original target of + // MOUSE_PRESSED. + if (target == mouseEventTarget) + redispatch(ev, mouseEventTarget, id); + break; + case MouseEvent.MOUSE_DRAGGED: + if (isDragging(ev)) + redispatch(ev, mouseEventTarget, id); + break; + case MouseEvent.MOUSE_WHEEL: + redispatch(ev, mouseEventTarget, id); + } + ev.consume(); + } + + return ev.isConsumed(); + } + + /** + * Finds the actual target for a mouseevent, starting at c. + * This searches through the children of the container and finds the first + * one which is showing, at the location from the mouse event and has + * a MouseListener or MouseMotionListener attached. If no such child component + * is found, null is returned. + * + * @param c the container to search through + * @param loc the mouse event point + * + * @return the actual receiver of the mouse event, or null, if no such + * component has been found + */ + private Component findTarget(final Container c, final int x, final int y) + { + Component target = null; + + // First we check the children of the container. + + // Note: It is important that we use the package private Container + // fields ncomponents and component here. There are applications + // that override getComponentCount() + // and getComponent() to hide internal components, which makes + // the LightweightDispatcher not work correctly in these cases. + // As a positive sideeffect this is slightly more efficient. + int nChildren = c.ncomponents; + for (int i = 0; i < nChildren && target == null; i++) + { + Component child = c.component[i]; + int childX = x - child.x; + int childY = y - child.y; + if (child != null && child.visible + && child.peer instanceof LightweightPeer + && child.contains(childX, childY)) + { + // Check if there's a deeper possible target. + if (child instanceof Container) + { + Component deeper = findTarget((Container) child, + childX, childY); + if (deeper != null) + target = deeper; + } + // Check if the child itself is interested in mouse events. + else if (isMouseListening(child)) + target = child; + } + } + + // Check the container itself, if we didn't find a target yet. + if (target == null && c.contains(x, y) && isMouseListening(c)) + target = c; + + return target; + } + + /** + * Checks if the specified component would be interested in a mouse event. + * + * @param c the component to check + * + * @return true if the component has mouse listeners installed, + * false otherwise + */ + private boolean isMouseListening(final Component c) + { + // Note: It is important to NOT check if the component is listening + // for a specific event (for instance, mouse motion events). The event + // gets dispatched to the component if the component is listening + // for ANY mouse event, even when the component is not listening for the + // specific type of event. There are applications that depend on this + // (sadly). + return c.mouseListener != null + || c.mouseMotionListener != null + || c.mouseWheelListener != null + || (c.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0 + || (c.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0 + || (c.eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0; + } + + /** + * Tracks MOUSE_ENTERED and MOUSE_EXIT as well as MOUSE_MOVED and + * MOUSE_DRAGGED and creates synthetic MOUSE_ENTERED and MOUSE_EXITED for + * lightweight component.s + * + * @param target the current mouse event target + * @param ev the mouse event + */ + private void trackEnterExit(final Component target, final MouseEvent ev) + { + int id = ev.getID(); + if (target != lastTarget) + { + if (lastTarget != null) + redispatch(ev, lastTarget, MouseEvent.MOUSE_EXITED); + if (id == MouseEvent.MOUSE_EXITED) + ev.consume(); + if (target != null) + redispatch(ev, target, MouseEvent.MOUSE_ENTERED); + if (id == MouseEvent.MOUSE_ENTERED) + ev.consume(); + lastTarget = target; + } + + } + + /** + * Redispatches the specified mouse event to the specified target with the + * specified id. + * + * @param ev the mouse event + * @param target the new target + * @param id the new id + */ + private void redispatch(MouseEvent ev, Component target, int id) + { + Component source = ev.getComponent(); + assert target != null; + if (target.isShowing()) + { + // Translate coordinates. + int x = ev.getX(); + int y = ev.getY(); + for (Component c = target; c != null && c != source; c = c.getParent()) + { + x -= c.x; + y -= c.y; + } + + // Retarget event. + MouseEvent retargeted; + if (id == MouseEvent.MOUSE_WHEEL) + { + MouseWheelEvent mwe = (MouseWheelEvent) ev; + retargeted = new MouseWheelEvent(target, id, ev.getWhen(), + ev.getModifiers() + | ev.getModifiersEx(), x, y, + ev.getClickCount(), + ev.isPopupTrigger(), + mwe.getScrollType(), + mwe.getScrollAmount(), + mwe.getWheelRotation()); + } + else + { + retargeted = new MouseEvent(target, id, ev.getWhen(), + ev.getModifiers() | ev.getModifiersEx(), + x, y, ev.getClickCount(), + ev.isPopupTrigger(), ev.getButton()); + } + + if (target == source) + ((Container) target).dispatchNoLightweight(retargeted); + else + target.dispatchEvent(retargeted); + } + } + + /** + * Determines if we are in the middle of a drag operation, that is, if + * any of the buttons is held down. + * + * @param ev the mouse event to check + * + * @return true if we are in the middle of a drag operation, + * false otherwise + */ + private boolean isDragging(MouseEvent ev) + { + int mods = ev.getModifiersEx(); + int id = ev.getID(); + if (id == MouseEvent.MOUSE_PRESSED || id == MouseEvent.MOUSE_RELEASED) + { + switch (ev.getButton()) + { + case MouseEvent.BUTTON1: + mods ^= InputEvent.BUTTON1_DOWN_MASK; + break; + case MouseEvent.BUTTON2: + mods ^= InputEvent.BUTTON2_DOWN_MASK; + break; + case MouseEvent.BUTTON3: + mods ^= InputEvent.BUTTON3_DOWN_MASK; + break; + } + } + return (mods & (InputEvent.BUTTON1_DOWN_MASK + | InputEvent.BUTTON2_DOWN_MASK + | InputEvent.BUTTON3_DOWN_MASK)) != 0; + } +} diff --git a/libjava/classpath/java/awt/List.java b/libjava/classpath/java/awt/List.java new file mode 100644 index 000000000..dc1199bbc --- /dev/null +++ b/libjava/classpath/java/awt/List.java @@ -0,0 +1,1222 @@ +/* List.java -- A listbox widget + Copyright (C) 1999, 2002, 2004, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.peer.ListPeer; +import java.util.EventListener; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * Class that implements a listbox widget + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class List extends Component + implements ItemSelectable, Accessible +{ + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_list_number; + + // Serialization constant + private static final long serialVersionUID = -3304312411574666869L; + + // FIXME: Need read/writeObject + + /** + * @serial The items in the list. + */ + private Vector items = new Vector(); + + /** + * @serial Indicates whether or not multiple items can be selected + * simultaneously. + */ + private boolean multipleMode; + + /** + * @serial The number of rows in the list. This is set on creation + * only and cannot be modified. + */ + private int rows; + + /** + * @serial An array of the item indices that are selected. + */ + private int[] selected; + + /** + * @serial An index value used by makeVisible() and + * getVisibleIndex. + */ + private int visibleIndex = -1; + + // The list of ItemListeners for this object. + private ItemListener item_listeners; + + // The list of ActionListeners for this object. + private ActionListener action_listeners; + + /** + * Initializes a new instance of List with no visible lines + * and multi-select disabled. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * @since 1.1 + */ + public List() + { + this(4, false); + } + + /** + * Initializes a new instance of List with the specified + * number of visible lines and multi-select disabled. + * + * @param rows The number of visible rows in the list. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public List(int rows) + { + this(rows, false); + } + + /** + * Initializes a new instance of List with the specified + * number of lines and the specified multi-select setting. + * + * @param rows The number of visible rows in the list. + * @param multipleMode true if multiple lines can be selected + * simultaneously, false otherwise. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public List(int rows, boolean multipleMode) + { + if (rows == 0) + this.rows = 4; + else + this.rows = rows; + + this.multipleMode = multipleMode; + selected = new int[0]; + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + + } + + /** + * Returns the number of items in this list. + * + * @return The number of items in this list. + * + * @since 1.1 + */ + public int getItemCount() + { + return countItems(); + } + + /** + * Returns the number of items in this list. + * + * @return The number of items in this list. + * + * @deprecated This method is deprecated in favor of + * getItemCount() + */ + public int countItems() + { + return items.size(); + } + + /** + * Returns the complete list of items. + * + * @return The complete list of items in the list. + * + * @since 1.1 + */ + public synchronized String[] getItems() + { + String[] l_items = new String[getItemCount()]; + + items.copyInto(l_items); + return(l_items); + } + + /** + * Returns the item at the specified index. + * + * @param index The index of the item to retrieve. + * + * @exception IndexOutOfBoundsException If the index value is not valid. + */ + public String getItem(int index) + { + return((String) items.elementAt(index)); + } + + /** + * Returns the number of visible rows in the list. + * + * @return The number of visible rows in the list. + */ + public int getRows() + { + return(rows); + } + + /** + * Tests whether or not multi-select mode is enabled. + * + * @return true if multi-select mode is enabled, + * false otherwise. + * + * @since 1.1 + */ + public boolean isMultipleMode() + { + return allowsMultipleSelections (); + } + + /** + * Tests whether or not multi-select mode is enabled. + * + * @return true if multi-select mode is enabled, + * false otherwise. + * + * @deprecated This method is deprecated in favor of + * isMultipleMode(). + */ + public boolean allowsMultipleSelections() + { + return multipleMode; + } + + /** + * This method enables or disables multiple selection mode for this + * list. + * + * @param multipleMode true to enable multiple mode, + * false otherwise. + * + * @since 1.1 + */ + public void setMultipleMode(boolean multipleMode) + { + setMultipleSelections (multipleMode); + } + + /** + * This method enables or disables multiple selection mode for this + * list. + * + * @param multipleMode true to enable multiple mode, + * false otherwise. + * + * @deprecated + */ + public void setMultipleSelections(boolean multipleMode) + { + this.multipleMode = multipleMode; + + ListPeer peer = (ListPeer) getPeer(); + if (peer != null) + peer.setMultipleMode(multipleMode); + + } + + /** + * Returns the minimum size of this component. + * + * @return The minimum size of this component. + * + * @since 1.1 + */ + public Dimension getMinimumSize() + { + return getMinimumSize(getRows()); + } + + /** + * Returns the minimum size of this component. + * + * @return The minimum size of this component. + * + * @deprecated This method is deprecated in favor of + * getMinimumSize. + */ + public Dimension minimumSize() + { + return minimumSize(getRows()); + } + + /** + * Returns the minimum size of this component assuming it had the specified + * number of rows. + * + * @param rows The number of rows to size for. + * + * @return The minimum size of this component. + * + * @since 1.1 + */ + public Dimension getMinimumSize(int rows) + { + return minimumSize(rows); + } + + /** + * Returns the minimum size of this component assuming it had the specified + * number of rows. + * + * @param rows The number of rows to size for. + * + * @return The minimum size of this component. + * + * @deprecated This method is deprecated in favor of + * getMinimumSize(int)> + */ + public Dimension minimumSize(int rows) + { + ListPeer peer = (ListPeer) getPeer(); + if (peer != null) + return peer.minimumSize(rows); + else + return new Dimension(0, 0); + } + + /** + * Returns the preferred size of this component. + * + * @return The preferred size of this component. + * + * @since 1.1 + */ + public Dimension getPreferredSize() + { + return getPreferredSize(getRows()); + } + + /** + * Returns the preferred size of this component. + * + * @return The preferred size of this component. + * + * @deprecated This method is deprecated in favor of + * getPreferredSize. + */ + public Dimension preferredSize() + { + return preferredSize(getRows()); + } + + /** + * Returns the preferred size of this component assuming it had the specified + * number of rows. + * + * @param rows The number of rows to size for. + * + * @return The preferred size of this component. + * + * @since 1.1 + */ + public Dimension getPreferredSize(int rows) + { + return preferredSize(rows); + } + + /** + * Returns the preferred size of this component assuming it had the specified + * number of rows. + * + * @param rows The number of rows to size for. + * + * @return The preferred size of this component. + * + * @deprecated This method is deprecated in favor of + * getPreferredSize(int)> + */ + public Dimension preferredSize(int rows) + { + ListPeer peer = (ListPeer)getPeer(); + if (peer != null) + return peer.preferredSize(rows); + else + return getSize(); + } + + /** + * This method adds the specified item to the end of the list. + * + * @param item The item to add to the list. + * + * @since 1.1 + */ + public void add(String item) + { + add (item, -1); + } + + /** + * This method adds the specified item to the end of the list. + * + * @param item The item to add to the list. + * + * @deprecated Use add() instead. + */ + public void addItem(String item) + { + addItem(item, -1); + } + + /** + * Adds the specified item to the specified location in the list. + * If the desired index is -1 or greater than the number of rows + * in the list, then the item is added to the end. + * + * @param item The item to add to the list. + * @param index The location in the list to add the item, or -1 to add + * to the end. + * + * @since 1.1 + */ + public void add(String item, int index) + { + addItem(item, index); + } + + /** + * Adds the specified item to the specified location in the list. + * If the desired index is -1 or greater than the number of rows + * in the list, then the item is added to the end. + * + * @param item The item to add to the list. + * @param index The location in the list to add the item, or -1 to add + * to the end. + * + * @deprecated Use add() instead. + */ + public void addItem(String item, int index) + { + if (item == null) + item = ""; + + if (index < -1) + index = -1; + + if ((index == -1) || (index >= items.size ())) + items.addElement (item); + else + items.insertElementAt(item, index); + + ListPeer peer = (ListPeer) getPeer(); + if (peer != null) + peer.add (item, index); + } + + /** + * Deletes the item at the specified index. + * + * @param index The index of the item to delete. + * + * @exception IllegalArgumentException If the index is not valid + * + * @deprecated + */ + public void delItem(int index) throws IllegalArgumentException + { + boolean selected = false; + if (isSelected(index)) + { + selected = true; + deselect(index); + } + + items.removeElementAt (index); + + if (selected) + select(index); + + ListPeer peer = (ListPeer) getPeer(); + if (peer != null) + peer.delItems (index, index); + } + + /** + * Deletes the item at the specified index. + * + * @param index The index of the item to delete. + * + * @exception IllegalArgumentException If the index is not valid + * + * @since 1.1 + */ + public void remove(int index) throws IllegalArgumentException + { + delItem(index); + } + + /** + * Deletes all items in the specified index range. + * + * @param start The beginning index of the range to delete. + * @param end The ending index of the range to delete. + * + * @exception IllegalArgumentException If the indexes are not valid + * + * @deprecated This method is deprecated for some unknown reason. + */ + public synchronized void delItems(int start, int end) + throws IllegalArgumentException + { + // We must run the loop in reverse direction. + for (int i = end; i >= start; --i) + items.removeElementAt (i); + if (peer != null) + { + ListPeer l = (ListPeer) peer; + l.delItems (start, end); + } + } + + /** + * Deletes the first occurrence of the specified item from the list. + * + * @param item The item to delete. + * + * @exception IllegalArgumentException If the specified item does not exist. + * + * @since 1.1 + */ + public synchronized void remove(String item) throws IllegalArgumentException + { + int index = items.indexOf(item); + if (index == -1) + throw new IllegalArgumentException("List element to delete not found"); + + remove(index); + } + + /** + * Deletes all of the items from the list. + * + * @since 1.1 + */ + public synchronized void removeAll() + { + clear(); + } + + /** + * Deletes all of the items from the list. + * + * @deprecated This method is deprecated in favor of removeAll(). + */ + public void clear() + { + items.clear(); + + ListPeer peer = (ListPeer) getPeer(); + if (peer != null) + peer.removeAll(); + + selected = new int[0]; + } + + /** + * Replaces the item at the specified index with the specified item. + * + * @param item The new item value. + * @param index The index of the item to replace. + * + * @exception ArrayIndexOutOfBoundsException If the index is not valid. + */ + public synchronized void replaceItem(String item, int index) + throws ArrayIndexOutOfBoundsException + { + if ((index < 0) || (index >= items.size())) + throw new ArrayIndexOutOfBoundsException("Bad list index: " + index); + + items.insertElementAt(item, index + 1); + items.removeElementAt (index); + + if (peer != null) + { + ListPeer l = (ListPeer) peer; + + /* We add first and then remove so that the selected + item remains the same */ + l.add (item, index + 1); + l.delItems (index, index); + } + } + + /** + * Returns the index of the currently selected item. -1 will be returned + * if there are no selected rows or if there are multiple selected rows. + * + * @return The index of the selected row. + */ + public synchronized int getSelectedIndex() + { + if (peer != null) + { + ListPeer l = (ListPeer) peer; + selected = l.getSelectedIndexes (); + } + + if (selected == null || selected.length != 1) + return -1; + + return selected[0]; + } + + /** + * Returns an array containing the indexes of the rows that are + * currently selected. + * + * @return A list of indexes of selected rows. + */ + public synchronized int[] getSelectedIndexes() + { + if (peer != null) + { + ListPeer l = (ListPeer) peer; + selected = l.getSelectedIndexes(); + } + + return selected; + } + + /** + * Returns the item that is currently selected, or null if there + * is no item selected. FIXME: What happens if multiple items selected? + * + * @return The selected item, or null if there is no + * selected item. + */ + public synchronized String getSelectedItem() + { + int index = getSelectedIndex(); + if (index == -1) + return(null); + + return((String) items.elementAt(index)); + } + + /** + * Returns the list of items that are currently selected in this list. + * + * @return The list of currently selected items. + */ + public synchronized String[] getSelectedItems() + { + int[] indexes = getSelectedIndexes(); + if (indexes == null) + return(new String[0]); + + String[] retvals = new String[indexes.length]; + if (retvals.length > 0) + for (int i = 0 ; i < retvals.length; i++) + retvals[i] = (String)items.elementAt(indexes[i]); + + return(retvals); + } + + /** + * Returns the list of items that are currently selected in this list as + * an array of type Object[] instead of String[]. + * + * @return The list of currently selected items. + */ + public synchronized Object[] getSelectedObjects() + { + int[] indexes = getSelectedIndexes(); + if (indexes == null) + return(new Object[0]); + + Object[] retvals = new Object[indexes.length]; + if (retvals.length > 0) + for (int i = 0 ; i < retvals.length; i++) + retvals[i] = items.elementAt(indexes[i]); + + return(retvals); + } + + /** + * Tests whether or not the specified index is selected. + * + * @param index The index to test. + * + * @return true if the index is selected, false + * otherwise. + * + * @since 1.1 + */ + public boolean isIndexSelected(int index) + { + return isSelected(index); + } + + /** + * Tests whether or not the specified index is selected. + * + * @param index The index to test. + * + * @return true if the index is selected, false + * otherwise. + * + * @deprecated This method is deprecated in favor of + * isIndexSelected(int). + */ + public boolean isSelected(int index) + { + int[] indexes = getSelectedIndexes(); + + for (int i = 0; i < indexes.length; i++) + if (indexes[i] == index) + return true; + + return false; + } + + /** + * This method ensures that the item at the specified index is visible. + * + * @param index The index of the item to be made visible. + */ + public synchronized void makeVisible(int index) + throws IllegalArgumentException + { + visibleIndex = index; + if (peer != null) + { + ListPeer l = (ListPeer) peer; + l.makeVisible (index); + } + } + + /** + * Returns the index of the last item that was made visible via the + * makeVisible() method. + * + * @return The index of the last item made visible via the + * makeVisible() method. + */ + public int getVisibleIndex() + { + return visibleIndex; + } + + /** + * Makes the item at the specified index selected. + * + * @param index The index of the item to select. + */ + public synchronized void select(int index) + { + ListPeer lp = (ListPeer) getPeer(); + if (lp != null) + lp.select(index); + + if (selected != null) + { + boolean found = false; + for (int i = 0; i < selected.length; i++) + { + if (selected[i] == index) + found = true; + } + if (! found) + { + if (! isMultipleMode()) + { + selected = new int[] { index }; + return; + } + int[] temp = new int[selected.length + 1]; + System.arraycopy(selected, 0, temp, 0, selected.length); + temp[selected.length] = index; + selected = temp; + } + } + else + { + selected = new int[1]; + selected[0] = index; + } + } + + /** + * Makes the item at the specified index not selected. + * + * @param index The index of the item to unselect. + */ + public synchronized void deselect(int index) + { + if (isSelected(index)) + { + ListPeer lp = (ListPeer)getPeer(); + if (lp != null) + lp.deselect(index); + + int[] temp = new int[selected.length - 1]; + for (int i = 0; i < temp.length; i++) + { + if (selected[i] != index) + temp[i] = selected[i]; + else + { + System.arraycopy(selected, i + 1, temp, i, + selected.length - i - 1); + break; + } + } + selected = temp; + } + } + + /** + * Notifies this object to create its native peer. + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit ().createList(this); + super.addNotify (); + } + + /** + * Notifies this object to destroy its native peer. + */ + public void removeNotify() + { + super.removeNotify(); + } + + /** + * Adds the specified ActionListener to the list of + * registered listeners for this object. + * + * @param listener The listener to add. + * + * @since 1.1 + */ + public synchronized void addActionListener(ActionListener listener) + { + action_listeners = AWTEventMulticaster.add(action_listeners, listener); + } + + /** + * Removes the specified ActionListener from the list of + * registers listeners for this object. + * + * @param listener The listener to remove. + * + * @since 1.1 + */ + public synchronized void removeActionListener(ActionListener listener) + { + action_listeners = AWTEventMulticaster.remove(action_listeners, listener); + } + + /** + * Adds the specified ItemListener to the list of + * registered listeners for this object. + * + * @param listener The listener to add. + * + * @since 1.1 + */ + public synchronized void addItemListener(ItemListener listener) + { + item_listeners = AWTEventMulticaster.add(item_listeners, listener); + } + + /** + * Removes the specified ItemListener from the list of + * registers listeners for this object. + * + * @param listener The listener to remove. + * + * @since 1.1 + */ + public synchronized void removeItemListener(ItemListener listener) + { + item_listeners = AWTEventMulticaster.remove(item_listeners, listener); + } + + /** + * Processes the specified event for this object. If the event is an + * instance of ActionEvent then the + * processActionEvent() method is called. Similarly, if the + * even is an instance of ItemEvent then the + * processItemEvent() method is called. Otherwise the + * superclass method is called to process this event. + * + * @param event The event to process. + * + * @since 1.1 + */ + protected void processEvent(AWTEvent event) + { + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); + else if (event instanceof ItemEvent) + processItemEvent((ItemEvent)event); + else + super.processEvent(event); + } + + /** + * This method processes the specified event by dispatching it to any + * registered listeners. Note that this method will only get called if + * action events are enabled. This will happen automatically if any + * listeners are added, or it can be done "manually" by calling + * the enableEvents() method. + * + * @param event The event to process. + * + * @since 1.1 + */ + protected void processActionEvent(ActionEvent event) + { + if (action_listeners != null) + action_listeners.actionPerformed(event); + } + + /** + * This method processes the specified event by dispatching it to any + * registered listeners. Note that this method will only get called if + * item events are enabled. This will happen automatically if any + * listeners are added, or it can be done "manually" by calling + * the enableEvents() method. + * + * @param event The event to process. + * + * @since 1.1 + */ + protected void processItemEvent(ItemEvent event) + { + if (item_listeners != null) + item_listeners.itemStateChanged(event); + } + + void dispatchEventImpl(AWTEvent e) + { + if (e.id <= ItemEvent.ITEM_LAST + && e.id >= ItemEvent.ITEM_FIRST + && (item_listeners != null + || (eventMask & AWTEvent.ITEM_EVENT_MASK) != 0)) + processEvent(e); + else if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); + } + + /** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ + protected String paramString() + { + return "multiple=" + multipleMode + ",rows=" + rows + super.paramString(); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this List. FooListeners are registered using the + * addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + * + * @since 1.3 + */ + public T[] getListeners (Class listenerType) + { + if (listenerType == ActionListener.class) + return AWTEventMulticaster.getListeners (action_listeners, listenerType); + + if (listenerType == ItemListener.class) + return AWTEventMulticaster.getListeners (item_listeners, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Returns all action listeners registered to this object. + * + * @since 1.4 + */ + public ActionListener[] getActionListeners () + { + return (ActionListener[]) getListeners (ActionListener.class); + } + + /** + * Returns all action listeners registered to this object. + * + * @since 1.4 + */ + public ItemListener[] getItemListeners () + { + return (ItemListener[]) getListeners (ItemListener.class); + } + + // Accessibility internal class + protected class AccessibleAWTList extends AccessibleAWTComponent + implements AccessibleSelection, ItemListener, ActionListener + { + private static final long serialVersionUID = 7924617370136012829L; + + protected class AccessibleAWTListChild extends AccessibleAWTComponent + implements Accessible + { + private static final long serialVersionUID = 4412022926028300317L; + + // Field names are fixed by serialization spec. + private List parent; + private int indexInParent; + + public AccessibleAWTListChild(List parent, int indexInParent) + { + this.parent = parent; + this.indexInParent = indexInParent; + if (parent == null) + this.indexInParent = -1; + } + + /* (non-Javadoc) + * @see javax.accessibility.Accessible#getAccessibleContext() + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LIST_ITEM; + } + + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (parent.isIndexSelected(indexInParent)) + states.add(AccessibleState.SELECTED); + return states; + } + + public int getAccessibleIndexInParent() + { + return indexInParent; + } + + } + + public AccessibleAWTList() + { + addItemListener(this); + addActionListener(this); + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LIST; + } + + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + states.add(AccessibleState.SELECTABLE); + if (isMultipleMode()) + states.add(AccessibleState.MULTISELECTABLE); + return states; + } + + public int getAccessibleChildrenCount() + { + return getItemCount(); + } + + public Accessible getAccessibleChild(int i) + { + if (i >= getItemCount()) + return null; + return new AccessibleAWTListChild(List.this, i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#getAccessibleSelectionCount() + */ + public int getAccessibleSelectionCount() + { + return getSelectedIndexes().length; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#getAccessibleSelection() + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#getAccessibleSelection(int) + */ + public Accessible getAccessibleSelection(int i) + { + int[] items = getSelectedIndexes(); + if (i >= items.length) + return null; + return new AccessibleAWTListChild(List.this, items[i]); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#isAccessibleChildSelected(int) + */ + public boolean isAccessibleChildSelected(int i) + { + return isIndexSelected(i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#addAccessibleSelection(int) + */ + public void addAccessibleSelection(int i) + { + select(i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#removeAccessibleSelection(int) + */ + public void removeAccessibleSelection(int i) + { + deselect(i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#clearAccessibleSelection() + */ + public void clearAccessibleSelection() + { + for (int i = 0; i < getItemCount(); i++) + deselect(i); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleSelection#selectAllAccessibleSelection() + */ + public void selectAllAccessibleSelection() + { + if (isMultipleMode()) + for (int i = 0; i < getItemCount(); i++) + select(i); + } + + /* (non-Javadoc) + * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent) + */ + public void itemStateChanged(ItemEvent event) + { + } + + /* (non-Javadoc) + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + public void actionPerformed(ActionEvent event) + { + } + + } + + /** + * Gets the AccessibleContext associated with this List. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTList(); + return accessibleContext; + } + + /** + * Generate a unique name for this List. + * + * @return A unique name for this List. + */ + String generateName() + { + return "list" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_list_number++; + } +} // class List diff --git a/libjava/classpath/java/awt/MediaTracker.java b/libjava/classpath/java/awt/MediaTracker.java new file mode 100644 index 000000000..e69d23e8b --- /dev/null +++ b/libjava/classpath/java/awt/MediaTracker.java @@ -0,0 +1,698 @@ +/* MediaTracker.java -- Class used for keeping track of images + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ImageObserver; +import java.util.ArrayList; + +/** + * This class is used for keeping track of the status of various media + * objects. + * + * Media objects are tracked by assigning them an ID. It is possible + * to assign the same ID to mutliple objects, effectivly grouping them + * together. In this case the status flags ({@link #statusID}) and error flag + * (@link #isErrorID} and {@link #getErrorsID}) are ORed together. This + * means that you cannot say exactly which media object has which status, + * at most you can say that there are certain media objects with + * some certain status. + * + * At the moment only images are supported by this class. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Bryce McKinlay + */ +public class MediaTracker implements java.io.Serializable +{ + /** Indicates that the media is still loading. */ + public static final int LOADING = 1 << 0; + + /** Indicates that the loading operation has been aborted. */ + public static final int ABORTED = 1 << 1; + + /** Indicates that an error has occured during loading of the media. */ + public static final int ERRORED = 1 << 2; + + /** Indicates that the media has been successfully and completely loaded. */ + public static final int COMPLETE = 1 << 3; + + /** The component on which the media is eventually been drawn. */ + Component target; + + /** The head of the linked list of tracked media objects. */ + MediaEntry head; + + /** Our serialVersionUID for serialization. */ + static final long serialVersionUID = -483174189758638095L; + + /** + * This represents a media object that is tracked by a MediaTracker. + * It also implements a simple linked list. + */ + // FIXME: The serialized form documentation says MediaEntry is a + // serializable field, but the serialized form of MediaEntry itself + // doesn't appear to be documented. + class MediaEntry implements ImageObserver + { + /** The ID of the media object. */ + int id; + + /** The media object. (only images are supported ATM). */ + Image image; + + /** The link to the next entry in the list. */ + MediaEntry next; + + /** The tracking status. */ + int status; + + /** The width of the image. */ + int width; + + /** The height of the image. */ + int height; + + /** + * Receives notification from an {@link java.awt.image.ImageProducer} + * that more data of the image is available. + * + * @param img the image that is updated + * @param flags flags from the ImageProducer that indicate the status + * of the loading process + * @param x the X coordinate of the upper left corner of the image + * @param y the Y coordinate of the upper left corner of the image + * @param width the width of the image + * @param height the height of the image + * + * @return true if more data is needed, false + * otherwise + * + * @see java.awt.image.ImageObserver + */ + public boolean imageUpdate(Image img, int flags, int x, int y, + int width, int height) + { + if ((flags & ABORT) != 0) + status = ABORTED; + else if ((flags & ERROR) != 0) + status = ERRORED; + else if ((flags & ALLBITS) != 0) + status = COMPLETE; + else + status = 0; + + synchronized (MediaTracker.this) + { + MediaTracker.this.notifyAll(); + } + + // If status is not COMPLETE then we need more updates. + return ((status & (COMPLETE | ERRORED | ABORTED)) == 0); + } + } + + /** + * Constructs a new MediaTracker for the component c. The + * component should be the component that uses the media (i.e. draws it). + * + * @param c the Component that wants to use the media + */ + public MediaTracker(Component c) + { + target = c; + } + + /** + * Adds an image to the tracker with the specified ID. + * + * @param image the image to be added + * @param id the ID of the tracker list to which the image is added + */ + public void addImage(Image image, int id) + { + MediaEntry e = new MediaEntry(); + e.id = id; + e.image = image; + synchronized(this) + { + e.next = head; + head = e; + } + } + + /** + * Adds an image to the tracker with the specified ID. + * The image is expected to be rendered with the specified width and + * height. + * + * @param image the image to be added + * @param id the ID of the tracker list to which the image is added + * @param width the width of the image + * @param height the height of the image + */ + public void addImage(Image image, int id, int width, int height) + { + MediaEntry e = new MediaEntry(); + e.id = id; + e.image = image; + e.width = width; + e.height = height; + synchronized(this) + { + e.next = head; + head = e; + } + } + + /** + * Checks if all media objects have finished loading, i.e. are + * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}. + * + * If the media objects are not already loading, a call to this + * method does not start loading. This is equivalent to + * a call to checkAll(false). + * + * @return if all media objects have finished loading either by beeing + * complete, have been aborted or errored. + */ + public boolean checkAll() + { + return checkAll(false); + } + + /** + * Checks if all media objects have finished loading, i.e. are + * {@link #COMPLETE}, {@link #ABORTED} or {@link #ERRORED}. + * + * If the media objects are not already loading, and load + * is true then a call to this + * method starts loading the media objects. + * + * @param load if true this method starts loading objects + * that are not already loading + * + * @return if all media objects have finished loading either by beeing + * complete, have been aborted or errored. + */ + public boolean checkAll(boolean load) + { + MediaEntry e = head; + boolean result = true; + + while (e != null) + { + if ((e.status & (COMPLETE | ERRORED | ABORTED)) == 0) + { + if (load && ((e.status & LOADING) == 0)) + { + if (target.prepareImage(e.image, e)) + e.status = COMPLETE; + else + { + e.status = LOADING; + int flags = target.checkImage(e.image, e); + if ((flags & ImageObserver.ABORT) != 0) + e.status = ABORTED; + else if ((flags & ImageObserver.ERROR) != 0) + e.status = ERRORED; + else if ((flags & ImageObserver.ALLBITS) != 0) + e.status = COMPLETE; + } + boolean complete = (e.status + & (COMPLETE | ABORTED | ERRORED)) != 0; + if (!complete) + result = false; + } + else + result = false; + } + e = e.next; + } + return result; + } + + /** + * Checks if any of the registered media objects has encountered an error + * during loading. + * + * @return true if at least one media object has encountered + * an error during loading, false otherwise + * + */ + public boolean isErrorAny() + { + MediaEntry e = head; + while (e != null) + { + if ((e.status & ERRORED) != 0) + return true; + e = e.next; + } + return false; + } + + /** + * Returns all media objects that have encountered errors during loading. + * + * @return an array of all media objects that have encountered errors + * or null if there were no errors at all + */ + public Object[] getErrorsAny() + { + MediaEntry e = head; + ArrayList result = null; + while (e != null) + { + if ((e.status & ERRORED) != 0) + { + if (result == null) + result = new ArrayList(); + result.add(e.image); + } + e = e.next; + } + if (result == null) + return null; + else + return result.toArray(); + } + + /** + * Waits for all media objects to finish loading, either by completing + * successfully or by aborting or encountering an error. + * + * @throws InterruptedException if another thread interrupted the + * current thread while waiting + */ + public void waitForAll() throws InterruptedException + { + synchronized (this) + { + while (checkAll(true) == false) + wait(); + } + } + + /** + * Waits for all media objects to finish loading, either by completing + * successfully or by aborting or encountering an error. + * + * This method waits at most ms milliseconds. If the + * media objects have not completed loading within this timeframe, this + * method returns false, otherwise true. + * + * @param ms timeframe in milliseconds to wait for the media objects to + * finish + * + * @return true if all media objects have successfully loaded + * within the timeframe, false otherwise + * + * @throws InterruptedException if another thread interrupted the + * current thread while waiting + */ + public boolean waitForAll(long ms) throws InterruptedException + { + long start = System.currentTimeMillis(); + boolean result = checkAll(true); + synchronized (this) + { + while (result == false) + { + wait(ms); + result = checkAll(true); + if ((System.currentTimeMillis() - start) > ms) + break; + } + } + + return result; + } + + /** + * Returns the status flags of all registered media objects ORed together. + * If load is true then media objects that + * are not already loading will be started to load. + * + * @param load if set to true then media objects that are + * not already loading are started + * + * @return the status flags of all tracked media objects ORed together + */ + public int statusAll(boolean load) + { + int result = 0; + MediaEntry e = head; + while (e != null) + { + if (load && e.status == 0) + { + if (target.prepareImage(e.image, e)) + e.status = COMPLETE; + else + { + e.status = LOADING; + int flags = target.checkImage(e.image, e); + if ((flags & ImageObserver.ABORT) != 0) + e.status = ABORTED; + else if ((flags & ImageObserver.ERROR) != 0) + e.status = ERRORED; + else if ((flags & ImageObserver.ALLBITS) != 0) + e.status = COMPLETE; + } + } + result |= e.status; + e = e.next; + } + return result; + } + + /** + * Checks if the media objects with ID have completed loading. + * + * @param id the ID of the media objects to check + * + * @return true if all media objects with ID + * have successfully finished + */ + public boolean checkID(int id) + { + return checkID(id, false); + } + + /** + * Checks if the media objects with ID have completed loading. + * If load is true then media objects that + * are not already loading will be started to load. + * + * @param id the ID of the media objects to check + * @param load if set to true then media objects that are + * not already loading are started + * + * @return true if all media objects with ID + * have successfully finished + */ + public boolean checkID(int id, boolean load) + { + MediaEntry e = head; + boolean result = true; + + while (e != null) + { + if (e.id == id && ((e.status & (COMPLETE | ABORTED | ERRORED)) == 0)) + { + if (load && ((e.status & LOADING) == 0)) + { + e.status = LOADING; + if (target.prepareImage(e.image, e)) + e.status = COMPLETE; + else + { + int flags = target.checkImage(e.image, e); + if ((flags & ImageObserver.ABORT) != 0) + e.status = ABORTED; + else if ((flags & ImageObserver.ERROR) != 0) + e.status = ERRORED; + else if ((flags & ImageObserver.ALLBITS) != 0) + e.status = COMPLETE; + } + boolean complete = (e.status + & (COMPLETE | ABORTED | ERRORED)) != 0; + if (!complete) + result = false; + } + else + result = false; + } + e = e.next; + } + return result; + } + + /** + * Returns true if any of the media objects with ID + * have encountered errors during loading, false otherwise. + * + * @param id the ID of the media objects to check + * + * @return true if any of the media objects with ID + * have encountered errors during loading, false otherwise + */ + public boolean isErrorID(int id) + { + MediaEntry e = head; + while (e != null) + { + if (e.id == id && ((e.status & ERRORED) != 0)) + return true; + e = e.next; + } + return false; + } + + /** + * Returns all media objects with the specified ID that have encountered + * an error. + * + * @param id the ID of the media objects to check + * + * @return an array of all media objects with the specified ID that + * have encountered an error + */ + public Object[] getErrorsID(int id) + { + MediaEntry e = head; + ArrayList result = null; + while (e != null) + { + if (e.id == id && ((e.status & ERRORED) != 0)) + { + if (result == null) + result = new ArrayList(); + result.add(e.image); + } + e = e.next; + } + if (result == null) + return null; + else + return result.toArray(); + } + + /** + * Waits for all media objects with the specified ID to finish loading, + * either by completing successfully or by aborting or encountering an error. + * + * @param id the ID of the media objects to wait for + * + * @throws InterruptedException if another thread interrupted the + * current thread while waiting + */ + public void waitForID(int id) throws InterruptedException + { + MediaEntry e = head; + synchronized (this) + { + while (checkID (id, true) == false) + wait(); + } + } + + /** + * Waits for all media objects with the specified ID to finish loading, + * either by completing successfully or by aborting or encountering an error. + * + * This method waits at most ms milliseconds. If the + * media objects have not completed loading within this timeframe, this + * method returns false, otherwise true. + * + * @param id the ID of the media objects to wait for + * @param ms timeframe in milliseconds to wait for the media objects to + * finish + * + * @return true if all media objects have successfully loaded + * within the timeframe, false otherwise + * + * @throws InterruptedException if another thread interrupted the + * current thread while waiting + */ + public boolean waitForID(int id, long ms) throws InterruptedException + { + MediaEntry e = head; + long start = System.currentTimeMillis(); + boolean result = checkID(id, true); + + synchronized (this) + { + while (result == false) + { + wait(ms); + result = checkID(id, true); + if ((System.currentTimeMillis() - start) > ms) + break; + } + } + + return result; + } + + /** + * Returns the status flags of the media objects with the specified ID + * ORed together. + * + * If load is true then media objects that + * are not already loading will be started to load. + * + * @param load if set to true then media objects that are + * not already loading are started + * + * @return the status flags of all tracked media objects ORed together + */ + public int statusID(int id, boolean load) + { + int result = 0; + MediaEntry e = head; + while (e != null) + { + if (e.id == id) + { + if (load && e.status == 0) + { + if (target.prepareImage(e.image, e)) + e.status = COMPLETE; + else + { + e.status = LOADING; + int flags = target.checkImage(e.image, e); + if ((flags & ImageObserver.ABORT) != 0) + e.status = ABORTED; + else if ((flags & ImageObserver.ERROR) != 0) + e.status = ERRORED; + else if ((flags & ImageObserver.ALLBITS) != 0) + e.status = COMPLETE; + } + } + result |= e.status; + } + e = e.next; + } + return result; + } + + /** + * Removes an image from this MediaTracker. + * + * @param image the image to be removed + */ + public void removeImage(Image image) + { + synchronized (this) + { + MediaEntry e = head; + MediaEntry prev = null; + while (e != null) + { + if (e.image == image) + { + if (prev == null) + head = e.next; + else + prev.next = e.next; + } + else + prev = e; + e = e.next; + } + } + } + + /** + * Removes an image with the specified ID from this MediaTracker. + * + * @param image the image to be removed + */ + public void removeImage(Image image, int id) + { + synchronized (this) + { + MediaEntry e = head; + MediaEntry prev = null; + while (e != null) + { + if (e.id == id && e.image == image) + { + if (prev == null) + head = e.next; + else + prev.next = e.next; + } + else + prev = e; + e = e.next; + } + } + } + + /** + * Removes an image with the specified ID and scale from this MediaTracker. + * + * @param image the image to be removed + */ + public void removeImage(Image image, int id, int width, int height) + { + synchronized (this) + { + MediaEntry e = head; + MediaEntry prev = null; + while (e != null) + { + if (e.id == id && e.image == image + && e.width == width && e.height == height) + { + if (prev == null) + head = e.next; + else + prev.next = e.next; + } + else + prev = e; + e = e.next; + } + } + } +} diff --git a/libjava/classpath/java/awt/Menu.java b/libjava/classpath/java/awt/Menu.java new file mode 100644 index 000000000..c640b5e85 --- /dev/null +++ b/libjava/classpath/java/awt/Menu.java @@ -0,0 +1,444 @@ +/* Menu.java -- A Java AWT Menu + Copyright (C) 1999, 2002, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.MenuPeer; +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Vector; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This class represents a pull down or tear off menu in Java's AWT. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Menu extends MenuItem implements MenuContainer, Serializable +{ + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_menu_number; + + // Serialization Constant + private static final long serialVersionUID = -8809584163345499784L; + + /** + * @serial The actual items in the menu + */ + private Vector items = new Vector(); + + /** + * @serial Flag indicating whether or not this menu is a tear off + */ + private boolean tearOff; + + /** + * @serial Indicates whether or not this is a help menu. + */ + private boolean isHelpMenu; + + /* + * @serial Unused in this implementation, but present in Sun's + * serialization spec. Value obtained via reflection. + */ + private int menuSerializedDataVersion = 1; + + static final transient String separatorLabel = "-"; + + /** + * Initializes a new instance of Menu with no label and that + * is not a tearoff; + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Menu() + { + } + + /** + * Initializes a new instance of Menu that is not a tearoff and + * that has the specified label. + * + * @param label The menu label. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Menu(String label) + { + this(label, false); + } + + /** + * Initializes a new instance of Menu with the specified + * label and tearoff status. + * + * @param label The label for this menu + * @param isTearOff true if this menu is a tear off menu, + * false otherwise. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Menu(String label, boolean isTearOff) + { + super(label); + + tearOff = isTearOff; + + if (label.equals("Help")) + isHelpMenu = true; + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } + + /** + * Tests whether or not this menu is a tearoff. + * + * @return true if this menu is a tearoff, false + * otherwise. + */ + public boolean isTearOff() + { + return(tearOff); + } + + /** + * Returns the number of items in this menu. + * + * @return The number of items in this menu. + */ + public int getItemCount() + { + return countItems(); + } + + /** + * Returns the number of items in this menu. + * + * @return The number of items in this menu. + * + * @deprecated As of JDK 1.1, replaced by getItemCount(). + */ + public int countItems() + { + return items.size(); + } + + /** + * Returns the item at the specified index. + * + * @param index the item index. + * + * @return The item at the specified index. + * + * @exception ArrayIndexOutOfBoundsException If the index value is not valid. + */ + public MenuItem getItem(int index) + { + return((MenuItem) items.elementAt(index)); + } + + /** + * Adds the specified item to this menu. If it was previously part of + * another menu, it is first removed from that menu. + * + * @param item The new item to add. + * + * @return The item that was added. + */ + public MenuItem add(MenuItem item) + { + MenuContainer parent = item.getParent(); + if (parent != null) + parent.remove(item); + + items.addElement(item); + item.setParent(this); + + if (peer != null) + { + item.addNotify(); + MenuPeer mp = (MenuPeer) peer; + mp.addItem(item); + } + + return item; + } + + /** + * Add an item with the specified label to this menu. + * + * @param label The label of the menu item to add. + */ + public void add(String label) + { + add(new MenuItem(label)); + } + + /** + * Inserts the specified menu item into this menu at the specified index. If + * the index is greater than or equal to the number of items already in the + * menu, the new item is added as the last item in the menu. + * + * @param item The menu item to add (null not permitted). + * @param index The index of the menu item (>= 0). + * + * @throws IllegalArgumentException if the index is less than zero. + * @throws NullPointerException if item is null. + */ + public void insert(MenuItem item, int index) + { + if (index < 0) + throw new IllegalArgumentException("Index is less than zero"); + + int count = getItemCount(); + + if (index >= count) + add(item); + else + { + MenuContainer parent = item.getParent(); + if (parent != null) + parent.remove(item); + + items.insertElementAt(item, index); + item.setParent(this); + + MenuPeer peer = (MenuPeer) getPeer(); + if (peer == null) + return; + + for (int i = count - 1; i >= index; i--) + peer.delItem(i); + + item.addNotify(); + peer.addItem(item); + + // bear in mind that count is the number of items *before* the new + // item was added + for (int i = index + 1; i <= count; i++) + peer.addItem((MenuItem) items.elementAt(i)); + } + + } + + /** + * Inserts an item with the specified label into this menu at the specified + * index. If the index is greater than or equal to the number of items + * already in the menu, the new item is added as the last item in the menu. + * + * @param label The label of the item to add. + * @param index The index of the menu item (>= 0). + * + * @throws IllegalArgumentException If the index is less than zero. + */ + public void insert(String label, int index) + { + insert(new MenuItem(label), index); + } + + /** + * Adds a separator bar at the current menu location. + */ + public void addSeparator() + { + add(new MenuItem(separatorLabel)); + } + + /** + * Inserts a separator bar at the specified index value. + * + * @param index The index at which to insert a separator bar. + * + * @exception IllegalArgumentException If the index is less than zero. + * @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid. + */ + public void insertSeparator(int index) + { + insert(new MenuItem(separatorLabel), index); + } + + /** + * Deletes the item at the specified index from this menu. + * + * @param index The index of the item to remove. + * + * @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid. + */ + public synchronized void remove(int index) + { + MenuItem item = (MenuItem) items.remove(index); + + MenuPeer mp = (MenuPeer) getPeer(); + if (mp != null) + { + mp.delItem(index); + item.removeNotify(); + } + item.setParent(null); + } + + /** + * Removes the specifed item from the menu. If the specified component + * does not exist, this method does nothing. + * + * @param item The component to remove. + */ + public void remove(MenuComponent item) + { + int index = items.indexOf(item); + if (index == -1) + return; + + remove(index); + } + + /** + * Removes all the elements from this menu. + */ + public synchronized void removeAll() + { + int count = getItemCount(); + for(int i = 0; i < count; i++) + { + // We must always remove item 0. + remove(0); + } + } + + /** + * Creates the native peer for this object. + */ + public void addNotify() + { + MenuPeer peer = (MenuPeer) getPeer(); + if (peer == null) + { + peer = getToolkit().createMenu(this); + setPeer(peer); + } + + Enumeration e = items.elements(); + while (e.hasMoreElements()) + { + MenuItem mi = (MenuItem)e.nextElement(); + mi.addNotify(); + peer.addItem(mi); + } + + super.addNotify(); + } + + /** + * Destroys the native peer for this object. + */ + public void removeNotify() + { + Enumeration e = items.elements(); + while (e.hasMoreElements()) + { + MenuItem mi = (MenuItem) e.nextElement(); + mi.removeNotify(); + } + super.removeNotify(); + } + + /** + * Returns a debugging string for this menu. + * + * @return A debugging string for this menu. + */ + public String paramString() + { + return (",tearOff=" + tearOff + ",isHelpMenu=" + isHelpMenu + + super.paramString()); + } + + /** + * Basic Accessibility class for Menu. Details get provided in derived + * classes. + */ + protected class AccessibleAWTMenu extends AccessibleAWTMenuItem + { + private static final long serialVersionUID = 5228160894980069094L; + + protected AccessibleAWTMenu() + { + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU; + } + } + + /** + * Gets the AccessibleContext associated with this Menu. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTMenu(); + return accessibleContext; + } + + /** + * Generate a unique name for this Menu. + * + * @return A unique name for this Menu. + */ + String generateName() + { + return "menu" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_menu_number++; + } + +} // class Menu diff --git a/libjava/classpath/java/awt/MenuBar.java b/libjava/classpath/java/awt/MenuBar.java new file mode 100644 index 000000000..3fc5622ac --- /dev/null +++ b/libjava/classpath/java/awt/MenuBar.java @@ -0,0 +1,390 @@ +/* MenuBar.java -- An AWT menu bar class + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.MenuBarPeer; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This class implements a menu bar in the AWT system. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class MenuBar extends MenuComponent + implements MenuContainer, Serializable, Accessible +{ + + // Serialization Constant + private static final long serialVersionUID = -4930327919388951260L; + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_menubar_number; + + /** + * @serial The menu used for providing help information + */ + private Menu helpMenu; + + /** + * @serial The menus contained in this menu bar. + */ + private Vector menus = new Vector(); + + /** + * Initializes a new instance of MenuBar. + * + * @throws HeadlessException if GraphicsEnvironment.isHeadless() is true + */ + public MenuBar() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } + + /** + * Returns the help menu for this menu bar. This may be null. + * + * @return the help menu for this menu bar + */ + public Menu getHelpMenu() + { + return helpMenu; + } + + /** + * Sets the help menu for this menu bar. + * + * @param menu the new help menu for this menu bar + */ + public synchronized void setHelpMenu(Menu menu) + { + MenuBarPeer myPeer = (MenuBarPeer) getPeer (); + + if (helpMenu != null) + { + if (myPeer != null) + helpMenu.removeNotify(); + helpMenu.setParent(null); + } + helpMenu = menu; + + MenuContainer parent = menu.getParent(); + if (parent != null) + parent.remove(menu); + menu.setParent(this); + + if (myPeer != null) + { + menu.addNotify(); + myPeer.addHelpMenu(menu); + } + } + + /** + * Add a menu to this MenuBar. If the menu has already has a + * parent, it is first removed from its old parent before being + * added. + * + * @param menu the menu to add + * + * @return the menu that was added + */ + public synchronized Menu add(Menu menu) + { + MenuBarPeer myPeer = (MenuBarPeer) getPeer (); + + MenuContainer parent = menu.getParent(); + if (parent != null) + parent.remove(menu); + + menus.addElement(menu); + menu.setParent(this); + + if (myPeer != null) + { + menu.addNotify(); + myPeer.addMenu(menu); + } + return menu; + } + + /** + * Removes the menu at the specified index. + * + * @param index the index of the menu to remove from the menu bar + */ + public synchronized void remove(int index) + { + Menu m = (Menu) menus.remove(index); + MenuBarPeer mp = (MenuBarPeer) getPeer(); + + if (mp != null) + m.removeNotify(); + + m.setParent(null); + + if (mp != null) + mp.delMenu(index); + } + + /** + * Removes the specified menu from the menu bar. + * + * @param menu the menu to remove from the menu bar + */ + public void remove(MenuComponent menu) + { + int index = menus.indexOf(menu); + if (index == -1) + return; + + remove(index); + } + + /** + * Returns the number of elements in this menu bar. + * + * @return the number of elements in the menu bar + */ + public int getMenuCount() + { + return countMenus(); + } + + /** + * Returns the number of elements in this menu bar. + * + * @return the number of elements in the menu bar + * + * @deprecated This method is deprecated in favor of + * getMenuCount(). + */ + public int countMenus() + { + return menus.size() + (getHelpMenu() == null ? 0 : 1); + } + + /** + * Returns the menu at the specified index. + * + * @param index the index of the menu + * + * @return the requested menu + * + * @throws ArrayIndexOutOfBoundsException if the index is not valid + */ + public Menu getMenu(int index) + { + return (Menu) menus.elementAt(index); + } + + /** + * Creates this object's native peer. + */ + public void addNotify() + { + MenuBarPeer peer = (MenuBarPeer) getPeer(); + if (peer == null) + { + peer = getToolkit().createMenuBar(this); + setPeer(peer); + } + + Enumeration e = menus.elements(); + while (e.hasMoreElements()) + { + Menu mi = (Menu)e.nextElement(); + mi.addNotify(); + peer.addMenu(mi); + } + + if (helpMenu != null) + { + helpMenu.addNotify(); + peer.addHelpMenu(helpMenu); + } + } + + /** + * Destroys this object's native peer. + */ + public void removeNotify() + { + Enumeration e = menus.elements(); + while (e.hasMoreElements()) + { + Menu mi = (Menu) e.nextElement(); + mi.removeNotify(); + } + super.removeNotify(); + } + + /** + * Returns a list of all shortcuts for the menus in this menu bar. + * + * @return a list of all shortcuts for the menus in this menu bar + */ + public synchronized Enumeration shortcuts() + { + Vector shortcuts = new Vector(); + Enumeration e = menus.elements(); + + while (e.hasMoreElements()) + { + Menu menu = (Menu)e.nextElement(); + if (menu.getShortcut() != null) + shortcuts.addElement(menu.getShortcut()); + } + + return shortcuts.elements(); + } + + /** + * Returns the menu item for the specified shortcut, or null + * if no such item exists. + * + * @param shortcut the shortcut to return the menu item for + * + * @return the menu item for the specified shortcut + */ + public MenuItem getShortcutMenuItem(MenuShortcut shortcut) + { + Enumeration e = menus.elements(); + + while (e.hasMoreElements()) + { + Menu menu = (Menu) e.nextElement(); + MenuShortcut s = menu.getShortcut(); + if ((s != null) && s.equals(shortcut)) + return menu; + } + + return null; + } + + /** + * Deletes the specified menu shortcut. + * + * @param shortcut the shortcut to delete + */ + public void deleteShortcut(MenuShortcut shortcut) + { + MenuItem it; + // This is a slow implementation, but it probably doesn't matter. + while ((it = getShortcutMenuItem (shortcut)) != null) + it.deleteShortcut(); + } + + /** + * Gets the AccessibleContext associated with this MenuBar. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + // Create the context if this is the first request. + if (accessibleContext == null) + accessibleContext = new AccessibleAWTMenuBar(); + return accessibleContext; + } + + /** + * Generate a unique name for this MenuBar. + * + * @return A unique name for this MenuBar. + */ + String generateName() + { + return "menubar" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_menubar_number++; + } + + /** + * This class provides accessibility support for AWT menu bars. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + protected class AccessibleAWTMenuBar + extends AccessibleAWTMenuComponent + { + + /** + * Compatible with JDK 1.4.2 revision 5 + */ + private static final long serialVersionUID = -8577604491830083815L; + + /** + * This is the default constructor, which simply calls the default + * constructor of the superclass. + */ + protected AccessibleAWTMenuBar() + { + super(); + } + + /** + * Returns the accessible role relating to the menu bar. + * + * @return AccessibleRole.MENU_BAR + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU_BAR; + } + + } + +} diff --git a/libjava/classpath/java/awt/MenuComponent.java b/libjava/classpath/java/awt/MenuComponent.java new file mode 100644 index 000000000..fb0f14eb0 --- /dev/null +++ b/libjava/classpath/java/awt/MenuComponent.java @@ -0,0 +1,1302 @@ +/* MenuComponent.java -- Superclass of all AWT menu components + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.peer.MenuComponentPeer; +import java.io.Serializable; +import java.util.Locale; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleComponent; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleStateSet; + +/** + * This is the superclass of all menu AWT widgets. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public abstract class MenuComponent implements Serializable +{ + +//Serialization Constant + private static final long serialVersionUID = -4536902356223894379L; + + /** + * The font for this component. + * + * @see #getFont() + * @see #setFont(java.awt.Font) + * @serial the component's font. + */ + private Font font; + + /** + * The name of the component. + * + * @see #getName() + * @see #setName(String) + * @serial the component's name. + */ + private String name; + + /** + * The parent of this component. + * + * @see #getParent() + * @see #setParent(java.awt.MenuContainer) + * @serial ignored. + */ + transient MenuContainer parent; + + /** + * The native peer for this component. + * + * @see #getPeer() + * @see #setPeer(java.awt.peer.MenuComponentPeer) + * @serial ignored. + */ + transient MenuComponentPeer peer; + + /** + * The synchronization locking object for this component. + * + * @serial ignored. + */ + private transient Object tree_lock = this; + + /** + * The toolkit for this object. + * + * @see #getToolkit() + * @serial ignored. + */ + private static transient Toolkit toolkit = Toolkit.getDefaultToolkit(); + + /** + * The accessible context for this component. + * + * @see #getAccessibleContext() + * @serial the accessibility information for this component. + */ + AccessibleContext accessibleContext; + + /** + * Was the name of the component set? This value defaults + * to false and becomes true after a call to setName(). + * Please note that this does not guarantee that name will then + * be non-null, as this may be the value passed to setName(). + * + * @see #setName(String) + * @serial true if the name value has been explicitly set by calling + * setName(). + */ + private boolean nameExplicitlySet; + + /** + * Does this component handle new events? Events will be handled + * by this component if this is true. Otherwise, they will be forwarded + * up the component hierarchy. This implementation does not use this + * variable; it is merely provided for serialization compatability. + * + * @see #dispatchEvent(AWTEvent) + * @serial true if events are to be processed locally. Unused. + */ + private boolean newEventsOnly; + + /** + * The focus listener chain handler which deals with focus events for + * the accessible context of this component. + * + * @see AccessibleAWTMenuComponent#addFocusListener(java.awt.event.FocusListener) + * @serial ignored. + * This is package-private to avoid an accessor method. + */ + transient FocusListener focusListener; + + /** + * Default constructor for subclasses. + * + * @throws HeadlessException ff GraphicsEnvironment.isHeadless() is true + */ + public MenuComponent() + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + } + +/** + * Returns the font in use for this component. + * + * @return the font for this component + */ + public Font getFont() + { + if (font != null) + return font; + + if (parent != null) + return parent.getFont(); + + return null; + } + + /** + * Sets the font for this component to the specified font. + * + * @param font the new font for this component + */ + public void setFont(Font font) + { + this.font = font; + } + + /** + * Returns the name of this component. + * + * @return the name of this component + */ + public String getName() + { + if (name == null && ! nameExplicitlySet) + name = generateName(); + return name; + } + + /** + * Subclasses should override this to return unique component names like + * "menuitem0". + * + * @return the generated name for this menu component + */ + String generateName() + { + // MenuComponent is abstract. + return null; + } + + /** + * Sets the name of this component to the specified name. + * + * @param name the new name of this component + */ + public void setName(String name) + { + this.name = name; + nameExplicitlySet = true; + } + + /** + * Returns the parent of this component. + * + * @return the parent of this component + */ + public MenuContainer getParent() + { + return parent; + } + + /** + * Sets the parent of this component. + * + * @param parent the parent to set + */ + final void setParent(MenuContainer parent) + { + this.parent = parent; + } + + /** + * Returns the native windowing system peer for this component. + * + * @return the peer for this component + * + * @deprecated + */ + public MenuComponentPeer getPeer() + { + return peer; + } + + /** + * Sets the peer for this component. + * + * @param peer the peer to set + */ + final void setPeer(MenuComponentPeer peer) + { + this.peer = peer; + } + + /** + * Destroys this component's native peer + */ + public void removeNotify() + { + if (peer != null) + peer.dispose(); + peer = null; + } + + /** + * Returns the toolkit in use for this component. + * + * @return the toolkit for this component + */ + final Toolkit getToolkit() + { + return toolkit; + } + + /** + * Returns the object used for synchronization locks on this component + * when performing tree and layout functions. + * + * @return the synchronization lock for this component + */ + protected final Object getTreeLock() + { + return tree_lock; + } + + /** + * Sets the sync lock object for this component. + * + * @param treeLock the sync lock to set + */ + final void setTreeLock(Object treeLock) + { + this.tree_lock = treeLock; + } + + /** + * AWT 1.0 event dispatcher. + * + * @return true if the event was dispatched, false otherwise + * + * @deprecated Deprecated in favor of dispatchEvent(). + */ + public boolean + postEvent(Event event) + { + boolean retVal = false; + MenuContainer parent = getParent(); + if (parent != null) + retVal = parent.postEvent(event); + + return retVal; + } + + /** + * Sends this event to this component or a subcomponent for processing. + * + * @param event The event to dispatch + */ + public final void dispatchEvent(AWTEvent event) + { + // Convert AWT 1.1 event to AWT 1.0 event. + Event oldStyleEvent = Component.translateEvent(event); + if (oldStyleEvent != null) + { + postEvent(oldStyleEvent); + } + + // See comment in Component.dispatchEvent(). + dispatchEventImpl(event); + } + + /** + * Implementation of dispatchEvent. Allows trusted package classes + * to dispatch additional events first. This implementation first + * translates event to an AWT 1.0 event and sends the + * result to {@link #postEvent}. The event is then + * passed on to {@link #processEvent} for local processing. + * + * @param event the event to dispatch + */ + void dispatchEventImpl(AWTEvent event) + { + // Do local processing. + processEvent(event); + } + + /** + * Processes the specified event. In this class, this method simply + * calls one of the more specific event handlers. + * + * @param event the event to process + */ + protected void processEvent(AWTEvent event) + { + // Pass a focus event to the focus listener for + // the accessibility context. + if (event instanceof FocusEvent) + { + if (focusListener != null) + { + switch (event.id) + { + case FocusEvent.FOCUS_GAINED: + focusListener.focusGained((FocusEvent) event); + break; + case FocusEvent.FOCUS_LOST: + focusListener.focusLost((FocusEvent) event); + break; + } + } + } + } + + /** + * Returns a string representation of this component. + * + * @return a string representation of this component + */ + public String toString() + { + return getClass().getName() + "[" + paramString() + "]"; + } + + /** + * Returns a debugging string for this component + */ + protected String paramString() + { + return "name=" + getName(); + } + + /** + * Gets the AccessibleContext associated with this MenuComponent. + * As an abstract class, we return null. Concrete subclasses should return + * their implementation of the accessibility context. + * + * @return null + */ + public AccessibleContext getAccessibleContext() + { + return null; + } + + /** + * This class provides a base for the accessibility support of menu + * components. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + protected abstract class AccessibleAWTMenuComponent + extends AccessibleContext + implements Serializable, AccessibleComponent, AccessibleSelection + { + + /** + * Compatible with JDK 1.4.2 revision 5 + */ + private static final long serialVersionUID = -4269533416223798698L; + + /** + * This is the default constructor. It should be called by + * concrete subclasses to ensure necessary groundwork is completed. + */ + protected AccessibleAWTMenuComponent() + { + // Nothing to do here. + } + + /** + * Replaces or supplements the component's selection with the + * Accessible child at the supplied index. If + * the component supports multiple selection, the child is + * added to the current selection. Otherwise, the current + * selection becomes the specified child. If the child is + * already selected, nothing happens. + *
+ *
+ * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the specified child within a + * zero-based list of the component's children + */ + public void addAccessibleSelection(int index) + { + // Subclasses with children should implement this. + } + + /** + * Registers the specified focus listener to receive + * focus events from this component. + * + * @param listener the new focus listener + */ + public void addFocusListener(FocusListener listener) + { + // Chain the new focus listener to the existing chain + // of focus listeners. Each new focus listener is + // coupled via multicasting to the existing chain. + focusListener = AWTEventMulticaster.add(focusListener, listener); + } + + /** + * Clears the component's current selection. Following + * the calling of this method, no children of the component + * will be selected. + *
+ *
+ * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + */ + public void clearAccessibleSelection() + { + // Nothing to do here. + } + + /** + * Returns true if the specified point lies within the + * component. The supplied co-ordinates are assumed to + * be relative to the co-ordinate system of the component + * itself. Thus, the point (0,0) is the upper left corner + * of this component. + *
+ *
+ * Please note that this method depends on a correctly implemented + * version of the getBounds() method. Subclasses + * must provide the bounding rectangle via getBounds() + * in order for this method to work. + * + * @param point the point to check against this component + * @return true if the point is within this component + * @see #getBounds() + */ + public boolean contains(Point point) + { + // We can simply return the result of a + // test for containment in the bounding rectangle. + return getBounds().contains(point); + } + + /** + * Returns the Accessible child of this component present + * at the specified point. The supplied co-ordinates are + * assumed to be relative to the co-ordinate system of this + * component (the parent of any returned accessible). Thus, + * the point (0,0) is the upper left corner of this menu + * component. + *
+ *
+ * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param point the point at which the returned accessible + * is located + * @return null + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns the Accessible child at the supplied + * index within the list of children of this component. + *
+ *
+ * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the Accessible child + * to retrieve + * + * @return null + */ + public Accessible getAccessibleChild(int index) + { + return null; + } + + /** + * Returns the number of children of this component which + * implement the Accessible interface. If + * all children of this component are accessible, then + * the returned value will be the same as the number of + * children. + *
+ *
+ * + * @return 0 + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Retrieves the AccessibleComponent associated + * with this accessible context and its component. As the + * context itself implements AccessibleComponent, + * this is the return value. + * + * @return the context itself + */ + public AccessibleComponent getAccessibleComponent() + { + return this; + } + + /** + * Returns the accessible name for this menu component. This + * is the name given to the component, which may be null if + * not set using setName(). + *
+ *
+ * The name is not the most appropriate description of this + * object. Subclasses should preferably provide a more + * accurate description. For example, a File menu could + * have the description `Lists commands related to the + * file system'. + * + * @return a description of the component. Currently, + * this is just the contents of the name property + * + * @see MenuComponent#setName(String) + */ + public String getAccessibleDescription() + { + return MenuComponent.this.getName(); + } + + /** + * Retrieves the index of this component within its parent. + * If no parent exists, -1 is returned. + * + * @return -1 as the parent, a MenuContainer + * is not Accessible + */ + public int getAccessibleIndexInParent() + { + return -1; + } + + /** + * Returns the accessible name of this component. This + * is the name given to the component, which may be null if + * not set using setName(). + *
+ *
+ * The name property is not the most suitable string to return + * for this method. The string should be localized, and + * relevant to the operation of the component. For example, + * it could be the text of a menu item. However, this can + * not be used at this level of abstraction, so it is the + * responsibility of subclasses to provide a more appropriate + * name. + * + * @return a localized name for this component. Currently, this + * is just the contents of the name property + * + * @see MenuComponent#setName(String) + */ + public String getAccessibleName() + { + return MenuComponent.this.getName(); + } + + /** + * Returns the Accessible parent of this component. + * As the parent of a MenuComponent is a + * MenuContainer, which doesn't implement + * Accessible, this method returns null. + * + * @return null + */ + public Accessible getAccessibleParent() + { + return null; + } + + /** + * Returns the accessible role of this component. + *
+ *
+ * The abstract implementation of this method returns + * AccessibleRole.AWT_COMPONENT, + * as the abstract component has no specific role. This + * method should be overridden by concrete subclasses, so + * as to return an appropriate role for the component. + * + * @return AccessibleRole.AWT_COMPONENT + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.AWT_COMPONENT; + } + + /** + * Retrieves the AccessibleSelection associated + * with this accessible context and its component. As the + * context itself implements AccessibleSelection, + * this is the return value. + * + * @return the context itself + */ + public AccessibleSelection getAccessibleSelection() + { + return this; + } + + /** + * Retrieves the Accessible selected child + * at the specified index. If there are no selected children + * or the index is outside the range of selected children, + * null is returned. Please note that the index refers + * to the index of the child in the list of selected + * children, and not the index of the child in + * the list of all Accessible children. + *
+ *
+ * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the selected Accessible + * child + */ + public Accessible getAccessibleSelection(int index) + { + return null; + } + + /** + * Returns a count of the number of Accessible + * children of this component which are currently selected. + * If there are no children currently selected, 0 is returned. + *
+ *
+ * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @return 0 + */ + public int getAccessibleSelectionCount() + { + return 0; + } + + /** + * Retrieves the current state of this component + * in an accessible form. For example, a given component + * may be visible, selected, disabled, etc. + *
+ *
+ * As this class tells us virtually nothing about the component, + * except for its name and font, no state information can be + * provided. This implementation thus returns an empty + * state set, and it is left to concrete subclasses to provide + * a more acceptable and relevant state set. Changes to these + * properties also need to be handled using + * PropertyChangeListeners. + * + * @return an empty AccessibleStateSet + */ + public AccessibleStateSet getAccessibleStateSet() + { + return new AccessibleStateSet(); + } + + /** + * Returns the background color of the component, or null + * if this property is unsupported. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns the + * default system background color used for rendering menus. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @return the default system background color for menus + * + * @see #setBackground(java.awt.Color) + */ + public Color getBackground() + { + return SystemColor.menu; + } + + /** + * Returns a Rectangle which represents the + * bounds of this component. The returned rectangle has the + * height and width of the component's bounds, and is positioned + * at a location relative to this component's parent, the + * MenuContainer. null is returned if bounds + * are not supported by the component. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns null. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @return null + * + * @see #setBounds(java.awt.Rectangle) + */ + public Rectangle getBounds() + { + return null; + } + + /** + * Returns the Cursor displayed when the pointer + * is positioned over this component. Alternatively, null + * is returned if the component doesn't support the cursor + * property. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns the default + * system cursor. Concrete subclasses which handle the drawing + * of an onscreen menu component may override this method and provide + * the appropriate information. + * + * @return the default system cursor + * + * @see #setCursor(java.awt.Cursor) + */ + public Cursor getCursor() + { + return Cursor.getDefaultCursor(); + } + + /** + * Returns the Font used for text created by this component. + * + * @return the current font + * + * @see #setFont(java.awt.Font) + */ + public Font getFont() + { + return MenuComponent.this.getFont(); + } + + /** + * Retrieves information on the rendering and metrics of the supplied + * font. If font metrics are not supported by this component, null + * is returned. + *
+ *
+ * The abstract implementation of this method simply uses the toolkit + * to obtain the FontMetrics. Concrete subclasses may + * find it more efficient to invoke their peer class directly, if one + * is available. + * + * @param font the font about which to retrieve rendering and metric + * information + * + * @return the metrics of the given font, as provided by the system + * toolkit + * + * @throws NullPointerException if the supplied font was null + */ + public FontMetrics getFontMetrics(Font font) + { + return MenuComponent.this.getToolkit().getFontMetrics(font); + } + + /** + * Returns the foreground color of the component, or null + * if this property is unsupported. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns the + * default system text color used for rendering menus. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @return the default system text color for menus + * + * @see #setForeground(java.awt.Color) + */ + public Color getForeground() + { + return SystemColor.menuText; + } + + /** + * Returns the locale currently in use by this component. + *
+ *
+ * This abstract class has no property relating to the + * locale used by the component, so this method simply + * returns the default locale for the current instance + * of the Java Virtual Machine (JVM). Concrete subclasses + * which maintain such a property should override this method + * and provide the locale information more accurately. + * + * @return the default locale for this JVM instance + */ + public Locale getLocale() + { + return Locale.getDefault(); + } + + /** + * Returns the location of the component, with co-ordinates + * relative to the parent component and using the co-ordinate + * space of the screen. Thus, the point (0,0) is the upper + * left corner of the parent component. + *
+ *
+ * Please note that this method depends on a correctly implemented + * version of the getBounds() method. Subclasses + * must provide the bounding rectangle via getBounds() + * in order for this method to work. + * + * @return the location of the component, relative to its parent + * + * @see #setLocation(java.awt.Point) + */ + public Point getLocation() + { + // Simply return the location of the bounding rectangle. + return getBounds().getLocation(); + } + + /** + * Returns the location of the component, with co-ordinates + * relative to the screen. Thus, the point (0,0) is the upper + * left corner of the screen. null is returned if the component + * is either not on screen or if this property is unsupported. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply returns null. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @return the location of the component, relative to the screen + */ + public Point getLocationOnScreen() + { + return null; + } + + /** + * Returns the size of the component. + *
+ *
+ * Please note that this method depends on a correctly implemented + * version of the getBounds() method. Subclasses + * must provide the bounding rectangle via getBounds() + * in order for this method to work. + * + * @return the size of the component + * + * @see #setSize(java.awt.Dimension) + */ + public Dimension getSize() + { + // Simply return the size of the bounding rectangle. + return getBounds().getSize(); + } + + /** + * Returns true if the accessible child specified by the supplied index + * is currently selected. + *
+ *
+ * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the accessible child to check for selection + * + * @return false + */ + public boolean isAccessibleChildSelected(int index) + { + return false; + } + + /** + * Returns true if this component is currently enabled. + *
+ *
+ * As this abstract component has no properties related to + * its enabled or disabled state, the implementation of this + * method is left to subclasses. + * + * @return false + * + * @see #setEnabled(boolean) + */ + public boolean isEnabled() + { + return false; + } + + /** + * Returns true if this component is included in the traversal + * of the current focus from one component to the other. + *
+ *
+ * As this abstract component has no properties related to + * its ability to accept the focus, the implementation of this + * method is left to subclasses. + * + * @return false + */ + public boolean isFocusTraversable() + { + return false; + } + + /** + * Returns true if the component is being shown on screen. + * A component is determined to be shown if it is visible, + * and each parent component is also visible. Please note + * that, even when a component is showing, it may still be + * obscured by other components in front. This method only + * determines if the component is being drawn on the screen. + *
+ *
+ * As this abstract component and its parent have no properties + * relating to visibility, the implementation of this method is + * left to subclasses. + * + * @return false + * + * @see #isVisible() + */ + public boolean isShowing() + { + return false; + } + + /** + * Returns true if the component is visible. A component may + * be visible but not drawn on the screen if one of its parent + * components is not visible. To determine if the component is + * actually drawn on screen, isShowing() should be + * used. + *
+ *
+ * As this abstract component has no properties relating to its + * visibility, the implementation of this method is left to subclasses. + * + * @return false + * + * @see #isShowing() + * @see #setVisible(boolean) + */ + public boolean isVisible() + { + return false; + } + + /** + * Removes the accessible child specified by the supplied index from + * the list of currently selected children. If the child specified + * is not selected, nothing happens. + *
+ *
+ * As the existence of children can not be determined from + * this abstract class, the implementation of this method + * is left to subclasses. + * + * @param index the index of the Accessible child + */ + public void removeAccessibleSelection(int index) + { + // Subclasses with children should implement this. + } + + /** + * Removes the specified focus listener from the list of registered + * focus listeners for this component. + * + * @param listener the listener to remove + */ + public void removeFocusListener(FocusListener listener) + { + // Remove the focus listener from the chain. + focusListener = AWTEventMulticaster.remove(focusListener, listener); + } + + /** + * Requests that this component gains focus. This depends on the + * component being focus traversable. + *
+ *
+ * As this abstract component has no properties relating to its + * focus traversability, or access to a peer with request focusing + * abilities, the implementation of this method is left to subclasses. + */ + public void requestFocus() + { + // Ignored. + } + + /** + * Selects all Accessible children of this component which + * it is possible to select. The component needs to support multiple + * selections. + *
+ *
+ * This abstract component provides a simplistic implementation of this + * method, which ignores the ability of the component to support multiple + * selections and simply uses addAccessibleSelection to + * add each Accessible child to the selection. The last + * Accessible component is thus selected for components + * which don't support multiple selections. Concrete implementations should + * override this with a more appopriate and efficient implementation, which + * properly takes into account the ability of the component to support multiple + * selections. + */ + public void selectAllAccessibleSelection() + { + // Simply call addAccessibleSelection() on all accessible children. + for (int a = 0; a < getAccessibleChildrenCount(); ++a) + { + addAccessibleSelection(a); + } + } + + /** + * Sets the background color of the component to that specified. + * Unspecified behaviour occurs when null is given as the new + * background color. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply ignores the supplied + * color and continues to use the default system color. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @param color the new color to use for the background + * + * @see #getBackground() + */ + public void setBackground(Color color) + { + // Ignored. + } + + /** + * Sets the height and width of the component, and its position + * relative to this component's parent, to the values specified + * by the supplied rectangle. Unspecified behaviour occurs when + * null is given as the new bounds. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply ignores the new + * rectangle and continues to return null from getBounds(). + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @param rectangle a rectangle which specifies the new bounds of + * the component + * + * @see #getBounds() + */ + public void setBounds(Rectangle rectangle) + { + // Ignored. + } + + /** + * Sets the Cursor used when the pointer is positioned over the + * component. Unspecified behaviour occurs when null is given as the new + * cursor. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply ignores the new cursor + * and continues to return the default system cursor. Concrete + * subclasses which handle the drawing of an onscreen menu component + * may override this method and provide the appropriate information. + * + * @param cursor the new cursor to use + * + * @see #getCursor() + */ + public void setCursor(Cursor cursor) + { + // Ignored. + } + + /** + * Sets the enabled/disabled state of this component. + *
+ *
+ * As this abstract component has no properties related to + * its enabled or disabled state, the implementation of this + * method is left to subclasses. + * + * @param enabled true if the component should be enabled, + * false otherwise + * + * @see #isEnabled() + */ + public void setEnabled(boolean enabled) + { + // Ignored. + } + + /** + * Sets the Font used for text created by this component. + * Unspecified behaviour occurs when null is given as the new + * font. + * + * @param font the new font to use for text. + * @see #getFont() + */ + public void setFont(Font font) + { + // Call the method of the enclosing component. + MenuComponent.this.setFont(font); + } + + /** + * Sets the foreground color of the component to that specified. + * Unspecified behaviour occurs when null is given as the new + * background color. + *
+ *
+ * This abstract class knows nothing about how the component + * is drawn on screen, so this method simply ignores the supplied + * color and continues to return the default system text color used + * for rendering menus. + * Concrete subclasses which handle the drawing of an onscreen + * menu component should override this method and provide + * the appropriate information. + * + * @param color the new foreground color + * + * @see #getForeground() + */ + public void setForeground(Color color) + { + // Ignored. + } + + /** + * Sets the location of the component, with co-ordinates + * relative to the parent component and using the co-ordinate + * space of the screen. Thus, the point (0,0) is the upper + * left corner of the parent component. + *
+ *
+ * Please note that this method depends on a correctly implemented + * version of the getBounds() method. Subclasses + * must provide the bounding rectangle via getBounds() + * in order for this method to work. + * + * @param point the location of the component, relative to its parent + * + * @see #getLocation() + */ + public void setLocation(Point point) + { + getBounds().setLocation(point); + } + + /** + * Sets the size of the component. + *
+ *
+ * Please note that this method depends on a correctly implemented + * version of the getBounds() method. Subclasses + * must provide the bounding rectangle via getBounds() + * in order for this method to work. + * + * @param size the new size of the component + * + * @see #getSize() + */ + public void setSize(Dimension size) + { + getBounds().setSize(size); + } + + /** + * Sets the visibility state of the component. A component may + * be visible but not drawn on the screen if one of its parent + * components is not visible. To determine if the component is + * actually drawn on screen, isShowing() should be + * used. + *
+ *
+ * As this abstract component has no properties relating to its + * visibility, the implementation of this method is left to subclasses. + * + * @param visibility the new visibility of the component -- true if + * the component is visible, false if not + * + * @see #isShowing() + * @see #isVisible() + */ + public void setVisible(boolean visibility) + { + // Ignored. + } + + } + +} diff --git a/libjava/classpath/java/awt/MenuContainer.java b/libjava/classpath/java/awt/MenuContainer.java new file mode 100644 index 000000000..c76ec96c2 --- /dev/null +++ b/libjava/classpath/java/awt/MenuContainer.java @@ -0,0 +1,71 @@ +/* MenuContainer.java -- container for menu items + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This interface is a container for menu components. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface MenuContainer +{ + /** + * Returns the font in use by this container. + * + * @return the menu font + */ + Font getFont(); + + /** + * Removes the specified menu component from the menu. + * + * @param component the menu component to remove + */ + void remove(MenuComponent component); + + /** + * Posts an event to the listeners. + * + * @param event the event to dispatch + * @deprecated use {@link MenuComponent#dispatchEvent(AWTEvent)} instead + */ + boolean postEvent(Event event); +} // interface MenuContainer diff --git a/libjava/classpath/java/awt/MenuItem.java b/libjava/classpath/java/awt/MenuItem.java new file mode 100644 index 000000000..52d0b4ddf --- /dev/null +++ b/libjava/classpath/java/awt/MenuItem.java @@ -0,0 +1,623 @@ +/* MenuItem.java -- An item in a menu + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.peer.MenuItemPeer; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleValue; + +/** + * This class represents an item in a menu. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class MenuItem extends MenuComponent + implements Serializable, Accessible +{ + +/* + * Static Variables + */ + + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_menuitem_number; + + // Serialization Constant + private static final long serialVersionUID = - 21757335363267194L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The name of the action command generated by this item. + * This is package-private to avoid an accessor method. + */ +String actionCommand; + +/** + * @serial Indicates whether or not this menu item is enabled. + * This is package-private to avoid an accessor method. + */ +boolean enabled = true; + +/** + * @serial The mask of events that are enabled for this menu item. + */ +long eventMask; + +/** + * @serial This menu item's label + * This is package-private to avoid an accessor method. + */ +String label = ""; + +/** + * @serial The shortcut for this menu item, if any + */ +private MenuShortcut shortcut; + +// The list of action listeners for this menu item. +private transient ActionListener action_listeners; + + protected class AccessibleAWTMenuItem + extends MenuComponent.AccessibleAWTMenuComponent + implements AccessibleAction, AccessibleValue + { + private static final long serialVersionUID = -217847831945965825L; + + /** Constructor */ + protected AccessibleAWTMenuItem() + { + super(); + } + + + + public String getAccessibleName() + { + return label; + } + + public AccessibleAction getAccessibleAction() + { + return this; + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.MENU_ITEM; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#getAccessibleActionCount() + */ + public int getAccessibleActionCount() + { + return 1; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#getAccessibleActionDescription(int) + */ + public String getAccessibleActionDescription(int i) + { + if (i == 0) + return label; + else + return null; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleAction#doAccessibleAction(int) + */ + public boolean doAccessibleAction(int i) + { + if (i != 0) + return false; + processActionEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand)); + return true; + } + + public AccessibleValue getAccessibleValue() + { + return this; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue() + */ + public Number getCurrentAccessibleValue() + { + return (enabled) ? new Integer(1) : new Integer(0); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number) + */ + public boolean setCurrentAccessibleValue(Number number) + { + boolean result = (number.intValue() != 0); + // this. is required by javac 1.3, otherwise it is confused with + // MenuItem.this.setEnabled. + this.setEnabled(result); + return result; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue() + */ + public Number getMinimumAccessibleValue() + { + return new Integer(0); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue() + */ + public Number getMaximumAccessibleValue() + { + return new Integer(0); + } + + } + + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of MenuItem with no label + * and no shortcut. + */ +public +MenuItem() +{ +} + +/*************************************************************************/ + +/** + * Initializes a new instance of MenuItem with the specified + * label and no shortcut. + * + * @param label The label for this menu item. + */ +public +MenuItem(String label) +{ + this.label = label; +} + +/*************************************************************************/ + +/** + * Initializes a new instance of MenuItem with the specified + * label and shortcut. + * + * @param label The label for this menu item. + * @param shortcut The shortcut for this menu item. + */ +public +MenuItem(String label, MenuShortcut shortcut) +{ + this.label = label; + this.shortcut = shortcut; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the label for this menu item, which may be null. + * + * @return The label for this menu item. + */ +public String +getLabel() +{ + return(label); +} + +/*************************************************************************/ + +/** + * This method sets the label for this menu to the specified value. + * + * @param label The new label for this menu item. + */ +public synchronized void +setLabel(String label) +{ + this.label = label; + if (peer != null) + { + MenuItemPeer mp = (MenuItemPeer) peer; + mp.setLabel (label); + } +} + +/*************************************************************************/ + +/** + * Tests whether or not this menu item is enabled. + * + * @return true if this menu item is enabled, false + * otherwise. + */ +public boolean +isEnabled() +{ + return(enabled); +} + +/*************************************************************************/ + +/** + * Sets the enabled status of this menu item. + * + * @param enabled true to enable this menu item, + * false otherwise. + */ +public synchronized void +setEnabled(boolean enabled) +{ + enable (enabled); +} + +/*************************************************************************/ + +/** + * Sets the enabled status of this menu item. + * + * @param enabled true to enable this menu item, + * false otherwise. + * + * @deprecated This method is deprecated in favor of setEnabled(). + */ +public void +enable(boolean enabled) +{ + if (enabled) + enable (); + else + disable (); +} + +/*************************************************************************/ + +/** + * Enables this menu item. + * + * @deprecated This method is deprecated in favor of setEnabled(). + */ +public void +enable() +{ + if (enabled) + return; + + this.enabled = true; + if (peer != null) + ((MenuItemPeer) peer).setEnabled (true); +} + +/*************************************************************************/ + +/** + * Disables this menu item. + * + * @deprecated This method is deprecated in favor of setEnabled(). + */ +public void +disable() +{ + if (!enabled) + return; + + this.enabled = false; + if (peer != null) + ((MenuItemPeer) peer).setEnabled (false); +} + +/*************************************************************************/ + +/** + * Returns the shortcut for this menu item, which may be null. + * + * @return The shortcut for this menu item. + */ +public MenuShortcut +getShortcut() +{ + return(shortcut); +} + +/*************************************************************************/ + +/** + * Sets the shortcut for this menu item to the specified value. This + * must be done before the native peer is created. + * + * @param shortcut The new shortcut for this menu item. + */ +public void +setShortcut(MenuShortcut shortcut) +{ + this.shortcut = shortcut; +} + +/*************************************************************************/ + +/** + * Deletes the shortcut for this menu item if one exists. This must be + * done before the native peer is created. + */ +public void +deleteShortcut() +{ + shortcut = null; +} + +/*************************************************************************/ + +/** + * Returns the name of the action command in the action events + * generated by this menu item. + * + * @return The action command name + */ +public String +getActionCommand() +{ + if (actionCommand == null) + return label; + else + return actionCommand; +} + +/*************************************************************************/ + +/** + * Sets the name of the action command in the action events generated by + * this menu item. + * + * @param actionCommand The new action command name. + */ +public void +setActionCommand(String actionCommand) +{ + this.actionCommand = actionCommand; +} + +/*************************************************************************/ + +/** + * Enables the specified events. This is done automatically when a + * listener is added and does not normally need to be done by + * application code. + * + * @param events The events to enable, which should be the bit masks + * from AWTEvent. + */ +protected final void +enableEvents(long events) +{ + eventMask |= events; + // TODO: see comment in Component.enableEvents(). +} + +/*************************************************************************/ + +/** + * Disables the specified events. + * + * @param events The events to enable, which should be the bit masks + * from AWTEvent. + */ +protected final void +disableEvents(long events) +{ + eventMask &= ~events; +} + +/*************************************************************************/ + +/** + * Creates the native peer for this object. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createMenuItem (this); +} + +/*************************************************************************/ + +/** + * Adds the specified listener to the list of registered action listeners + * for this component. + * + * @param listener The listener to add. + */ +public synchronized void +addActionListener(ActionListener listener) +{ + action_listeners = AWTEventMulticaster.add(action_listeners, listener); + + enableEvents(AWTEvent.ACTION_EVENT_MASK); +} + +public synchronized void +removeActionListener(ActionListener l) +{ + action_listeners = AWTEventMulticaster.remove(action_listeners, l); +} + + public synchronized ActionListener[] getActionListeners() + { + return (ActionListener[]) + AWTEventMulticaster.getListeners(action_listeners, + ActionListener.class); + } + +/** Returns all registered EventListers of the given listenerType. + * listenerType must be a subclass of EventListener, or a + * ClassClassException is thrown. + * @since 1.3 + */ + public T[] getListeners(Class listenerType) + { + if (listenerType == ActionListener.class) + return (T[]) getActionListeners(); + return (T[]) Array.newInstance(listenerType, 0); + } + +/*************************************************************************/ + +void +dispatchEventImpl(AWTEvent e) +{ + if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + + // Send the event to the parent menu if it has not yet been + // consumed. + if (!e.isConsumed ()) + ((Menu) getParent ()).processEvent (e); +} + +/** + * Processes the specified event by calling processActionEvent() + * if it is an instance of ActionEvent. + * + * @param event The event to process. + */ +protected void +processEvent(AWTEvent event) +{ + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); +} + +/*************************************************************************/ + +/** + * Processes the specified event by dispatching it to any registered listeners. + * + * @param event The event to process. + */ +protected void +processActionEvent(ActionEvent event) +{ + if (action_listeners != null) + { + event.setSource(this); + action_listeners.actionPerformed(event); + } +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +public String +paramString() +{ + return ("label=" + label + ",enabled=" + enabled + + ",actionCommand=" + actionCommand + "," + super.paramString()); +} + +/** + * Gets the AccessibleContext associated with this MenuItem. + * The context is created, if necessary. + * + * @return the associated context + */ +public AccessibleContext getAccessibleContext() +{ + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTMenuItem(); + return accessibleContext; +} + +/** + * Generate a unique name for this MenuItem. + * + * @return A unique name for this MenuItem. + */ +String generateName() +{ + return "menuitem" + getUniqueLong(); +} + +private static synchronized long getUniqueLong() +{ + return next_menuitem_number++; +} + +} // class MenuItem diff --git a/libjava/classpath/java/awt/MenuShortcut.java b/libjava/classpath/java/awt/MenuShortcut.java new file mode 100644 index 000000000..4939438f1 --- /dev/null +++ b/libjava/classpath/java/awt/MenuShortcut.java @@ -0,0 +1,434 @@ +/* MenuShortcut.java -- A class for menu accelerators + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This class implements a keyboard accelerator for a menu item. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class MenuShortcut implements java.io.Serializable +{ + +/* + * Static Variables + */ + +// Serialization Constant +private static final long serialVersionUID = 143448358473180225L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The virtual keycode for the shortcut. + */ +private int key; + +/** + * @serial true if the shift key was used with this shortcut, + * or false otherwise. + */ +private boolean usesShift; + +private String keyName; + +/*************************************************************************/ + +/** + * Initializes a new instance of MenuShortcut with the + * specified virtual key value. + * + * @param key The virtual keycode for the shortcut. + */ +public +MenuShortcut(int key) +{ + this(key, false); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of MenuShortcut with the + * specified virtual key value and shift setting. + * + * @param key The virtual keycode for the shortcut. + * @param usesShift true if the shift key was pressed, + * false otherwise. + */ +public +MenuShortcut(int key, boolean usesShift) +{ + this.key = key; + this.usesShift = usesShift; + setKeyName(key); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the virtual keycode for this shortcut. + * + * @return The virtual keycode for this shortcut. + */ +public int +getKey() +{ + return(key); +} + +/*************************************************************************/ + +/** + * Returns the shift setting for this shortcut. + * + * @return true if the shift key was pressed, false + * otherwise. + */ +public boolean +usesShiftModifier() +{ + return(usesShift); +} + +/*************************************************************************/ + +/** + * Tests this object for equality against the specified object. The two + * objects will be considered equal if and only if the specified object + * is an instance of MenuShortcut and has the same key value + * and shift setting as this object. + * + * @param obj The object to test for equality against. + * + * @return true if the two objects are equal, false + * otherwise. + */ +public boolean +equals(MenuShortcut obj) +{ + if (obj == null) + return(false); + + if (obj.key != this.key) + return(false); + + if (obj.usesShift != this.usesShift) + return(false); + + return(true); +} + +public boolean +equals(Object obj) +{ + if (obj instanceof MenuShortcut) + { + MenuShortcut ms = (MenuShortcut) obj; + return (ms.key == key && ms.usesShift == usesShift); + } + return false; +} + +/*************************************************************************/ + +/** + * Returns a string representation of this shortcut. + * + * @return A string representation of this shortcut. + */ +public String +toString() +{ + String temp = "Ctrl+"; + if (usesShift) + temp = temp + "Shift+"; + temp = temp + keyName; + return temp; +} + +public int +hashCode() +{ + // Arbitrary. + return key + (usesShift ? 23 : 57); +} + +/*************************************************************************/ + +/** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ +protected String +paramString() +{ + return "key=" + key + ",usesShift=" + usesShift; +} + +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/libjava/classpath/java/awt/MouseInfo.java b/libjava/classpath/java/awt/MouseInfo.java new file mode 100644 index 000000000..62d7d3068 --- /dev/null +++ b/libjava/classpath/java/awt/MouseInfo.java @@ -0,0 +1,102 @@ +/* MouseInfo.java -- utility methods for mice. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; +import java.awt.peer.MouseInfoPeer; + +/** + * MouseInfo is a class containing utility functions for mouse information. + * + * @author Sven de Marothy + * @since 1.5 + */ +public class MouseInfo +{ + private static MouseInfoPeer peer; + + /** + * Private constructor to prevent instance creation. + */ + private MouseInfo() + { + } + + /** + * Returns a PointerInfo object containing information about the current + * location of the mouse pointer + * + * @throws HeadlessException if the current GraphicsEnvironment is headless. + * @return a PointerInfo object. + */ + public static PointerInfo getPointerInfo() throws HeadlessException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission( new AWTPermission("watchMousePointer") ); + + if( GraphicsEnvironment.isHeadless() ) + throw new HeadlessException(); + + if( peer == null ) + peer = Toolkit.getDefaultToolkit().getMouseInfoPeer(); + + Point p = new Point(); + int screen = peer.fillPointWithCoords( p ); + + GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment(). + getScreenDevices(); + + return new PointerInfo( gds[ screen ], p ); + } + + /** + * Returns the number of mouse buttons, or -1 if no mouse is connected. + * (mentioned in the 1.5 release notes) + * + * @throws HeadlessException if the current GraphicsEnvironment is headless. + * @return an integer number of buttons. + */ + public static int getNumberOfButtons() throws HeadlessException + { + if( GraphicsEnvironment.isHeadless() ) + throw new HeadlessException(); + return ((ClasspathToolkit)Toolkit.getDefaultToolkit()). + getMouseNumberOfButtons(); + } +} diff --git a/libjava/classpath/java/awt/PageAttributes.java b/libjava/classpath/java/awt/PageAttributes.java new file mode 100644 index 000000000..825d0feb3 --- /dev/null +++ b/libjava/classpath/java/awt/PageAttributes.java @@ -0,0 +1,482 @@ +/* PageAttributes.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.util.Locale; + +/** + * Missing Documentation + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4, but missing documentation + */ +public final class PageAttributes implements Cloneable +{ + public static final class ColorType extends AttributeValue + { + private static final String[] NAMES = { "color", "monochrome" }; + public static final ColorType COLOR = new ColorType(0); + public static final ColorType MONOCHROME = new ColorType(1); + private ColorType(int value) + { + super(value, NAMES); + } + } // class ColorType + public static final class MediaType extends AttributeValue + { + private static final String[] NAMES + = { "iso-4a0", "iso-2a0", "iso-a0", "iso-a1", "iso-a2", "iso-a3", + "iso-a4", "iso-a5", "iso-a6", "iso-a7", "iso-a8", "iso-a9", + "iso-a10", "iso-b0", "iso-b1", "iso-b2", "iso-b3", "iso-b4", + "iso-b5", "iso-b6", "iso-b7", "iso-b8", "iso-b9", "iso-b10", + "jis-b0", "jis-b1", "jis-b2", "jis-b3", "jis-b4", "jis-b5", + "jis-b6", "jis-b7", "jis-b8", "jis-b9", "jis-b10", "iso-c0", + "iso-c1", "iso-c2", "iso-c3", "iso-c4", "iso-c5", "iso-c6", + "iso-c7", "iso-c8", "iso-c9", "iso-c10", "iso-designated-long", + "executive", "folio", "invoice", "ledger", "na-letter", "na-legal", + "quarto", "a", "b", "c", "d", "e", "na-10x15-envelope", + "na-10x14-envelope", "na-10x13-envelope", "na-9x12-envelope", + "na-9x11-envelope", "na-7x9-envelope", "na-6x9-envelope", + "na-number-9-envelope", "na-number-10-envelope", + "na-number-11-envelope", "na-number-12-envelope", + "na-number-14-envelope", "invite-envelope", "italy-envelope", + "monarch-envelope", "personal-envelope" }; + public static final MediaType ISO_4A0 = new MediaType(0); + public static final MediaType ISO_2A0 = new MediaType(1); + public static final MediaType ISO_A0 = new MediaType(2); + public static final MediaType ISO_A1 = new MediaType(3); + public static final MediaType ISO_A2 = new MediaType(4); + public static final MediaType ISO_A3 = new MediaType(5); + public static final MediaType ISO_A4 = new MediaType(6); + public static final MediaType ISO_A5 = new MediaType(7); + public static final MediaType ISO_A6 = new MediaType(8); + public static final MediaType ISO_A7 = new MediaType(9); + public static final MediaType ISO_A8 = new MediaType(10); + public static final MediaType ISO_A9 = new MediaType(11); + public static final MediaType ISO_A10 = new MediaType(12); + public static final MediaType ISO_B0 = new MediaType(13); + public static final MediaType ISO_B1 = new MediaType(14); + public static final MediaType ISO_B2 = new MediaType(15); + public static final MediaType ISO_B3 = new MediaType(16); + public static final MediaType ISO_B4 = new MediaType(17); + public static final MediaType ISO_B5 = new MediaType(18); + public static final MediaType ISO_B6 = new MediaType(19); + public static final MediaType ISO_B7 = new MediaType(20); + public static final MediaType ISO_B8 = new MediaType(21); + public static final MediaType ISO_B9 = new MediaType(22); + public static final MediaType ISO_B10 = new MediaType(23); + public static final MediaType JIS_B0 = new MediaType(24); + public static final MediaType JIS_B1 = new MediaType(25); + public static final MediaType JIS_B2 = new MediaType(26); + public static final MediaType JIS_B3 = new MediaType(27); + public static final MediaType JIS_B4 = new MediaType(28); + public static final MediaType JIS_B5 = new MediaType(29); + public static final MediaType JIS_B6 = new MediaType(30); + public static final MediaType JIS_B7 = new MediaType(31); + public static final MediaType JIS_B8 = new MediaType(32); + public static final MediaType JIS_B9 = new MediaType(33); + public static final MediaType JIS_B10 = new MediaType(34); + public static final MediaType ISO_C0 = new MediaType(35); + public static final MediaType ISO_C1 = new MediaType(36); + public static final MediaType ISO_C2 = new MediaType(37); + public static final MediaType ISO_C3 = new MediaType(38); + public static final MediaType ISO_C4 = new MediaType(39); + public static final MediaType ISO_C5 = new MediaType(40); + public static final MediaType ISO_C6 = new MediaType(41); + public static final MediaType ISO_C7 = new MediaType(42); + public static final MediaType ISO_C8 = new MediaType(43); + public static final MediaType ISO_C9 = new MediaType(44); + public static final MediaType ISO_C10 = new MediaType(45); + public static final MediaType ISO_DESIGNATED_LONG = new MediaType(46); + public static final MediaType EXECUTIVE = new MediaType(47); + public static final MediaType FOLIO = new MediaType(48); + public static final MediaType INVOICE = new MediaType(49); + public static final MediaType LEDGER = new MediaType(50); + public static final MediaType NA_LETTER = new MediaType(51); + public static final MediaType NA_LEGAL = new MediaType(52); + public static final MediaType QUARTO = new MediaType(53); + public static final MediaType A = new MediaType(54); + public static final MediaType B = new MediaType(55); + public static final MediaType C = new MediaType(56); + public static final MediaType D = new MediaType(57); + public static final MediaType E = new MediaType(58); + public static final MediaType NA_10X15_ENVELOPE = new MediaType(59); + public static final MediaType NA_10X14_ENVELOPE = new MediaType(60); + public static final MediaType NA_10X13_ENVELOPE = new MediaType(61); + public static final MediaType NA_9X12_ENVELOPE = new MediaType(62); + public static final MediaType NA_9X11_ENVELOPE = new MediaType(63); + public static final MediaType NA_7X9_ENVELOPE = new MediaType(64); + public static final MediaType NA_6X9_ENVELOPE = new MediaType(65); + public static final MediaType NA_NUMBER_9_ENVELOPE = new MediaType(66); + public static final MediaType NA_NUMBER_10_ENVELOPE = new MediaType(67); + public static final MediaType NA_NUMBER_11_ENVELOPE = new MediaType(68); + public static final MediaType NA_NUMBER_12_ENVELOPE = new MediaType(69); + public static final MediaType NA_NUMBER_14_ENVELOPE = new MediaType(70); + public static final MediaType INVITE_ENVELOPE = new MediaType(71); + public static final MediaType ITALY_ENVELOPE = new MediaType(72); + public static final MediaType MONARCH_ENVELOPE = new MediaType(73); + public static final MediaType PERSONAL_ENVELOPE = new MediaType(74); + public static final MediaType A0 = ISO_A0; + public static final MediaType A1 = ISO_A1; + public static final MediaType A2 = ISO_A2; + public static final MediaType A3 = ISO_A3; + public static final MediaType A4 = ISO_A4; + public static final MediaType A5 = ISO_A5; + public static final MediaType A6 = ISO_A6; + public static final MediaType A7 = ISO_A7; + public static final MediaType A8 = ISO_A8; + public static final MediaType A9 = ISO_A9; + public static final MediaType A10 = ISO_A10; + public static final MediaType B0 = ISO_B0; + public static final MediaType B1 = ISO_B1; + public static final MediaType B2 = ISO_B2; + public static final MediaType B3 = ISO_B3; + public static final MediaType B4 = ISO_B4; + public static final MediaType ISO_B4_ENVELOPE = ISO_B4; + public static final MediaType B5 = ISO_B5; + public static final MediaType ISO_B5_ENVELOPE = ISO_B4; + public static final MediaType B6 = ISO_B6; + public static final MediaType B7 = ISO_B7; + public static final MediaType B8 = ISO_B8; + public static final MediaType B9 = ISO_B9; + public static final MediaType B10 = ISO_B10; + public static final MediaType C0 = ISO_B0; + public static final MediaType ISO_C0_ENVELOPE = ISO_C0; + public static final MediaType C1 = ISO_C1; + public static final MediaType ISO_C1_ENVELOPE = ISO_C1; + public static final MediaType C2 = ISO_C2; + public static final MediaType ISO_C2_ENVELOPE = ISO_C2; + public static final MediaType C3 = ISO_C3; + public static final MediaType ISO_C3_ENVELOPE = ISO_C3; + public static final MediaType C4 = ISO_C4; + public static final MediaType ISO_C4_ENVELOPE = ISO_C4; + public static final MediaType C5 = ISO_C5; + public static final MediaType ISO_C5_ENVELOPE = ISO_C5; + public static final MediaType C6 = ISO_C6; + public static final MediaType ISO_C6_ENVELOPE = ISO_C6; + public static final MediaType C7 = ISO_C7; + public static final MediaType ISO_C7_ENVELOPE = ISO_C7; + public static final MediaType C8 = ISO_C8; + public static final MediaType ISO_C8_ENVELOPE = ISO_C8; + public static final MediaType C9 = ISO_C9; + public static final MediaType ISO_C9_ENVELOPE = ISO_C9; + public static final MediaType C10 = ISO_C10; + public static final MediaType ISO_C10_ENVELOPE = ISO_C10; + public static final MediaType ISO_DESIGNATED_LONG_ENVELOPE + = ISO_DESIGNATED_LONG; + public static final MediaType STATEMENT = INVOICE; + public static final MediaType TABLOID = LEDGER; + public static final MediaType LETTER = NA_LETTER; + public static final MediaType NOTE = NA_LETTER; + public static final MediaType LEGAL = NA_LEGAL; + public static final MediaType ENV_10X15 = NA_10X15_ENVELOPE; + public static final MediaType ENV_10X14 = NA_10X14_ENVELOPE; + public static final MediaType ENV_10X13 = NA_10X13_ENVELOPE; + public static final MediaType ENV_9X12 = NA_9X12_ENVELOPE; + public static final MediaType ENV_9X11 = NA_9X11_ENVELOPE; + public static final MediaType ENV_7X9 = NA_7X9_ENVELOPE; + public static final MediaType ENV_6X9 = NA_6X9_ENVELOPE; + public static final MediaType ENV_9 = NA_NUMBER_9_ENVELOPE; + public static final MediaType ENV_10 = NA_NUMBER_10_ENVELOPE; + public static final MediaType ENV_11 = NA_NUMBER_11_ENVELOPE; + public static final MediaType ENV_12 = NA_NUMBER_12_ENVELOPE; + public static final MediaType ENV_14 = NA_NUMBER_14_ENVELOPE; + public static final MediaType ENV_INVITE = INVITE_ENVELOPE; + public static final MediaType ENV_ITALY = ITALY_ENVELOPE; + public static final MediaType ENV_MONARCH = MONARCH_ENVELOPE; + public static final MediaType ENV_PERSONAL = PERSONAL_ENVELOPE; + public static final MediaType INVITE = INVITE_ENVELOPE; + public static final MediaType ITALY = ITALY_ENVELOPE; + public static final MediaType MONARCH = MONARCH_ENVELOPE; + public static final MediaType PERSONAL = PERSONAL_ENVELOPE; + private MediaType(int value) + { + super(value, NAMES); + } + } // class MediaType + public static final class OrientationRequestedType extends AttributeValue + { + private static final String[] NAMES = { "portrait", "landscape" }; + public static final OrientationRequestedType PORTRAIT + = new OrientationRequestedType(0); + public static final OrientationRequestedType LANDSCAPE + = new OrientationRequestedType(1); + private OrientationRequestedType(int value) + { + super(value, NAMES); + } + } // class OrientationRequestedType + public static final class OriginType extends AttributeValue + { + private static final String[] NAMES = { "physical", "printable" }; + public static final OriginType PHYSICAL = new OriginType(0); + public static final OriginType PRINTABLE = new OriginType(1); + private OriginType(int value) + { + super(value, NAMES); + } + } // class OriginType + public static final class PrintQualityType extends AttributeValue + { + private static final String[] NAMES = { "high", "normal", "draft" }; + public static final PrintQualityType HIGH = new PrintQualityType(0); + public static final PrintQualityType NORMAL = new PrintQualityType(1); + public static final PrintQualityType DRAFT = new PrintQualityType(2); + private PrintQualityType(int value) + { + super(value, NAMES); + } + } // class PrintQualityType + + + private ColorType color; + private MediaType media; + private OrientationRequestedType orientation; + private OriginType origin; + private PrintQualityType quality; + private int resolutionX; + private int resolutionY; + private int resolutionScale; + public PageAttributes() + { + color = ColorType.MONOCHROME; + setMediaToDefault(); + orientation = OrientationRequestedType.PORTRAIT; + origin = OriginType.PHYSICAL; + quality = PrintQualityType.NORMAL; + setPrinterResolutionToDefault(); + } + + public PageAttributes(PageAttributes attr) + { + set(attr); + } + + public PageAttributes(ColorType color, MediaType media, + OrientationRequestedType orientation, + OriginType origin, PrintQualityType quality, + int[] resolution) + { + if (color == null || media == null || orientation == null + || origin == null || quality == null) + throw new IllegalArgumentException(); + setPrinterResolution(resolution); + this.color = color; + this.media = media; + this.orientation = orientation; + this.origin = origin; + this.quality = quality; + } + + public Object clone() + { + return new PageAttributes(this); + } + + public void set(PageAttributes attr) + { + color = attr.color; + media = attr.media; + orientation = attr.orientation; + origin = attr.origin; + quality = attr.quality; + resolutionX = attr.resolutionX; + resolutionY = attr.resolutionY; + resolutionScale = attr.resolutionScale; + } + + public ColorType getColor() + { + return color; + } + + public void setColor(ColorType color) + { + if (color == null) + throw new IllegalArgumentException(); + this.color = color; + } + + public MediaType getMedia() + { + return media; + } + + public void setMedia(MediaType media) + { + if (media == null) + throw new IllegalArgumentException(); + this.media = media; + } + + public void setMediaToDefault() + { + String country = Locale.getDefault().getCountry(); + media = ("US".equals(country) || "CA".equals(country)) ? MediaType.LETTER + : MediaType.A4; + } + + public OrientationRequestedType getOrientationRequested() + { + return orientation; + } + + public void setOrientationRequested(OrientationRequestedType orientation) + { + if (orientation == null) + throw new IllegalArgumentException(); + this.orientation = orientation; + } + + public void setOrientationRequested(int orientation) + { + if (orientation == 3) + this.orientation = OrientationRequestedType.PORTRAIT; + else if (orientation == 4) + this.orientation = OrientationRequestedType.LANDSCAPE; + else + throw new IllegalArgumentException(); + } + + public void setOrientationRequestedToDefault() + { + orientation = OrientationRequestedType.PORTRAIT; + } + + public OriginType getOrigin() + { + return origin; + } + + public void setOrigin(OriginType origin) + { + if (origin == null) + throw new IllegalArgumentException(); + this.origin = origin; + } + + public PrintQualityType getPrintQuality() + { + return quality; + } + + public void setPrintQuality(PrintQualityType quality) + { + if (quality == null) + throw new IllegalArgumentException(); + this.quality = quality; + } + + public void setPrintQuality(int quality) + { + if (quality == 3) + this.quality = PrintQualityType.DRAFT; + else if (quality == 4) + this.quality = PrintQualityType.NORMAL; + else if (quality == 5) + this.quality = PrintQualityType.HIGH; + else + throw new IllegalArgumentException(); + } + + public void setPrintQualityToDefault() + { + quality = PrintQualityType.NORMAL; + } + + public int[] getPrinterResolution() + { + return new int[] { resolutionX, resolutionY, resolutionScale }; + } + + public void setPrinterResolution(int[] resolution) + { + if (resolution == null || resolution.length != 3 || resolution[0] <= 0 + || resolution[1] <= 0 || resolution[2] < 3 || resolution[2] > 4) + throw new IllegalArgumentException(); + resolutionX = resolution[0]; + resolutionY = resolution[1]; + resolutionScale = resolution[2]; + } + + public void setPrinterResolution(int resolution) + { + if (resolution <= 0) + throw new IllegalArgumentException(); + resolutionX = resolution; + resolutionY = resolution; + resolutionScale = 3; + } + + public void setPrinterResolutionToDefault() + { + resolutionX = 72; + resolutionY = 72; + resolutionScale = 3; + } + + public boolean equals(Object o) + { + if (this == o) + return true; + if (! (o instanceof PageAttributes)) + return false; + PageAttributes pa = (PageAttributes) o; + return color == pa.color && media == pa.media + && orientation == pa.orientation && origin == pa.origin + && quality == pa.quality && resolutionX == pa.resolutionX + && resolutionY == pa.resolutionY + && resolutionScale == pa.resolutionScale; + } + public int hashCode() + { + return (color.value << 31) ^ (media.value << 24) + ^ (orientation.value << 23) ^ (origin.value << 22) + ^ (quality.value << 20) ^ (resolutionScale << 19) + ^ (resolutionY << 10) ^ resolutionX; + } + public String toString() + { + return "color=" + color + ",media=" + media + ",orientation-requested=" + + orientation + ",origin=" + origin + ",print-quality=" + quality + + ",printer-resolution=[" + resolutionX + ',' + resolutionY + ',' + + resolutionScale + ']'; + } +} // class PageAttributes diff --git a/libjava/classpath/java/awt/Paint.java b/libjava/classpath/java/awt/Paint.java new file mode 100644 index 000000000..0f099cc0b --- /dev/null +++ b/libjava/classpath/java/awt/Paint.java @@ -0,0 +1,79 @@ +/* Paint.java -- generate colors for Graphics2D operations + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; + +/** + * Defines how color patterns are generated for Graphics2D operations. This + * is used to perform the draw and fill methods + * of the graphics object. Instances must be immutable, because the graphics + * object does not clone them. + * + * @author Warren Levy (warrenl@cygnus.com) + * @see PaintContext + * @see Color + * @see GradientPaint + * @see TexturePaint + * @see Graphics2D#setPaint(Paint) + * @since 1.1 + * @status updated to 1.4 + */ +public interface Paint extends Transparency +{ + /** + * Create the context necessary for performing the color pattern generation. + * The color model is a hint, and may be null for Classpath implementations; + * however some legacy code may throw a NullPointerException when passed a + * null. Leaving the color model null provides the most efficiency and leeway + * in the generation of the color pattern. + * + * @param cm the color model, used as a hint + * @param deviceBounds the device space bounding box of the painted area + * @param userBounds the user space bounding box of the painted area + * @param xform the transformation from user space to device space + * @param hints any hints for choosing between rendering alternatives + * @return the context for performing the paint + */ + PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, AffineTransform xform, + RenderingHints hints); +} // interface Paint diff --git a/libjava/classpath/java/awt/PaintContext.java b/libjava/classpath/java/awt/PaintContext.java new file mode 100644 index 000000000..3d5fdcdf0 --- /dev/null +++ b/libjava/classpath/java/awt/PaintContext.java @@ -0,0 +1,76 @@ +/* PaintContext.java -- the environment for performing a paint operation + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.image.ColorModel; +import java.awt.image.Raster; + +/** + * @author Warren Levy (warrenl@cygnus.com) + * @see Paint + * @since 1.1 + * @status updated to 1.4 + */ +public interface PaintContext +{ + /** + * Release the resources allocated for the paint. + */ + void dispose(); + + /** + * Return the color model of this context. It may be different from the + * hint specified during createContext, as not all contexts can generate + * color patterns in an arbitrary model. + * + * @return the context color model + */ + ColorModel getColorModel(); + + /** + * Return a raster containing the colors for the graphics operation. + * + * @param x the x-coordinate, in device space + * @param y the y-coordinate, in device space + * @param w the width, in device space + * @param h the height, in device space + * @return a raster for the given area and color + */ + Raster getRaster(int x, int y, int w, int h); +} // interface PaintContext diff --git a/libjava/classpath/java/awt/Panel.java b/libjava/classpath/java/awt/Panel.java new file mode 100644 index 000000000..cc17eef22 --- /dev/null +++ b/libjava/classpath/java/awt/Panel.java @@ -0,0 +1,173 @@ +/* Panel.java -- Simple container object + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * A panel is a simple container class. It's default layout is the + * FlowLayout manager. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see FlowLayout + * @since 1.0 + * @status updated to 1.4 + */ +public class Panel extends Container implements Accessible +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2728009084054400034L; + + /** The cached accessible context. */ + private transient AccessibleContext context; + + /** Flag set when the first system-requested paint event is + dispatched. */ + private transient boolean initialSystemUpdateDone; + + /** Flag set when the first application-requested paint event is + consumed. */ + private transient boolean initialUpdateConsumed; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_panel_number; + + /** + * Initializes a new instance of Panel that has a default + * layout manager of FlowLayout. + */ + public Panel() + { + this(new FlowLayout()); + } + + /** + * Initializes a new instance of Panel with the specified + * layout manager. + * + * @param layoutManager the layout manager for this object + * @since 1.1 + */ + public Panel(LayoutManager layoutManager) + { + setLayout(layoutManager); + } + + /** + * Notifies this object to create its native peer. + * + * @see #isDisplayable() + * @see #removeNotify() + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit().createPanel(this); + super.addNotify(); + } + + /** + * Gets the AccessibleContext associated with this panel, creating one if + * necessary. This always returns an instance of {@link AccessibleAWTPanel}. + * + * @return the accessibility context of this panel + * @since 1.3 + */ + public AccessibleContext getAccessibleContext() + { + if (context == null) + context = new AccessibleAWTPanel(); + return context; + } + + /** + * This class provides accessibility support for Panels, and is the + * runtime type returned by {@link #getAccessibleContext()}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + */ + protected class AccessibleAWTPanel extends AccessibleAWTContainer + { + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -6409552226660031050L; + + /** + * The default constructor. + */ + protected AccessibleAWTPanel() + { + } + + /** + * Get the role of this accessible object, a panel. + * + * @return the role of the object + * @see AccessibleRole#PANEL + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.PANEL; + } + } + + /** + * Generate a unique name for this panel. + * + * @return A unique name for this panel. + */ + String generateName () + { + return "panel" + getUniqueLong (); + } + + private static synchronized long getUniqueLong () + { + return next_panel_number++; + } +} diff --git a/libjava/classpath/java/awt/Point.java b/libjava/classpath/java/awt/Point.java new file mode 100644 index 000000000..871202491 --- /dev/null +++ b/libjava/classpath/java/awt/Point.java @@ -0,0 +1,250 @@ +/* Point.java -- represents a point in 2-D space + Copyright (C) 1999, 2002, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.Point2D; +import java.io.Serializable; + +/** + * This class represents a point on the screen using cartesian coordinates. + * Remember that in screen coordinates, increasing x values go from left to + * right, and increasing y values go from top to bottom. + * + *

There are some public fields; if you mess with them in an inconsistent + * manner, it is your own fault when you get invalid results. Also, this + * class is not threadsafe. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Point extends Point2D implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5276940640259749850L; + + /** + * The x coordinate. + * + * @see #getLocation() + * @see #move(int, int) + * @serial the X coordinate of the point + */ + public int x; + + /** + * The y coordinate. + * + * @see #getLocation() + * @see #move(int, int) + * @serial The Y coordinate of the point + */ + public int y; + + /** + * Initializes a new instance of Point representing the + * coordinates (0, 0). + * + * @since 1.1 + */ + public Point() + { + } + + /** + * Initializes a new instance of Point with coordinates + * identical to the coordinates of the specified point. + * + * @param p the point to copy the coordinates from + * @throws NullPointerException if p is null + */ + public Point(Point p) + { + x = p.x; + y = p.y; + } + + /** + * Initializes a new instance of Point with the specified + * coordinates. + * + * @param x the X coordinate + * @param y the Y coordinate + */ + public Point(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * Get the x coordinate. + * + * @return the value of x, as a double + */ + public double getX() + { + return x; + } + + /** + * Get the y coordinate. + * + * @return the value of y, as a double + */ + public double getY() + { + return y; + } + + /** + * Returns the location of this point. A pretty useless method, as this + * is already a point. + * + * @return a copy of this point + * @see #setLocation(Point) + * @since 1.1 + */ + public Point getLocation() + { + return new Point(x, y); + } + + /** + * Sets this object's coordinates to match those of the specified point. + * + * @param p the point to copy the coordinates from + * @throws NullPointerException if p is null + * @since 1.1 + */ + public void setLocation(Point p) + { + x = p.x; + y = p.y; + } + + /** + * Sets this object's coordinates to the specified values. This method + * is identical to the move() method. + * + * @param x the new X coordinate + * @param y the new Y coordinate + */ + public void setLocation(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * Sets this object's coordinates to the specified values. This method + * rounds to the nearest integer coordinates by adding 0.5 and calling + * {@link Math#floor(double)}. + * + * @param x the new X coordinate + * @param y the new Y coordinate + */ + public void setLocation(double x, double y) + { + this.x = (int) Math.floor(x + 0.5); + this.y = (int) Math.floor(y + 0.5); + } + + /** + * Sets this object's coordinates to the specified values. This method + * is identical to the setLocation(int, int) method. + * + * @param x the new X coordinate + * @param y the new Y coordinate + */ + public void move(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * Changes the coordinates of this point such that the specified + * dx parameter is added to the existing X coordinate and + * dy is added to the existing Y coordinate. + * + * @param dx the amount to add to the X coordinate + * @param dy the amount to add to the Y coordinate + */ + public void translate(int dx, int dy) + { + x += dx; + y += dy; + } + + /** + * Tests whether or not this object is equal to the specified object. + * This will be true if and only if the specified object is an instance + * of Point2D and has the same X and Y coordinates. + * + * @param obj the object to test against for equality + * @return true if the specified object is equal + */ + public boolean equals(Object obj) + { + // NOTE: No special hashCode() method is required for this class, + // as this equals() implementation is functionally equivalent to + // super.equals(), which does define a proper hashCode(). + + if (! (obj instanceof Point2D)) + return false; + Point2D p = (Point2D) obj; + return x == p.getX() && y == p.getY(); + } + + /** + * Returns a string representation of this object. The format is: + * getClass().getName() + "[x=" + x + ",y=" + y + ']'. + * + * @return a string representation of this object + */ + public String toString() + { + return getClass().getName() + "[x=" + x + ",y=" + y + ']'; + } +} // class Point diff --git a/libjava/classpath/java/awt/PointerInfo.java b/libjava/classpath/java/awt/PointerInfo.java new file mode 100644 index 000000000..2bbd97677 --- /dev/null +++ b/libjava/classpath/java/awt/PointerInfo.java @@ -0,0 +1,84 @@ +/* PointerInfo.java -- mouse pointer data + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt; + +/** + * PointerInfo represents information about the mouse pointer, + * i.e. its GraphicsDevice and location. + * + * PointerInfo objects cannot be instantiated directly, but are + * retrieved from MouseInfo.getPointerInfo(). PointerInfo objects + * are immutable and will not be updated for future mouse motions. + * + * @since 1.5 + * @author Sven de Marothy + */ +public class PointerInfo +{ + private GraphicsDevice gd; + private Point p; + + /** + * Package-private constructor used by MouseInfo. + */ + PointerInfo( GraphicsDevice gd, Point p ) + { + this.gd = gd; + this.p = p; + } + + /** + * Returns the GraphicsDevice on which the mouse pointer was located + * + * @return a GraphicsDevice object. + */ + public GraphicsDevice getDevice() + { + return gd; + } + + /** + * Returns the coordinates of the mouse pointer. + * + * @return a Point object containing the pointer coordinates. + */ + public Point getLocation() + { + return p; + } +} diff --git a/libjava/classpath/java/awt/Polygon.java b/libjava/classpath/java/awt/Polygon.java new file mode 100644 index 000000000..03e91f8bd --- /dev/null +++ b/libjava/classpath/java/awt/Polygon.java @@ -0,0 +1,611 @@ +/* Polygon.java -- class representing a polygon + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +/** + * This class represents a polygon, a closed, two-dimensional region in a + * coordinate space. The region is bounded by an arbitrary number of line + * segments, between (x,y) coordinate vertices. The polygon has even-odd + * winding, meaning that a point is inside the shape if it crosses the + * boundary an odd number of times on the way to infinity. + * + *

There are some public fields; if you mess with them in an inconsistent + * manner, it is your own fault when you get NullPointerException, + * ArrayIndexOutOfBoundsException, or invalid results. Also, this class is + * not threadsafe. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Polygon implements Shape, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -6460061437900069969L; + + /** + * This total number of endpoints. + * + * @serial the number of endpoints, possibly less than the array sizes + */ + public int npoints; + + /** + * The array of X coordinates of endpoints. This should not be null. + * + * @see #addPoint(int, int) + * @serial the x coordinates + */ + public int[] xpoints; + + /** + * The array of Y coordinates of endpoints. This should not be null. + * + * @see #addPoint(int, int) + * @serial the y coordinates + */ + public int[] ypoints; + + /** + * The bounding box of this polygon. This is lazily created and cached, so + * it must be invalidated after changing points. + * + * @see #getBounds() + * @serial the bounding box, or null + */ + protected Rectangle bounds; + + /** A big number, but not so big it can't survive a few float operations */ + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + + /** + * Initializes an empty polygon. + */ + public Polygon() + { + // Leave room for growth. + xpoints = new int[4]; + ypoints = new int[4]; + } + + /** + * Create a new polygon with the specified endpoints. The arrays are copied, + * so that future modifications to the parameters do not affect the polygon. + * + * @param xpoints the array of X coordinates for this polygon + * @param ypoints the array of Y coordinates for this polygon + * @param npoints the total number of endpoints in this polygon + * @throws NegativeArraySizeException if npoints is negative + * @throws IndexOutOfBoundsException if npoints exceeds either array + * @throws NullPointerException if xpoints or ypoints is null + */ + public Polygon(int[] xpoints, int[] ypoints, int npoints) + { + this.xpoints = new int[npoints]; + this.ypoints = new int[npoints]; + System.arraycopy(xpoints, 0, this.xpoints, 0, npoints); + System.arraycopy(ypoints, 0, this.ypoints, 0, npoints); + this.npoints = npoints; + } + + /** + * Reset the polygon to be empty. The arrays are left alone, to avoid object + * allocation, but the number of points is set to 0, and all cached data + * is discarded. If you are discarding a huge number of points, it may be + * more efficient to just create a new Polygon. + * + * @see #invalidate() + * @since 1.4 + */ + public void reset() + { + npoints = 0; + invalidate(); + } + + /** + * Invalidate or flush all cached data. After direct manipulation of the + * public member fields, this is necessary to avoid inconsistent results + * in methods like contains. + * + * @see #getBounds() + * @since 1.4 + */ + public void invalidate() + { + bounds = null; + } + + /** + * Translates the polygon by adding the specified values to all X and Y + * coordinates. This updates the bounding box, if it has been calculated. + * + * @param dx the amount to add to all X coordinates + * @param dy the amount to add to all Y coordinates + * @since 1.1 + */ + public void translate(int dx, int dy) + { + int i = npoints; + while (--i >= 0) + { + xpoints[i] += dx; + ypoints[i] += dy; + } + if (bounds != null) + { + bounds.x += dx; + bounds.y += dy; + } + } + + /** + * Adds the specified endpoint to the polygon. This updates the bounding + * box, if it has been created. + * + * @param x the X coordinate of the point to add + * @param y the Y coordiante of the point to add + */ + public void addPoint(int x, int y) + { + if (npoints + 1 > xpoints.length) + { + int[] newx = new int[npoints + 1]; + System.arraycopy(xpoints, 0, newx, 0, npoints); + xpoints = newx; + } + if (npoints + 1 > ypoints.length) + { + int[] newy = new int[npoints + 1]; + System.arraycopy(ypoints, 0, newy, 0, npoints); + ypoints = newy; + } + xpoints[npoints] = x; + ypoints[npoints] = y; + npoints++; + if (bounds != null) + { + if (npoints == 1) + { + bounds.x = x; + bounds.y = y; + } + else + { + if (x < bounds.x) + { + bounds.width += bounds.x - x; + bounds.x = x; + } + else if (x > bounds.x + bounds.width) + bounds.width = x - bounds.x; + if (y < bounds.y) + { + bounds.height += bounds.y - y; + bounds.y = y; + } + else if (y > bounds.y + bounds.height) + bounds.height = y - bounds.y; + } + } + } + + /** + * Returns the bounding box of this polygon. This is the smallest + * rectangle with sides parallel to the X axis that will contain this + * polygon. + * + * @return the bounding box for this polygon + * @see #getBounds2D() + * @since 1.1 + */ + public Rectangle getBounds() + { + return getBoundingBox(); + } + + /** + * Returns the bounding box of this polygon. This is the smallest + * rectangle with sides parallel to the X axis that will contain this + * polygon. + * + * @return the bounding box for this polygon + * @see #getBounds2D() + * @deprecated use {@link #getBounds()} instead + */ + public Rectangle getBoundingBox() + { + if (bounds == null) + { + if (npoints == 0) + return bounds = new Rectangle(); + int i = npoints - 1; + int minx = xpoints[i]; + int maxx = minx; + int miny = ypoints[i]; + int maxy = miny; + while (--i >= 0) + { + int x = xpoints[i]; + int y = ypoints[i]; + if (x < minx) + minx = x; + else if (x > maxx) + maxx = x; + if (y < miny) + miny = y; + else if (y > maxy) + maxy = y; + } + bounds = new Rectangle(minx, miny, maxx - minx, maxy - miny); + } + return bounds; + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param p the point to test + * @return true if the point is inside this polygon + * @throws NullPointerException if p is null + * @see #contains(double, double) + */ + public boolean contains(Point p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside this polygon + * @see #contains(double, double) + * @since 1.1 + */ + public boolean contains(int x, int y) + { + return contains((double) x, (double) y); + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside this polygon + * @see #contains(double, double) + * @deprecated use {@link #contains(int, int)} instead + */ + public boolean inside(int x, int y) + { + return contains((double) x, (double) y); + } + + /** + * Returns a high-precision bounding box of this polygon. This is the + * smallest rectangle with sides parallel to the X axis that will contain + * this polygon. + * + * @return the bounding box for this polygon + * @see #getBounds() + * @since 1.2 + */ + public Rectangle2D getBounds2D() + { + // For polygons, the integer version is exact! + return getBounds(); + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside this polygon + * @since 1.2 + */ + public boolean contains(double x, double y) + { + return ((evaluateCrossings(x, y, false, BIG_VALUE) & 1) != 0); + } + + /** + * Tests whether or not the specified point is inside this polygon. + * + * @param p the point to test + * @return true if the point is inside this polygon + * @throws NullPointerException if p is null + * @see #contains(double, double) + * @since 1.2 + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Test if a high-precision rectangle intersects the shape. This is true + * if any point in the rectangle is in the shape. This implementation is + * precise. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle, treated as point if negative + * @param h the height of the rectangle, treated as point if negative + * @return true if the rectangle intersects this shape + * @since 1.2 + */ + public boolean intersects(double x, double y, double w, double h) + { + /* Does any edge intersect? */ + if (evaluateCrossings(x, y, false, w) != 0 /* top */ + || evaluateCrossings(x, y + h, false, w) != 0 /* bottom */ + || evaluateCrossings(x + w, y, true, h) != 0 /* right */ + || evaluateCrossings(x, y, true, h) != 0) /* left */ + return true; + + /* No intersections, is any point inside? */ + if ((evaluateCrossings(x, y, false, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Test if a high-precision rectangle intersects the shape. This is true + * if any point in the rectangle is in the shape. This implementation is + * precise. + * + * @param r the rectangle + * @return true if the rectangle intersects this shape + * @throws NullPointerException if r is null + * @see #intersects(double, double, double, double) + * @since 1.2 + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Test if a high-precision rectangle lies completely in the shape. This is + * true if all points in the rectangle are in the shape. This implementation + * is precise. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle, treated as point if negative + * @param h the height of the rectangle, treated as point if negative + * @return true if the rectangle is contained in this shape + * @since 1.2 + */ + public boolean contains(double x, double y, double w, double h) + { + if (! getBounds2D().intersects(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (evaluateCrossings(x, y, false, w) != 0 /* top */ + || evaluateCrossings(x, y + h, false, w) != 0 /* bottom */ + || evaluateCrossings(x + w, y, true, h) != 0 /* right */ + || evaluateCrossings(x, y, true, h) != 0) /* left */ + return false; + + /* No intersections, is any point inside? */ + if ((evaluateCrossings(x, y, false, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Test if a high-precision rectangle lies completely in the shape. This is + * true if all points in the rectangle are in the shape. This implementation + * is precise. + * + * @param r the rectangle + * @return true if the rectangle is contained in this shape + * @throws NullPointerException if r is null + * @see #contains(double, double, double, double) + * @since 1.2 + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. This class is not + * threadsafe to begin with, so the path iterator is not either. + * + * @param transform an optional transform to apply to the iterator + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(final AffineTransform transform) + { + return new PathIterator() + { + /** The current vertex of iteration. */ + private int vertex; + + public int getWindingRule() + { + return WIND_EVEN_ODD; + } + + public boolean isDone() + { + return vertex > npoints; + } + + public void next() + { + vertex++; + } + + public int currentSegment(float[] coords) + { + if (vertex >= npoints) + return SEG_CLOSE; + coords[0] = xpoints[vertex]; + coords[1] = ypoints[vertex]; + if (transform != null) + transform.transform(coords, 0, coords, 0, 1); + return vertex == 0 ? SEG_MOVETO : SEG_LINETO; + } + + public int currentSegment(double[] coords) + { + if (vertex >= npoints) + return SEG_CLOSE; + coords[0] = xpoints[vertex]; + coords[1] = ypoints[vertex]; + if (transform != null) + transform.transform(coords, 0, coords, 0, 1); + return vertex == 0 ? SEG_MOVETO : SEG_LINETO; + } + }; + } + + /** + * Return an iterator along the flattened version of the shape boundary. + * Since polygons are already flat, the flatness parameter is ignored, and + * the resulting iterator only has SEG_MOVETO, SEG_LINETO and SEG_CLOSE + * points. If the optional transform is provided, the iterator is + * transformed accordingly. Each call returns a new object, independent + * from others in use. This class is not threadsafe to begin with, so the + * path iterator is not either. + * + * @param transform an optional transform to apply to the iterator + * @param flatness the maximum distance for deviation from the real boundary + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(AffineTransform transform, + double flatness) + { + return getPathIterator(transform); + } + + /** + * Helper for contains, intersects, calculates the number of intersections + * between the polygon and a line extending from the point (x, y) along + * the positive X, or Y axis, within a given interval. + * + * @return the winding number. + * @see #contains(double, double) + */ + private int evaluateCrossings(double x, double y, boolean useYaxis, + double distance) + { + double x0; + double x1; + double y0; + double y1; + double epsilon = 0.0; + int crossings = 0; + int[] xp; + int[] yp; + + if (useYaxis) + { + xp = ypoints; + yp = xpoints; + double swap; + swap = y; + y = x; + x = swap; + } + else + { + xp = xpoints; + yp = ypoints; + } + + /* Get a value which is small but not insignificant relative the path. */ + epsilon = 1E-7; + + x0 = xp[0] - x; + y0 = yp[0] - y; + for (int i = 1; i < npoints; i++) + { + x1 = xp[i] - x; + y1 = yp[i] - y; + + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (y0 * y1 < 0) + if (Line2D.linesIntersect(x0, y0, x1, y1, epsilon, 0.0, distance, 0.0)) + ++crossings; + + x0 = xp[i] - x; + y0 = yp[i] - y; + } + + // end segment + x1 = xp[0] - x; + y1 = yp[0] - y; + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (y0 * y1 < 0) + if (Line2D.linesIntersect(x0, y0, x1, y1, epsilon, 0.0, distance, 0.0)) + ++crossings; + + return crossings; + } +} // class Polygon diff --git a/libjava/classpath/java/awt/PopupMenu.java b/libjava/classpath/java/awt/PopupMenu.java new file mode 100644 index 000000000..8595300f1 --- /dev/null +++ b/libjava/classpath/java/awt/PopupMenu.java @@ -0,0 +1,190 @@ +/* PopupMenu.java -- An AWT popup menu + Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.peer.PopupMenuPeer; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + +/** + * This class implement an AWT popup menu widget + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PopupMenu extends Menu +{ + +/* + * Static Variables + */ + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_popup_number; + + // Serialization Constant + private static final long serialVersionUID = - 4620452533522760060L; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of PopupMenu. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +PopupMenu() +{ +} + +/*************************************************************************/ + +/** + * Initializes a new instance of PopupMenu with the specified + * label. + * + * @param label The label for this popup menu. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ +public +PopupMenu(String label) +{ + super(label); + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Creates this object's native peer. + */ +public void +addNotify() +{ + if (peer == null) + peer = getToolkit ().createPopupMenu (this); + super.addNotify (); +} + +/*************************************************************************/ + +/** + * Displays this popup menu at the specified coordinates relative to + * the specified component. + * + * @param component The component to which the display coordinates are relative. + * @param x The X coordinate of the menu. + * @param y The Y coordinate of the menu. + */ +public void +show(Component component, int x, int y) +{ + if (getPeer() == null) + this.addNotify(); + PopupMenuPeer pmp = (PopupMenuPeer)getPeer(); + if (pmp != null) + { + /* XXX + Event e = new Event (component, Event.ACTION_EVENT, component); + e.x = x; + e.y = y;*/ + pmp.show (component, x, y); + } +} + + protected class AccessibleAWTPopupMenu extends AccessibleAWTMenu + { + private static final long serialVersionUID = -4282044795947239955L; + + protected AccessibleAWTPopupMenu() + { + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.POPUP_MENU; + } + + } + + /** + * Gets the AccessibleContext associated with this PopupMenu. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTPopupMenu(); + return accessibleContext; + } + + /** + * Generate a unique name for this PopupMenu. + * + * @return A unique name for this PopupMenu. + */ + String generateName() + { + return "popup" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_popup_number++; + } + +} // class PopupMenu diff --git a/libjava/classpath/java/awt/PrintGraphics.java b/libjava/classpath/java/awt/PrintGraphics.java new file mode 100644 index 000000000..e7f857797 --- /dev/null +++ b/libjava/classpath/java/awt/PrintGraphics.java @@ -0,0 +1,57 @@ +/* PrintGraphics.java -- a print graphics context + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This interface allows the originating print job to be obtained. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.0 + * @status updated to 1.4 + */ +public interface PrintGraphics +{ + /** + * Returns the PrintJob that this object is being + * managed by. + * + * @return the print job for this object + */ + PrintJob getPrintJob(); +} // interface PrintGraphics diff --git a/libjava/classpath/java/awt/PrintJob.java b/libjava/classpath/java/awt/PrintJob.java new file mode 100644 index 000000000..62aa8b195 --- /dev/null +++ b/libjava/classpath/java/awt/PrintJob.java @@ -0,0 +1,104 @@ +/* PrintJob.java -- A print job class + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.util.Properties; + +/** + * This abstract class represents a print job. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Toolkit#getPrintJob(Frame, String, Properties) + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class PrintJob +{ + /** + * Create a new PrintJob. + */ + public PrintJob() + { + } + + /** + * Returns a graphics context suitable for rendering the next page. The + * return must also implement {@link PrintGraphics}. + * + * @return a graphics context for printing the next page + */ + public abstract Graphics getGraphics(); + + /** + * Returns the dimension of the page in pixels. The resolution will be + * chosen to be similar to the on screen image. + * + * @return the page dimensions + */ + public abstract Dimension getPageDimension(); + + /** + * Returns the resolution of the page in pixels per inch. Note that this is + * not necessarily the printer's resolution. + * + * @return the resolution of the page in pixels per inch + */ + public abstract int getPageResolution(); + + /** + * Tests whether or not the last page will be printed first. + * + * @return true if the last page prints first + */ + public abstract boolean lastPageFirst(); + + /** + * Informs the print job that printing is complete or should be aborted. + */ + public abstract void end(); + + /** + * This method explicitly ends the print job in the event the job + * becomes un-referenced without the application having done so. + */ + public void finalize() + { + end(); + } +} // class PrintJob diff --git a/libjava/classpath/java/awt/Rectangle.java b/libjava/classpath/java/awt/Rectangle.java new file mode 100644 index 000000000..1d5a8da07 --- /dev/null +++ b/libjava/classpath/java/awt/Rectangle.java @@ -0,0 +1,753 @@ +/* Rectangle.java -- represents a graphics rectangle + Copyright (C) 1999, 2000, 2001, 2002, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +/** + * This class represents a rectangle and all the interesting things you + * might want to do with it. Note that the coordinate system uses + * the origin (0,0) as the top left of the screen, with the x and y + * values increasing as they move to the right and down respectively. + * + *

It is valid for a rectangle to have negative width or height; but it + * is considered to have no area or internal points. Therefore, the behavior + * in methods like contains or intersects is + * undefined unless the rectangle has positive width and height. + * + *

There are some public fields; if you mess with them in an inconsistent + * manner, it is your own fault when you get NullPointerException, + * ArrayIndexOutOfBoundsException, or invalid results. Also, this class is + * not threadsafe. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Rectangle extends Rectangle2D implements Shape, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4345857070255674764L; + + /** + * The X coordinate of the top-left corner of the rectangle. + * + * @see #setLocation(int, int) + * @see #getLocation() + * @serial the x coordinate + */ + public int x; + + /** + * The Y coordinate of the top-left corner of the rectangle. + * + * @see #setLocation(int, int) + * @see #getLocation() + * @serial the y coordinate + */ + public int y; + + /** + * The width of the rectangle. + * + * @see #setSize(int, int) + * @see #getSize() + * @serial + */ + public int width; + + /** + * The height of the rectangle. + * + * @see #setSize(int, int) + * @see #getSize() + * @serial + */ + public int height; + + /** + * Initializes a new instance of Rectangle with a top + * left corner at (0,0) and a width and height of 0. + */ + public Rectangle() + { + } + + /** + * Initializes a new instance of Rectangle from the + * coordinates of the specified rectangle. + * + * @param r the rectangle to copy from + * @since 1.1 + */ + public Rectangle(Rectangle r) + { + x = r.x; + y = r.y; + width = r.width; + height = r.height; + } + + /** + * Initializes a new instance of Rectangle from the specified + * inputs. + * + * @param x the X coordinate of the top left corner + * @param y the Y coordinate of the top left corner + * @param width the width of the rectangle + * @param height the height of the rectangle + */ + public Rectangle(int x, int y, int width, int height) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + /** + * Initializes a new instance of Rectangle with the specified + * width and height. The upper left corner of the rectangle will be at + * the origin (0,0). + * + * @param width the width of the rectangle + * @param height the height of the rectange + */ + public Rectangle(int width, int height) + { + this.width = width; + this.height = height; + } + + /** + * Initializes a new instance of Rectangle with a top-left + * corner represented by the specified point and the width and height + * represented by the specified dimension. + * + * @param p the upper left corner of the rectangle + * @param d the width and height of the rectangle + */ + public Rectangle(Point p, Dimension d) + { + x = p.x; + y = p.y; + width = d.width; + height = d.height; + } + + /** + * Initializes a new instance of Rectangle with a top left + * corner at the specified point and a width and height of zero. + * + * @param p the upper left corner of the rectangle + */ + public Rectangle(Point p) + { + x = p.x; + y = p.y; + } + + /** + * Initializes a new instance of Rectangle with an + * upper left corner at the origin (0,0) and a width and height represented + * by the specified dimension. + * + * @param d the width and height of the rectangle + */ + public Rectangle(Dimension d) + { + width = d.width; + height = d.height; + } + + /** + * Get the X coordinate of the upper-left corner. + * + * @return the value of x, as a double + */ + public double getX() + { + return x; + } + + /** + * Get the Y coordinate of the upper-left corner. + * + * @return the value of y, as a double + */ + public double getY() + { + return y; + } + + /** + * Get the width of the rectangle. + * + * @return the value of width, as a double + */ + public double getWidth() + { + return width; + } + + /** + * Get the height of the rectangle. + * + * @return the value of height, as a double + */ + public double getHeight() + { + return height; + } + + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle; it is included to mimic the + * getBounds method in Component. + * + * @return a copy of this rectangle + * @see #setBounds(Rectangle) + * @since 1.1 + */ + public Rectangle getBounds() + { + return new Rectangle(this); + } + + /** + * Returns the high-precision bounds of this rectangle. A pretty useless + * method, as this is already a rectangle. + * + * @return a copy of this rectangle + * @see #setBounds(Rectangle) + * @since 1.2 + */ + public Rectangle2D getBounds2D() + { + return new Rectangle(x, y, width, height); + } + + /** + * Updates this rectangle to match the dimensions of the specified + * rectangle. + * + * @param r the rectangle to update from + * @throws NullPointerException if r is null + * @see #setBounds(int, int, int, int) + * @since 1.1 + */ + public void setBounds(Rectangle r) + { + setBounds (r.x, r.y, r.width, r.height); + } + + /** + * Updates this rectangle to have the specified dimensions. + * + * @param x the new X coordinate of the upper left hand corner + * @param y the new Y coordinate of the upper left hand corner + * @param width the new width of this rectangle + * @param height the new height of this rectangle + * @since 1.1 + */ + public void setBounds(int x, int y, int width, int height) + { + reshape (x, y, width, height); + } + + /** + * Updates this rectangle to have the specified dimensions, rounded to the + * integer precision used by this class (the values are rounded "outwards" so + * that the stored rectangle completely encloses the specified double + * precision rectangle). + * + * @param x the new X coordinate of the upper left hand corner + * @param y the new Y coordinate of the upper left hand corner + * @param width the new width of this rectangle + * @param height the new height of this rectangle + * @since 1.2 + */ + public void setRect(double x, double y, double width, double height) + { + this.x = (int) Math.floor(x); + this.y = (int) Math.floor(y); + this.width = (int) Math.ceil(x + width) - this.x; + this.height = (int) Math.ceil(y + height) - this.y; + } + + /** + * Updates this rectangle to have the specified dimensions. + * + * @param x the new X coordinate of the upper left hand corner + * @param y the new Y coordinate of the upper left hand corner + * @param width the new width of this rectangle + * @param height the new height of this rectangle + * @deprecated use {@link #setBounds(int, int, int, int)} instead + */ + public void reshape(int x, int y, int width, int height) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + /** + * Returns the location of this rectangle, which is the coordinates of + * its upper left corner. + * + * @return the point where this rectangle is located + * @see #setLocation(Point) + * @since 1.1 + */ + public Point getLocation() + { + return new Point(x,y); + } + + /** + * Moves the location of this rectangle by setting its upper left + * corner to the specified point. + * + * @param p the point to move the rectangle to + * @throws NullPointerException if p is null + * @see #getLocation() + * @since 1.1 + */ + public void setLocation(Point p) + { + setLocation (p.x, p.y); + } + + /** + * Moves the location of this rectangle by setting its upper left + * corner to the specified coordinates. + * + * @param x the new X coordinate for this rectangle + * @param y the new Y coordinate for this rectangle + * @since 1.1 + */ + public void setLocation(int x, int y) + { + move (x, y); + } + + /** + * Moves the location of this rectangle by setting its upper left + * corner to the specified coordinates. + * + * @param x the new X coordinate for this rectangle + * @param y the new Y coordinate for this rectangle + * @deprecated use {@link #setLocation(int, int)} instead + */ + public void move(int x, int y) + { + this.x = x; + this.y = y; + } + + /** + * Translate the location of this rectangle by the given amounts. + * + * @param dx the x distance to move by + * @param dy the y distance to move by + * @see #setLocation(int, int) + */ + public void translate(int dx, int dy) + { + x += dx; + y += dy; + } + + /** + * Returns the size of this rectangle. + * + * @return the size of this rectangle + * @see #setSize(Dimension) + * @since 1.1 + */ + public Dimension getSize() + { + return new Dimension(width, height); + } + + /** + * Sets the size of this rectangle based on the specified dimensions. + * + * @param d the new dimensions of the rectangle + * @throws NullPointerException if d is null + * @see #getSize() + * @since 1.1 + */ + public void setSize(Dimension d) + { + setSize (d.width, d.height); + } + + /** + * Sets the size of this rectangle based on the specified dimensions. + * + * @param width the new width of the rectangle + * @param height the new height of the rectangle + * @since 1.1 + */ + public void setSize(int width, int height) + { + resize (width, height); + } + + /** + * Sets the size of this rectangle based on the specified dimensions. + * + * @param width the new width of the rectangle + * @param height the new height of the rectangle + * @deprecated use {@link #setSize(int, int)} instead + */ + public void resize(int width, int height) + { + this.width = width; + this.height = height; + } + + /** + * Tests whether or not the specified point is inside this rectangle. + * According to the contract of Shape, a point on the border is in only if + * it has an adjacent point inside the rectangle in either the increasing + * x or y direction. + * + * @param p the point to test + * @return true if the point is inside the rectangle + * @throws NullPointerException if p is null + * @see #contains(int, int) + * @since 1.1 + */ + public boolean contains(Point p) + { + return contains (p.x, p.y); + } + + /** + * Tests whether or not the specified point is inside this rectangle. + * According to the contract of Shape, a point on the border is in only if + * it has an adjacent point inside the rectangle in either the increasing + * x or y direction. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside the rectangle + * @since 1.1 + */ + public boolean contains(int x, int y) + { + return inside (x, y); + } + + /** + * Checks whether all points in the given rectangle are contained in this + * rectangle. + * + * @param r the rectangle to check + * @return true if r is contained in this rectangle + * @throws NullPointerException if r is null + * @see #contains(int, int, int, int) + * @since 1.1 + */ + public boolean contains(Rectangle r) + { + return contains (r.x, r.y, r.width, r.height); + } + + /** + * Checks whether all points in the given rectangle are contained in this + * rectangle. + * + * @param x the x coordinate of the rectangle to check + * @param y the y coordinate of the rectangle to check + * @param w the width of the rectangle to check + * @param h the height of the rectangle to check + * @return true if the parameters are contained in this rectangle + * @since 1.1 + */ + public boolean contains(int x, int y, int w, int h) + { + return width > 0 && height > 0 && w > 0 && h > 0 + && x >= this.x && x + w <= this.x + this.width + && y >= this.y && y + h <= this.y + this.height; + } + + /** + * Tests whether or not the specified point is inside this rectangle. + * + * @param x the X coordinate of the point to test + * @param y the Y coordinate of the point to test + * @return true if the point is inside the rectangle + * @deprecated use {@link #contains(int, int)} instead + */ + public boolean inside(int x, int y) + { + return width > 0 && height > 0 + && x >= this.x && x < this.x + width + && y >= this.y && y < this.y + height; + } + + /** + * Tests whether or not the specified rectangle intersects this rectangle. + * This means the two rectangles share at least one internal point. + * + * @param r the rectangle to test against + * @return true if the specified rectangle intersects this one + * @throws NullPointerException if r is null + * @since 1.2 + */ + public boolean intersects(Rectangle r) + { + return r.width > 0 && r.height > 0 && width > 0 && height > 0 + && r.x < x + width && r.x + r.width > x + && r.y < y + height && r.y + r.height > y; + } + + /** + * Determines the rectangle which is formed by the intersection of this + * rectangle with the specified rectangle. If the two do not intersect, + * an empty rectangle will be returned (meaning the width and/or height + * will be non-positive). + * + * @param r the rectange to calculate the intersection with + * @return a new rectangle bounding the intersection + * @throws NullPointerException if r is null + */ + public Rectangle intersection(Rectangle r) + { + Rectangle res = new Rectangle(); + intersect(this, r, res); + return res; + } + + /** + * Returns the smallest rectangle that contains both this rectangle + * and the specified rectangle. + * + * @param r the rectangle to compute the union with + * @return the smallest rectangle containing both rectangles + * @throws NullPointerException if r is null + */ + public Rectangle union(Rectangle r) + { + Rectangle res = new Rectangle(); + union(this, r, res); + return res; + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to contains may + * return false. + * + * @param x the X coordinate of the point to add to this rectangle + * @param y the Y coordinate of the point to add to this rectangle + */ + public void add(int x, int y) + { + add((double) x, (double) y); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to contains may + * return false. + * + * @param p the point to add to this rectangle + * @throws NullPointerException if p is null + */ + public void add(Point p) + { + add((double) p.x, (double) p.y); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified rectangle. + * + * @param r the rectangle to add to this rectangle + * @throws NullPointerException if r is null + * @see #union(Rectangle) + */ + public void add(Rectangle r) + { + union(this, r, this); + } + + /** + * Expands the rectangle by the specified amount. The horizontal + * and vertical expansion values are applied both to the X,Y coordinate + * of this rectangle, and its width and height. Thus the width and + * height will increase by 2h and 2v accordingly. + * + * @param h the horizontal expansion value + * @param v the vertical expansion value + */ + public void grow(int h, int v) + { + x -= h; + y -= v; + width += h + h; + height += v + v; + } + + /** + * Tests whether or not this rectangle is empty. An empty rectangle + * has a non-positive width or height. + * + * @return true if the rectangle is empty + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + * @since 1.2 + */ + public int outcode(double x, double y) + { + int result = 0; + if (width <= 0) + result |= OUT_LEFT | OUT_RIGHT; + else if (x < this.x) + result |= OUT_LEFT; + else if (x > this.x + width) + result |= OUT_RIGHT; + if (height <= 0) + result |= OUT_BOTTOM | OUT_TOP; + else if (y < this.y) // Remember that +y heads top-to-bottom. + result |= OUT_TOP; + else if (y > this.y + height) + result |= OUT_BOTTOM; + return result; + } + + /** + * Determines the rectangle which is formed by the intersection of this + * rectangle with the specified rectangle. If the two do not intersect, + * an empty rectangle will be returned (meaning the width and/or height + * will be non-positive). + * + * @param r the rectange to calculate the intersection with + * @return a new rectangle bounding the intersection + * @throws NullPointerException if r is null + * @since 1.2 + */ + public Rectangle2D createIntersection(Rectangle2D r) + { + // Favor runtime type of other rectangle. + Rectangle2D res = r.getBounds2D(); + intersect(this, r, res); + return res; + } + + /** + * Returns the smallest rectangle that contains both this rectangle + * and the specified rectangle. + * + * @param r the rectangle to compute the union with + * @return the smallest rectangle containing both rectangles + * @throws NullPointerException if r is null + * @since 1.2 + */ + public Rectangle2D createUnion(Rectangle2D r) + { + // Favor runtime type of other rectangle. + Rectangle2D res = r.getBounds2D(); + union(this, r, res); + return res; + } + + /** + * Tests this rectangle for equality against the specified object. This + * will be true if an only if the specified object is an instance of + * Rectangle2D with the same coordinates and dimensions. + * + * @param obj the object to test against for equality + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + // NOTE: No special hashCode() method is required for this class, + // as this equals() implementation is functionally equivalent to + // super.equals(), which does define a proper hashCode(). + + if (! (obj instanceof Rectangle2D)) + return false; + Rectangle2D r = (Rectangle2D) obj; + return r.getX() == x && r.getY() == y + && r.getWidth() == width && r.getHeight() == height; + } + + /** + * Returns a string representation of this rectangle. This is in the form + * getClass().getName() + "[x=" + x + ",y=" + y + ",width=" + width + * + ",height=" + height + ']'. + * + * @return a string representation of this rectangle + */ + public String toString() + { + return getClass().getName() + "[x=" + x + ",y=" + y + ",width=" + width + + ",height=" + height + ']'; + } +} // class Rectangle diff --git a/libjava/classpath/java/awt/RenderingHints.java b/libjava/classpath/java/awt/RenderingHints.java new file mode 100644 index 000000000..5de6a608f --- /dev/null +++ b/libjava/classpath/java/awt/RenderingHints.java @@ -0,0 +1,804 @@ +/* RenderingHints.java -- + Copyright (C) 2000, 2001, 2002, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A collection of (key, value) items that provide 'hints' for the + * {@link java.awt.Graphics2D} rendering pipeline. Because these + * items are hints only, they may be ignored by a particular + * {@link java.awt.Graphics2D} implementation. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author Eric Blake (ebb9@email.byu.edu) + */ +public class RenderingHints + implements Map, Cloneable +{ + /** + * The base class used to represent keys. + */ + public abstract static class Key + { + private final int key; + + /** + * Creates a new key. + * + * @param privateKey the private key. + */ + protected Key(int privateKey) + { + key = privateKey; + } + + /** + * Returns true if the specified value is compatible with + * this key, and false otherwise. + * + * @param value the value (null permitted). + * + * @return A boolean. + */ + public abstract boolean isCompatibleValue(Object value); + + /** + * Returns the private key for this instance. + * + * @return The private key. + */ + protected final int intKey() + { + return key; + } + + /** + * Returns a hash code for the key. + * + * @return A hash code. + */ + public final int hashCode() + { + return System.identityHashCode(this); + } + + /** + * Checks this key for equality with an arbitrary object. + * + * @param other the object (null permitted) + * + * @return A boolean. + */ + public final boolean equals(Object other) + { + return this == other; + } + } // class Key + + private static final class KeyImpl extends Key + { + final String description; + final Object v1; + final Object v2; + final Object v3; + + KeyImpl(int privateKey, String description, + Object v1, Object v2, Object v3) + { + super(privateKey); + this.description = description; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + } + + /** + * Returns true if the specified value is compatible with + * this key, and false otherwise. + * + * @param value the value (null permitted). + * + * @return A boolean. + */ + public boolean isCompatibleValue(Object value) + { + return value == v1 || value == v2 || value == v3; + } + + /** + * Returns a string representation of the key. + * + * @return A string. + */ + public String toString() + { + return description; + } + } // class KeyImpl + + private HashMap hintMap = new HashMap(); + + /** + * A key for the 'antialiasing' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_ANTIALIAS_OFF}Render without antialiasing (better speed).
{@link #VALUE_ANTIALIAS_ON}Render with antialiasing (better quality).
{@link #VALUE_ANTIALIAS_DEFAULT}Use the default value for antialiasing.
+ */ + public static final Key KEY_ANTIALIASING; + + /** + * This value is for use with the {@link #KEY_ANTIALIASING} key. + */ + public static final Object VALUE_ANTIALIAS_ON + = "Antialiased rendering mode"; + + /** + * This value is for use with the {@link #KEY_ANTIALIASING} key. + */ + public static final Object VALUE_ANTIALIAS_OFF + = "Nonantialiased rendering mode"; + + /** + * This value is for use with the {@link #KEY_ANTIALIASING} key. + */ + public static final Object VALUE_ANTIALIAS_DEFAULT + = "Default antialiasing rendering mode"; + + /** + * A key for the 'rendering' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_RENDER_SPEED}Prefer speed over quality when rendering.
{@link #VALUE_RENDER_QUALITY}Prefer quality over speed when rendering.
{@link #VALUE_RENDER_DEFAULT}Use the default value for quality vs. speed when rendering.
+ */ + public static final Key KEY_RENDERING; + + /** + * This value is for use with the {@link #KEY_RENDERING} key. + */ + public static final Object VALUE_RENDER_SPEED + = "Fastest rendering methods"; + + /** + * This value is for use with the {@link #KEY_RENDERING} key. + */ + public static final Object VALUE_RENDER_QUALITY + = "Highest quality rendering methods"; + + /** + * This value is for use with the {@link #KEY_RENDERING} key. + */ + public static final Object VALUE_RENDER_DEFAULT + = "Default rendering methods"; + + /** + * A key for the 'dithering' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_DITHER_DISABLE}Disable dithering.
{@link #VALUE_DITHER_ENABLE}Enable dithering.
{@link #VALUE_DITHER_DEFAULT}Use the default value for dithering.
+ */ + public static final Key KEY_DITHERING; + + /** + * This value is for use with the {@link #KEY_DITHERING} key. + */ + public static final Object VALUE_DITHER_DISABLE + = "Nondithered rendering mode"; + + /** + * This value is for use with the {@link #KEY_DITHERING} key. + */ + public static final Object VALUE_DITHER_ENABLE + = "Dithered rendering mode"; + + /** + * This value is for use with the {@link #KEY_DITHERING} key. + */ + public static final Object VALUE_DITHER_DEFAULT + = "Default dithering mode"; + + /** + * A key for the 'text antialiasing' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_TEXT_ANTIALIAS_ON}Render text with antialiasing (better quality usually).
{@link #VALUE_TEXT_ANTIALIAS_OFF}Render test without antialiasing (better speed).
{@link #VALUE_TEXT_ANTIALIAS_DEFAULT}Use the default value for text antialiasing.
+ */ + public static final Key KEY_TEXT_ANTIALIASING; + + /** + * This value is for use with the {@link #KEY_TEXT_ANTIALIASING} key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_ON + = "Antialiased text mode"; + + /** + * This value is for use with the {@link #KEY_TEXT_ANTIALIASING} key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_OFF + = "Nonantialiased text mode"; + + /** + * This value is for use with the {@link #KEY_TEXT_ANTIALIASING} key. + */ + public static final Object VALUE_TEXT_ANTIALIAS_DEFAULT + = "Default antialiasing text mode"; + + /** + * A key for the 'fractional metrics' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_FRACTIONALMETRICS_OFF}Render text with fractional metrics off.
{@link #VALUE_FRACTIONALMETRICS_ON}Render text with fractional metrics on.
{@link #VALUE_FRACTIONALMETRICS_DEFAULT}Use the default value for fractional metrics.
+ */ + public static final Key KEY_FRACTIONALMETRICS; + + /** + * This value is for use with the {@link #KEY_FRACTIONALMETRICS} key. + */ + public static final Object VALUE_FRACTIONALMETRICS_OFF + = "Integer text metrics mode"; + + /** + * This value is for use with the {@link #KEY_FRACTIONALMETRICS} key. + */ + public static final Object VALUE_FRACTIONALMETRICS_ON + = "Fractional text metrics mode"; + + /** + * This value is for use with the {@link #KEY_FRACTIONALMETRICS} key. + */ + public static final Object VALUE_FRACTIONALMETRICS_DEFAULT + = "Default fractional text metrics mode"; + + /** + * A key for the 'interpolation' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_INTERPOLATION_NEAREST_NEIGHBOR}Use nearest neighbour interpolation.
{@link #VALUE_INTERPOLATION_BILINEAR}Use bilinear interpolation.
{@link #VALUE_INTERPOLATION_BICUBIC}Use bicubic interpolation.
+ */ + public static final Key KEY_INTERPOLATION; + + /** + * This value is for use with the {@link #KEY_INTERPOLATION} key. + */ + public static final Object VALUE_INTERPOLATION_NEAREST_NEIGHBOR + = "Nearest Neighbor image interpolation mode"; + + /** + * This value is for use with the {@link #KEY_INTERPOLATION} key. + */ + public static final Object VALUE_INTERPOLATION_BILINEAR + = "Bilinear image interpolation mode"; + + /** + * This value is for use with the {@link #KEY_INTERPOLATION} key. + */ + public static final Object VALUE_INTERPOLATION_BICUBIC + = "Bicubic image interpolation mode"; + + /** + * A key for the 'alpha interpolation' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_ALPHA_INTERPOLATION_SPEED}Prefer speed over quality.
{@link #VALUE_ALPHA_INTERPOLATION_QUALITY}Prefer quality over speed.
{@link #VALUE_ALPHA_INTERPOLATION_DEFAULT}Use the default setting.
+ */ + public static final Key KEY_ALPHA_INTERPOLATION; + + /** + * This value is for use with the {@link #KEY_ALPHA_INTERPOLATION} key. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_SPEED + = "Fastest alpha blending methods"; + + /** + * This value is for use with the {@link #KEY_ALPHA_INTERPOLATION} key. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_QUALITY + = "Highest quality alpha blending methods"; + + /** + * This value is for use with the {@link #KEY_ALPHA_INTERPOLATION} key. + */ + public static final Object VALUE_ALPHA_INTERPOLATION_DEFAULT + = "Default alpha blending methods"; + + /** + * A key for the 'color rendering' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_COLOR_RENDER_SPEED}Prefer speed over quality.
{@link #VALUE_COLOR_RENDER_QUALITY}Prefer quality over speed.
{@link #VALUE_COLOR_RENDER_DEFAULT}Use the default setting.
+ */ + public static final Key KEY_COLOR_RENDERING; + + /** + * This value is for use with the {@link #KEY_COLOR_RENDERING} key. + */ + public static final Object VALUE_COLOR_RENDER_SPEED + = "Fastest color rendering mode"; + + /** + * This value is for use with the {@link #KEY_COLOR_RENDERING} key. + */ + public static final Object VALUE_COLOR_RENDER_QUALITY + = "Highest quality color rendering mode"; + + /** + * This value is for use with the {@link #KEY_COLOR_RENDERING} key. + */ + public static final Object VALUE_COLOR_RENDER_DEFAULT + = "Default color rendering mode"; + + /** + * A key for the 'stroke control' hint. Permitted values are: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
{@link #VALUE_STROKE_DEFAULT}Use the default setting.
{@link #VALUE_STROKE_NORMALIZE}XXX
{@link #VALUE_STROKE_PURE}XXX
+ */ + public static final Key KEY_STROKE_CONTROL; + + /** + * This value is for use with the {@link #KEY_STROKE_CONTROL} key. + */ + public static final Object VALUE_STROKE_DEFAULT + = "Default stroke normalization"; + + /** + * This value is for use with the {@link #KEY_STROKE_CONTROL} key. + */ + public static final Object VALUE_STROKE_NORMALIZE + = "Normalize strokes for consistent rendering"; + + /** + * This value is for use with the {@link #KEY_STROKE_CONTROL} key. + */ + public static final Object VALUE_STROKE_PURE + = "Pure stroke conversion for accurate paths"; + + static + { + KEY_ANTIALIASING = new KeyImpl(1, "Global antialiasing enable key", + VALUE_ANTIALIAS_ON, + VALUE_ANTIALIAS_OFF, + VALUE_ANTIALIAS_DEFAULT); + KEY_RENDERING = new KeyImpl(2, "Global rendering quality key", + VALUE_RENDER_SPEED, + VALUE_RENDER_QUALITY, + VALUE_RENDER_DEFAULT); + KEY_DITHERING = new KeyImpl(3, "Dithering quality key", + VALUE_DITHER_DISABLE, + VALUE_DITHER_ENABLE, + VALUE_DITHER_DEFAULT); + KEY_TEXT_ANTIALIASING + = new KeyImpl(4, "Text-specific antialiasing enable key", + VALUE_TEXT_ANTIALIAS_ON, + VALUE_TEXT_ANTIALIAS_OFF, + VALUE_TEXT_ANTIALIAS_DEFAULT); + KEY_FRACTIONALMETRICS = new KeyImpl(5, "Fractional metrics enable key", + VALUE_FRACTIONALMETRICS_OFF, + VALUE_FRACTIONALMETRICS_ON, + VALUE_FRACTIONALMETRICS_DEFAULT); + KEY_INTERPOLATION = new KeyImpl(6, "Image interpolation method key", + VALUE_INTERPOLATION_NEAREST_NEIGHBOR, + VALUE_INTERPOLATION_BILINEAR, + VALUE_INTERPOLATION_BICUBIC); + KEY_ALPHA_INTERPOLATION + = new KeyImpl(7, "Alpha blending interpolation method key", + VALUE_ALPHA_INTERPOLATION_SPEED, + VALUE_ALPHA_INTERPOLATION_QUALITY, + VALUE_ALPHA_INTERPOLATION_DEFAULT); + KEY_COLOR_RENDERING = new KeyImpl(8, "Color rendering quality key", + VALUE_COLOR_RENDER_SPEED, + VALUE_COLOR_RENDER_QUALITY, + VALUE_COLOR_RENDER_DEFAULT); + KEY_STROKE_CONTROL = new KeyImpl(9, "Stroke normalization control key", + VALUE_STROKE_DEFAULT, + VALUE_STROKE_NORMALIZE, + VALUE_STROKE_PURE); + } + + /** + * Creates a new collection of hints containing all the (key, value) pairs + * in the specified map. + * + * @param init a map containing a collection of hints (null + * permitted). + */ + public RenderingHints(Map init) + { + if (init != null) + putAll(init); + } + + /** + * Creates a new collection containing a single (key, value) pair. + * + * @param key the key. + * @param value the value. + */ + public RenderingHints(Key key, Object value) + { + put(key, value); + } + + /** + * Returns the number of hints in the collection. + * + * @return The number of hints. + */ + public int size() + { + return hintMap.size(); + } + + /** + * Returns true if there are no hints in the collection, + * and false otherwise. + * + * @return A boolean. + */ + public boolean isEmpty() + { + return hintMap.isEmpty(); + } + + /** + * Returns true if the collection of hints contains the + * specified key, and false otherwise. + * + * @param key the key (null not permitted). + * + * @return A boolean. + * + * @throws NullPointerException if key is null. + * @throws ClassCastException if key is not a {@link Key}. + */ + public boolean containsKey(Object key) + { + if (key == null) + throw new NullPointerException(); + // don't remove the cast, it is necessary to throw the required exception + return hintMap.containsKey((Key) key); + } + + /** + * Returns true if the collection of hints contains the + * specified value, and false otherwise. + * + * @param value the value. + * + * @return A boolean. + */ + public boolean containsValue(Object value) + { + return hintMap.containsValue(value); + } + + /** + * Returns the value associated with the specified key, or null + * if there is no value defined for the key. + * + * @param key the key (null permitted). + * + * @return The value (possibly null). + * + * @throws ClassCastException if key is not a {@link Key}. + * + * @see #containsKey(Object) + */ + public Object get(Object key) + { + // don't remove the cast, it is necessary to throw the required exception + return hintMap.get((Key) key); + } + + /** + * Adds a (key, value) pair to the collection of hints (if the + * collection already contains the specified key, then the + * value is updated). + * + * @param key the key. + * @param value the value. + * + * @return the previous value of the key or null if the key + * didn't have a value yet. + */ + public Object put(Object key, Object value) + { + if (key == null || value == null) + throw new NullPointerException(); + if (! ((Key) key).isCompatibleValue(value)) + throw new IllegalArgumentException(); + return hintMap.put(key, value); + } + + /** + * Adds all the hints from a collection to this collection. + * + * @param hints the hint collection. + */ + public void add(RenderingHints hints) + { + hintMap.putAll(hints); + } + + /** + * Clears all the hints from this collection. + */ + public void clear() + { + hintMap.clear(); + } + + /** + * Removes a hint from the collection. + * + * @param key the key. + * + * @return The value that was associated with the key, or null if + * the key was not part of the collection + * + * @throws ClassCastException if the key is not a subclass of + * {@link RenderingHints.Key}. + */ + public Object remove(Object key) + { + // don't remove the (Key) cast, it is necessary to throw the exception + // required by the spec + return hintMap.remove((Key) key); + } + + /** + * Adds a collection of (key, value) pairs to the collection. + * + * @param m a map containing (key, value) items. + * + * @throws ClassCastException if the map contains a key that is not + * a subclass of {@link RenderingHints.Key}. + * @throws IllegalArgumentException if the map contains a value that is + * not compatible with its key. + */ + public void putAll(Map m) + { + // preprocess map to generate appropriate exceptions + Iterator iterator = m.keySet().iterator(); + while (iterator.hasNext()) + { + Key key = (Key) iterator.next(); + if (!key.isCompatibleValue(m.get(key))) + throw new IllegalArgumentException(); + } + // map is OK, update + hintMap.putAll(m); + } + + /** + * Returns a set containing the keys from this collection. + * + * @return A set of keys. + */ + public Set keySet() + { + return hintMap.keySet(); + } + + /** + * Returns a collection of the values from this hint collection. The + * collection is backed by the RenderingHints instance, + * so updates to one will affect the other. + * + * @return A collection of values. + */ + public Collection values() + { + return hintMap.values(); + } + + /** + * Returns a set of entries from the collection. + * + * @return A set of entries. + */ + public Set> entrySet() + { + return Collections.unmodifiableSet(hintMap.entrySet()); + } + + /** + * Checks this collection for equality with an arbitrary object. + * + * @param o the object (null permitted) + * + * @return A boolean. + */ + public boolean equals(Object o) + { + return hintMap.equals(o); + } + + /** + * Returns a hash code for the collection of hints. + * + * @return A hash code. + */ + public int hashCode() + { + return hintMap.hashCode(); + } + + /** + * Creates a clone of this instance. + * + * @return A clone. + */ + public Object clone() + { + try + { + RenderingHints copy = (RenderingHints) super.clone(); + copy.hintMap = new HashMap(hintMap); + return copy; + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Returns a string representation of this instance. + * + * @return A string. + */ + public String toString() + { + return hintMap.toString(); + } +} // class RenderingHints diff --git a/libjava/classpath/java/awt/Robot.java b/libjava/classpath/java/awt/Robot.java new file mode 100644 index 000000000..30de5ca84 --- /dev/null +++ b/libjava/classpath/java/awt/Robot.java @@ -0,0 +1,423 @@ +/* Robot.java -- a native input event generator + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.ClasspathToolkit; + +import java.lang.reflect.InvocationTargetException; +import java.awt.event.InputEvent; +import java.awt.image.BufferedImage; +import java.awt.peer.RobotPeer; + +/** + * The Robot class is used to simulate user interaction with graphical + * programs. It can generate native windowing system input events and + * retrieve image data from the current screen. Robot is used to test + * the AWT and Swing library implementations; it can also be used to + * create self-running demo programs. + * + * Since Robot generates native windowing system events, rather than + * simply inserting {@link AWTEvent}s on the AWT event queue, its use + * is not restricted to Java programs. It can be used to + * programatically drive any graphical application. + * + * This implementation requires an X server that supports the XTest + * extension. + * + * @author Thomas Fitzsimmons (fitzsim@redhat.com) + * + * @since 1.3 + */ +public class Robot +{ + private boolean waitForIdle; + private int autoDelay; + private RobotPeer peer; + + /** + * Construct a Robot object that operates on the default screen. + * + * @exception AWTException if GraphicsEnvironment.isHeadless() + * returns true or if the X server does not support the XTest + * extension + * @exception SecurityException if createRobot permission is not + * granted + */ + public Robot () throws AWTException + { + if (GraphicsEnvironment.isHeadless ()) + throw new AWTException ("Robot: headless graphics environment"); + + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + sm.checkPermission (new AWTPermission ("createRobot")); + + ClasspathToolkit tk = (ClasspathToolkit) Toolkit.getDefaultToolkit (); + + // createRobot will throw AWTException if XTest is not supported. + peer = tk.createRobot (GraphicsEnvironment.getLocalGraphicsEnvironment () + .getDefaultScreenDevice ()); + } + + /** + * Construct a Robot object that operates on the specified screen. + * + * @exception AWTException if GraphicsEnvironment.isHeadless() + * returns true or if the X server does not support the XTest + * extension + * @exception IllegalArgumentException if screen is not a screen + * GraphicsDevice + * @exception SecurityException if createRobot permission is not + * granted + */ + public Robot (GraphicsDevice screen) throws AWTException + { + if (GraphicsEnvironment.isHeadless ()) + throw new AWTException ("Robot: headless graphics environment"); + + if (screen.getType () != GraphicsDevice.TYPE_RASTER_SCREEN) + throw new IllegalArgumentException ("Robot: graphics" + + " device is not a screen"); + + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + sm.checkPermission (new AWTPermission ("createRobot")); + + ClasspathToolkit tk = (ClasspathToolkit) Toolkit.getDefaultToolkit (); + + // createRobot will throw AWTException if XTest is not supported. + peer = tk.createRobot (screen); + } + + /** + * Move the mouse pointer to absolute coordinates (x, y). + * + * @param x the destination x coordinate + * @param y the destination y coordinate + */ + public void mouseMove(int x, int y) + { + peer.mouseMove (x, y); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Press one or more mouse buttons. + * + * @param buttons the buttons to press; a bitmask of one or more of + * these {@link InputEvent} fields: + * + *
    + *
  • BUTTON1_MASK
  • + *
  • BUTTON2_MASK
  • + *
  • BUTTON3_MASK
  • + *
+ * + * @exception IllegalArgumentException if the button mask is invalid + */ + public void mousePress (int buttons) + { + if ((buttons & InputEvent.BUTTON1_MASK) == 0 + && (buttons & InputEvent.BUTTON2_MASK) == 0 + && (buttons & InputEvent.BUTTON3_MASK) == 0) + throw new IllegalArgumentException ("Robot: mousePress:" + + " invalid button mask"); + + peer.mousePress (buttons); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Release one or more mouse buttons. + * + * @param buttons the buttons to release; a bitmask of one or more + * of these {@link InputEvent} fields: + * + *
    + *
  • BUTTON1_MASK
  • + *
  • BUTTON2_MASK
  • + *
  • BUTTON3_MASK
  • + *
+ * + * @exception IllegalArgumentException if the button mask is invalid + */ + public void mouseRelease(int buttons) + { + if ((buttons & InputEvent.BUTTON1_MASK) == 0 + && (buttons & InputEvent.BUTTON2_MASK) == 0 + && (buttons & InputEvent.BUTTON3_MASK) == 0) + throw new IllegalArgumentException ("Robot: mouseRelease:" + + " invalid button mask"); + + peer.mouseRelease (buttons); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Rotate the mouse scroll wheel. + * + * @param wheelAmt number of steps to rotate mouse wheel. negative + * to rotate wheel up (away from the user), positive to rotate wheel + * down (toward the user). + * + * @since 1.4 + */ + public void mouseWheel (int wheelAmt) + { + peer.mouseWheel (wheelAmt); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Press a key. + * + * @param keycode key to press, a {@link java.awt.event.KeyEvent} VK_ constant + * + * @exception IllegalArgumentException if keycode is not a valid key + */ + public void keyPress (int keycode) + { + peer.keyPress (keycode); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Release a key. + * + * @param keycode key to release, a {@link java.awt.event.KeyEvent} VK_ + * constant + * + * @exception IllegalArgumentException if keycode is not a valid key + */ + public void keyRelease (int keycode) + { + peer.keyRelease (keycode); + + if (waitForIdle) + waitForIdle (); + + if (autoDelay > 0) + delay (autoDelay); + } + + /** + * Return the color of the pixel at the given screen coordinates. + * + * @param x the x coordinate of the pixel + * @param y the y coordinate of the pixel + * + * @return the Color of the pixel at screen coodinates (x, y) + */ + public Color getPixelColor (int x, int y) + { + return new Color (peer.getRGBPixel (x, y)); + } + + /** + * Create an image containing pixels read from the screen. The + * image does not include the mouse pointer. + * + * @param screenRect the rectangle of pixels to capture, in screen + * coordinates + * + * @return a BufferedImage containing the requested pixels + * + * @exception IllegalArgumentException if requested width and height + * are not both greater than zero + * @exception SecurityException if readDisplayPixels permission is + * not granted + */ + public BufferedImage createScreenCapture (Rectangle screenRect) + { + if (screenRect.width <= 0) + throw new IllegalArgumentException ("Robot: capture width is <= 0"); + + if (screenRect.height <= 0) + throw new IllegalArgumentException ("Robot: capture height is <= 0"); + + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + sm.checkPermission (new AWTPermission ("readDisplayPixels")); + + int[] pixels = peer.getRGBPixels (screenRect); + + BufferedImage bufferedImage = + new BufferedImage (screenRect.width, screenRect.height, + BufferedImage.TYPE_INT_ARGB); + + bufferedImage.setRGB (0, 0, screenRect.width, screenRect.height, + pixels, 0, screenRect.width); + + return bufferedImage; + } + + /** + * Check if this Robot automatically calls {@link #waitForIdle()} after + * generating an event. + * + * @return true if waitForIdle is automatically called + */ + public boolean isAutoWaitForIdle () + { + return waitForIdle; + } + + /** + * Set whether or not this Robot automatically calls {@link + * #waitForIdle()} after generating an event. + * + * @param isOn true if waitForIdle should be called automatically + */ + public void setAutoWaitForIdle (boolean isOn) + { + waitForIdle = isOn; + } + + /** + * Retrieve the length of time this Robot sleeps after generating an + * event. + * + * @return the length of time in milliseconds + */ + public int getAutoDelay () + { + return autoDelay; + } + + /** + * Set the length of time this Robot sleeps after generating an + * event. + * + * @param ms the length of time in milliseconds + * + * @exception IllegalArgumentException if ms is not between 0 and + * 60,000 milliseconds inclusive + */ + public void setAutoDelay (int ms) + { + if (ms <= 0 || ms >= 60000) + throw new IllegalArgumentException ("Robot: delay length out-of-bounds"); + + autoDelay = ms; + } + + /** + * Sleep for a specified length of time. + * + * @param ms the length of time in milliseconds + * + * @exception IllegalArgumentException if ms is not between 0 and + * 60,000 milliseconds inclusive + */ + public void delay (int ms) + { + if (ms < 0 || ms > 60000) + throw new IllegalArgumentException ("Robot: delay length out-of-bounds"); + + try + { + Thread.sleep (ms); + } + catch (InterruptedException e) + { + System.err.println ("Robot: delay interrupted"); + } + } + + /** + * Wait until all events currently on the event queue have been + * dispatched. + */ + public void waitForIdle () + { + if (EventQueue.isDispatchThread ()) + throw new IllegalThreadStateException ("Robot: waitForIdle called from " + + "the event dispatch thread"); + + try + { + EventQueue.invokeAndWait (new Runnable () { public void run () { } }); + } + catch (InterruptedException e) + { + System.err.println ("Robot: waitForIdle interrupted"); + } + catch (InvocationTargetException e) + { + System.err.println ("Robot: waitForIdle cannot invoke target"); + } + } + + /** + * Return a string representation of this Robot. + * + * @return a string representation + */ + public String toString () + { + return getClass ().getName () + + "[ autoDelay = " + autoDelay + ", autoWaitForIdle = " + + waitForIdle + " ]"; + } +} diff --git a/libjava/classpath/java/awt/ScrollPane.java b/libjava/classpath/java/awt/ScrollPane.java new file mode 100644 index 000000000..14d15f648 --- /dev/null +++ b/libjava/classpath/java/awt/ScrollPane.java @@ -0,0 +1,680 @@ +/* ScrollPane.java -- Scrolling window + Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.MouseEvent; +import java.awt.peer.ComponentPeer; +import java.awt.peer.ScrollPanePeer; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; + + +/** + * This widget provides a scrollable region that allows a single + * subcomponent to be viewed through a smaller window. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class ScrollPane extends Container implements Accessible +{ + +/* + * Static Variables + */ + +/** + * Constant indicating that scrollbars are created as needed in this + * windows. + */ +public static final int SCROLLBARS_AS_NEEDED = 0; + +/** + * Constant indicating that scrollbars are always displayed in this + * window. + */ +public static final int SCROLLBARS_ALWAYS = 1; + +/** + * Constant indicating that scrollbars are never displayed in this window. + */ +public static final int SCROLLBARS_NEVER = 2; + +/** + * The number used to generate the name returned by getName. + */ +private static transient long next_scrollpane_number; + +// Serialization constant +private static final long serialVersionUID = 7956609840827222915L; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * @serial The horizontal scrollbar for this window. The methods + * setMinimum(), setMaximum, and + * setVisibleAmount must not be called on this scrollbar. + */ +private ScrollPaneAdjustable hAdjustable; + +/** + * @serial The vertical scrollbar for this window. The methods + * setMinimum(), setMaximum, and + * setVisibleAmount must not be called on this scrollbar. + */ +private ScrollPaneAdjustable vAdjustable; + +/** + * @serial Indicates when scrollbars are displayed in this window, will + * be one of the constants from this class. + */ +private int scrollbarDisplayPolicy; + +// Current scroll position +private Point scrollPosition = new Point(0, 0); + +private boolean wheelScrollingEnabled; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes a new instance of ScrollPane with a default + * scrollbar policy of SCROLLBARS_AS_NEEDED. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +ScrollPane() +{ + this(SCROLLBARS_AS_NEEDED); +} + +/*************************************************************************/ + +/** + * Initializes a new instance of ScrollPane with the + * specified scrollbar policy. + * + * @param scrollbarDisplayPolicy When to display scrollbars, which must + * be one of the constants defined in this class. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ +public +ScrollPane(int scrollbarDisplayPolicy) +{ + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); + + this.scrollbarDisplayPolicy = scrollbarDisplayPolicy; + + if (scrollbarDisplayPolicy != SCROLLBARS_ALWAYS + && scrollbarDisplayPolicy != SCROLLBARS_AS_NEEDED + && scrollbarDisplayPolicy != SCROLLBARS_NEVER) + throw new IllegalArgumentException("Bad scrollbarDisplayPolicy: " + + scrollbarDisplayPolicy); + + if (scrollbarDisplayPolicy != SCROLLBARS_NEVER) + { + hAdjustable = new ScrollPaneAdjustable (this, Scrollbar.HORIZONTAL); + vAdjustable = new ScrollPaneAdjustable (this, Scrollbar.VERTICAL); + } + + wheelScrollingEnabled = true; + + // Default size. + setSize(100,100); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Returns the current scrollbar display policy. + * + * @return The current scrollbar display policy. + */ +public int +getScrollbarDisplayPolicy() +{ + return(scrollbarDisplayPolicy); +} + +/*************************************************************************/ + +/** + * Returns the horizontal scrollbar for this object. If the scrollbar + * display policy is set to SCROLLBARS_NEVER then this + * will be null. + * + * @return The horizontal scrollbar for this window. + */ +public Adjustable +getHAdjustable() +{ + return(hAdjustable); +} + +/*************************************************************************/ + +/** + * Returns the vertical scrollbar for this object. If the scrollbar + * display policy is set to SCROLLBARS_NEVER then this + * will be null. + * + * @return The horizontal scrollbar for this window. + */ +public Adjustable +getVAdjustable() +{ + return(vAdjustable); +} + +/*************************************************************************/ + +/** + * Returns the current viewport size. The viewport is the region of + * this object's window where the child is actually displayed. + * + * @return The viewport size. + */ +public Dimension getViewportSize () +{ + Dimension viewsize = getSize (); + Insets insets = getInsets (); + + viewsize.width -= (insets.left + insets.right); + viewsize.height -= (insets.top + insets.bottom); + + Component[] list = getComponents(); + if ((list == null) || (list.length <= 0)) + return viewsize; + + Dimension dim = list[0].getPreferredSize(); + + if (dim.width <= 0 && dim.height <= 0) + return viewsize; + + int vScrollbarWidth = getVScrollbarWidth (); + int hScrollbarHeight = getHScrollbarHeight (); + + if (scrollbarDisplayPolicy == SCROLLBARS_ALWAYS) + { + viewsize.width -= vScrollbarWidth; + viewsize.height -= hScrollbarHeight; + return viewsize; + } + + if (scrollbarDisplayPolicy == SCROLLBARS_NEVER) + return viewsize; + + // The scroll policy is SCROLLBARS_AS_NEEDED, so we need to see if + // either scrollbar is needed. + + // Assume we don't need either scrollbar. + boolean mayNeedVertical = false; + boolean mayNeedHorizontal = false; + + boolean needVertical = false; + boolean needHorizontal = false; + + // Check if we need vertical scrollbars. If we do, then we need to + // subtract the width of the vertical scrollbar from the viewport's + // width. + if (dim.height > viewsize.height) + needVertical = true; + else if (dim.height > (viewsize.height - hScrollbarHeight)) + // This is tricky. In this case the child is tall enough that its + // bottom edge would be covered by a horizontal scrollbar, if one + // were present. This means that if there's a horizontal + // scrollbar then we need a vertical scrollbar. + mayNeedVertical = true; + + if (dim.width > viewsize.width) + needHorizontal = true; + else if (dim.width > (viewsize.width - vScrollbarWidth)) + mayNeedHorizontal = true; + + if (needVertical && mayNeedHorizontal) + needHorizontal = true; + + if (needHorizontal && mayNeedVertical) + needVertical = true; + + if (needHorizontal) + viewsize.height -= hScrollbarHeight; + + if (needVertical) + viewsize.width -= vScrollbarWidth; + + return viewsize; +} + +/*************************************************************************/ + +/** + * Returns the height of a horizontal scrollbar. + * + * @return The height of a horizontal scrollbar. + */ +public int +getHScrollbarHeight() +{ + ScrollPanePeer spp = (ScrollPanePeer)getPeer(); + if (spp != null) + return(spp.getHScrollbarHeight()); + else + return(0); // FIXME: What to do here? +} + +/*************************************************************************/ + +/** + * Returns the width of a vertical scrollbar. + * + * @return The width of a vertical scrollbar. + */ +public int +getVScrollbarWidth() +{ + ScrollPanePeer spp = (ScrollPanePeer)getPeer(); + if (spp != null) + return(spp.getVScrollbarWidth()); + else + return(0); // FIXME: What to do here? +} + +/*************************************************************************/ + +/** + * Returns the current scroll position of the viewport. + * + * @return The current scroll position of the viewport. + * + * @throws NullPointerException if the scrollpane does have a child. + */ +public Point +getScrollPosition() +{ + if (getComponentCount() == 0) + throw new NullPointerException(); + + int x = 0; + int y = 0; + + Adjustable v = getVAdjustable(); + Adjustable h = getHAdjustable(); + + if (v != null) + y = v.getValue(); + if (h != null) + x = h.getValue(); + + return(new Point(x, y)); +} + +/*************************************************************************/ + +/** + * Sets the scroll position to the specified value. + * + * @param scrollPosition The new scrollPosition. + * + * @exception IllegalArgumentException If the specified value is outside + * the legal scrolling range. + */ +public void +setScrollPosition(Point scrollPosition) throws IllegalArgumentException +{ + setScrollPosition(scrollPosition.x, scrollPosition.y); +} + +/*************************************************************************/ + +/** + * Sets the scroll position to the specified value. + * + * @param x The new X coordinate of the scroll position. + * @param y The new Y coordinate of the scroll position. + * + * @throws NullPointerException if scrollpane does not have a child. + * + * @exception IllegalArgumentException If the specified value is outside + * the legal scrolling range. + */ +public void +setScrollPosition(int x, int y) +{ + if (getComponentCount() == 0) + throw new NullPointerException("child is null"); + + if (x > (int) (getComponent(0).getWidth() - getViewportSize().getWidth())) + x = (int) (getComponent(0).getWidth() - getViewportSize().getWidth()); + if (y > (int) (getComponent(0).getHeight() - getViewportSize().getHeight())) + y = (int) (getComponent(0).getHeight() - getViewportSize().getHeight()); + + if (x < 0) + x = 0; + if (y < 0) + y = 0; + + Adjustable h = getHAdjustable(); + Adjustable v = getVAdjustable(); + + if (h != null) + h.setValue(x); + if (v != null) + v.setValue(y); + + ScrollPanePeer spp = (ScrollPanePeer)getPeer(); + if (spp != null) + spp.setScrollPosition(x, y); +} + +/*************************************************************************/ + +/** + * Notifies this object that it should create its native peer. + */ +public void +addNotify() +{ + if (peer != null) + return; + + setPeer((ComponentPeer)getToolkit().createScrollPane(this)); + super.addNotify(); + + Component[] list = getComponents(); + if (list != null && list.length > 0 && list[0].isLightweight()) + { + Panel panel = new Panel(); + panel.setLayout(new BorderLayout()); + panel.add(list[0], BorderLayout.CENTER); + add(panel); + } +} + +/*************************************************************************/ + +/** + * Notifies this object that it should destroy its native peers. + */ +public void +removeNotify() +{ + super.removeNotify(); +} + +/*************************************************************************/ + +/** + * Adds the specified child component to this container. A + * ScrollPane can have at most one child, so if a second + * one is added, then first one is removed. + * + * @param component The component to add to this container. + * @param constraints A list of layout constraints for this object. + * @param index The index at which to add the child, which is ignored + * in this implementation. + */ + protected final void addImpl (Component component, Object constraints, + int index) +{ + Component[] list = getComponents(); + if ((list != null) && (list.length > 0)) + remove(list[0]); + + super.addImpl(component, constraints, index); +} + +/*************************************************************************/ + +/** + * Lays out this component. This consists of resizing the sole child + * component to its perferred size. + */ +public void +doLayout() +{ + layout (); +} + +/*************************************************************************/ + +/** + * Lays out this component. This consists of resizing the sole child + * component to its perferred size. + * + * @deprecated This method is deprecated in favor of + * doLayout(). + */ +public void +layout() +{ + Component[] list = getComponents (); + if ((list != null) && (list.length > 0)) + { + Dimension dim = list[0].getPreferredSize (); + Dimension vp = getViewportSize (); + + if (dim.width < vp.width) + dim.width = vp.width; + + if (dim.height < vp.height) + dim.height = vp.height; + + ScrollPanePeer peer = (ScrollPanePeer) getPeer (); + if (peer != null) + peer.childResized (dim.width, dim.height); + + list[0].setSize (dim); + + Point p = getScrollPosition (); + if (p.x > dim.width) + p.x = dim.width; + if (p.y > dim.height) + p.y = dim.height; + + setScrollPosition (p); + + list[0].setLocation(new Point()); + } +} + +/*************************************************************************/ + +/** + * This method overrides its superclass method to ensure no layout + * manager is set for this container. ScrollPane's do + * not have layout managers. + * + * @param layoutManager Ignored + * @throws AWTError Always throws this error when called. + */ +public final void +setLayout(LayoutManager layoutManager) +{ + throw new AWTError("ScrollPane controls layout"); +} + +/*************************************************************************/ + +/** + * Prints all of the components in this container. + * + * @param graphics The desired graphics context for printing. + */ +public void +printComponents(Graphics graphics) +{ + super.printComponents(graphics); +} + +/*************************************************************************/ + +/** + * Returns a debug string for this object. + * + * @return A debug string for this object. + */ +public String +paramString() +{ + Insets insets = getInsets(); + return getName() + "," + + getX() + "," + + getY() + "," + + getWidth() + "x" + getHeight() + "," + + getIsValidString() + "," + + "ScrollPosition=(" + scrollPosition.x + "," + + scrollPosition.y + ")," + + "Insets=(" + insets.top + "," + + insets.left + "," + + insets.bottom + "," + + insets.right + ")," + + "ScrollbarDisplayPolicy=" + getScrollbarDisplayPolicyString() + "," + + "wheelScrollingEnabled=" + isWheelScrollingEnabled(); +} + +private String +getScrollbarDisplayPolicyString() +{ + if (getScrollbarDisplayPolicy() == 0) + return "as-needed"; + else if (getScrollbarDisplayPolicy() == 1) + return "always"; + else + return "never"; +} + +private String +getIsValidString() +{ + if (isValid()) + return "valid"; + else + return "invalid"; +} + + /** + * Tells whether or not an event is enabled. + * + * @since 1.4 + */ + protected boolean eventTypeEnabled (int type) + { + if (type == MouseEvent.MOUSE_WHEEL) + return wheelScrollingEnabled; + + return super.eventTypeEnabled (type); + } + + /** + * Tells whether or not wheel scrolling is enabled. + * + * @since 1.4 + */ + public boolean isWheelScrollingEnabled () + { + return wheelScrollingEnabled; + } + + /** + * Enables/disables wheel scrolling. + * + * @since 1.4 + */ + public void setWheelScrollingEnabled (boolean enable) + { + wheelScrollingEnabled = enable; + } + + protected class AccessibleAWTScrollPane extends AccessibleAWTContainer + { + private static final long serialVersionUID = 6100703663886637L; + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.SCROLL_PANE; + } + } + + /** + * Gets the AccessibleContext associated with this ScrollPane. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTScrollPane(); + return accessibleContext; + } + + /** + * Generate a unique name for this ScrollPane. + * + * @return A unique name for this ScrollPane. + */ + String generateName() + { + return "scrollpane" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_scrollpane_number++; + } + +} // class ScrollPane diff --git a/libjava/classpath/java/awt/ScrollPaneAdjustable.java b/libjava/classpath/java/awt/ScrollPaneAdjustable.java new file mode 100644 index 000000000..0f2986d59 --- /dev/null +++ b/libjava/classpath/java/awt/ScrollPaneAdjustable.java @@ -0,0 +1,241 @@ +/* ScrollPaneAdjustable.java -- Scrollbars for a ScrollPane + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.AdjustmentListener; +import java.io.Serializable; + +/** + * Need this class since the serialization spec for ScrollPane + * uses it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.4 + */ +public class ScrollPaneAdjustable + implements Adjustable, Serializable +{ + private static final long serialVersionUID = -3359745691033257079L; + + ScrollPane sp; + int orientation; + int value; + int minimum; + int maximum; + int visibleAmount; + int unitIncrement = 1; + int blockIncrement = 1; + AdjustmentListener adjustmentListener; + + private transient boolean valueIsAdjusting = false; + + ScrollPaneAdjustable (ScrollPane sp, int orientation) + { + this.sp = sp; + this.orientation = orientation; + } + + ScrollPaneAdjustable (ScrollPane sp, int orientation, int value, int minimum, + int maximum, int visibleAmount, int unitIncrement, + int blockIncrement) + { + this.sp = sp; + this.orientation = orientation; + this.value = value; + this.minimum = minimum; + this.maximum = maximum; + this.visibleAmount = visibleAmount; + this.unitIncrement = unitIncrement; + this.blockIncrement = blockIncrement; + } + + public void addAdjustmentListener (AdjustmentListener listener) + { + if (listener == null) + return; + adjustmentListener = AWTEventMulticaster.add (adjustmentListener, listener); + } + + public void removeAdjustmentListener (AdjustmentListener listener) + { + if (listener == null) + return; + adjustmentListener = AWTEventMulticaster.remove (adjustmentListener, listener); + } + + public AdjustmentListener[] getAdjustmentListeners () + { + return (AdjustmentListener[]) AWTEventMulticaster.getListeners + (adjustmentListener, AdjustmentListener.class); + } + + public int getBlockIncrement () + { + return blockIncrement; + } + + public int getMaximum () + { + return maximum; + } + + public int getMinimum () + { + return minimum; + } + + public int getOrientation () + { + return orientation; + } + + public int getUnitIncrement () + { + return unitIncrement; + } + + public int getValue () + { + return value; + } + + public int getVisibleAmount () + { + return visibleAmount; + } + + public void setBlockIncrement (int blockIncrement) + { + this.blockIncrement = blockIncrement; + } + + /** + * This method should never be called. + * + * @param maximum The maximum value to be set. + * @throws AWTError Always throws this error when called. + */ + public void setMaximum (int maximum) throws AWTError + { + throw new AWTError("Can be set by scrollpane only"); + } + + /** + * This method should never be called. + * + * @param minimum The minimum value to be set. + * @throws AWTError Always throws this error when called. + */ + public void setMinimum (int minimum) + { + throw new AWTError("Can be set by scrollpane only"); + } + + public void setUnitIncrement (int unitIncrement) + { + this.unitIncrement = unitIncrement; + } + + public void setValue (int value) + { + this.value = value; + + if (value < minimum) + minimum = value; + + if (value > maximum) + maximum = value; + } + + /** + * This method should never be called. + * + * @param visibleAmount The visible amount to be set. + * @throws AWTError Always throws this error when called. + */ + public void setVisibleAmount (int visibleAmount) + { + throw new AWTError("Can be set by scrollpane only"); + } + + public String paramString () + { + return paramStringHelper() + + ",[" + getMinimum() + ".." + getMaximum() + + "],val=" + getValue() + + ",vis=" + getVisibleAmount() + + ",unit=" + getUnitIncrement() + + ",block=" + getBlockIncrement() + + ",isAdjusting=" + valueIsAdjusting; + } + + private String paramStringHelper() + { + if (getOrientation() == HORIZONTAL) + return "horizontal"; + else + return "vertical"; + } + + public String toString() + { + return getClass().getName() + "[" + paramString() + "]"; + } + + /** + * Returns true if the value is in the process of changing. + * + * @since 1.4 + */ + public boolean getValueIsAdjusting () + { + return valueIsAdjusting; + } + + /** + * Sets the value of valueIsAdjusting. + * + * @since 1.4 + */ + public void setValueIsAdjusting (boolean valueIsAdjusting) + { + this.valueIsAdjusting = valueIsAdjusting; + } + +} // class ScrollPaneAdjustable diff --git a/libjava/classpath/java/awt/Scrollbar.java b/libjava/classpath/java/awt/Scrollbar.java new file mode 100644 index 000000000..4a9e36c31 --- /dev/null +++ b/libjava/classpath/java/awt/Scrollbar.java @@ -0,0 +1,815 @@ +/* Scrollbar.java -- AWT Scrollbar widget + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.awt.peer.ScrollbarPeer; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleValue; + +/** + * This class implements a scrollbar widget. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class Scrollbar extends Component implements Accessible, Adjustable +{ + // FIXME: Serialization readObject/writeObject + + /** + * Constant indicating that a scrollbar is horizontal. + */ + public static final int HORIZONTAL = 0; + + /** + * Constant indicating that a scrollbar is vertical. + */ + public static final int VERTICAL = 1; + + /** + * Serialization Constant. + */ + private static final long serialVersionUID = 8451667562882310543L; + + /** + * @serial The amount by which the value of the scrollbar is changed + * when incrementing in line mode. + */ + private int lineIncrement; + + /** + * @serial The amount by which the value of the scrollbar is changed + * when incrementing in page mode. + */ + private int pageIncrement; + + /** + * @serial The maximum value for this scrollbar + */ + private int maximum; + + /** + * @serial The minimum value for this scrollbar + */ + private int minimum; + + /** + * @serial The orientation of this scrollbar, which will be either + * the HORIZONTAL or VERTICAL constant + * from this class. + */ + private int orientation; + + /** + * @serial The current value of this scrollbar. + */ + private int value; + + /** + * @serial The width of the scrollbar's thumb, which is relative + * to the minimum and maximum value of the scrollbar. + */ + private int visibleAmount; + + /** + * List of AdjustmentListener's. + */ + private AdjustmentListener adjustment_listeners; + + /** + * true if the scrollbar is adjusting, false otherwise. + */ + private transient boolean valueIsAdjusting = false; + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_scrollbar_number; + + /** + * Initializes a new instance of Scrollbar with a + * vertical orientation and default values for all other parameters. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public Scrollbar() + { + this(VERTICAL); + } + + /** + * Initializes a new instance of Scrollbar with the + * specified orientation and default values for all other parameters. + * The orientation must be either the constant HORIZONTAL or + * VERTICAL from this class. An incorrect value will throw + * an exception. + * + * @param orientation The orientation of this scrollbar. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + * @exception IllegalArgumentException If the orientation value is not valid. + */ + public Scrollbar(int orientation) throws IllegalArgumentException + { + this(orientation, 0, 10, 0, 100); + } + + /** + * Initializes a new instance of Scrollbar with the + * specified parameters. The orientation must be either the constant + * HORIZONTAL or VERTICAL. An incorrect value + * will throw an exception. Inconsistent values for other parameters + * are silently corrected to valid values. + * + * @param orientation The orientation of this scrollbar. + * @param value The initial value of the scrollbar. + * @param visibleAmount The width of the scrollbar thumb. + * @param minimum The minimum value of the scrollbar. + * @param maximum The maximum value of the scrollbar. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + * @exception IllegalArgumentException If the orientation value is not valid. + */ + public Scrollbar(int orientation, int value, int visibleAmount, int minimum, + int maximum) throws IllegalArgumentException + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException(); + + if ((orientation != HORIZONTAL) && (orientation != VERTICAL)) + throw new IllegalArgumentException("Bad orientation value: " + + orientation); + + this.orientation = orientation; + + setValues(value, visibleAmount, minimum, maximum); + + // Default is 1 according to online docs. + lineIncrement = 1; + + // Default is 10 according to javadocs. + pageIncrement = 10; + } + + /** + * Returns the orientation constant for this object. + * + * @return The orientation constant for this object. + */ + public int getOrientation() + { + return orientation; + } + + /** + * Sets the orientation of this scrollbar to the specified value. This + * value must be either the constant HORIZONTAL or + * VERTICAL from this class or an exception will be thrown. + * + * @param orientation The new orientation value. + * + * @exception IllegalArgumentException If the orientation value is not valid. + */ + public void setOrientation(int orientation) + { + if ((orientation != HORIZONTAL) && (orientation != VERTICAL)) + throw new IllegalArgumentException("Bad orientation value: " + + orientation); + + // FIXME: Communicate to peer? Or must this be called before peer creation? + this.orientation = orientation; + } + + /** + * Returns the current value for this scrollbar. + * + * @return The current value for this scrollbar. + */ + public int getValue() + { + return value; + } + + /** + * Sets the current value for this scrollbar to the specified value. + * If this is inconsistent with the minimum and maximum values for this + * scrollbar, the value is silently adjusted. + * + * @param value The new value for this scrollbar. + */ + public void setValue(int value) + { + setValues(value, visibleAmount, minimum, maximum); + } + + /** + * Returns the maximum value for this scrollbar. + * + * @return The maximum value for this scrollbar. + */ + public int getMaximum() + { + return maximum; + } + + /** + * Sets the maximum value for this scrollbar to the specified value. + * If the value is less than the current minimum value, it is silent + * set to equal the minimum value. + * + * @param maximum The new maximum value for this scrollbar. + */ + public void setMaximum(int maximum) + { + setValues(value, visibleAmount, minimum, maximum); + } + + /** + * Returns the minimum value for this scrollbar. + * + * @return The minimum value for this scrollbar. + */ + public int getMinimum() + { + return minimum; + } + + /** + * Sets the minimum value for this scrollbar to the specified value. If + * this is not consistent with the current value and maximum, it is + * silently adjusted to be consistent. + * + * @param minimum The new minimum value for this scrollbar. + */ + public void setMinimum(int minimum) + { + setValues(value, visibleAmount, minimum, maximum); + } + + /** + * Returns the width of the scrollbar's thumb, in units relative to the + * maximum and minimum value of the scrollbar. + * + * @return The width of the scrollbar's thumb. + */ + public int getVisibleAmount() + { + return getVisible(); + } + + /** + * Returns the width of the scrollbar's thumb, in units relative to the + * maximum and minimum value of the scrollbar. + * + * @return The width of the scrollbar's thumb. + * + * @deprecated This method is deprecated in favor of + * getVisibleAmount(). + */ + public int getVisible() + { + return visibleAmount; + } + + /** + * Sets the width of the scrollbar's thumb, in units relative to the + * maximum and minimum value of the scrollbar. + * + * @param visibleAmount The new visible amount value of the scrollbar. + */ + public void setVisibleAmount(int visibleAmount) + { + setValues(value, visibleAmount, minimum, maximum); + } + + /** + * Sets the current value, visible amount, minimum, and maximum for this + * scrollbar. These values are adjusted to be internally consistent + * if necessary. + * + * @param value The new value for this scrollbar. + * @param visibleAmount The new visible amount for this scrollbar. + * @param minimum The new minimum value for this scrollbar. + * @param maximum The new maximum value for this scrollbar. + */ + public synchronized void setValues(int value, int visibleAmount, + int minimum, int maximum) + { + if (visibleAmount <= 0) + visibleAmount = 1; + + if (maximum <= minimum) + maximum = minimum + 1; + + if (value < minimum) + value = minimum; + + if (visibleAmount > maximum - minimum) + visibleAmount = maximum - minimum; + + // According to documentation, the actual maximum + // value is (maximum - visibleAmount) + if (value > maximum - visibleAmount) + value = maximum - visibleAmount; + + ScrollbarPeer peer = (ScrollbarPeer) getPeer(); + if (peer != null + && (this.value != value || this.visibleAmount != visibleAmount + || this.minimum != minimum || this.maximum != maximum)) + peer.setValues(value, visibleAmount, minimum, maximum); + + this.value = value; + this.visibleAmount = visibleAmount; + this.minimum = minimum; + this.maximum = maximum; + } + + /** + * Returns the value added or subtracted when the user activates the scrollbar + * scroll by a "unit" amount. + * + * @return The unit increment value. + */ + public int getUnitIncrement() + { + return getLineIncrement(); + } + + /** + * Returns the value added or subtracted when the user selects the scrollbar + * scroll by a "unit" amount control. + * + * @return The unit increment value. + * + * @deprecated This method is deprecated in favor of + * getUnitIncrement(). + */ + public int getLineIncrement() + { + return lineIncrement; + } + + /** + * Sets the value added or subtracted to the scrollbar value when the + * user selects the scroll by a "unit" amount control. + * + * @param unitIncrement The new unit increment amount. + */ + public synchronized void setUnitIncrement(int unitIncrement) + { + setLineIncrement(unitIncrement); + } + + /** + * Sets the value added or subtracted to the scrollbar value when the + * user selects the scroll by a "unit" amount control. + * + * @param lineIncrement The new unit increment amount. + * + * @deprecated This method is deprecated in favor of + * setUnitIncrement(). + */ + public void setLineIncrement(int lineIncrement) + { + if (lineIncrement < 0) + throw new IllegalArgumentException("Unit increment less than zero."); + + if (lineIncrement == 0) + lineIncrement = 1; + + if (lineIncrement == this.lineIncrement) + return; + + this.lineIncrement = lineIncrement; + + ScrollbarPeer peer = (ScrollbarPeer) getPeer(); + if (peer != null) + peer.setLineIncrement(this.lineIncrement); + } + + /** + * Returns the value added or subtracted when the user activates the scrollbar + * scroll by a "block" amount. + * + * @return The block increment value. + */ + public int getBlockIncrement() + { + return getPageIncrement(); + } + + /** + * Returns the value added or subtracted when the user selects the scrollbar + * scroll by a "block" amount control. + * + * @return The block increment value. + * + * @deprecated This method is deprecated in favor of + * getBlockIncrement(). + */ + public int getPageIncrement() + { + return pageIncrement; + } + + /** + * Sets the value added or subtracted to the scrollbar value when the + * user selects the scroll by a "block" amount control. + * + * @param blockIncrement The new block increment amount. + */ + public synchronized void setBlockIncrement(int blockIncrement) + { + setPageIncrement(blockIncrement); + } + + /** + * Sets the value added or subtracted to the scrollbar value when the + * user selects the scroll by a "block" amount control. + * + * @param pageIncrement The new block increment amount. + * + * @deprecated This method is deprecated in favor of + * setBlockIncrement(). + */ + public void setPageIncrement(int pageIncrement) + { + if (pageIncrement < 0) + throw new IllegalArgumentException("Block increment less than zero."); + + if (pageIncrement == 0) + pageIncrement = 1; + + if (pageIncrement == this.pageIncrement) + return; + + this.pageIncrement = pageIncrement; + + ScrollbarPeer peer = (ScrollbarPeer) getPeer(); + if (peer != null) + peer.setPageIncrement(this.pageIncrement); + } + + /** + * Notifies this object to create its native peer. + */ + public synchronized void addNotify() + { + if (peer == null) + peer = getToolkit().createScrollbar(this); + super.addNotify(); + } + + /** + * Adds a new adjustment listener to the list of registered listeners + * for this object. + * + * @param listener The listener to add. + */ + public synchronized void addAdjustmentListener(AdjustmentListener listener) + { + adjustment_listeners = AWTEventMulticaster.add(adjustment_listeners, + listener); + enableEvents(AWTEvent.ADJUSTMENT_EVENT_MASK); + } + + /** + * Removes the specified listener from the list of registered listeners + * for this object. + * + * @param listener The listener to remove. + */ + public synchronized void removeAdjustmentListener(AdjustmentListener listener) + { + adjustment_listeners = AWTEventMulticaster.remove(adjustment_listeners, + listener); + } + + /** + * Processes events for this scrollbar. It does this by calling + * processAdjustmentEvent() if the event is an instance of + * AdjustmentEvent, otherwise it calls the superclass to + * process the event. + * + * @param event The event to process. + */ + protected void processEvent(AWTEvent event) + { + if (event instanceof AdjustmentEvent) + processAdjustmentEvent((AdjustmentEvent) event); + else + super.processEvent(event); + } + + /** + * Processes adjustment events for this object by dispatching them to + * any registered listeners. Note that this method will only be called + * if adjustment events are enabled. This will happen automatically if + * any listeners are registered. Otherwise, it can be enabled by a + * call to enableEvents(). + * + * @param event The event to process. + */ + protected void processAdjustmentEvent(AdjustmentEvent event) + { + value = event.getValue(); + if (adjustment_listeners != null) + adjustment_listeners.adjustmentValueChanged(event); + } + + /** + * Package private method to determine whether to call + * processEvent() or not. Will handle events from peer and update + * the current value. + */ + void dispatchEventImpl(AWTEvent e) + { + if (e.id <= AdjustmentEvent.ADJUSTMENT_LAST + && e.id >= AdjustmentEvent.ADJUSTMENT_FIRST) + { + AdjustmentEvent ae = (AdjustmentEvent) e; + boolean adjusting = ae.getValueIsAdjusting(); + if (adjusting) + setValueIsAdjusting(true); + try + { + setValue(((AdjustmentEvent) e).getValue()); + if (adjustment_listeners != null + || (eventMask & AWTEvent.ADJUSTMENT_EVENT_MASK) != 0) + processEvent(e); + } + finally + { + if (adjusting) + setValueIsAdjusting(false); + } + } + else + super.dispatchEventImpl(e); + } + + /** + * Returns a debugging string for this object. + * + * @return A debugging string for this object. + */ + protected String paramString() + { + return ("value=" + getValue() + ",visibleAmount=" + getVisibleAmount() + + ",minimum=" + getMinimum() + ",maximum=" + getMaximum() + + ",pageIncrement=" + pageIncrement + ",lineIncrement=" + + lineIncrement + ",orientation=" + + (orientation == HORIZONTAL ? "HORIZONTAL" : "VERTICAL") + + super.paramString()); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this Scrollbar. FooListeners are registered using the + * addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + */ + public T[] getListeners(Class listenerType) + { + if (listenerType == AdjustmentListener.class) + return AWTEventMulticaster.getListeners(adjustment_listeners, + listenerType); + + return super.getListeners(listenerType); + } + + /** + * Returns an array of all registered adjustment listeners. + */ + public AdjustmentListener[] getAdjustmentListeners() + { + return (AdjustmentListener[]) getListeners(AdjustmentListener.class); + } + + /** + * Returns true if the value is in the process of changing. + * + * @since 1.4 + */ + public boolean getValueIsAdjusting() + { + return valueIsAdjusting; + } + + /** + * Sets the value of valueIsAdjusting. + * + * @since 1.4 + */ + public void setValueIsAdjusting(boolean valueIsAdjusting) + { + this.valueIsAdjusting = valueIsAdjusting; + } + + /** + * Generate a unique name for this scroll bar. + * + * @return A unique name for this scroll bar. + */ + String generateName() + { + return "scrollbar" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_scrollbar_number++; + } + + /** + * This class provides accessibility support for the + * scrollbar. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + protected class AccessibleAWTScrollBar extends AccessibleAWTComponent + implements AccessibleValue + { + /** + * Serialization constant to match JDK 1.5 + */ + private static final long serialVersionUID = -344337268523697807L; + + /** + * Returns the role of this accessible object. + * + * @return the instance of AccessibleRole, + * which describes this object. + * + * @see javax.accessibility.AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.SCROLL_BAR; + } + + /** + * Returns the state set of this accessible object. + * + * @return a set of AccessibleStates which + * represent the current state of the accessible object. + * + * @see javax.accessibility.AccessibleState + * @see javax.accessibility.AccessibleStateSet + */ + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (getOrientation() == HORIZONTAL) + states.add(AccessibleState.HORIZONTAL); + else + states.add(AccessibleState.VERTICAL); + if (getValueIsAdjusting()) + states.add(AccessibleState.BUSY); + return states; + } + + /** + * Returns an implementation of the AccessibleValue + * interface for this accessible object. In this case, the + * current instance is simply returned (with a more appropriate + * type), as it also implements the accessible value as well as + * the context. + * + * @return the accessible value associated with this context. + * + * @see javax.accessibility.AccessibleValue + */ + public AccessibleValue getAccessibleValue() + { + return this; + } + + /** + * Returns the current value of this accessible object. + * In this case, this is the same as the value for + * the scrollbar, wrapped in an Integer + * object. + * + * @return the numeric value of this scrollbar. + * + * @see javax.accessibility.AccessibleValue#getCurrentAccessibleValue() + */ + public Number getCurrentAccessibleValue() + { + return new Integer(getValue()); + } + + /** + * Sets the current value of this accessible object + * to that supplied. In this case, the value of the + * scrollbar is set, and this method always returns + * true. + * + * @param number the new accessible value. + * + * @return true if the value was set. + * + * @see javax.accessibility.AccessibleValue#setCurrentAccessibleValue(java.lang.Number) + */ + public boolean setCurrentAccessibleValue(Number number) + { + setValue(number.intValue()); + return true; + } + + /** + * Returns the minimum acceptable accessible value used + * by this object. In this case, this is the same as + * the minimum value of the scrollbar, wrapped in an + * object. + * + * @return the minimum value of this scrollbar. + * + * @see javax.accessibility.AccessibleValue#getMinimumAccessibleValue() + */ + public Number getMinimumAccessibleValue() + { + return new Integer(getMinimum()); + } + + /** + * Returns the maximum acceptable accessible value used + * by this object. In this case, this is the same as + * the maximum value of the scrollbar, wrapped in an + * object. + * + * @return the maximum value of this scrollbar. + * + * @see javax.accessibility.AccessibleValue#getMaximumAccessibleValue() + */ + public Number getMaximumAccessibleValue() + { + return new Integer(getMaximum()); + } + } + + /** + * Gets the AccessibleContext associated with this Scrollbar. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTScrollBar(); + + return accessibleContext; + } +} diff --git a/libjava/classpath/java/awt/Shape.java b/libjava/classpath/java/awt/Shape.java new file mode 100644 index 000000000..495a980cb --- /dev/null +++ b/libjava/classpath/java/awt/Shape.java @@ -0,0 +1,205 @@ +/* Shape.java -- the classic Object-Oriented shape interface + Copyright (C) 1999, 2002, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * This interface represents an abstract shape. The shape is described by + * a {@link PathIterator}, and has callbacks for determining bounding box, + * where points and rectangles lie in relation to the shape, and tracing + * the trajectory. + * + *

A point is inside if it is completely inside, or on the boundary and + * adjacent points in the increasing x or y direction are completely inside. + * Unclosed shapes are considered as implicitly closed when performing + * contains or intersects. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see PathIterator + * @see AffineTransform + * @see java.awt.geom.FlatteningPathIterator + * @see java.awt.geom.GeneralPath + * @since 1.0 + * @status updated to 1.4 + */ +public interface Shape +{ + /** + * Returns a Rectange that bounds the shape. There is no + * guarantee that this is the minimum bounding box, particularly if + * the shape overflows the finite integer range of a bound. Generally, + * getBounds2D returns a tighter bound. + * + * @return the shape's bounding box + * @see #getBounds2D() + */ + Rectangle getBounds(); + + /** + * Returns a high precision bounding box of the shape. There is no guarantee + * that this is the minimum bounding box, but at least it never overflows. + * + * @return the shape's bounding box + * @see #getBounds() + * @since 1.2 + */ + Rectangle2D getBounds2D(); + + /** + * Test if the coordinates lie in the shape. + * + * @param x the x coordinate + * @param y the y coordinate + * @return true if (x,y) lies inside the shape + * @since 1.2 + */ + boolean contains(double x, double y); + + /** + * Test if the point lie in the shape. + * + * @param p the high-precision point + * @return true if p lies inside the shape + * @throws NullPointerException if p is null + * @since 1.2 + */ + boolean contains(Point2D p); + + /** + * Test if a high-precision rectangle intersects the shape. This is true + * if any point in the rectangle is in the shape, with the caveat that the + * operation may include high probability estimates when the actual + * calculation is prohibitively expensive. The {@link java.awt.geom.Area} + * class can be used for more precise answers. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle, undefined results if negative + * @param h the height of the rectangle, undefined results if negative + * @return true if the rectangle intersects this shape + * @see java.awt.geom.Area + * @since 1.2 + */ + boolean intersects(double x, double y, double w, double h); + + /** + * Test if a high-precision rectangle intersects the shape. This is true + * if any point in the rectangle is in the shape, with the caveat that the + * operation may include high probability estimates when the actual + * calculation is prohibitively expensive. The {@link java.awt.geom.Area} + * class can be used for more precise answers. + * + * @param r the rectangle + * @return true if the rectangle intersects this shape + * @throws NullPointerException if r is null + * @see #intersects(double, double, double, double) + * @since 1.2 + */ + boolean intersects(Rectangle2D r); + + /** + * Test if a high-precision rectangle lies completely in the shape. This is + * true if all points in the rectangle are in the shape, with the caveat + * that the operation may include high probability estimates when the actual + * calculation is prohibitively expensive. The {@link java.awt.geom.Area} + * class can be used for more precise answers. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle, undefined results if negative + * @param h the height of the rectangle, undefined results if negative + * @return true if the rectangle is contained in this shape + * @see java.awt.geom.Area + * @since 1.2 + */ + boolean contains(double x, double y, double w, double h); + + /** + * Test if a high-precision rectangle lies completely in the shape. This is + * true if all points in the rectangle are in the shape, with the caveat + * that the operation may include high probability estimates when the actual + * calculation is prohibitively expensive. The {@link java.awt.geom.Area} + * class can be used for more precise answers. + * + * @param r the rectangle + * @return true if the rectangle is contained in this shape + * @throws NullPointerException if r is null + * @see #contains(double, double, double, double) + * @since 1.2 + */ + boolean contains(Rectangle2D r); + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. It is recommended, but + * not required, that the Shape isolate iterations from future changes to + * the boundary, and document this fact. + * + * @param transform an optional transform to apply to the + * iterator (null permitted). + * @return a new iterator over the boundary + * @since 1.2 + */ + PathIterator getPathIterator(AffineTransform transform); + + /** + * Return an iterator along the flattened version of the shape boundary. + * Only SEG_MOVETO, SEG_LINETO, and SEG_CLOSE points are returned in the + * iterator. The flatness parameter controls how far points are allowed to + * differ from the real curve; although a limit on accuracy may cause this + * parameter to be enlarged if needed. + * + *

If the optional transform is provided, the iterator is transformed + * accordingly. Each call returns a new object, independent from others in + * use. It is recommended, but not required, that the Shape isolate + * iterations from future changes to the boundary, and document this fact. + * + * @param transform an optional transform to apply to the + * iterator (null permitted). + * @param flatness the maximum distance for deviation from the real boundary + * @return a new iterator over the boundary + * @since 1.2 + */ + PathIterator getPathIterator(AffineTransform transform, double flatness); +} diff --git a/libjava/classpath/java/awt/Stroke.java b/libjava/classpath/java/awt/Stroke.java new file mode 100644 index 000000000..1a4c61cfb --- /dev/null +++ b/libjava/classpath/java/awt/Stroke.java @@ -0,0 +1,65 @@ +/* Stroke.java -- a stroked outline of a shape + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +/** + * This interface allows a Graphics2D to grab the outline of a shape, as if + * stroked by a marking pen of appropriate size and shape. The area inked + * by the pen is the area of this stroke. Anything in the graphic which + * traces an outline will use this stroke, such as drawLine. + * Strokes must be immutable, because the graphics object does not clone + * them. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see BasicStroke + * @see Graphics2D#setStroke(Stroke) + * @since 1.1 + * @status updated to 1.4 + */ +public interface Stroke +{ + /** + * Returns a shape which outlines the boundary of the given shape, in + * effect converting the infinitely thin line into a new shape. + * + * @param s the shape to stroke + * @return the stroked outline shape + */ + Shape createStrokedShape(Shape s); +} // interface Stroke diff --git a/libjava/classpath/java/awt/SystemColor.java b/libjava/classpath/java/awt/SystemColor.java new file mode 100644 index 000000000..95bcaa696 --- /dev/null +++ b/libjava/classpath/java/awt/SystemColor.java @@ -0,0 +1,462 @@ +/* SystemColor.java -- access dynamic system color values + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.ColorModel; +import java.io.Serializable; + +/** + * This class contains the various "system colors" in use by the native + * windowing system. The getRGB() method is dynamic on systems + * which support dynamic system color changes, and most methods in the + * superclass are written to use this dynamic value when reporting colors. + * However, the equals() method is not dynamic, and does not + * track the actual color of instances in this class. This means that equals + * may give surprising results; you are better off relying on getRGB. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public final class SystemColor extends Color implements Serializable +{ + // Implementation note: To be serial compatible with JDK, this class must + // violate the semantic meaning of super.value to be one of the + // NUM_COLORS constants instead of the actual RGB value. Hence there are + // a lot of ugly workarounds in Color and in this class. I would have + // designed it MUCH differently, making a separate id field in this class. + + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4503142729533789064L; + + /** + * Array index of the desktop color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #desktop + */ + public static final int DESKTOP = 0; + + /** + * Array index of the active caption color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #activeCaption + */ + public static final int ACTIVE_CAPTION = 1; + + /** + * Array index of the active caption text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #activeCaptionText + */ + public static final int ACTIVE_CAPTION_TEXT = 2; + + /** + * Array index of the active caption border color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #activeCaptionBorder + */ + public static final int ACTIVE_CAPTION_BORDER = 3; + + /** + * Array index of the inactive caption color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #inactiveCaption + */ + public static final int INACTIVE_CAPTION = 4; + + /** + * Array index of the inactive caption text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #inactiveCaptionText + */ + public static final int INACTIVE_CAPTION_TEXT = 5; + + /** + * Array index of the inactive caption border color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #inactiveCaptionBorder + */ + public static final int INACTIVE_CAPTION_BORDER = 6; + + /** + * Array index of the window background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #window + */ + public static final int WINDOW = 7; + + /** + * Array index of the window border color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #windowBorder + */ + public static final int WINDOW_BORDER = 8; + + /** + * Array index of the window text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #windowText + */ + public static final int WINDOW_TEXT = 9; + + /** + * Array index of the menu background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #menu + */ + public static final int MENU = 10; + + /** + * Array index of the menu text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #menuText + */ + public static final int MENU_TEXT = 11; + + /** + * Array index of the text background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #text + */ + public static final int TEXT = 12; + + /** + * Array index of the text foreground color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #textText + */ + public static final int TEXT_TEXT = 13; + + /** + * Array index of the highlighted text background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #textHighlight + */ + public static final int TEXT_HIGHLIGHT = 14; + + /** + * Array index of the highlighted text foreground color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #textHighlightText + */ + public static final int TEXT_HIGHLIGHT_TEXT = 15; + + /** + * Array index of the inactive text foreground color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #textInactiveText + */ + public static final int TEXT_INACTIVE_TEXT = 16; + + /** + * Array index of the control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #control + */ + public static final int CONTROL = 17; + + /** + * Array index of the control text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlText + */ + public static final int CONTROL_TEXT = 18; + + /** + * Array index of the highlighted control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlHighlight + */ + public static final int CONTROL_HIGHLIGHT = 19; + + /** + * Array index of the lightly highlighted control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlLtHighlight + */ + public static final int CONTROL_LT_HIGHLIGHT = 20; + + /** + * Array index of the shadowed control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlShadow + */ + public static final int CONTROL_SHADOW = 21; + + /** + * Array index of the darkly shadowed control background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #controlDkShadow + */ + public static final int CONTROL_DK_SHADOW = 22; + + /** + * Array index of the scrollbar background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #scrollbar + */ + public static final int SCROLLBAR = 23; + + /** + * Array index of the info background color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #info + */ + public static final int INFO = 24; + + /** + * Array index of the info text color. Used by + * {@link Toolkit#loadSystemColors(int[])}. + * + * @see #infoText + */ + public static final int INFO_TEXT = 25; + + /** + * The number of system colors. Used by + * {@link Toolkit#loadSystemColors(int[])}. + */ + public static final int NUM_COLORS = 26; + + /** + * The internal array used to dynamically update getRGB(). + */ + private static final int[] colors = new int[NUM_COLORS]; + + /** The desktop color. */ + public static final SystemColor desktop + = new SystemColor(DESKTOP); + + /** The active caption background color. */ + public static final SystemColor activeCaption + = new SystemColor(ACTIVE_CAPTION); + + /** The active caption text color. */ + public static final SystemColor activeCaptionText + = new SystemColor(ACTIVE_CAPTION_TEXT); + + /** The active caption border color. */ + public static final SystemColor activeCaptionBorder + = new SystemColor(ACTIVE_CAPTION_BORDER); + + /** The inactive caption background color. */ + public static final SystemColor inactiveCaption + = new SystemColor(INACTIVE_CAPTION); + + /** The inactive caption text color. */ + public static final SystemColor inactiveCaptionText + = new SystemColor(INACTIVE_CAPTION_TEXT); + + /** The inactive caption border color. */ + public static final SystemColor inactiveCaptionBorder + = new SystemColor(INACTIVE_CAPTION_BORDER); + + /** The window background color. */ + public static final SystemColor window + = new SystemColor(WINDOW); + + /** The window border color. */ + public static final SystemColor windowBorder + = new SystemColor(WINDOW_BORDER); + + /** The window text color. */ + public static final SystemColor windowText + = new SystemColor(WINDOW_TEXT); + + /** The menu background color. */ + public static final SystemColor menu + = new SystemColor(MENU); + + /** The menu text color. */ + public static final SystemColor menuText + = new SystemColor(MENU_TEXT); + + /** The text background color. */ + public static final SystemColor text + = new SystemColor(TEXT); + + /** The text foreground color. */ + public static final SystemColor textText + = new SystemColor(TEXT_TEXT); + + /** The highlighted text background color. */ + public static final SystemColor textHighlight + = new SystemColor(TEXT_HIGHLIGHT); + + /** The highlighted text foreground color. */ + public static final SystemColor textHighlightText + = new SystemColor(TEXT_HIGHLIGHT_TEXT); + + /** The inactive text color. */ + public static final SystemColor textInactiveText + = new SystemColor(TEXT_INACTIVE_TEXT); + + /** The control background color. */ + public static final SystemColor control + = new SystemColor(CONTROL); + + /** The control text color. */ + public static final SystemColor controlText + = new SystemColor(CONTROL_TEXT); + + /** The control highlight color. */ + public static final SystemColor controlHighlight + = new SystemColor(CONTROL_HIGHLIGHT); + + /** The control light highlight color. */ + public static final SystemColor controlLtHighlight + = new SystemColor(CONTROL_LT_HIGHLIGHT); + + /** The control shadow color. */ + public static final SystemColor controlShadow + = new SystemColor(CONTROL_SHADOW); + + /** The control dark shadow color. */ + public static final SystemColor controlDkShadow + = new SystemColor(CONTROL_DK_SHADOW); + + /** The scrollbar color. */ + public static final SystemColor scrollbar + = new SystemColor(SCROLLBAR); + + /** The info text background color. */ + public static final SystemColor info + = new SystemColor(INFO); + + /** The info text foreground color. */ + public static final SystemColor infoText + = new SystemColor(INFO_TEXT); + + /** + * Construct a system color which is dynamically updated. + * + * @param id the color id + */ + private SystemColor(int id) + { + // Note: See Color#Color(int, boolean) to explain why we use this + // particular constructor. + super(id, true); + } + + /** + * Returns the RGB value for this color, in the sRGB color space. The blue + * value will be in bits 0-7, green in 8-15, red in 6-23, and the alpha + * value (bits 24-31) is 0xff. This is dynamically updated, so it may not + * match the results of getRed(), getGreen(), or + * getBlue(). + * + * @return the current RGB value + */ + public int getRGB() + { + Toolkit.getDefaultToolkit().loadSystemColors(colors); + return colors[value] | ALPHA_MASK; + } + + /** + * Returns a paint context, used for filling areas of a raster scan with + * the current value of this system color. Since the system colors may be + * dynamically updated, the returned value may not always be the same; but + * as the system color is solid, the context does not need any of the + * passed parameters to do its job. + * + * @param cm the requested color model + * @param deviceBounds the bounding box in device coordinates, ignored + * @param userBounds the bounding box in user coordinates, ignored + * @param xform the bounds transformation, ignored + * @param hints any rendering hints, ignored + * @return a context for painting this solid color + */ + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, + RenderingHints hints) + { + Toolkit.getDefaultToolkit().loadSystemColors(colors); + int color = colors[value] | ALPHA_MASK; + if (context == null || color != context.color || !context.getColorModel().equals(cm)) + context = new ColorPaintContext(cm,color); + return context; + } + + /** + * Returns a string describing this color. This is in the format + * "java.awt.SystemColor[i=" + index + ']', where index is one of the + * integer constants of this class. Unfortunately, this description + * does not describe the current value of the color; for that you should + * use new Color(syscolor.getRGB()).toString(). + * + * @return a string describing this color + */ + public String toString() + { + return "java.awt.SystemColor[i=" + value + ']'; + } +} // class SystemColor diff --git a/libjava/classpath/java/awt/TextArea.java b/libjava/classpath/java/awt/TextArea.java new file mode 100644 index 000000000..265f8c8b6 --- /dev/null +++ b/libjava/classpath/java/awt/TextArea.java @@ -0,0 +1,631 @@ +/* TextArea.java -- A multi-line text entry component + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.KeyEvent; +import java.awt.peer.ComponentPeer; +import java.awt.peer.TextAreaPeer; +import java.util.HashSet; +import java.util.Set; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleStateSet; + + +/** + * A TextArea is a text component capable of displaying multiple lines + * of user-editable text. A TextArea handles its own scrolling and + * can display vertical and horizontal scrollbars as navigation aids. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class TextArea extends TextComponent implements java.io.Serializable +{ + /** + * Display both horiztonal and vertical scroll bars. + */ + public static final int SCROLLBARS_BOTH = 0; + + /** + * Display vertical scroll bar only. + */ + public static final int SCROLLBARS_VERTICAL_ONLY = 1; + + /** + * Display horizatonal scroll bar only. + */ + public static final int SCROLLBARS_HORIZONTAL_ONLY = 2; + + /** + * Do not display scrollbars. + */ + public static final int SCROLLBARS_NONE = 3; + + /** + * Serialization constant. + */ + private static final long serialVersionUID = 3692302836626095722L; + + /** + * @serial The number of columns used in this text area's preferred + * and minimum size calculations. + */ + private int columns; + + /** + * @serial The number of rows used in this text area's preferred and + * minimum size calculations. + */ + private int rows; + + /** + * @serial The scrollbar display policy. One of SCROLLBARS_BOTH, + * SCROLLBARS_VERTICAL_ONLY, SCROLLBARS_HORIZONTAL_ONLY, + * SCROLLBARS_NONE. + */ + private int scrollbarVisibility; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_text_number; + + /** + * Initialize a new instance of TextArea that is empty. + * Conceptually the TextArea has 0 rows and 0 columns + * but its initial bounds are defined by its peer or by the + * container in which it is packed. Both horizontal and vertical + * scrollbars will be displayed. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea () + { + this ("", 0, 0, SCROLLBARS_BOTH); + } + + /** + * Initialize a new instance of TextArea that contains + * the specified text. Conceptually the TextArea has 0 + * rows and 0 columns but its initial bounds are defined by its peer + * or by the container in which it is packed. Both horizontal and + * veritcal scrollbars will be displayed. The TextArea initially contains + * the specified text. If text specified as null, it will + * be set to "". + * + * @param text The text to display in this text area (null permitted). + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea (String text) + { + this (text, 0, 0, SCROLLBARS_BOTH); + } + + /** + * Initialize a new instance of TextArea that is empty + * and can display the specified number of rows and columns of text, + * without the need to scroll. Both horizontal and vertical + * scrollbars will be displayed. + * + * @param rows The number of rows in this text area. + * @param columns The number of columns in this text area. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea (int rows, int columns) + { + this ("", rows, columns, SCROLLBARS_BOTH); + } + + /** + * Initialize a new instance of TextArea that can + * display the specified number of rows and columns of text, without + * the need to scroll. The TextArea initially contains the + * specified text. If text specified as null, it will + * be set to "". + * + * @param text The text to display in this text area (null permitted). + * @param rows The number of rows in this text area. + * @param columns The number of columns in this text area. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea (String text, int rows, int columns) + { + this (text, rows, columns, SCROLLBARS_BOTH); + } + + /** + * Initialize a new instance of TextArea that initially + * contains the specified text. The TextArea can display the + * specified number of rows and columns of text, without the need to + * scroll. This constructor allows specification of the scroll bar + * display policy. The TextArea initially contains the specified text. + * If text specified as null, it will be set to "". + * + * @param text The text to display in this text area (null permitted). + * @param rows The number of rows in this text area. + * @param columns The number of columns in this text area. + * @param scrollbarVisibility The scroll bar display policy. One of + * SCROLLBARS_BOTH, SCROLLBARS_VERTICAL_ONLY, + * SCROLLBARS_HORIZONTAL_ONLY, SCROLLBARS_NONE. + * + * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true + */ + public TextArea (String text, int rows, int columns, int scrollbarVisibility) + { + super (text); + + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); + + if (rows < 0) + this.rows = 0; + else + this.rows = rows; + + if (columns < 0) + this.columns = 0; + else + this.columns = columns; + + if (scrollbarVisibility < 0 || scrollbarVisibility > 4) + this.scrollbarVisibility = SCROLLBARS_BOTH; + else + this.scrollbarVisibility = scrollbarVisibility; + + // TextAreas need to receive tab key events so we override the + // default forward and backward traversal key sets. + Set s = new HashSet (); + s.add (AWTKeyStroke.getAWTKeyStroke (KeyEvent.VK_TAB, + KeyEvent.CTRL_DOWN_MASK)); + setFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, s); + s = new HashSet (); + s.add (AWTKeyStroke.getAWTKeyStroke (KeyEvent.VK_TAB, + KeyEvent.SHIFT_DOWN_MASK + | KeyEvent.CTRL_DOWN_MASK)); + setFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, s); + } + + /** + * Retrieve the number of columns that this text area would prefer + * to display. This value may or may not correspond to the number + * of columns that are actually displayed. + * + * @return The preferred number of columns. + */ + public int getColumns () + { + return columns; + } + + /** + * Set the preferred number of columns for this text area. This + * method does not cause the number of columns displayed by the text + * area to be updated, if the text area is currently visible. + * + * @param columns The preferred number of columns. + * + * @exception IllegalArgumentException If columns is less than zero. + */ + public synchronized void setColumns (int columns) + { + if (columns < 0) + throw new IllegalArgumentException ("Value is less than zero: " + + columns); + + this.columns = columns; + } + + /** + * Retrieve the number of rows that this text area would prefer to + * display. This value may or may not correspond to the number of + * rows that are actually displayed. + * + * @return The preferred number of rows. + */ + public int getRows () + { + return rows; + } + + /** + * Set the preferred number of rows for this text area. This method + * does not cause the number of columns displayed by the text area + * to be updated, if the text area is currently visible. + * + * @param rows The preferred number of rows. + * + * @exception IllegalArgumentException If rows is less than zero. + */ + public synchronized void setRows (int rows) + { + if (rows < 1) + throw new IllegalArgumentException ("Value is less than one: " + rows); + + this.rows = rows; + } + + /** + * Retrieve the minimum size for this text area. + * + * @return The minimum size for this text field. + */ + public Dimension getMinimumSize () + { + return getMinimumSize (getRows (), getColumns ()); + } + + /** + * Retrieve the minimum size for this text area. If the minimum + * size has been set, then rows and columns are used in the calculation. + * + * @param rows The number of rows to use in the minimum size + * calculation. + * @param columns The number of columns to use in the minimum size + * calculation. + * + * @return The minimum size for this text area. + */ + public Dimension getMinimumSize (int rows, int columns) + { + return minimumSize (rows, columns); + } + + /** + * Retrieve the minimum size for this text area. + * + * @return The minimum size for this text area. + * + * @deprecated This method is deprecated in favor of + * getMinimumSize (). + */ + public Dimension minimumSize () + { + return minimumSize (getRows (), getColumns ()); + } + + /** + * Retrieve the minimum size for this text area. If the minimum + * size has been set, then rows and columns are used in the calculation. + * + * @param rows The number of rows to use in the minimum size + * calculation. + * @param columns The number of columns to use in the minimum size + * calculation. + * + * @return The minimum size for this text area. + * + * @deprecated This method is deprecated in favor of + * getMinimumSize (int, int). + */ + public Dimension minimumSize (int rows, int columns) + { + if (isMinimumSizeSet()) + return new Dimension(minSize); + + TextAreaPeer peer = (TextAreaPeer) getPeer (); + if (peer == null) + return new Dimension (getWidth(), getHeight()); + + return peer.getMinimumSize (rows, columns); + } + + /** + * Retrieve the preferred size for this text area. + * + * @return The preferred size for this text field. + */ + public Dimension getPreferredSize () + { + return getPreferredSize (getRows (), getColumns ()); + } + + /** + * Retrieve the preferred size for this text area. If the preferred + * size has been set, then rows and columns are used in the calculation. + * + * @param rows The number of rows to use in the preferred size + * calculation. + * @param columns The number of columns to use in the preferred size + * calculation. + * + * @return The preferred size for this text area. + */ + public Dimension getPreferredSize (int rows, int columns) + { + return preferredSize (rows, columns); + } + + /** + * Retrieve the preferred size for this text area. + * + * @return The preferred size for this text field. + * + * @deprecated This method is deprecated in favor of + * getPreferredSize (). + */ + public Dimension preferredSize () + { + return preferredSize (getRows (), getColumns ()); + } + + /** + * Retrieve the preferred size for this text area. If the preferred + * size has been set, then rows and columns are used in the calculation. + * + * @param rows The number of rows to use in the preferred size + * calculation. + * @param columns The number of columns to use in the preferred size + * calculation. + * + * @return The preferred size for this text area. + * + * @deprecated This method is deprecated in favor of + * getPreferredSize (int, int). + */ + public Dimension preferredSize (int rows, int columns) + { + if (isPreferredSizeSet()) + return new Dimension(prefSize); + + TextAreaPeer peer = (TextAreaPeer) getPeer (); + if (peer == null) + return new Dimension (getWidth(), getHeight()); + + return peer.getPreferredSize (rows, columns); + } + + /** + * Retrieve the scroll bar display policy -- one of SCROLLBARS_BOTH, + * SCROLLBARS_VERTICAL_ONLY, SCROLLBARS_HORIZONTAL_ONLY, + * SCROLLBARS_NONE. + * + * @return The current scroll bar display policy. + */ + public int getScrollbarVisibility () + { + return scrollbarVisibility; + } + + /** + * Notify this object that it should create its native peer. + */ + public void addNotify () + { + if (getPeer () == null) + setPeer ((ComponentPeer) getToolkit().createTextArea (this)); + } + + /** + * Append the specified text to the end of the current text. + * + * @param str The text to append. + */ + public void append (String str) + { + appendText (str); + } + + /** + * Append the specified text to the end of the current text. + * + * @param str The text to append. + * + * @deprecated This method is deprecated in favor of + * append (). + */ + public void appendText (String str) + { + TextAreaPeer peer = (TextAreaPeer) getPeer (); + + if (peer != null) + peer.insert (str, peer.getText().length ()); + else + setText(getText() + str); + } + + /** + * Insert the specified text at the specified position. The first + * character in the text area is at position zero. + * + * @param str The text to insert. + * @param pos The position at which to insert text. + */ + public void insert (String str, int pos) + { + insertText (str, pos); + } + + /** + * Insert the specified text at the specified position. The first + * character in the text area is at position zero. + * + * @param str The text to insert. + * @param pos The position at which to insert text. + * + * @deprecated This method is deprecated in favor of + * insert (). + */ + public void insertText (String str, int pos) + { + String tmp1 = null; + String tmp2 = null; + + TextAreaPeer peer = (TextAreaPeer) getPeer (); + + if (peer != null) + peer.insert (str, pos); + else + { + tmp1 = getText().substring(0, pos); + tmp2 = getText().substring(pos, getText().length()); + setText(tmp1 + str + tmp2); + } + } + + /** + * Replace a range of characters with the specified text. The + * character at the start position will be replaced, unless start == + * end. The character at the end posistion will not be replaced. + * The first character in the text area is at position zero. The + * length of the replacement text may differ from the length of the + * text that is replaced. + * + * @param str The new text for the range. + * @param start The start position of the replacement range. + * @param end The end position of the replacement range. + */ + public void replaceRange (String str, int start, int end) + { + replaceText (str, start, end); + } + + /** + * Replace a range of characters with the specified text. The + * character at the start position will be replaced, unless start == + * end. The character at the end posistion will not be replaced. + * The first character in the text area is at position zero. The + * length of the replacement text may differ from the length of the + * text that is replaced. + * + * @param str The new text for the range. + * @param start The start position of the replacement range. + * @param end The end position of the replacement range. + * + * @deprecated This method is deprecated in favor of + * replaceRange (). + */ + public void replaceText (String str, int start, int end) + { + String tmp1 = null; + String tmp2 = null; + + TextAreaPeer peer = (TextAreaPeer) getPeer(); + + if (peer != null) + peer.replaceRange(str, start, end); + else + { + tmp1 = getText().substring(0, start); + tmp2 = getText().substring(end, getText().length()); + setText(tmp1 + str + tmp2); + } + } + + /** + * Retrieve a debugging string for this text area. + * + * @return A debugging string for this text area. + */ + protected String paramString () + { + String sbVisibility = ""; + + switch (scrollbarVisibility) + { + case SCROLLBARS_BOTH: + sbVisibility = "both"; + break; + case SCROLLBARS_VERTICAL_ONLY: + sbVisibility = "vertical-only"; + break; + case SCROLLBARS_HORIZONTAL_ONLY: + sbVisibility = "horizontal-only"; + break; + case SCROLLBARS_NONE: + sbVisibility = "none"; + break; + } + + String editable = ""; + if (isEditable ()) + editable = "editable,"; + + return getName () + "," + getX () + "," + getY () + "," + getWidth () + + "x" + getHeight () + "," + "text=" + getText () + "," + editable + + "selection=" + getSelectionStart () + "-" + getSelectionEnd () + + ",rows=" + rows + ",columns=" + columns + ",scrollbarVisibility=" + + sbVisibility; + } + + /** + * Generate a unique name for this text area. + * + * @return A unique name for this text area. + */ + String generateName () + { + return "text" + getUniqueLong (); + } + + private static synchronized long getUniqueLong () + { + return next_text_number++; + } + + protected class AccessibleAWTTextArea extends AccessibleAWTTextComponent + { + private static final long serialVersionUID = 3472827823632144419L; + + protected AccessibleAWTTextArea() + { + } + + public AccessibleStateSet getAccessibleStateSet() + { + return super.getAccessibleStateSet(); + } + } + + /** + * Gets the AccessibleContext associated with this TextArea. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTTextArea(); + return accessibleContext; + } +} diff --git a/libjava/classpath/java/awt/TextComponent.java b/libjava/classpath/java/awt/TextComponent.java new file mode 100644 index 000000000..31ea3f5de --- /dev/null +++ b/libjava/classpath/java/awt/TextComponent.java @@ -0,0 +1,676 @@ +/* TextComponent.java -- Widgets for entering text + Copyright (C) 1999, 2002, 2003, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.TextEvent; +import java.awt.event.TextListener; +import java.awt.peer.TextComponentPeer; +import java.io.Serializable; +import java.text.BreakIterator; +import java.util.EventListener; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleText; +import javax.swing.text.AttributeSet; + +/** + * This class provides common functionality for widgets than + * contain text. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class TextComponent extends Component + implements Serializable, Accessible +{ + + private static final long serialVersionUID = -2214773872412987419L; + + /** + * @serial Indicates whether or not this component is editable. + * This is package-private to avoid an accessor method. + */ + boolean editable; + + /** + * @serial The starting position of the selected text region. + * This is package-private to avoid an accessor method. + */ + int selectionStart; + + /** + * @serial The ending position of the selected text region. + * This is package-private to avoid an accessor method. + */ + int selectionEnd; + + /** + * @serial The text in the component + * This is package-private to avoid an accessor method. + */ + String text; + + /** + * A list of listeners that will receive events from this object. + */ + protected transient TextListener textListener; + + protected class AccessibleAWTTextComponent + extends AccessibleAWTComponent + implements AccessibleText, TextListener + { + private static final long serialVersionUID = 3631432373506317811L; + + // Constructor + // Adds a listener for tracking caret changes + public AccessibleAWTTextComponent() + { + TextComponent.this.addTextListener(this); + } + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.TEXT; + } + + public AccessibleStateSet getAccessibleStateSet() + { + // TODO: Docs say PropertyChangeEvent will fire if this state changes. + // That means that the event has to fire when editable changes. + AccessibleStateSet ss = super.getAccessibleStateSet(); + if (editable) + ss.add(AccessibleState.EDITABLE); + return ss; + } + + public AccessibleText getAccessibleText() + { + return this; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getIndexAtPoint(java.awt.Point) + */ + public int getIndexAtPoint(Point point) + { + return TextComponent.this.getIndexAtPoint(point); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getCharacterBounds(int) + */ + public Rectangle getCharacterBounds(int index) + { + return TextComponent.this.getCharacterBounds(index); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getCharCount() + */ + public int getCharCount() + { + return text.length(); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getCaretPosition() + */ + public int getCaretPosition() + { + return TextComponent.this.getCaretPosition(); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getAtIndex(int, int) + */ + public String getAtIndex(int part, int index) + { + if (index < 0 || index >= text.length()) + return null; + BreakIterator it = null; + switch (part) + { + case CHARACTER: + return text.substring(index, index + 1); + case WORD: + it = BreakIterator.getWordInstance(); + break; + case SENTENCE: + it = BreakIterator.getSentenceInstance(); + break; + default: + return null; + } + it.setText(text); + int start = index; + if (!it.isBoundary(index)) + start = it.preceding(index); + int end = it.following(index); + if (end == -1) + return text.substring(index); + else + return text.substring(index, end); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getAfterIndex(int, int) + */ + public String getAfterIndex(int part, int index) { + if (index < 0 || index >= text.length()) + return null; + BreakIterator it = null; + switch (part) + { + case CHARACTER: + return text.substring(index, index + 1); + case WORD: + it = BreakIterator.getWordInstance(); + break; + case SENTENCE: + it = BreakIterator.getSentenceInstance(); + break; + default: + return null; + } + it.setText(text); + int start = index; + if (!it.isBoundary(index)) + start = it.following(index); + // Make sure there was a complete unit. I.e. if index is in the middle + // of a word, return null if there is no word after the that one. + if (start == -1) + return null; + int end = it.following(start); + if (end == -1) + return text.substring(index); + else + return text.substring(index, end); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getBeforeIndex(int, int) + */ + public String getBeforeIndex(int part, int index) + { + if (index < 1 || index >= text.length()) + return null; + BreakIterator it = null; + switch (part) + { + case CHARACTER: + return text.substring(index - 1, index); + case WORD: + it = BreakIterator.getWordInstance(); + break; + case SENTENCE: + it = BreakIterator.getSentenceInstance(); + break; + default: + return null; + } + it.setText(text); + int end = index; + if (!it.isBoundary(index)) + end = it.preceding(index); + // Make sure there was a complete unit. I.e. if index is in the middle + // of a word, return null if there is no word before that one. + if (end == -1) + return null; + int start = it.preceding(end); + if (start == -1) + return text.substring(0, end); + else + return text.substring(start, end); + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getCharacterAttribute(int) + */ + public AttributeSet getCharacterAttribute(int index) + { + // FIXME: I suspect this really gets filled in by subclasses. + return null; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getSelectionStart() + */ + public int getSelectionStart() { + // TODO Auto-generated method stub + return selectionStart; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getSelectionEnd() + */ + public int getSelectionEnd() + { + return selectionEnd; + } + + /* (non-Javadoc) + * @see javax.accessibility.AccessibleText#getSelectedText() + */ + public String getSelectedText() + { + if (selectionEnd - selectionStart > 0) + return text.substring(selectionStart, selectionEnd); + else + return null; + } + + /* (non-Javadoc) + * @see java.awt.event.TextListener#textValueChanged(java.awt.event.TextEvent) + */ + public void textValueChanged(TextEvent event) + { + // TODO Auto-generated method stub + + } + + } + + + TextComponent(String text) + { + if (text == null) + this.text = ""; + else + this.text = text; + + this.editable = true; + } + + + /** + * Returns the text in this component + * + * @return The text in this component. + */ + public synchronized String getText() + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + text = tcp.getText(); + + return(text); + } + + /** + * Sets the text in this component to the specified string. + * + * @param text The new text for this component. + */ + public synchronized void setText(String text) + { + if (text == null) + text = ""; + + this.text = text; + + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + tcp.setText(text); + setCaretPosition(0); + } + + /** + * Returns a string that contains the text that is currently selected. + * + * @return The currently selected text region. + */ + public synchronized String getSelectedText() + { + String alltext = getText(); + int start = getSelectionStart(); + int end = getSelectionEnd(); + + return(alltext.substring(start, end)); + } + + /** + * Returns the starting position of the selected text region. + * If the text is not selected then caret position is returned. + * + * @return The starting position of the selected text region. + */ + public synchronized int getSelectionStart() + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + selectionStart = tcp.getSelectionStart(); + + return(selectionStart); + } + + /** + * Sets the starting position of the selected region to the + * specified value. If the specified value is out of range, then it + * will be silently changed to the nearest legal value. + * + * @param selectionStart The new start position for selected text. + */ + public synchronized void setSelectionStart(int selectionStart) + { + select(selectionStart, + (getSelectionEnd() < selectionStart) + ? selectionStart : getSelectionEnd()); + } + + /** + * Returns the ending position of the selected text region. + * If the text is not selected, then caret position is returned + * + * @return The ending position of the selected text region. + */ + public synchronized int getSelectionEnd() + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + selectionEnd = tcp.getSelectionEnd(); + + return(selectionEnd); + } + + /** + * Sets the ending position of the selected region to the + * specified value. If the specified value is out of range, then it + * will be silently changed to the nearest legal value. + * + * @param selectionEnd The new start position for selected text. + */ + public synchronized void setSelectionEnd(int selectionEnd) + { + select(getSelectionStart(), selectionEnd); + } + + /** + * This method sets the selected text range to the text between the + * specified start and end positions. Illegal values for these + * positions are silently fixed. + * + * @param selectionStart The new start position for the selected text. + * @param selectionEnd The new end position for the selected text. + */ + public synchronized void select(int selectionStart, int selectionEnd) + { + if (selectionStart < 0) + selectionStart = 0; + + if (selectionStart > getText().length()) + selectionStart = text.length(); + + if (selectionEnd > text.length()) + selectionEnd = text.length(); + + if (selectionStart > selectionEnd) + selectionStart = selectionEnd; + + this.selectionStart = selectionStart; + this.selectionEnd = selectionEnd; + + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + tcp.select(selectionStart, selectionEnd); + } + + /** + * Selects all of the text in the component. + */ + public synchronized void selectAll() + { + select(0, getText().length()); + } + + /** + * Returns the current caret position in the text. + * + * @return The caret position in the text. + */ + public synchronized int getCaretPosition() + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + return(tcp.getCaretPosition()); + else + return(0); + } + + /** + * Sets the caret position to the specified value. + * + * @param caretPosition The new caret position. + * + * @exception IllegalArgumentException If the value supplied for position + * is less than zero. + * + * @since 1.1 + */ + public synchronized void setCaretPosition(int caretPosition) + { + if (caretPosition < 0) + throw new IllegalArgumentException(); + + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + tcp.setCaretPosition(caretPosition); + } + + /** + * Tests whether or not this component's text can be edited. + * + * @return true if the text can be edited, false + * otherwise. + */ + public boolean isEditable() + { + return(editable); + } + + /** + * Sets whether or not this component's text can be edited. + * + * @param editable true to enable editing of the text, + * false to disable it. + */ + public synchronized void setEditable(boolean editable) + { + this.editable = editable; + + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + tcp.setEditable(editable); + } + + /** + * Notifies the component that it should destroy its native peer. + */ + public void removeNotify() + { + super.removeNotify(); + } + + /** + * Adds a new listener to the list of text listeners for this + * component. + * + * @param listener The listener to be added. + */ + public synchronized void addTextListener(TextListener listener) + { + textListener = AWTEventMulticaster.add(textListener, listener); + + enableEvents(AWTEvent.TEXT_EVENT_MASK); + } + + /** + * Removes the specified listener from the list of listeners + * for this component. + * + * @param listener The listener to remove. + */ + public synchronized void removeTextListener(TextListener listener) + { + textListener = AWTEventMulticaster.remove(textListener, listener); + } + + /** + * Processes the specified event for this component. Text events are + * processed by calling the processTextEvent() method. + * All other events are passed to the superclass method. + * + * @param event The event to process. + */ + protected void processEvent(AWTEvent event) + { + if (event instanceof TextEvent) + processTextEvent((TextEvent)event); + else + super.processEvent(event); + } + + /** + * Processes the specified text event by dispatching it to any listeners + * that are registered. Note that this method will only be called + * if text event's are enabled. This will be true if there are any + * registered listeners, or if the event has been specifically + * enabled using enableEvents(). + * + * @param event The text event to process. + */ + protected void processTextEvent(TextEvent event) + { + if (textListener != null) + textListener.textValueChanged(event); + } + + void dispatchEventImpl(AWTEvent e) + { + if (e.id <= TextEvent.TEXT_LAST + && e.id >= TextEvent.TEXT_FIRST + && (textListener != null + || (eventMask & AWTEvent.TEXT_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); + } + + /** + * Returns a debugging string. + * + * @return A debugging string. + */ + protected String paramString() + { + return(getClass().getName() + "(text=" + getText() + ")"); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this TextComponent. FooListeners are registered using + * the addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + */ + public T[] getListeners(Class listenerType) + { + if (listenerType == TextListener.class) + return AWTEventMulticaster.getListeners(textListener, listenerType); + + return super.getListeners(listenerType); + } + + /** + * Returns all text listeners registered to this object. + */ + public TextListener[] getTextListeners() + { + return (TextListener[]) getListeners(TextListener.class); + } + + /** + * Gets the AccessibleContext associated with this TextComponent. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTTextComponent(); + return accessibleContext; + } + + + // Provide AccessibleAWTTextComponent access to several peer functions that + // aren't publicly exposed. This is package-private to avoid an accessor + // method. + synchronized int getIndexAtPoint(Point p) + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + return tcp.getIndexAtPoint(p.x, p.y); + return -1; + } + + synchronized Rectangle getCharacterBounds(int i) + { + TextComponentPeer tcp = (TextComponentPeer) getPeer(); + if (tcp != null) + return tcp.getCharacterBounds(i); + return null; + } + + /** + * All old mouse events for this component should + * be ignored. + * + * @return true to ignore all old mouse events. + */ + static boolean ignoreOldMouseEvents() + { + return true; + } + +} // class TextComponent diff --git a/libjava/classpath/java/awt/TextField.java b/libjava/classpath/java/awt/TextField.java new file mode 100644 index 000000000..5a58b440d --- /dev/null +++ b/libjava/classpath/java/awt/TextField.java @@ -0,0 +1,484 @@ +/* TextField.java -- A one line text entry field + Copyright (C) 1999, 2002, 2004, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.peer.ComponentPeer; +import java.awt.peer.TextFieldPeer; +import java.util.EventListener; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleStateSet; + +/** + * This class implements a single line text entry field widget + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class TextField extends TextComponent +{ + + /** + * The number used to generate the name returned by getName. + */ + private static transient long next_textfield_number; + + + private static final long serialVersionUID = -2966288784432217853L; + + + /** + * @serial The number of columns in the text entry field. + */ + private int columns; + + /** + * @serial The character that is echoed when doing protected input + */ + private char echoChar; + + // List of registered ActionListener's for this object. + private ActionListener action_listeners; + + /** + * Initializes a new instance of TextField that is empty + * and has one column. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public TextField() + { + this("", 0); + } + + /** + * Initializes a new instance of TextField containing + * the specified text. The number of columns will be equal to the + * length of the text string. + * + * @param text The text to display in the field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public TextField(String text) + { + this(text, (text == null) ? 0 : text.length()); + } + + /** + * Initializes a new instance of TextField that is empty + * and has the specified number of columns. + * + * @param columns The number of columns in the text field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public TextField(int columns) + { + this("", columns); + } + + /** + * Initializes a new instance of TextField with the + * specified text and number of columns. + * + * @param text The text to display in the field. + * @param columns The number of columns in the field. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true, + */ + public TextField(String text, int columns) + { + super(text); + + if (columns < 0) + this.columns = 0; + else + this.columns = columns; + + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException (); + } + + /** + * Returns the number of columns in the field. + * + * @return The number of columns in the field. + */ + public int getColumns() + { + return(columns); + } + + /** + * Sets the number of columns in this field to the specified value. + * + * @param columns The new number of columns in the field. + * + * @exception IllegalArgumentException If columns is less than zero. + */ + public synchronized void setColumns(int columns) + { + if (columns < 0) + throw new IllegalArgumentException("Value is less than zero: " + + columns); + + this.columns = columns; + // FIXME: How to we communicate this to our peer? + } + + /** + * Returns the character that is echoed to the screen when a text + * field is protected (such as when a password is being entered). + * + * @return The echo character for this text field. + */ + public char getEchoChar() + { + return(echoChar); + } + + /** + * Sets the character that is echoed when protected input such as + * a password is displayed. + * + * @param echoChar The new echo character. + */ + public void setEchoChar(char echoChar) + { + setEchoCharacter(echoChar); + } + + /** + * Sets the character that is echoed when protected input such as + * a password is displayed. + * + * @param echoChar The new echo character. + * + * @deprecated This method is deprecated in favor of + * setEchoChar() + */ + public void setEchoCharacter(char echoChar) + { + this.echoChar = echoChar; + + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer != null) + peer.setEchoChar (echoChar); + } + + /** + * Tests whether or not this text field has an echo character set + * so that characters the user type are not echoed to the screen. + * + * @return true if an echo character is set, + * false otherwise. + */ + public boolean echoCharIsSet() + { + if (echoChar == '\u0000') + return(false); + else + return(true); + } + + /** + * Returns the minimum size for this text field. + * + * @return The minimum size for this text field. + */ + public Dimension getMinimumSize() + { + return getMinimumSize (getColumns ()); + } + + /** + * Returns the minimum size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the minimum size for. + */ + public Dimension getMinimumSize(int columns) + { + return minimumSize(columns); + } + + /** + * Returns the minimum size for this text field. + * + * @return The minimum size for this text field. + * + * @deprecated This method is deprecated in favor of + * getMinimumSize(). + */ + public Dimension minimumSize() + { + return minimumSize(getColumns ()); + } + + /** + * Returns the minimum size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the minimum size for. + * + * @deprecated This method is deprecated in favor of + * getMinimumSize(int). + */ + public Dimension minimumSize(int columns) + { + if (isMinimumSizeSet()) + return new Dimension(minSize); + + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer == null) + return new Dimension(getWidth(), getHeight()); + + return peer.getMinimumSize (columns); + } + + /** + * Returns the preferred size for this text field. + * + * @return The preferred size for this text field. + */ + public Dimension getPreferredSize() + { + return getPreferredSize(getColumns ()); + } + + /** + * Returns the preferred size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the preferred size for. + */ + public Dimension getPreferredSize(int columns) + { + return preferredSize(columns); + } + + /** + * Returns the preferred size for this text field. + * + * @return The preferred size for this text field. + * + * @deprecated This method is deprecated in favor of + * getPreferredSize(). + */ + public Dimension preferredSize() + { + return preferredSize(getColumns ()); + } + + /** + * Returns the preferred size of a text field with the specified number + * of columns. + * + * @param columns The number of columns to get the preferred size for. + * + * @deprecated This method is deprecated in favor of + * getPreferredSize(int). + */ + public Dimension preferredSize(int columns) + { + if (isPreferredSizeSet()) + return new Dimension(prefSize); + + TextFieldPeer peer = (TextFieldPeer) getPeer (); + if (peer == null) + return new Dimension (getWidth(), getHeight()); + + return peer.getPreferredSize (columns); + } + + /** + * Notifies this object that it should create its native peer. + */ + public void addNotify() + { + if (getPeer() != null) + return; + + setPeer((ComponentPeer)getToolkit().createTextField(this)); + super.addNotify(); + } + + /** + * Addes a new listener to the list of action listeners for this + * object. + * + * @param listener The listener to add to the list. + */ + public synchronized void addActionListener(ActionListener listener) + { + action_listeners = AWTEventMulticaster.add(action_listeners, listener); + + enableEvents(AWTEvent.ACTION_EVENT_MASK); + } + + /** + * Removes the specified listener from the list of action listeners + * for this object. + * + * @param listener The listener to remove from the list. + */ + public synchronized void removeActionListener(ActionListener listener) + { + action_listeners = AWTEventMulticaster.remove(action_listeners, listener); + } + + /** + * Processes the specified event. If the event is an instance of + * ActionEvent then processActionEvent() is + * called to process it, otherwise the event is sent to the + * superclass. + * + * @param event The event to process. + */ + protected void processEvent(AWTEvent event) + { + if (event instanceof ActionEvent) + processActionEvent((ActionEvent)event); + else + super.processEvent(event); + } + + /** + * Processes an action event by calling any registered listeners. + * Note to subclasses: This method is not called unless action events + * are enabled on this object. This will be true if any listeners + * are registered, or if action events were specifically enabled + * using enableEvents(). + * + * @param event The event to process. + */ + protected void processActionEvent(ActionEvent event) + { + if (action_listeners != null) + action_listeners.actionPerformed(event); + } + + void dispatchEventImpl(AWTEvent e) + { + if (e.id <= ActionEvent.ACTION_LAST + && e.id >= ActionEvent.ACTION_FIRST + && (action_listeners != null + || (eventMask & AWTEvent.ACTION_EVENT_MASK) != 0)) + processEvent(e); + else + super.dispatchEventImpl(e); + } + + /** + * Returns a debug string for this object. + * + * @return A debug string for this object. + */ + protected String paramString() + { + return(getClass().getName() + "(columns=" + getColumns() + ",echoChar=" + + getEchoChar()); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this TextField. FooListeners are registered using the + * addFooListener method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + * + * @since 1.3 + */ + public T[] getListeners (Class listenerType) + { + if (listenerType == ActionListener.class) + return AWTEventMulticaster.getListeners (action_listeners, listenerType); + + return super.getListeners (listenerType); + } + + /** + * Return all ActionListeners register to this TextField object + * as an array. + * + * @since 1.4 + */ + public ActionListener[] getActionListeners () + { + return (ActionListener[]) getListeners (ActionListener.class); + } + + /** + * Generate a unique name for this TextField. + * + * @return A unique name for this TextField. + */ + String generateName() + { + return "textfield" + getUniqueLong(); + } + + private static synchronized long getUniqueLong() + { + return next_textfield_number++; + } + + protected class AccessibleAWTTextField extends AccessibleAWTTextComponent + { + private static final long serialVersionUID = 6219164359235943158L; + + protected AccessibleAWTTextField() + { + } + + public AccessibleStateSet getAccessibleStateSet() + { + return super.getAccessibleStateSet(); + } + } + + public AccessibleContext getAccessibleContext() + { + return new AccessibleAWTTextField(); + } + +} diff --git a/libjava/classpath/java/awt/TexturePaint.java b/libjava/classpath/java/awt/TexturePaint.java new file mode 100644 index 000000000..f88e77b23 --- /dev/null +++ b/libjava/classpath/java/awt/TexturePaint.java @@ -0,0 +1,118 @@ +/* TexturePaint.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.java.awt.java2d.TexturePaintContext; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; + +/** + * This class provides a way to fill a Shape with a texture that is + * specified by a BufferedImage. + */ +public class TexturePaint implements Paint +{ + private final BufferedImage texture; + private final Rectangle2D anchor; + + /** + * Constructor. + * + * @param texture - the texture + * @param anchor - the shape + */ + public TexturePaint(BufferedImage texture, Rectangle2D anchor) + { + this.texture = texture; + this.anchor = anchor; + } + + /** + * Gets the texture image. + * + * @return the texture + */ + public BufferedImage getImage() + { + return texture; + } + + /** + * Gets the shape anchor. + * + * @return the shape anchor + */ + public Rectangle2D getAnchorRect() + { + return anchor; + } + + /** + * Creates the context used to paint the texture. + * + * @param cm - the ColorModel that receives the Paint data. Used only as a hint. + * @param deviceBounds - the device space being rendered. + * @param userBounds - the user space being rendered + * @param xform - the AffineTransform from user space into device space + * @param hints - a RenderingHints object that is used to specify how the + * pattern is rendered + * @return the paint context used to paint the texture + */ + public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, + Rectangle2D userBounds, + AffineTransform xform, RenderingHints hints) + { + // TODO: Maybe add some hook for providing alternative/accelerated + // implementations of this. + return new TexturePaintContext(this, deviceBounds, userBounds, xform); + } + + /** + * Returns the transparency mode. + * + * @return the transparency mode. + */ + public int getTransparency() + { + return texture.getTransparency(); + } +} // class TexturePaint diff --git a/libjava/classpath/java/awt/Toolkit.java b/libjava/classpath/java/awt/Toolkit.java new file mode 100644 index 000000000..6b1eb9d4b --- /dev/null +++ b/libjava/classpath/java/awt/Toolkit.java @@ -0,0 +1,1429 @@ +/* Toolkit.java -- AWT Toolkit superclass + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import gnu.classpath.SystemProperties; +import gnu.java.awt.AWTUtilities; +import gnu.java.awt.peer.GLightweightPeer; +import gnu.java.awt.peer.headless.HeadlessToolkit; + +import java.awt.datatransfer.Clipboard; +import java.awt.dnd.DragGestureEvent; +import java.awt.dnd.DragGestureListener; +import java.awt.dnd.DragGestureRecognizer; +import java.awt.dnd.DragSource; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.awt.event.AWTEventListener; +import java.awt.event.AWTEventListenerProxy; +import java.awt.event.KeyEvent; +import java.awt.font.TextAttribute; +import java.awt.im.InputMethodHighlight; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.peer.ButtonPeer; +import java.awt.peer.CanvasPeer; +import java.awt.peer.CheckboxMenuItemPeer; +import java.awt.peer.CheckboxPeer; +import java.awt.peer.ChoicePeer; +import java.awt.peer.DesktopPeer; +import java.awt.peer.DialogPeer; +import java.awt.peer.FileDialogPeer; +import java.awt.peer.FontPeer; +import java.awt.peer.FramePeer; +import java.awt.peer.LabelPeer; +import java.awt.peer.LightweightPeer; +import java.awt.peer.ListPeer; +import java.awt.peer.MenuBarPeer; +import java.awt.peer.MenuItemPeer; +import java.awt.peer.MenuPeer; +import java.awt.peer.MouseInfoPeer; +import java.awt.peer.PanelPeer; +import java.awt.peer.PopupMenuPeer; +import java.awt.peer.ScrollPanePeer; +import java.awt.peer.ScrollbarPeer; +import java.awt.peer.TextAreaPeer; +import java.awt.peer.TextFieldPeer; +import java.awt.peer.WindowPeer; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * The AWT system uses a set of native peer objects to implement its + * widgets. These peers are provided by a peer toolkit, that is accessed + * via a subclass of this superclass. The system toolkit is retrieved + * by the static methods getDefaultToolkit. This method + * determines the system toolkit by examining the system property + * awt.toolkit. That property is set to the name of the + * Toolkit subclass for the specified peer set. If the + * awt.toolkit property is not set, then the default + * toolkit gnu.java.awt.peer.gtk.GtkToolkit is used. This + * toolkit creates its peers using the GTK+ toolkit. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class Toolkit +{ + /** The default toolkit name. */ + private static String default_toolkit_name + = gnu.classpath.Configuration.default_awt_peer_toolkit; + + /** + * The toolkit in use. Once we load it, we don't ever change it + * if the awt.toolkit property is set. + */ + private static Toolkit toolkit; + + /** The toolkit properties. */ + private static Properties props = new Properties(); + + protected final Map desktopProperties = + new Hashtable(); + + protected final PropertyChangeSupport desktopPropsSupport + = new PropertyChangeSupport(this); + + /** + * All registered AWTEventListener objects. This is package private, so the + * event queue can efficiently access this list. + */ + AWTEventListenerProxy[] awtEventListeners; + + /** + * The shared peer for all lightweight components. + */ + private GLightweightPeer lightweightPeer; + + /** + * Default constructor for subclasses. + */ + public Toolkit() + { + awtEventListeners = new AWTEventListenerProxy[0]; + } + + /** + * + * @param target + * @return + * @throws HeadlessException + */ + protected abstract DesktopPeer createDesktopPeer(Desktop target) + throws HeadlessException; + + /** + * Creates a peer object for the specified Button. + * + * @param target The Button to create the peer for. + * + * @return The peer for the specified Button object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ButtonPeer createButton(Button target); + + /** + * Creates a peer object for the specified TextField. + * + * @param target The TextField to create the peer for. + * + * @return The peer for the specified TextField object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract TextFieldPeer createTextField(TextField target); + + /** + * Creates a peer object for the specified Label. + * + * @param target The Label to create the peer for. + * + * @return The peer for the specified Label object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract LabelPeer createLabel(Label target); + + /** + * Creates a peer object for the specified List. + * + * @param target The List to create the peer for. + * + * @return The peer for the specified List object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ListPeer createList(List target); + + /** + * Creates a peer object for the specified Checkbox. + * + * @param target The Checkbox to create the peer for. + * + * @return The peer for the specified Checkbox object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract CheckboxPeer createCheckbox(Checkbox target); + + /** + * Creates a peer object for the specified Scrollbar. + * + * @param target The Scrollbar to create the peer for. + * + * @return The peer for the specified Scrollbar object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ScrollbarPeer createScrollbar(Scrollbar target); + + /** + * Creates a peer object for the specified ScrollPane. + * + * @param target The ScrollPane to create the peer for. + * + * @return The peer for the specified ScrollPane object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ScrollPanePeer createScrollPane(ScrollPane target); + + /** + * Creates a peer object for the specified TextArea. + * + * @param target The TextArea to create the peer for. + * + * @return The peer for the specified TextArea object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract TextAreaPeer createTextArea(TextArea target); + + /** + * Creates a peer object for the specified Choice. + * + * @param target The Choice to create the peer for. + * + * @return The peer for the specified Choice object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract ChoicePeer createChoice(Choice target); + + /** + * Creates a peer object for the specified Frame. + * + * @param target The Frame to create the peer for. + * + * @return The peer for the specified Frame object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract FramePeer createFrame(Frame target); + + /** + * Creates a peer object for the specified Canvas. + * + * @param target The Canvas to create the peer for. + * + * @return The peer for the specified Canvas object. + */ + protected abstract CanvasPeer createCanvas(Canvas target); + + /** + * Creates a peer object for the specified Panel. + * + * @param target The Panel to create the peer for. + * + * @return The peer for the specified Panel object. + */ + protected abstract PanelPeer createPanel(Panel target); + + /** + * Creates a peer object for the specified Window. + * + * @param target The Window to create the peer for. + * + * @return The peer for the specified Window object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract WindowPeer createWindow(Window target); + + /** + * Creates a peer object for the specified Dialog. + * + * @param target The dialog to create the peer for + * + * @return The peer for the specified font name. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract DialogPeer createDialog(Dialog target); + + /** + * Creates a peer object for the specified MenuBar. + * + * @param target The MenuBar to create the peer for. + * + * @return The peer for the specified MenuBar object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract MenuBarPeer createMenuBar(MenuBar target); + + /** + * Creates a peer object for the specified Menu. + * + * @param target The Menu to create the peer for. + * + * @return The peer for the specified Menu object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract MenuPeer createMenu(Menu target); + + /** + * Creates a peer object for the specified PopupMenu. + * + * @param target The PopupMenu to create the peer for. + * + * @return The peer for the specified PopupMenu object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract PopupMenuPeer createPopupMenu(PopupMenu target); + + /** + * Creates a peer object for the specified MenuItem. + * + * @param target The MenuItem to create the peer for. + * + * @return The peer for the specified MenuItem object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract MenuItemPeer createMenuItem(MenuItem target); + + /** + * Returns a MouseInfoPeer. + * The default implementation of this method throws + * UnsupportedOperationException. + * + * Toolkit implementations should overload this if possible, however. + */ + protected MouseInfoPeer getMouseInfoPeer() + { + throw new UnsupportedOperationException("No mouse info peer."); + } + + /** + * Creates a peer object for the specified FileDialog. + * + * @param target The FileDialog to create the peer for. + * + * @return The peer for the specified FileDialog object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract FileDialogPeer createFileDialog(FileDialog target); + + /** + * Creates a peer object for the specified CheckboxMenuItem. + * + * @param target The CheckboxMenuItem to create the peer for. + * + * @return The peer for the specified CheckboxMenuItem object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected abstract CheckboxMenuItemPeer + createCheckboxMenuItem(CheckboxMenuItem target); + + /** + * Creates a peer object for the specified Component. The + * peer returned by this method is not a native windowing system peer + * with its own native window. Instead, this method allows the component + * to draw on its parent window as a "lightweight" widget. + * + * @param target The Component to create the peer for. + * + * @return The peer for the specified Component object. + */ + protected LightweightPeer createComponent(Component target) + { + if (lightweightPeer == null) + lightweightPeer = new GLightweightPeer(); + return lightweightPeer; + } + + /** + * Creates a peer object for the specified font name. + * + * @param name The font to create the peer for. + * @param style The font style to create the peer for. + * + * @return The peer for the specified font name. + * + * @deprecated + */ + protected abstract FontPeer getFontPeer(String name, int style); + + /** + * Copies the current system colors into the specified array. This is + * the interface used by the SystemColor class. Although + * this method fills in the array with some default colors a real Toolkit + * should override this method and provide real system colors for the + * native GUI platform. + * + * @param systemColors The array to copy the system colors into. + * It must be at least 26 elements. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * + * @see java.awt.SystemColor + */ + protected void loadSystemColors(int systemColors[]) + { + systemColors[SystemColor.DESKTOP] = 0xFF005C5C; + systemColors[SystemColor.ACTIVE_CAPTION] = 0xFF000080; + systemColors[SystemColor.ACTIVE_CAPTION_TEXT] = 0xFFFFFFFF; + systemColors[SystemColor.ACTIVE_CAPTION_BORDER] = 0xFFC0C0C0; + systemColors[SystemColor.INACTIVE_CAPTION] = 0xFF808080; + systemColors[SystemColor.INACTIVE_CAPTION_TEXT] = 0xFFC0C0C0; + systemColors[SystemColor.INACTIVE_CAPTION_BORDER] = 0xFFC0C0C0; + systemColors[SystemColor.WINDOW] = 0xFFFFFFFF; + systemColors[SystemColor.WINDOW_BORDER] = 0xFF000000; + systemColors[SystemColor.WINDOW_TEXT] = 0xFF000000; + systemColors[SystemColor.MENU] = 0xFFC0C0C0; + systemColors[SystemColor.MENU_TEXT] = 0xFF000000; + systemColors[SystemColor.TEXT] = 0xFFC0C0C0; + systemColors[SystemColor.TEXT_TEXT] = 0xFF000000; + systemColors[SystemColor.TEXT_HIGHLIGHT] = 0xFF000090; + systemColors[SystemColor.TEXT_HIGHLIGHT_TEXT] = 0xFFFFFFFF; + systemColors[SystemColor.TEXT_INACTIVE_TEXT] = 0xFF808080; + systemColors[SystemColor.CONTROL] = 0xFFC0C0C0; + systemColors[SystemColor.CONTROL_TEXT] = 0xFF000000; + systemColors[SystemColor.CONTROL_HIGHLIGHT] = 0xFFFFFFFF; + systemColors[SystemColor.CONTROL_LT_HIGHLIGHT] = 0xFFE0E0E0; + systemColors[SystemColor.CONTROL_SHADOW] = 0xFF808080; + systemColors[SystemColor.CONTROL_DK_SHADOW] = 0xFF000000; + systemColors[SystemColor.SCROLLBAR] = 0xFFE0E0E0; + systemColors[SystemColor.INFO] = 0xFFE0E000; + systemColors[SystemColor.INFO_TEXT] = 0xFF000000; + } + + /** + * @since 1.4 + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public void setDynamicLayout(boolean dynamic) + { + } + + /** + * @since 1.4 + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + protected boolean isDynamicLayoutSet() + { + return false; + } + + /** + * @since 1.4 + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public boolean isDynamicLayoutActive() + { + return false; + } + + /** + * Returns the dimensions of the screen in pixels. + * + * @return The dimensions of the screen in pixels. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public abstract Dimension getScreenSize(); + + /** + * Returns the screen resolution in dots per square inch. + * + * @return The screen resolution in dots per square inch. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public abstract int getScreenResolution(); + + /** + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * + * @since 1.4 + */ + public Insets getScreenInsets(GraphicsConfiguration gc) + { + return new Insets(0, 0, 0, 0); + } + + /** + * Returns the color model of the screen. + * + * @return The color model of the screen. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public abstract ColorModel getColorModel(); + + /** + * Returns the names of the available fonts. + * + * @return The names of the available fonts. + * + * @deprecated + */ + public abstract String[] getFontList(); + + /** + * Return the font metrics for the specified font + * + * @param name The name of the font to return metrics for. + * + * @return The requested font metrics. + * + * @deprecated + */ + public abstract FontMetrics getFontMetrics(Font name); + + /** + * Flushes any buffered data to the screen so that it is in sync with + * what the AWT system has drawn to it. + */ + public abstract void sync(); + + /** + * Returns an instance of the default toolkit. The default toolkit is + * the subclass of Toolkit specified in the system property + * awt.toolkit, or gnu.java.awt.peer.gtk.GtkToolkit + * if the property is not set. + * + * @return An instance of the system default toolkit. + * + * @throws AWTError If the toolkit cannot be loaded. + */ + public static synchronized Toolkit getDefaultToolkit() + { + if (toolkit != null) + return toolkit; + + String toolkit_name = SystemProperties.getProperty("awt.toolkit", + default_toolkit_name); + try + { + ClassLoader cl; + cl = (ClassLoader) AccessController.doPrivileged + (new PrivilegedAction() + { + public Object run() + { + return ClassLoader.getSystemClassLoader(); + } + }); + Class cls = Class.forName(toolkit_name, true, cl); + Object obj = cls.newInstance(); + if (!(obj instanceof Toolkit)) + throw new AWTError(toolkit_name + " is not a subclass of " + + "java.awt.Toolkit"); + toolkit = (Toolkit) obj; + + initAccessibility(); + return toolkit; + } + catch (ThreadDeath death) + { + throw death; + } + catch (Throwable t) + { + // Check for the headless property. + if (GraphicsEnvironment.isHeadless()) + { + toolkit = new HeadlessToolkit(); + return toolkit; + } + else + { + AWTError e = new AWTError("Cannot load AWT toolkit: " + + toolkit_name); + throw (AWTError) e.initCause(t); + } + } + } + + /** + * Returns an image from the specified file, which must be in a + * recognized format. Supported formats vary from toolkit to toolkit. + * + * @return name The name of the file to read the image from. + */ + public abstract Image getImage(String name); + + /** + * Returns an image from the specified URL, which must be in a + * recognized format. Supported formats vary from toolkit to toolkit. + * + * @return url The URl to read the image from. + */ + public abstract Image getImage(URL url); + + public abstract Image createImage(String filename); + + public abstract Image createImage(URL url); + + /** + * Readies an image to be rendered on the screen. The width and height + * values can be set to the default sizes for the image by passing -1 + * in those parameters. + * + * @param image The image to prepare for rendering. + * @param width The width of the image. + * @param height The height of the image. + * @param observer The observer to receive events about the preparation + * process. + * + * @return true if the image is already prepared for rendering, + * false otherwise. + */ + public abstract boolean prepareImage(Image image, int width, int height, + ImageObserver observer); + + /** + * Checks the status of specified image as it is being readied for + * rendering. + * + * @param image The image to prepare for rendering. + * @param width The width of the image. + * @param height The height of the image. + * @param observer The observer to receive events about the preparation + * process. + * + * @return A union of the bitmasks from + * java.awt.image.ImageObserver that indicates the current + * state of the imaging readying process. + */ + public abstract int checkImage(Image image, int width, int height, + ImageObserver observer); + + /** + * Creates an image using the specified ImageProducer + * + * @param producer The ImageProducer to create the image from. + * + * @return The created image. + */ + public abstract Image createImage(ImageProducer producer); + + /** + * Creates an image from the specified byte array. The array must be in + * a recognized format. Supported formats vary from toolkit to toolkit. + * + * @param data The raw image data. + * + * @return The created image. + */ + public Image createImage(byte[] data) + { + return createImage(data, 0, data.length); + } + + /** + * Creates an image from the specified portion of the byte array passed. + * The array must be in a recognized format. Supported formats vary from + * toolkit to toolkit. + * + * @param data The raw image data. + * @param offset The offset into the data where the image data starts. + * @param len The length of the image data. + * + * @return The created image. + */ + public abstract Image createImage(byte[] data, int offset, int len); + + /** + * Returns a instance of PrintJob for the specified + * arguments. + * + * @param frame The window initiating the print job. + * @param title The print job title. + * @param props The print job properties. + * + * @return The requested print job, or null if the job + * was cancelled. + * + * @exception NullPointerException If frame is null, + * or GraphicsEnvironment.isHeadless() returns true. + * @exception SecurityException If this thread is not allowed to initiate + * a print job request. + */ + public abstract PrintJob getPrintJob(Frame frame, String title, + Properties props); + + /** + * Returns a instance of PrintJob for the specified + * arguments. + * + * @param frame The window initiating the print job. + * @param title The print job title. + * @param jobAttr A set of job attributes which will control the print job. + * @param pageAttr A set of page attributes which will control the print job. + * + * @exception NullPointerException If frame is null, and either jobAttr is null + * or jobAttr.getDialog() returns JobAttributes.DialogType.NATIVE. + * @exception IllegalArgumentException If pageAttrspecifies differing cross + * feed and feed resolutions, or when GraphicsEnvironment.isHeadless() returns + * true. + * @exception SecurityException If this thread is not allowed to initiate + * a print job request. + * + * @since 1.3 + */ + public PrintJob getPrintJob(Frame frame, String title, + JobAttributes jobAttr, PageAttributes pageAttr) + { + // FIXME: it is possible this check may be removed + // if this method, when written, always delegates to + // getPrintJob(Frame, String, Properties). + SecurityManager sm; + sm = System.getSecurityManager(); + if (sm != null) + sm.checkPrintJobAccess(); + + return null; + } + + /** + * Causes a "beep" tone to be generated. + */ + public abstract void beep(); + + /** + * Returns the system clipboard. + * + * @return THe system clipboard. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public abstract Clipboard getSystemClipboard(); + + /** + * Gets the singleton instance of the system selection as a + * Clipboard object. The system selection contains the selected text + * of the last component/widget that had focus and a text selection. + * The default implementation returns null. + * + * @return The Clipboard holding the system (text) selection or null + * if the Toolkit or system doesn't support a selection clipboard. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * is true. + * @exception SecurityException If the current security manager + * checkSystemClipboardAccess() doesn't allow access. + * + * @since 1.4 + */ + public Clipboard getSystemSelection() + { + return null; + } + + /** + * Returns the accelerator key mask for menu shortcuts. The default is + * Event.CTRL_MASK. A toolkit must override this method + * to change the default. + * + * @return The key mask for the menu accelerator key. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public int getMenuShortcutKeyMask() + { + return Event.CTRL_MASK; + } + + /** + * Returns whether the given locking key on the keyboard is currently in its + * "on" state. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * @exception IllegalArgumentException If keyCode is not one of the valid keys. + * @exception UnsupportedOperationException If the host system doesn't allow + * getting the state of this key programmatically, or if the keyboard doesn't + * have this key. + */ + public boolean getLockingKeyState(int keyCode) + { + if (AWTUtilities.isValidKey(keyCode)) + throw new UnsupportedOperationException + ("cannot get locking state of key code " + keyCode); + + throw new IllegalArgumentException("invalid key code " + keyCode); + } + + /** + * Sets the state of the given locking key on the keyboard. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * @exception IllegalArgumentException If keyCode is not one of the valid keys. + * @exception UnsupportedOperationException If the host system doesn't allow + * getting the state of this key programmatically, or if the keyboard doesn't + * have this key. + */ + public void setLockingKeyState(int keyCode, boolean on) + { + if (keyCode != KeyEvent.VK_CAPS_LOCK + && keyCode != KeyEvent.VK_NUM_LOCK + && keyCode != KeyEvent.VK_SCROLL_LOCK) + throw new IllegalArgumentException(); + + throw new UnsupportedOperationException(); + } + + /** + * Returns the native container object of the specified component. This + * method is necessary because the parent component might be a lightweight + * component. + * + * @param component The component to fetch the native container for. + * + * @return The native container object for this component. + */ + protected static Container getNativeContainer(Component component) + { + component = component.getParent(); + while (true) + { + if (component == null) + return null; + if (! (component instanceof Container)) + { + component = component.getParent(); + continue; + } + if (component.getPeer() instanceof LightweightPeer) + { + component = component.getParent(); + continue; + } + return (Container) component; + } + } + + /** + * Creates a new custom cursor object. + * + * @exception IndexOutOfBoundsException If the hotSpot values are outside + * the bounds of the cursor. + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Cursor createCustomCursor(Image cursor, Point hotSpot, String name) + { + // Presumably the only reason this isn't abstract is for backwards + // compatibility? FIXME? + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException("No custom cursor in an headless graphics " + + "environment."); + return null; + } + + /** + * Returns the supported cursor dimension which is closest to the + * desired sizes. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) + { + if (GraphicsEnvironment.isHeadless()) + throw new HeadlessException("No best cursor size in an headless " + + "graphics environment."); + return new Dimension (0,0); + } + + /** + * Returns the maximum number of colors the Toolkit supports in a custom + * cursor palette. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public int getMaximumCursorColors() + { + return 0; + } + + /** + * Returns whether Toolkit supports this state for Frames. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + * + * @since 1.4 + */ + public boolean isFrameStateSupported(int state) + { + return false; + } + + /** + * Returns the value of the property with the specified name, or the + * default value if the property does not exist. + * + * @param key The name of the property to retrieve. + * @param def The default value of the property. + */ + public static String getProperty(String key, String def) + { + return props.getProperty(key, def); + } + + + /** + * Returns the event queue that is suitable for the calling context. + * + *

Despite the word “System” in the name of this + * method, a toolkit may provide different event queues for each + * applet. There is no guarantee that the same queue is shared + * system-wide. + * + *

The implementation first checks whether a + * SecurityManager has been installed. If so, its {@link + * java.lang.SecurityManager#checkAwtEventQueueAccess()} method gets + * called. The security manager will throw a SecurityException if it + * does not grant the permission to access the event queue. + * + *

Next, the call is delegated to {@link + * #getSystemEventQueueImpl()}. + * + * @return The event queue for this applet (or application). + * + * @throws SecurityException if a security manager has been + * installed, and it does not grant the permission to access the + * event queue. + */ + public final EventQueue getSystemEventQueue() + { + SecurityManager sm; + + sm = System.getSecurityManager(); + if (sm != null) + sm.checkAwtEventQueueAccess(); + + return getSystemEventQueueImpl(); + } + + + /** + * Returns the event queue that is suitable for the calling context. + * + *

Despite the word “System” in the name of this + * method, a toolkit may provide different event queues for each + * applet. There is no guarantee that the same queue is shared + * system-wide. + * + *

No security checks are performed, which is why this method + * may only be called by Toolkits. + * + * @see #getSystemEventQueue() + */ + protected abstract EventQueue getSystemEventQueueImpl(); + + + /** + * @since 1.3 + */ + public abstract DragSourceContextPeer + createDragSourceContextPeer(DragGestureEvent e); + + /** + * @since 1.3 + */ + public T + createDragGestureRecognizer(Class recognizer, DragSource ds, + Component comp, int actions, + DragGestureListener l) + { + return null; + } + + public final Object getDesktopProperty(String propertyName) + { + return desktopProperties.get(propertyName); + } + + protected final void setDesktopProperty(String name, Object newValue) + { + Object oldValue = getDesktopProperty(name); + desktopProperties.put(name, newValue); + desktopPropsSupport.firePropertyChange(name, oldValue, newValue); + } + + protected Object lazilyLoadDesktopProperty(String name) + { + // FIXME - what is this?? + return null; + } + + protected void initializeDesktopProperties() + { + // Overridden by toolkit implementation? + } + + public void addPropertyChangeListener(String name, + PropertyChangeListener pcl) + { + desktopPropsSupport.addPropertyChangeListener(name, pcl); + } + + public void removePropertyChangeListener(String name, + PropertyChangeListener pcl) + { + desktopPropsSupport.removePropertyChangeListener(name, pcl); + } + + /** + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners() + { + return desktopPropsSupport.getPropertyChangeListeners(); + } + + /** + * @since 1.4 + */ + public PropertyChangeListener[] getPropertyChangeListeners(String name) + { + return desktopPropsSupport.getPropertyChangeListeners(name); + } + + /** + * Adds an AWTEventListener to this toolkit. This listener is informed about + * all events that pass the eventqueue that match the specified + * evenMask. The eventMask is an ORed combination + * of event masks as defined in {@link AWTEvent}. + * + * If a security manager is installed, it is asked first if an + * AWTPermission("listenToAllAWTEvents") is allowed. + * This may result in a SecurityException beeing thrown. + * + * It is not recommended to use this kind of notification for normal + * applications. It is intended solely for the purpose of debugging and to + * support special facilities. + * + * @param listener the listener to add + * @param eventMask the event mask of event types which the listener is + * interested in + * + * @since 1.2 + * + * @throws SecurityException if there is a SecurityManager that + * doesn't grant + * AWTPermission("listenToAllAWTEvents") + * + * @see #getAWTEventListeners() + * @see #getAWTEventListeners(long) + * @see #removeAWTEventListener(AWTEventListener) + */ + public void addAWTEventListener(AWTEventListener listener, long eventMask) + { + // First we must check the security permissions. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new AWTPermission("listenToAllAWTEvents")); + + // Go through the list and check if the requested listener is already + // registered. + boolean found = false; + for (int i = 0; i < awtEventListeners.length; ++i) + { + AWTEventListenerProxy proxy = awtEventListeners[i]; + if (proxy.getListener() == listener) + { + found = true; + // Modify the proxies event mask to include the new event mask. + AWTEventListenerProxy newProxy = + new AWTEventListenerProxy(proxy.getEventMask() | eventMask, + listener); + awtEventListeners[i] = newProxy; + break; + } + } + + // If that listener was not found, then add it. + if (! found) + { + AWTEventListenerProxy proxy = + new AWTEventListenerProxy(eventMask, listener); + AWTEventListenerProxy[] newArray = + new AWTEventListenerProxy[awtEventListeners.length + 1]; + System.arraycopy(awtEventListeners, 0, newArray, 0, + awtEventListeners.length); + newArray[newArray.length - 1] = proxy; + awtEventListeners = newArray; + } + } + + /** + * Removes an AWT event listener from this toolkit. This listener is no + * longer informed of any event types it was registered in. + * + * If a security manager is installed, it is asked first if an + * AWTPermission("listenToAllAWTEvents") is allowed. + * This may result in a SecurityException beeing thrown. + * + * It is not recommended to use this kind of notification for normal + * applications. It is intended solely for the purpose of debugging and to + * support special facilities. + * + * @param listener the listener to remove + * + * @throws SecurityException if there is a SecurityManager that + * doesn't grant + * AWTPermission("listenToAllAWTEvents") + * + * @since 1.2 + * + * @see #addAWTEventListener(AWTEventListener, long) + * @see #getAWTEventListeners() + * @see #getAWTEventListeners(long) + */ + public void removeAWTEventListener(AWTEventListener listener) + { + // First we must check the security permissions. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new AWTPermission("listenToAllAWTEvents")); + + + // Find the index of the listener. + int index = -1; + for (int i = 0; i < awtEventListeners.length; ++i) + { + AWTEventListenerProxy proxy = awtEventListeners[i]; + if (proxy.getListener() == listener) + { + index = i; + break; + } + } + + // Copy over the arrays and leave out the removed element. + if (index != -1) + { + AWTEventListenerProxy[] newArray = + new AWTEventListenerProxy[awtEventListeners.length - 1]; + if (index > 0) + System.arraycopy(awtEventListeners, 0, newArray, 0, index); + if (index < awtEventListeners.length - 1) + System.arraycopy(awtEventListeners, index + 1, newArray, index, + awtEventListeners.length - index - 1); + awtEventListeners = newArray; + } + } + + /** + * Returns all registered AWT event listeners. This method returns a copy of + * the listener array, so that application cannot trash the listener list. + * + * If a security manager is installed, it is asked first if an + * AWTPermission("listenToAllAWTEvents") is allowed. + * This may result in a SecurityException beeing thrown. + * + * It is not recommended to use this kind of notification for normal + * applications. It is intended solely for the purpose of debugging and to + * support special facilities. + * + * @return all registered AWT event listeners + * + * @throws SecurityException if there is a SecurityManager that + * doesn't grant + * AWTPermission("listenToAllAWTEvents") + * + * @since 1.4 + * + * @see #addAWTEventListener(AWTEventListener, long) + * @see #removeAWTEventListener(AWTEventListener) + * @see #getAWTEventListeners(long) + */ + public AWTEventListener[] getAWTEventListeners() + { + // First we must check the security permissions. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new AWTPermission("listenToAllAWTEvents")); + + // Create a copy of the array. + AWTEventListener[] copy = new AWTEventListener[awtEventListeners.length]; + System.arraycopy(awtEventListeners, 0, copy, 0, awtEventListeners.length); + return copy; + } + + /** + * Returns all registered AWT event listeners that listen for events with + * the specified eventMask. This method returns a copy of + * the listener array, so that application cannot trash the listener list. + * + * If a security manager is installed, it is asked first if an + * AWTPermission("listenToAllAWTEvents") is allowed. + * This may result in a SecurityException beeing thrown. + * + * It is not recommended to use this kind of notification for normal + * applications. It is intended solely for the purpose of debugging and to + * support special facilities. + * + * @param mask the event mask + * + * @throws SecurityException if there is a SecurityManager that + * doesn't grant + * AWTPermission("listenToAllAWTEvents") + * + * + * @since 1.4 + * + * @see #addAWTEventListener(AWTEventListener, long) + * @see #removeAWTEventListener(AWTEventListener) + * @see #getAWTEventListeners() + */ + public AWTEventListener[] getAWTEventListeners(long mask) + { + // First we must check the security permissions. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new AWTPermission("listenToAllAWTEvents")); + + // Create a copy of the array with only the requested listeners in it. + ArrayList l = new ArrayList(awtEventListeners.length); + for (int i = 0; i < awtEventListeners.length; ++i) + { + if ((awtEventListeners[i].getEventMask() & mask) != 0) + l.add(awtEventListeners[i]); + } + + return (AWTEventListener[] ) l.toArray(new AWTEventListener[l.size()]); + } + + + /** + * Dispatches events to listeners registered to this Toolkit. This is called + * by {@link Component#dispatchEventImpl(AWTEvent)} in order to dispatch + * events globally. + * + * @param ev the event to dispatch + */ + void globalDispatchEvent(AWTEvent ev) + { + // We do not use the accessor methods here because they create new + // arrays each time. We must be very efficient, so we access this directly. + for (int i = 0; i < awtEventListeners.length; ++i) + { + AWTEventListenerProxy proxy = awtEventListeners[i]; + if ((proxy.getEventMask() & AWTEvent.eventIdToMask(ev.getID())) != 0) + proxy.eventDispatched(ev); + } + } + + /** + * @since 1.3 + */ + public abstract Map + mapInputMethodHighlight(InputMethodHighlight highlight); + + public abstract boolean isModalExclusionTypeSupported + (Dialog.ModalExclusionType modalExclusionType); + + public abstract boolean isModalityTypeSupported + (Dialog.ModalityType modalityType); + + /** + * Initializes the accessibility framework. In particular, this loads the + * properties javax.accessibility.screen_magnifier_present and + * javax.accessibility.screen_reader_present and loads + * the classes specified in javax.accessibility.assistive_technologies. + */ + private static void initAccessibility() + { + AccessController.doPrivileged + (new PrivilegedAction() + { + public Object run() + { + Properties props = new Properties(); + String sep = File.separator; + + // Try the user configuration. + try + { + File propsFile = new File(System.getProperty("user.home") + sep + + ".accessibility.properties"); + FileInputStream in = new FileInputStream(propsFile); + props.load(in); + in.close(); + } + catch (Exception ex) + { + // User configuration not present, ignore. + } + + // Try the system configuration if there was no user configuration. + if (props.size() == 0) + { + try + { + File propsFile = + new File(System.getProperty("gnu.classpath.home.url") + + sep + "accessibility.properties"); + FileInputStream in = new FileInputStream(propsFile); + props.load(in); + in.close(); + } + catch (Exception ex) + { + // System configuration not present, ignore. + } + } + + // Fetch the screen_magnifier_present property. Check systen properties + // first, then fallback to the configuration file. + String magPresent = SystemProperties.getProperty + ("javax.accessibility.screen_magnifier_present"); + if (magPresent == null) + { + magPresent = props.getProperty("screen_magnifier_present"); + if (magPresent != null) + { + SystemProperties.setProperty + ("javax.accessibility.screen_magnifier_present", magPresent); + } + } + + // Fetch the screen_reader_present property. Check systen properties + // first, then fallback to the configuration file. + String readerPresent = SystemProperties.getProperty + ("javax.accessibility.screen_reader_present"); + if (readerPresent == null) + { + readerPresent = props.getProperty("screen_reader_present"); + if (readerPresent != null) + { + SystemProperties.setProperty + ("javax.accessibility.screen_reader_present", readerPresent); + } + } + + // Fetch the list of classes to be loaded. + String classes = SystemProperties.getProperty + ("javax.accessibility.assistive_technologies"); + if (classes == null) + { + classes = props.getProperty("assistive_technologies"); + if (classes != null) + { + SystemProperties.setProperty + ("javax.accessibility.assistive_technologies", classes); + } + } + + // Try to load the assisitive_technologies classes. + if (classes != null) + { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + StringTokenizer tokenizer = new StringTokenizer(classes, ","); + while (tokenizer.hasMoreTokens()) + { + String className = tokenizer.nextToken(); + try + { + Class atClass = cl.loadClass(className); + atClass.newInstance(); + } + catch (ClassNotFoundException ex) + { + AWTError err = new AWTError("Assistive Technology class not" + + " found: " + className); + err.initCause(ex); + throw err; + } + catch (InstantiationException ex) + { + AWTError err = + new AWTError("Assistive Technology class cannot be " + + "instantiated: " + className); + err.initCause(ex); + throw err; + } + catch (IllegalAccessException ex) + { + AWTError err = + new AWTError("Assistive Technology class cannot be " + + "accessed: " + className); + err.initCause(err); + throw err; + } + } + } + return null; + } + }); + + } + +} // class Toolkit diff --git a/libjava/classpath/java/awt/Transparency.java b/libjava/classpath/java/awt/Transparency.java new file mode 100644 index 000000000..888587176 --- /dev/null +++ b/libjava/classpath/java/awt/Transparency.java @@ -0,0 +1,67 @@ +/* Transparency.java -- common transparency modes in graphics + Copyright (C) 2000, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt; + +/** + * A common transparency mode for layering graphics. + * + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public interface Transparency +{ + /** Image data which is completely opaque, for an alpha value of 1.0. */ + int OPAQUE = 1; + + /** + * Image data which is either completely opaque or transparent, for an + * exact integer alpha value. + */ + int BITMASK = 2; + + /** Image data which is translucent, for a non-integer alpha value. */ + int TRANSLUCENT = 3; + + /** + * Return the transparency type. + * + * @return One of {@link #OPAQUE}, {@link #BITMASK}, or {@link #TRANSLUCENT}. + */ + int getTransparency(); +} // interface Transparency diff --git a/libjava/classpath/java/awt/Window.java b/libjava/classpath/java/awt/Window.java new file mode 100644 index 000000000..986d1304a --- /dev/null +++ b/libjava/classpath/java/awt/Window.java @@ -0,0 +1,1286 @@ +/* Window.java -- + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt; + +import java.awt.event.ComponentEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowFocusListener; +import java.awt.event.WindowListener; +import java.awt.event.WindowStateListener; +import java.awt.image.BufferStrategy; +import java.awt.peer.WindowPeer; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.EventListener; +import java.util.Iterator; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +/** + * This class represents a top-level window with no decorations. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class Window extends Container implements Accessible +{ + private static final long serialVersionUID = 4497834738069338734L; + + // Serialized fields, from Sun's serialization spec. + private String warningString = null; + private int windowSerializedDataVersion = 0; // FIXME + /** @since 1.2 */ + // private FocusManager focusMgr; // FIXME: what is this? + /** @since 1.2 */ + private int state = 0; + /** @since 1.4 */ + private boolean focusableWindowState = true; + /** @since 1.5 */ + private boolean alwaysOnTop = false; + + // A list of other top-level windows owned by this window. + private transient Vector ownedWindows = new Vector(); + + private transient WindowListener windowListener; + private transient WindowFocusListener windowFocusListener; + private transient WindowStateListener windowStateListener; + + private transient boolean shown; + + // This is package-private to avoid an accessor method. + transient Component windowFocusOwner; + + /* + * The number used to generate the name returned by getName. + */ + private static transient long next_window_number; + + protected class AccessibleAWTWindow extends AccessibleAWTContainer + { + private static final long serialVersionUID = 4215068635060671780L; + + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.WINDOW; + } + + public AccessibleStateSet getAccessibleStateSet() + { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (isActive()) + states.add(AccessibleState.ACTIVE); + return states; + } + } + + /** + * This (package access) constructor is used by subclasses that want + * to build windows that do not have parents. Eg. toplevel + * application frames. Subclasses cannot call super(null), since + * null is an illegal argument. + */ + Window() + { + visible = false; + // Windows are the only Containers that default to being focus + // cycle roots. + focusCycleRoot = true; + setLayout(new BorderLayout()); + + GraphicsEnvironment g = GraphicsEnvironment.getLocalGraphicsEnvironment(); + graphicsConfig = g.getDefaultScreenDevice().getDefaultConfiguration(); + } + + Window(GraphicsConfiguration gc) + { + this(); + graphicsConfig = gc; + } + + /** + * Initializes a new instance of Window with the specified + * parent. The window will initially be invisible. + * + * @param owner The owning Frame of this window. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null; this exception is always + * thrown when GraphicsEnvironment.isHeadless returns true. + */ + public Window(Frame owner) + { + this (owner, owner.getGraphicsConfiguration ()); + } + + /** + * Initializes a new instance of Window with the specified + * parent. The window will initially be invisible. + * + * @exception IllegalArgumentException If the owner's GraphicsConfiguration + * is not from a screen device, or if owner is null; this exception is always + * thrown when GraphicsEnvironment.isHeadless returns true. + * + * @since 1.2 + */ + public Window(Window owner) + { + this (owner, owner.getGraphicsConfiguration ()); + } + + /** + * Initializes a new instance of Window with the specified + * parent. The window will initially be invisible. + * + * @exception IllegalArgumentException If owner is null or if gc is not from a + * screen device; this exception is always thrown when + * GraphicsEnvironment.isHeadless returns true. + * + * @since 1.3 + */ + public Window(Window owner, GraphicsConfiguration gc) + { + this (); + + synchronized (getTreeLock()) + { + if (owner == null) + throw new IllegalArgumentException ("owner must not be null"); + + parent = owner; + owner.ownedWindows.add(new WeakReference(this)); + } + + // FIXME: make this text visible in the window. + SecurityManager s = System.getSecurityManager(); + if (s != null && ! s.checkTopLevelWindow(this)) + warningString = System.getProperty("awt.appletWarning"); + + if (gc != null + && gc.getDevice().getType() != GraphicsDevice.TYPE_RASTER_SCREEN) + throw new IllegalArgumentException ("gc must be from a screen device"); + + if (gc == null) + graphicsConfig = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration(); + else + graphicsConfig = gc; + } + + /** + * Creates the native peer for this window. + */ + public void addNotify() + { + if (peer == null) + peer = getToolkit().createWindow(this); + super.addNotify(); + } + + /** + * Relays out this window's child components at their preferred size. + * + * @specnote pack() doesn't appear to be called internally by show(), so + * we duplicate some of the functionality. + */ + public void pack() + { + if (parent != null && !parent.isDisplayable()) + parent.addNotify(); + if (peer == null) + addNotify(); + + setSize(getPreferredSize()); + + validate(); + } + + /** + * Shows on-screen this window and any of its owned windows for whom + * isVisible returns true. + * @specnote: Deprecated starting in 1.5. + */ + @Deprecated + public void show() + { + synchronized (getTreeLock()) + { + if (peer == null) + addNotify(); + + validate(); + if (visible) + toFront(); + else + { + super.show(); + // Show visible owned windows. + Iterator e = ownedWindows.iterator(); + while (e.hasNext()) + { + Window w = (Window) (((Reference) e.next()).get()); + if (w != null) + { + if (w.isVisible()) + w.getPeer().setVisible(true); + } + else + // Remove null weak reference from ownedWindows. + // Unfortunately this can't be done in the Window's + // finalize method because there is no way to guarantee + // synchronous access to ownedWindows there. + e.remove(); + } + } + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + manager.setGlobalFocusedWindow(this); + + if (! shown) + { + FocusTraversalPolicy policy = getFocusTraversalPolicy(); + Component initialFocusOwner = null; + + if (policy != null) + initialFocusOwner = policy.getInitialComponent(this); + + if (initialFocusOwner != null) + initialFocusOwner.requestFocusInWindow(); + + // Post WINDOW_OPENED from here. + if (windowListener != null + || (eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0) + { + WindowEvent ev = new WindowEvent(this, + WindowEvent.WINDOW_OPENED); + Toolkit tk = Toolkit.getDefaultToolkit(); + tk.getSystemEventQueue().postEvent(ev); + } + shown = true; + } + } + } + + /** + * @specnote: Deprecated starting in 1.5. + */ + @Deprecated + public void hide() + { + // Hide visible owned windows. + synchronized (getTreeLock ()) + { + Iterator e = ownedWindows.iterator(); + while(e.hasNext()) + { + Window w = (Window)(((Reference) e.next()).get()); + if (w != null) + { + if (w.isVisible() && w.getPeer() != null) + w.getPeer().setVisible(false); + } + else + e.remove(); + } + } + super.hide(); + } + + /** + * Destroys any resources associated with this window. This includes + * all components in the window and all owned top-level windows. + */ + public void dispose() + { + hide(); + + synchronized (getTreeLock ()) + { + Iterator e = ownedWindows.iterator(); + while(e.hasNext()) + { + Window w = (Window)(((Reference) e.next()).get()); + if (w != null) + w.dispose(); + else + // Remove null weak reference from ownedWindows. + e.remove(); + } + + for (int i = 0; i < ncomponents; ++i) + component[i].removeNotify(); + this.removeNotify(); + + // Post WINDOW_CLOSED from here. + if (windowListener != null + || (eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0) + { + WindowEvent ev = new WindowEvent(this, + WindowEvent.WINDOW_CLOSED); + Toolkit tk = Toolkit.getDefaultToolkit(); + tk.getSystemEventQueue().postEvent(ev); + } + } + } + + /** + * Sends this window to the back so that all other windows display in + * front of it. + * + * If the window is set to be always-on-top, this will remove its + * always-on-top status. + */ + public void toBack() + { + if (peer != null) + { + if( alwaysOnTop ) + setAlwaysOnTop( false ); + ( (WindowPeer) peer ).toBack(); + } + } + + /** + * Brings this window to the front so that it displays in front of + * any other windows. + */ + public void toFront() + { + if (peer != null) + ( (WindowPeer) peer ).toFront(); + } + + /** + * Returns the toolkit used to create this window. + * + * @return The toolkit used to create this window. + * + * @specnote Unlike Component.getToolkit, this implementation always + * returns the value of Toolkit.getDefaultToolkit(). + */ + public Toolkit getToolkit() + { + return Toolkit.getDefaultToolkit(); + } + + /** + * Returns the warning string that will be displayed if this window is + * popped up by an unsecure applet or application. + * + * @return The unsecure window warning message. + */ + public final String getWarningString() + { + return warningString; + } + + /** + * Returns the locale that this window is configured for. + * + * @return The locale this window is configured for. + */ + public Locale getLocale() + { + return locale == null ? Locale.getDefault() : locale; + } + + /* + /** @since 1.2 + public InputContext getInputContext() + { + // FIXME + } + */ + + /** + * Sets the cursor for this window to the specifiec cursor. + * + * @param cursor The new cursor for this window. + */ + public void setCursor(Cursor cursor) + { + super.setCursor(cursor); + } + + public Window getOwner() + { + return (Window) parent; + } + + /** @since 1.2 */ + public Window[] getOwnedWindows() + { + Window [] trimmedList; + synchronized (getTreeLock ()) + { + // Windows with non-null weak references in ownedWindows. + Window [] validList = new Window [ownedWindows.size()]; + + Iterator e = ownedWindows.iterator(); + int numValid = 0; + while (e.hasNext()) + { + Window w = (Window)(((Reference) e.next()).get()); + if (w != null) + validList[numValid++] = w; + else + // Remove null weak reference from ownedWindows. + e.remove(); + } + + if (numValid != validList.length) + { + trimmedList = new Window [numValid]; + System.arraycopy (validList, 0, trimmedList, 0, numValid); + } + else + trimmedList = validList; + } + return trimmedList; + } + + /** + * Adds the specified listener to the list of WindowListeners + * that will receive events for this window. + * + * @param listener The WindowListener to add. + */ + public synchronized void addWindowListener(WindowListener listener) + { + if (listener != null) + { + newEventsOnly = true; + windowListener = AWTEventMulticaster.add(windowListener, listener); + } + } + + /** + * Removes the specified listener from the list of + * WindowListeners that will receive events for this window. + * + * @param listener The WindowListener to remove. + */ + public synchronized void removeWindowListener(WindowListener listener) + { + windowListener = AWTEventMulticaster.remove(windowListener, listener); + } + + /** + * Returns an array of all the window listeners registered on this window. + * + * @since 1.4 + */ + public synchronized WindowListener[] getWindowListeners() + { + return (WindowListener[]) + AWTEventMulticaster.getListeners(windowListener, + WindowListener.class); + } + + /** + * Returns an array of all the window focus listeners registered on this + * window. + * + * @since 1.4 + */ + public synchronized WindowFocusListener[] getWindowFocusListeners() + { + return (WindowFocusListener[]) + AWTEventMulticaster.getListeners(windowFocusListener, + WindowFocusListener.class); + } + + /** + * Returns an array of all the window state listeners registered on this + * window. + * + * @since 1.4 + */ + public synchronized WindowStateListener[] getWindowStateListeners() + { + return (WindowStateListener[]) + AWTEventMulticaster.getListeners(windowStateListener, + WindowStateListener.class); + } + + /** + * Adds the specified listener to this window. + */ + public void addWindowFocusListener (WindowFocusListener wfl) + { + if (wfl != null) + { + newEventsOnly = true; + windowFocusListener = AWTEventMulticaster.add (windowFocusListener, + wfl); + } + } + + /** + * Adds the specified listener to this window. + * + * @since 1.4 + */ + public void addWindowStateListener (WindowStateListener wsl) + { + if (wsl != null) + { + newEventsOnly = true; + windowStateListener = AWTEventMulticaster.add (windowStateListener, + wsl); + } + } + + /** + * Removes the specified listener from this window. + */ + public void removeWindowFocusListener (WindowFocusListener wfl) + { + windowFocusListener = AWTEventMulticaster.remove (windowFocusListener, wfl); + } + + /** + * Removes the specified listener from this window. + * + * @since 1.4 + */ + public void removeWindowStateListener (WindowStateListener wsl) + { + windowStateListener = AWTEventMulticaster.remove (windowStateListener, wsl); + } + + /** + * Returns an array of all the objects currently registered as FooListeners + * upon this Window. FooListeners are registered using the addFooListener + * method. + * + * @exception ClassCastException If listenerType doesn't specify a class or + * interface that implements java.util.EventListener. + * + * @since 1.3 + */ + public T[] getListeners(Class listenerType) + { + if (listenerType == WindowListener.class) + return (T[]) getWindowListeners(); + return super.getListeners(listenerType); + } + + void dispatchEventImpl(AWTEvent e) + { + if (e.getID() == ComponentEvent.COMPONENT_RESIZED) + { + invalidate(); + validate(); + } + super.dispatchEventImpl(e); + } + + /** + * Processes the specified event for this window. If the event is an + * instance of WindowEvent, then + * processWindowEvent() is called to process the event, + * otherwise the superclass version of this method is invoked. + * + * @param evt The event to process. + */ + protected void processEvent(AWTEvent evt) + { + if (evt instanceof WindowEvent) + { + WindowEvent we = (WindowEvent) evt; + switch (evt.getID()) + { + case WindowEvent.WINDOW_OPENED: + case WindowEvent.WINDOW_CLOSED: + case WindowEvent.WINDOW_CLOSING: + case WindowEvent.WINDOW_ICONIFIED: + case WindowEvent.WINDOW_DEICONIFIED: + case WindowEvent.WINDOW_ACTIVATED: + case WindowEvent.WINDOW_DEACTIVATED: + processWindowEvent(we); + break; + case WindowEvent.WINDOW_GAINED_FOCUS: + case WindowEvent.WINDOW_LOST_FOCUS: + processWindowFocusEvent(we); + break; + case WindowEvent.WINDOW_STATE_CHANGED: + processWindowStateEvent(we); + break; + } + } + else + super.processEvent(evt); + } + + /** + * Dispatches this event to any listeners that are listening for + * WindowEvents on this window. This method only gets + * invoked if it is enabled via enableEvents() or if + * a listener has been added. + * + * @param evt The event to process. + */ + protected void processWindowEvent(WindowEvent evt) + { + if (windowListener != null) + { + switch (evt.getID()) + { + case WindowEvent.WINDOW_ACTIVATED: + windowListener.windowActivated(evt); + break; + case WindowEvent.WINDOW_CLOSED: + windowListener.windowClosed(evt); + break; + case WindowEvent.WINDOW_CLOSING: + windowListener.windowClosing(evt); + break; + case WindowEvent.WINDOW_DEACTIVATED: + windowListener.windowDeactivated(evt); + break; + case WindowEvent.WINDOW_DEICONIFIED: + windowListener.windowDeiconified(evt); + break; + case WindowEvent.WINDOW_ICONIFIED: + windowListener.windowIconified(evt); + break; + case WindowEvent.WINDOW_OPENED: + windowListener.windowOpened(evt); + break; + } + } + } + + /** + * Identifies if this window is active. The active window is a Frame or + * Dialog that has focus or owns the active window. + * + * @return true if active, else false. + * @since 1.4 + */ + public boolean isActive() + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + return manager.getActiveWindow() == this; + } + + /** + * Identifies if this window is focused. A window is focused if it is the + * focus owner or it contains the focus owner. + * + * @return true if focused, else false. + * @since 1.4 + */ + public boolean isFocused() + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + return manager.getFocusedWindow() == this; + } + + /** + * Returns the child window that has focus if this window is active. + * This method returns null if this window is not active + * or no children have focus. + * + * @return The component that has focus, or null if no + * component has focus. + */ + public Component getFocusOwner () + { + KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); + + Window activeWindow = manager.getActiveWindow (); + + // The currently-focused Component belongs to the active Window. + if (activeWindow == this) + return manager.getFocusOwner (); + else + return null; + } + + /** + * Returns the child component of this window that would receive + * focus if this window were to become focused. If the window + * already has the top-level focus, then this method returns the + * same component as getFocusOwner. If no child component has + * requested focus within the window, then the initial focus owner + * is returned. If this is a non-focusable window, this method + * returns null. + * + * @return the child component of this window that most recently had + * the focus, or null + * @since 1.4 + */ + public Component getMostRecentFocusOwner () + { + return windowFocusOwner; + } + + /** + * Set the focus owner for this window. This method is used to + * remember which component was focused when this window lost + * top-level focus, so that when it regains top-level focus the same + * child component can be refocused. + * + * @param windowFocusOwner the component in this window that owns + * the focus. + */ + void setFocusOwner (Component windowFocusOwner) + { + this.windowFocusOwner = windowFocusOwner; + } + + /** + * Post a Java 1.0 event to the event queue. + * + * @param e The event to post. + * + * @deprecated + */ + public boolean postEvent(Event e) + { + return handleEvent (e); + } + + /** + * Tests whether or not this window is visible on the screen. + * + * In contrast to the normal behaviour of Container, which is that + * a container is showing if its parent is visible and showing, a Window + * is even showing, if its parent (i.e. an invisible Frame) is not showing. + * + * @return true if this window is visible, false + * otherwise. + */ + public boolean isShowing() + { + return isVisible(); + } + + public void setLocationRelativeTo(Component c) + { + int x = 0; + int y = 0; + + if (c == null || !c.isShowing()) + { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Point center = ge.getCenterPoint(); + x = center.x - (width / 2); + y = center.y - (height / 2); + } + else + { + int cWidth = c.getWidth(); + int cHeight = c.getHeight(); + Dimension screenSize = getToolkit().getScreenSize(); + + x = c.getLocationOnScreen().x; + y = c.getLocationOnScreen().y; + + // If bottom of component is cut off, window placed + // on the left or the right side of component + if ((y + cHeight) > screenSize.height) + { + // If the right side of the component is closer to the center + if ((screenSize.width / 2 - x) <= 0) + { + if ((x - width) >= 0) + x -= width; + else + x = 0; + } + else + { + if ((x + cWidth + width) <= screenSize.width) + x += cWidth; + else + x = screenSize.width - width; + } + + y = screenSize.height - height; + } + else if (cWidth > width || cHeight > height) + { + // If right side of component is cut off + if ((x + width) > screenSize.width) + x = screenSize.width - width; + // If left side of component is cut off + else if (x < 0) + x = 0; + else + x += (cWidth - width) / 2; + + y += (cHeight - height) / 2; + } + else + { + // If right side of component is cut off + if ((x + width) > screenSize.width) + x = screenSize.width - width; + // If left side of component is cut off + else if (x < 0 || (x - (width - cWidth) / 2) < 0) + x = 0; + else + x -= (width - cWidth) / 2; + + if ((y - (height - cHeight) / 2) > 0) + y -= (height - cHeight) / 2; + else + y = 0; + } + } + + setLocation(x, y); + } + + /** + * A BltBufferStrategy for windows. + */ + private class WindowBltBufferStrategy extends BltBufferStrategy + { + /** + * Creates a block transfer strategy for this window. + * + * @param numBuffers the number of buffers in this strategy + * @param accelerated true if the buffer should be accelerated, + * false otherwise + */ + WindowBltBufferStrategy(int numBuffers, boolean accelerated) + { + super(numBuffers, + new BufferCapabilities(new ImageCapabilities(accelerated), + new ImageCapabilities(accelerated), + BufferCapabilities.FlipContents.COPIED)); + } + } + + /** + * A FlipBufferStrategy for windows. + */ + private class WindowFlipBufferStrategy extends FlipBufferStrategy + { + /** + * Creates a flip buffer strategy for this window. + * + * @param numBuffers the number of buffers in this strategy + * + * @throws AWTException if the requested number of buffers is not + * supported + */ + WindowFlipBufferStrategy(int numBuffers) + throws AWTException + { + super(numBuffers, + new BufferCapabilities(new ImageCapabilities(true), + new ImageCapabilities(true), + BufferCapabilities.FlipContents.COPIED)); + } + } + + /** + * Creates a buffering strategy that manages how this window is + * repainted. This method attempts to create the optimum strategy + * based on the desired number of buffers. Hardware or software + * acceleration may be used. + * + * createBufferStrategy attempts different levels of optimization, + * but guarantees that some strategy with the requested number of + * buffers will be created even if it is not optimal. First it + * attempts to create a page flipping strategy, then an accelerated + * blitting strategy, then an unaccelerated blitting strategy. + * + * Calling this method causes any existing buffer strategy to be + * destroyed. + * + * @param numBuffers the number of buffers in this strategy + * + * @throws IllegalArgumentException if requested number of buffers + * is less than one + * @throws IllegalStateException if this window is not displayable + * + * @since 1.4 + */ + public void createBufferStrategy(int numBuffers) + { + if (numBuffers < 1) + throw new IllegalArgumentException("Window.createBufferStrategy: number" + + " of buffers is less than one"); + + if (!isDisplayable()) + throw new IllegalStateException("Window.createBufferStrategy: window is" + + " not displayable"); + + BufferStrategy newStrategy = null; + + // try a flipping strategy + try + { + newStrategy = new WindowFlipBufferStrategy(numBuffers); + } + catch (AWTException e) + { + } + + // fall back to an accelerated blitting strategy + if (newStrategy == null) + newStrategy = new WindowBltBufferStrategy(numBuffers, true); + + bufferStrategy = newStrategy; + } + + /** + * Creates a buffering strategy that manages how this window is + * repainted. This method attempts to create a strategy based on + * the specified capabilities and throws an exception if the + * requested strategy is not supported. + * + * Calling this method causes any existing buffer strategy to be + * destroyed. + * + * @param numBuffers the number of buffers in this strategy + * @param caps the requested buffering capabilities + * + * @throws AWTException if the requested capabilities are not + * supported + * @throws IllegalArgumentException if requested number of buffers + * is less than one or if caps is null + * + * @since 1.4 + */ + public void createBufferStrategy(int numBuffers, BufferCapabilities caps) + throws AWTException + { + if (numBuffers < 1) + throw new IllegalArgumentException("Window.createBufferStrategy: number" + + " of buffers is less than one"); + + if (caps == null) + throw new IllegalArgumentException("Window.createBufferStrategy:" + + " capabilities object is null"); + + // a flipping strategy was requested + if (caps.isPageFlipping()) + bufferStrategy = new WindowFlipBufferStrategy(numBuffers); + else + bufferStrategy = new WindowBltBufferStrategy(numBuffers, true); + } + + /** + * Returns the buffer strategy used by the window. + * + * @return the buffer strategy. + * @since 1.4 + */ + public BufferStrategy getBufferStrategy() + { + return bufferStrategy; + } + + /** + * @since 1.2 + * + * @deprecated replaced by Component.applyComponentOrientation. + */ + public void applyResourceBundle(ResourceBundle rb) + { + applyComponentOrientation(ComponentOrientation.getOrientation(rb)); + } + + /** + * @since 1.2 + * + * @deprecated + */ + public void applyResourceBundle(String rbName) + { + ResourceBundle rb = ResourceBundle.getBundle(rbName, Locale.getDefault(), + ClassLoader.getSystemClassLoader()); + if (rb != null) + applyResourceBundle(rb); + } + + /** + * Gets the AccessibleContext associated with this Window. + * The context is created, if necessary. + * + * @return the associated context + */ + public AccessibleContext getAccessibleContext() + { + /* Create the context if this is the first request */ + if (accessibleContext == null) + accessibleContext = new AccessibleAWTWindow(); + return accessibleContext; + } + + /** + * Get graphics configuration. The implementation for Window will + * not ask any parent containers, since Window is a toplevel + * window and not actually embedded in the parent component. + */ + public GraphicsConfiguration getGraphicsConfiguration() + { + GraphicsConfiguration conf = graphicsConfig; + if (conf == null) + { + conf = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + graphicsConfig = conf; + } + return conf; + } + + protected void processWindowFocusEvent(WindowEvent event) + { + if (windowFocusListener != null) + { + switch (event.getID ()) + { + case WindowEvent.WINDOW_GAINED_FOCUS: + windowFocusListener.windowGainedFocus (event); + break; + + case WindowEvent.WINDOW_LOST_FOCUS: + windowFocusListener.windowLostFocus (event); + break; + + default: + break; + } + } + } + + /** + * @since 1.4 + */ + protected void processWindowStateEvent(WindowEvent event) + { + if (windowStateListener != null + && event.getID () == WindowEvent.WINDOW_STATE_CHANGED) + windowStateListener.windowStateChanged (event); + } + + /** + * Returns whether this Window can get the focus or not. + * + * @since 1.4 + */ + public final boolean isFocusableWindow () + { + if (getFocusableWindowState () == false) + return false; + + if (this instanceof Dialog + || this instanceof Frame) + return true; + + // FIXME: Implement more possible cases for returning true. + + return false; + } + + /** + * Returns the value of the focusableWindowState property. + * + * @since 1.4 + */ + public boolean getFocusableWindowState () + { + return focusableWindowState; + } + + /** + * Sets the value of the focusableWindowState property. + * + * @since 1.4 + */ + public void setFocusableWindowState (boolean focusableWindowState) + { + this.focusableWindowState = focusableWindowState; + } + + /** + * Check whether this Container is a focus cycle root. + * Returns always true as Windows are the + * root of the focus cycle. + * + * @return Always true. + * + * @since 1.4 + */ + public final boolean isFocusCycleRoot() + { + return true; + } + + /** + * Set whether or not this Container is the root of a focus + * traversal cycle. Windows are the root of the focus cycle + * and therefore this method does nothing. + * + * @param focusCycleRoot ignored. + * + * @since 1.4 + */ + public final void setFocusCycleRoot(boolean focusCycleRoot) + { + // calls to the method are ignored + } + + /** + * Returns the root container that owns the focus cycle where this + * component resides. Windows have no ancestors and this method + * returns always null. + * + * @return Always null. + * @since 1.4 + */ + public final Container getFocusCycleRootAncestor() + { + return null; + } + + /** + * Returns whether the Windows is an always-on-top window, + * meaning whether the window can be obscured by other windows or not. + * + * @return true if the windows is always-on-top, + * false otherwise. + * @since 1.5 + */ + public final boolean isAlwaysOnTop() + { + return alwaysOnTop; + } + + /** + * Sets the always-on-top state of this window (if supported). + * + * Setting a window to always-on-top means it will not be obscured + * by any other windows (with the exception of other always-on-top + * windows). Not all platforms may support this. + * + * If an window's always-on-top status is changed to false, the window + * will remain at the front but not be anchored there. + * + * Calling toBack() on an always-on-top window will change its + * always-on-top status to false. + * + * @since 1.5 + */ + public final void setAlwaysOnTop(boolean alwaysOnTop) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission( new AWTPermission("setWindowAlwaysOnTop") ); + + if( this.alwaysOnTop == alwaysOnTop ) + return; + + if( alwaysOnTop ) + toFront(); + + firePropertyChange("alwaysOnTop", this.alwaysOnTop, alwaysOnTop ); + this.alwaysOnTop = alwaysOnTop; + + if (peer != null) + ( (WindowPeer) peer).updateAlwaysOnTop(); + else + System.out.println("Null peer?!"); + } + + /** + * Generate a unique name for this window. + * + * @return A unique name for this window. + */ + String generateName() + { + return "win" + getUniqueLong(); + } + + /** + * Overridden to handle WindowEvents. + * + * @return true when the specified event type is enabled, + * false otherwise + */ + boolean eventTypeEnabled(int type) + { + boolean enabled = false; + switch (type) + { + case WindowEvent.WINDOW_OPENED: + case WindowEvent.WINDOW_CLOSED: + case WindowEvent.WINDOW_CLOSING: + case WindowEvent.WINDOW_ICONIFIED: + case WindowEvent.WINDOW_DEICONIFIED: + case WindowEvent.WINDOW_ACTIVATED: + case WindowEvent.WINDOW_DEACTIVATED: + enabled = ((eventMask & AWTEvent.WINDOW_EVENT_MASK) != 0) + || windowListener != null; + break; + case WindowEvent.WINDOW_GAINED_FOCUS: + case WindowEvent.WINDOW_LOST_FOCUS: + enabled = ((eventMask & AWTEvent.WINDOW_FOCUS_EVENT_MASK) != 0) + || windowFocusListener != null; + break; + case WindowEvent.WINDOW_STATE_CHANGED: + enabled = ((eventMask & AWTEvent.WINDOW_STATE_EVENT_MASK) != 0) + || windowStateListener != null; + break; + default: + enabled = super.eventTypeEnabled(type); + } + return enabled; + } + + private static synchronized long getUniqueLong() + { + return next_window_number++; + } +} diff --git a/libjava/classpath/java/awt/color/CMMException.java b/libjava/classpath/java/awt/color/CMMException.java new file mode 100644 index 000000000..ab328ec84 --- /dev/null +++ b/libjava/classpath/java/awt/color/CMMException.java @@ -0,0 +1,63 @@ +/* CMMException.java -- error in the native CMM + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +/** + * Thrown when there is an error in the native CMM. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class CMMException extends RuntimeException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 5775558044142994965L; + + /** + * Create a new instance with a specified detailed error message. + * + * @param message the message + */ + public CMMException(String message) + { + super(message); + } +} // class CMMException diff --git a/libjava/classpath/java/awt/color/ColorSpace.java b/libjava/classpath/java/awt/color/ColorSpace.java new file mode 100644 index 000000000..79369da71 --- /dev/null +++ b/libjava/classpath/java/awt/color/ColorSpace.java @@ -0,0 +1,183 @@ +/* ColorSpace.java -- transforms between color spaces + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +import java.io.Serializable; + +/** + * NEEDS DOCUMENTATION + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @since 1.2 + */ +public abstract class ColorSpace implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -409452704308689724L; + + public static final int TYPE_XYZ = 0; + public static final int TYPE_Lab = 1; + public static final int TYPE_Luv = 2; + public static final int TYPE_YCbCr = 3; + public static final int TYPE_Yxy = 4; + public static final int TYPE_RGB = 5; + public static final int TYPE_GRAY = 6; + public static final int TYPE_HSV = 7; + public static final int TYPE_HLS = 8; + public static final int TYPE_CMYK = 9; + // mysterious gap in the enumeration sequenece + public static final int TYPE_CMY = 11; + public static final int TYPE_2CLR = 12; + public static final int TYPE_3CLR = 13; + public static final int TYPE_4CLR = 14; + public static final int TYPE_5CLR = 15; + public static final int TYPE_6CLR = 16; + public static final int TYPE_7CLR = 17; + public static final int TYPE_8CLR = 18; + public static final int TYPE_9CLR = 19; + public static final int TYPE_ACLR = 20; + public static final int TYPE_BCLR = 21; + public static final int TYPE_CCLR = 22; + public static final int TYPE_DCLR = 23; + public static final int TYPE_ECLR = 24; + public static final int TYPE_FCLR = 25; + + public static final int CS_sRGB = 1000; + public static final int CS_LINEAR_RGB = 1004; + public static final int CS_CIEXYZ = 1001; + public static final int CS_PYCC = 1002; + public static final int CS_GRAY = 1003; + + private static final int CS_BASE = CS_sRGB; + private static final int CS_END = CS_LINEAR_RGB + 1; + private static final int CS_COUNT = CS_END - CS_BASE; + + // Instances are lazily instantiated + private static final ColorSpace[] INSTANCES = new ColorSpace[CS_COUNT]; + + /** + * @serial + */ + // Visible in subclass. + final int type; + + /** + * @serial + */ + // Visible in subclass. + final int numComponents; + + protected ColorSpace(int type, int numcomponents) + { + this.type = type; + numComponents = numcomponents; + } + + public static ColorSpace getInstance(int colorspace) + { + if ((colorspace >= CS_BASE) && (colorspace < CS_END)) + { + int instanceIndex = colorspace - CS_BASE; + if (INSTANCES[instanceIndex] == null) + { + ICC_Profile profile = new ICC_Profile(colorspace); + INSTANCES[instanceIndex] = new ICC_ColorSpace(profile); + } + return INSTANCES[instanceIndex]; + } + throw new IllegalArgumentException("unknown/unsupported colorspace"); + } + + public boolean isCS_sRGB() + { + return type == CS_sRGB; + } + + /** + * Transforms a color value assumed to be in this ColorSpace into a value in + * the default CS_sRGB color space. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at least + * the number of components in this ColorSpace. + */ + public abstract float[] toRGB(float[] colorvalue); + + public abstract float[] fromRGB(float[] rgbvalue); + + public abstract float[] toCIEXYZ(float[] colorvalue); + + public abstract float[] fromCIEXYZ(float[] colorvalue); + + public int getType() + { + return type; + } + + public int getNumComponents() + { + return numComponents; + } + + public String getName(int idx) + { + return "type " + type; + } + + /** + * @since 1.4 + */ + public float getMinValue(int idx) + { + if (idx < 0 || idx >= numComponents) + throw new IllegalArgumentException(); + return 0; + } + + /** + * @since 1.4 + */ + public float getMaxValue(int idx) + { + if (idx < 0 || idx >= numComponents) + throw new IllegalArgumentException(); + return 1; + } +} // class ColorSpace diff --git a/libjava/classpath/java/awt/color/ICC_ColorSpace.java b/libjava/classpath/java/awt/color/ICC_ColorSpace.java new file mode 100644 index 000000000..ac636fb26 --- /dev/null +++ b/libjava/classpath/java/awt/color/ICC_ColorSpace.java @@ -0,0 +1,314 @@ +/* ICC_ColorSpace.java -- the canonical color space implementation + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +import gnu.java.awt.color.CieXyzConverter; +import gnu.java.awt.color.ClutProfileConverter; +import gnu.java.awt.color.ColorSpaceConverter; +import gnu.java.awt.color.GrayProfileConverter; +import gnu.java.awt.color.GrayScaleConverter; +import gnu.java.awt.color.LinearRGBConverter; +import gnu.java.awt.color.PyccConverter; +import gnu.java.awt.color.RgbProfileConverter; +import gnu.java.awt.color.SrgbConverter; + +import java.io.IOException; +import java.io.ObjectInputStream; + +/** + * ICC_ColorSpace - an implementation of ColorSpace + * + * While an ICC_Profile class abstracts the data in an ICC profile file + * an ICC_ColorSpace performs the color space conversions defined by + * an ICC_Profile instance. + * + * Typically, an ICC_Profile will either be created using getInstance, + * either from the built-in colorspaces, or from an ICC profile file. + * Then a ICC_Colorspace will be used to perform transforms from the + * device colorspace to and from the profile color space. + * + * The PCS used by ColorSpace is CIE XYZ relative a D50 white point. + * (Profiles using a CIE Lab PCS will have their input and output converted + * to D50 CIE XYZ accordingly. + * + * Note that a valid profile may not contain transforms in both directions, + * in which case the output may be undefined. + * All built-in colorspaces have bidirectional transforms, but developers + * using an ICC profile file may want to check the profile class using + * the ICC_Profile.getProfileClass() method. Input class profiles are + * guaranteed to have transforms to the PCS, output class profiles are + * guaranteed to have transforms from the PCS to device space. + * + * @author Sven de Marothy + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @since 1.2 + */ +public class ICC_ColorSpace extends ColorSpace +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 3455889114070431483L; + + /** + * @serial + */ + private ICC_Profile thisProfile; + + /** + * @serial + */ + private float[] minVal; + + /** + * @serial + */ + private float[] maxVal; + + /** + * @serial + */ + private float[] diffMinMax; + + /** + * @serial + */ + private float[] invDiffMinMax; + + /** + * @serial + */ + private boolean needScaleInit; + + /** + * Tells us if the PCS is CIE LAB (must be CIEXYZ otherwise) + */ + private transient int type; + private transient int nComponents; + private transient ColorSpaceConverter converter; + + /** + * Constructs a new ICC_ColorSpace from an ICC_Profile object. + * + * @exception IllegalArgumentException If profile is inappropriate for + * representing a ColorSpace. + */ + public ICC_ColorSpace(ICC_Profile profile) + { + super(profile.getColorSpaceType(), profile.getNumComponents()); + + converter = getConverter(profile); + thisProfile = profile; + nComponents = profile.getNumComponents(); + type = profile.getColorSpaceType(); + makeArrays(); + } + + /** + * Return the profile + */ + public ICC_Profile getProfile() + { + return thisProfile; + } + + /** + * Transforms a color value assumed to be in this ColorSpace into a value in + * the default CS_sRGB color space. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at least + * the number of components in this ColorSpace. + */ + public float[] toRGB(float[] colorvalue) + { + return converter.toRGB(colorvalue); + } + + /** + * Transforms a color value assumed to be in the default CS_sRGB color space + * into this ColorSpace. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at + * least 3. + */ + public float[] fromRGB(float[] rgbvalue) + { + return converter.fromRGB(rgbvalue); + } + + /** + * Transforms a color value assumed to be in this ColorSpace into the + * CS_CIEXYZ conversion color space. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at + * least the number of components in this ColorSpace. + */ + public float[] toCIEXYZ(float[] colorvalue) + { + return converter.toCIEXYZ(colorvalue); + } + + /** + * Transforms a color value assumed to be in the CS_CIEXYZ conversion color + * space into this ColorSpace. + * + * @exception ArrayIndexOutOfBoundsException If array length is not at + * least 3. + */ + public float[] fromCIEXYZ(float[] colorvalue) + { + return converter.fromCIEXYZ(colorvalue); + } + + public boolean isCS_sRGB() + { + return converter instanceof SrgbConverter; + } + + /** + * Returns the minimum normalized color component value for the specified + * component. + * + * @exception IllegalArgumentException If component is less than 0 or greater + * than numComponents - 1. + * + * @since 1.4 + */ + public float getMinValue(int idx) + { + // FIXME: Not 100% certain of this. + if (type == ColorSpace.TYPE_Lab && (idx == 1 || idx == 2)) + return -128f; + + if (idx < 0 || idx >= nComponents) + throw new IllegalArgumentException(); + return 0; + } + + /** + * Returns the maximum normalized color component value for the specified + * component. + * + * @exception IllegalArgumentException If component is less than 0 or greater + * than numComponents - 1. + * + * @since 1.4 + */ + public float getMaxValue(int idx) + { + if (type == ColorSpace.TYPE_XYZ && idx >= 0 && idx <= 2) + return 1 + 32767 / 32768f; + else if (type == ColorSpace.TYPE_Lab) + { + if (idx == 0) + return 100; + if (idx == 1 || idx == 2) + return 127; + } + if (idx < 0 || idx >= nComponents) + throw new IllegalArgumentException(); + return 1; + } + + /** + * Returns a colorspace converter suitable for a given profile + */ + private ColorSpaceConverter getConverter(ICC_Profile profile) + { + ColorSpaceConverter converter; + switch (profile.isPredefined()) + { + case CS_sRGB: + converter = new SrgbConverter(); + break; + case CS_CIEXYZ: + converter = new CieXyzConverter(); + break; + case CS_GRAY: + converter = new GrayScaleConverter(); + break; + case CS_LINEAR_RGB: + converter = new LinearRGBConverter(); + break; + case CS_PYCC: + converter = new PyccConverter(); + break; + default: + if (profile instanceof ICC_ProfileRGB) + converter = new RgbProfileConverter((ICC_ProfileRGB) profile); + else if (profile instanceof ICC_ProfileGray) + converter = new GrayProfileConverter((ICC_ProfileGray) profile); + else + converter = new ClutProfileConverter(profile); + break; + } + return converter; + } + + /** + * Serialization compatibility requires these variable to be set, + * although we don't use them. Perhaps we should? + */ + private void makeArrays() + { + minVal = new float[nComponents]; + maxVal = new float[nComponents]; + + invDiffMinMax = diffMinMax = null; + for (int i = 0; i < nComponents; i++) + { + minVal[i] = getMinValue(i); + maxVal[i] = getMaxValue(i); + } + needScaleInit = true; + } + + /** + * Deserializes the object + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + // set up objects + converter = getConverter(thisProfile); + nComponents = thisProfile.getNumComponents(); + type = thisProfile.getColorSpaceType(); + } +} // class ICC_ColorSpace diff --git a/libjava/classpath/java/awt/color/ICC_Profile.java b/libjava/classpath/java/awt/color/ICC_Profile.java new file mode 100644 index 000000000..0eef22e49 --- /dev/null +++ b/libjava/classpath/java/awt/color/ICC_Profile.java @@ -0,0 +1,1249 @@ +/* ICC_Profile.java -- color space profiling + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +import gnu.java.awt.color.ProfileHeader; +import gnu.java.awt.color.TagEntry; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * ICC Profile - represents an ICC Color profile. + * The ICC profile format is a standard file format which maps the transform + * from a device color space to a standard Profile Color Space (PCS), which + * can either be CIE L*a*b or CIE XYZ. + * (With the exception of device link profiles which map from one device space + * to another) + * + * ICC profiles calibrated to specific input/output devices are used when color + * fidelity is of importance. + * + * An instance of ICC_Profile can be created using the getInstance() methods, + * either using one of the predefined color spaces enumerated in ColorSpace, + * or from an ICC profile file, or from an input stream. + * + * An ICC_ColorSpace object can then be created to transform color values + * through the profile. + * + * The ICC_Profile class implements the version 2 format specified by + * International Color Consortium Specification ICC.1:1998-09, + * and its addendum ICC.1A:1999-04, April 1999 + * (available at www.color.org) + * + * @author Sven de Marothy + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @since 1.2 + */ +public class ICC_Profile implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -3938515861990936766L; + + /** + * ICC Profile classes + */ + public static final int CLASS_INPUT = 0; + public static final int CLASS_DISPLAY = 1; + public static final int CLASS_OUTPUT = 2; + public static final int CLASS_DEVICELINK = 3; + public static final int CLASS_COLORSPACECONVERSION = 4; + public static final int CLASS_ABSTRACT = 5; + public static final int CLASS_NAMEDCOLOR = 6; + + /** + * ICC Profile class signatures + */ + public static final int icSigInputClass = 0x73636e72; // 'scnr' + public static final int icSigDisplayClass = 0x6d6e7472; // 'mntr' + public static final int icSigOutputClass = 0x70727472; // 'prtr' + public static final int icSigLinkClass = 0x6c696e6b; // 'link' + public static final int icSigColorSpaceClass = 0x73706163; // 'spac' + public static final int icSigAbstractClass = 0x61627374; // 'abst' + public static final int icSigNamedColorClass = 0x6e6d636c; // 'nmcl' + + /** + * Color space signatures + */ + public static final int icSigXYZData = 0x58595A20; // 'XYZ ' + public static final int icSigLabData = 0x4C616220; // 'Lab ' + public static final int icSigLuvData = 0x4C757620; // 'Luv ' + public static final int icSigYCbCrData = 0x59436272; // 'YCbr' + public static final int icSigYxyData = 0x59787920; // 'Yxy ' + public static final int icSigRgbData = 0x52474220; // 'RGB ' + public static final int icSigGrayData = 0x47524159; // 'GRAY' + public static final int icSigHsvData = 0x48535620; // 'HSV ' + public static final int icSigHlsData = 0x484C5320; // 'HLS ' + public static final int icSigCmykData = 0x434D594B; // 'CMYK' + public static final int icSigCmyData = 0x434D5920; // 'CMY ' + public static final int icSigSpace2CLR = 0x32434C52; // '2CLR' + public static final int icSigSpace3CLR = 0x33434C52; // '3CLR' + public static final int icSigSpace4CLR = 0x34434C52; // '4CLR' + public static final int icSigSpace5CLR = 0x35434C52; // '5CLR' + public static final int icSigSpace6CLR = 0x36434C52; // '6CLR' + public static final int icSigSpace7CLR = 0x37434C52; // '7CLR' + public static final int icSigSpace8CLR = 0x38434C52; // '8CLR' + public static final int icSigSpace9CLR = 0x39434C52; // '9CLR' + public static final int icSigSpaceACLR = 0x41434C52; // 'ACLR' + public static final int icSigSpaceBCLR = 0x42434C52; // 'BCLR' + public static final int icSigSpaceCCLR = 0x43434C52; // 'CCLR' + public static final int icSigSpaceDCLR = 0x44434C52; // 'DCLR' + public static final int icSigSpaceECLR = 0x45434C52; // 'ECLR' + public static final int icSigSpaceFCLR = 0x46434C52; // 'FCLR' + + /** + * Rendering intents + */ + public static final int icPerceptual = 0; + public static final int icRelativeColorimetric = 1; + public static final int icSaturation = 2; + public static final int icAbsoluteColorimetric = 3; + + /** + * Tag signatures + */ + public static final int icSigAToB0Tag = 0x41324230; // 'A2B0' + public static final int icSigAToB1Tag = 0x41324231; // 'A2B1' + public static final int icSigAToB2Tag = 0x41324232; // 'A2B2' + public static final int icSigBlueColorantTag = 0x6258595A; // 'bXYZ' + public static final int icSigBlueTRCTag = 0x62545243; // 'bTRC' + public static final int icSigBToA0Tag = 0x42324130; // 'B2A0' + public static final int icSigBToA1Tag = 0x42324131; // 'B2A1' + public static final int icSigBToA2Tag = 0x42324132; // 'B2A2' + public static final int icSigCalibrationDateTimeTag = 0x63616C74; // 'calt' + public static final int icSigCharTargetTag = 0x74617267; // 'targ' + public static final int icSigCopyrightTag = 0x63707274; // 'cprt' + public static final int icSigCrdInfoTag = 0x63726469; // 'crdi' + public static final int icSigDeviceMfgDescTag = 0x646D6E64; // 'dmnd' + public static final int icSigDeviceModelDescTag = 0x646D6464; // 'dmdd' + public static final int icSigDeviceSettingsTag = 0x64657673; // 'devs' + public static final int icSigGamutTag = 0x67616D74; // 'gamt' + public static final int icSigGrayTRCTag = 0x6b545243; // 'kTRC' + public static final int icSigGreenColorantTag = 0x6758595A; // 'gXYZ' + public static final int icSigGreenTRCTag = 0x67545243; // 'gTRC' + public static final int icSigLuminanceTag = 0x6C756d69; // 'lumi' + public static final int icSigMeasurementTag = 0x6D656173; // 'meas' + public static final int icSigMediaBlackPointTag = 0x626B7074; // 'bkpt' + public static final int icSigMediaWhitePointTag = 0x77747074; // 'wtpt' + public static final int icSigNamedColor2Tag = 0x6E636C32; // 'ncl2' + public static final int icSigOutputResponseTag = 0x72657370; // 'resp' + public static final int icSigPreview0Tag = 0x70726530; // 'pre0' + public static final int icSigPreview1Tag = 0x70726531; // 'pre1' + public static final int icSigPreview2Tag = 0x70726532; // 'pre2' + public static final int icSigProfileDescriptionTag = 0x64657363; // 'desc' + public static final int icSigProfileSequenceDescTag = 0x70736571; // 'pseq' + public static final int icSigPs2CRD0Tag = 0x70736430; // 'psd0' + public static final int icSigPs2CRD1Tag = 0x70736431; // 'psd1' + public static final int icSigPs2CRD2Tag = 0x70736432; // 'psd2' + public static final int icSigPs2CRD3Tag = 0x70736433; // 'psd3' + public static final int icSigPs2CSATag = 0x70733273; // 'ps2s' + public static final int icSigPs2RenderingIntentTag = 0x70733269; // 'ps2i' + public static final int icSigRedColorantTag = 0x7258595A; // 'rXYZ' + public static final int icSigRedTRCTag = 0x72545243; // 'rTRC' + public static final int icSigScreeningDescTag = 0x73637264; // 'scrd' + public static final int icSigScreeningTag = 0x7363726E; // 'scrn' + public static final int icSigTechnologyTag = 0x74656368; // 'tech' + public static final int icSigUcrBgTag = 0x62666420; // 'bfd ' + public static final int icSigViewingCondDescTag = 0x76756564; // 'vued' + public static final int icSigViewingConditionsTag = 0x76696577; // 'view' + public static final int icSigChromaticityTag = 0x6368726D; // 'chrm' + + /** + * Non-ICC tag 'head' for use in retrieving the header with getData() + */ + public static final int icSigHead = 0x68656164; + + /** + * Header offsets + */ + public static final int icHdrSize = 0; + public static final int icHdrCmmId = 4; + public static final int icHdrVersion = 8; + public static final int icHdrDeviceClass = 12; + public static final int icHdrColorSpace = 16; + public static final int icHdrPcs = 20; + public static final int icHdrDate = 24; + public static final int icHdrMagic = 36; + public static final int icHdrPlatform = 40; + public static final int icHdrFlags = 44; + public static final int icHdrManufacturer = 48; + public static final int icHdrModel = 52; + public static final int icHdrAttributes = 56; + public static final int icHdrRenderingIntent = 64; + public static final int icHdrIlluminant = 68; + public static final int icHdrCreator = 80; + + /** + * + */ + public static final int icTagType = 0; + public static final int icTagReserved = 4; + public static final int icCurveCount = 8; + public static final int icCurveData = 12; + public static final int icXYZNumberX = 8; + + /** + * offset of the Tag table + */ + private static final int tagTableOffset = 128; + + /** + * @serial + */ + private static final int iccProfileSerializedDataVersion = 1; + + /** + * Constants related to generating profiles for + * built-in colorspace profiles + */ + /** + * Copyright notice to stick into built-in-profile files. + */ + private static final String copyrightNotice = "Generated by GNU Classpath."; + + /** + * Resolution of the TRC to use for predefined profiles. + * 1024 should suffice. + */ + private static final int TRC_POINTS = 1024; + + /** + * CIE 1931 D50 white point (in Lab coordinates) + */ + private static final float[] D50 = { 0.96422f, 1.00f, 0.82521f }; + + /** + * Color space profile ID + * Set to the predefined profile class (e.g. CS_sRGB) if a predefined + * color space is used, set to -1 otherwise. + * (or if the profile has been modified) + */ + private transient int profileID; + + /** + * The profile header data + */ + private transient ProfileHeader header; + + /** + * A hashtable containing the profile tags as TagEntry objects + */ + private transient Hashtable tagTable; + + /** + * Contructor for predefined colorspaces + */ + ICC_Profile(int profileID) + { + header = null; + tagTable = null; + createProfile(profileID); + } + + /** + * Constructs an ICC_Profile from a header and a table of loaded tags. + */ + ICC_Profile(ProfileHeader h, Hashtable tags) throws IllegalArgumentException + { + header = h; + tagTable = tags; + profileID = -1; // Not a predefined color space + } + + /** + * Constructs an ICC_Profile from a byte array of data. + */ + ICC_Profile(byte[] data) throws IllegalArgumentException + { + // get header and verify it + header = new ProfileHeader(data); + header.verifyHeader(data.length); + tagTable = createTagTable(data); + profileID = -1; // Not a predefined color space + } + + /** + * Free up the used memory. + */ + protected void finalize() + { + } + + /** + * Returns an ICC_Profile instance from a byte array of profile data. + * + * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray + * may be returned if appropriate. + * + * @param data - the profile data + * @return An ICC_Profile object + * + * @throws IllegalArgumentException if the profile data is an invalid + * v2 profile. + */ + public static ICC_Profile getInstance(byte[] data) + { + ProfileHeader header = new ProfileHeader(data); + + // verify it as a correct ICC header, including size + header.verifyHeader(data.length); + + Hashtable tags = createTagTable(data); + + if (isRGBProfile(header, tags)) + return new ICC_ProfileRGB(data); + if (isGrayProfile(header, tags)) + return new ICC_ProfileGray(data); + + return new ICC_Profile(header, tags); + } + + /** + * Returns an predefined ICC_Profile instance. + * + * This will construct an ICC_Profile instance from one of the predefined + * color spaces in the ColorSpace class. (e.g. CS_sRGB, CS_GRAY, etc) + * + * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray + * may be returned if appropriate. + * + * @return An ICC_Profile object + */ + public static ICC_Profile getInstance(int cspace) + { + if (cspace == ColorSpace.CS_sRGB || cspace == ColorSpace.CS_LINEAR_RGB) + return new ICC_ProfileRGB(cspace); + if (cspace == ColorSpace.CS_GRAY) + return new ICC_ProfileGray(cspace); + return new ICC_Profile(cspace); + } + + /** + * Returns an ICC_Profile instance from an ICC Profile file. + * + * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray + * may be returned if appropriate. + * + * @param filename - the file name of the profile file. + * @return An ICC_Profile object + * + * @throws IllegalArgumentException if the profile data is an invalid + * v2 profile. + * @throws IOException if the file could not be read. + */ + public static ICC_Profile getInstance(String filename) + throws IOException + { + return getInstance(new FileInputStream(filename)); + } + + /** + * Returns an ICC_Profile instance from an InputStream. + * + * This method can be used for reading ICC profiles embedded in files + * which support this. (JPEG and SVG for instance). + * + * The stream is treated in the following way: The profile header + * (128 bytes) is read first, and the header is validated. If the profile + * header is valid, it will then attempt to read the rest of the profile + * from the stream. The stream is not closed after reading. + * + * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray + * may be returned if appropriate. + * + * @param in - the input stream to read the profile from. + * @return An ICC_Profile object + * + * @throws IllegalArgumentException if the profile data is an invalid + * v2 profile. + * @throws IOException if the stream could not be read. + */ + public static ICC_Profile getInstance(InputStream in) + throws IOException + { + // read the header + byte[] headerData = new byte[ProfileHeader.HEADERSIZE]; + if (in.read(headerData) != ProfileHeader.HEADERSIZE) + throw new IllegalArgumentException("Invalid profile header"); + + ProfileHeader header = new ProfileHeader(headerData); + + // verify it as a correct ICC header, but do not verify the + // size as we are reading from a stream. + header.verifyHeader(-1); + + // get the size + byte[] data = new byte[header.getSize()]; + System.arraycopy(headerData, 0, data, 0, ProfileHeader.HEADERSIZE); + + // read the rest + int totalBytes = header.getSize() - ProfileHeader.HEADERSIZE; + int bytesLeft = totalBytes; + while (bytesLeft > 0) + { + int read = in.read(data, + ProfileHeader.HEADERSIZE + (totalBytes - bytesLeft), + bytesLeft); + bytesLeft -= read; + } + + return getInstance(data); + } + + /** + * Returns the major version number + */ + public int getMajorVersion() + { + return header.getMajorVersion(); + } + + /** + * Returns the minor version number. + * + * Only the least-significant byte contains data, in BCD form: + * the least-significant nibble is the BCD bug fix revision, + * the most-significant nibble is the BCD minor revision number. + * + * (E.g. For a v2.1.0 profile this will return 0x10) + */ + public int getMinorVersion() + { + return header.getMinorVersion(); + } + + /** + * Returns the device class of this profile, + * + * (E.g. CLASS_INPUT for a scanner profile, + * CLASS_OUTPUT for a printer) + */ + public int getProfileClass() + { + return header.getProfileClass(); + } + + /** + * Returns the color space of this profile, in terms + * of the color space constants defined in ColorSpace. + * (For example, it may be a ColorSpace.TYPE_RGB) + */ + public int getColorSpaceType() + { + return header.getColorSpace(); + } + + /** + * Returns the color space of this profile's Profile Connection Space (OCS) + * + * In terms of the color space constants defined in ColorSpace. + * This may be TYPE_XYZ or TYPE_Lab + */ + public int getPCSType() + { + return header.getProfileColorSpace(); + } + + /** + * Writes the profile data to an ICC profile file. + * @param filename - The name of the file to write + * @throws IOException if the write failed. + */ + public void write(String filename) throws IOException + { + FileOutputStream out = new FileOutputStream(filename); + write(out); + out.flush(); + out.close(); + } + + /** + * Writes the profile data in ICC profile file-format to a stream. + * This is useful for embedding ICC profiles in file formats which + * support this (such as JPEG and SVG). + * + * The stream is not closed after writing. + * @param out - The outputstream to which the profile data should be written + * @throws IOException if the write failed. + */ + public void write(OutputStream out) throws IOException + { + out.write(getData()); + } + + /** + * Returns the data corresponding to this ICC_Profile as a byte array. + * + * @return The data in a byte array, + * where the first element corresponds to first byte of the profile file. + */ + public byte[] getData() + { + int size = getSize(); + byte[] data = new byte[size]; + + // Header + System.arraycopy(header.getData(size), 0, data, 0, ProfileHeader.HEADERSIZE); + // # of tags + byte[] tt = getTagTable(); + System.arraycopy(tt, 0, data, ProfileHeader.HEADERSIZE, tt.length); + + Enumeration e = tagTable.elements(); + while (e.hasMoreElements()) + { + TagEntry tag = (TagEntry) e.nextElement(); + System.arraycopy(tag.getData(), 0, + data, tag.getOffset(), tag.getSize()); + } + return data; + } + + /** + * Returns the ICC profile tag data + * The non ICC-tag icSigHead is also permitted to request the header data. + * + * @param tagSignature The ICC signature of the requested tag + * @return A byte array containing the tag data + */ + public byte[] getData(int tagSignature) + { + if (tagSignature == icSigHead) + return header.getData(getSize()); + + TagEntry t = (TagEntry) tagTable.get(TagEntry.tagHashKey(tagSignature)); + if (t == null) + return null; + return t.getData(); + } + + /** + * Sets the ICC profile tag data. + * + * Note that an ICC profile can only contain one tag of each type, if + * a tag already exists with the given signature, it is replaced. + * + * @param tagSignature - The signature of the tag to set + * @param data - A byte array containing the tag data + */ + public void setData(int tagSignature, byte[] data) + { + profileID = -1; // Not a predefined color space if modified. + + if (tagSignature == icSigHead) + header = new ProfileHeader(data); + else + { + TagEntry t = new TagEntry(tagSignature, data); + tagTable.put(t.hashKey(), t); + } + } + + /** + * Get the number of components in the profile's device color space. + */ + public int getNumComponents() + { + int[] lookup = + { + ColorSpace.TYPE_RGB, 3, ColorSpace.TYPE_CMY, 3, + ColorSpace.TYPE_CMYK, 4, ColorSpace.TYPE_GRAY, 1, + ColorSpace.TYPE_YCbCr, 3, ColorSpace.TYPE_XYZ, 3, + ColorSpace.TYPE_Lab, 3, ColorSpace.TYPE_HSV, 3, + ColorSpace.TYPE_2CLR, 2, ColorSpace.TYPE_Luv, 3, + ColorSpace.TYPE_Yxy, 3, ColorSpace.TYPE_HLS, 3, + ColorSpace.TYPE_3CLR, 3, ColorSpace.TYPE_4CLR, 4, + ColorSpace.TYPE_5CLR, 5, ColorSpace.TYPE_6CLR, 6, + ColorSpace.TYPE_7CLR, 7, ColorSpace.TYPE_8CLR, 8, + ColorSpace.TYPE_9CLR, 9, ColorSpace.TYPE_ACLR, 10, + ColorSpace.TYPE_BCLR, 11, ColorSpace.TYPE_CCLR, 12, + ColorSpace.TYPE_DCLR, 13, ColorSpace.TYPE_ECLR, 14, + ColorSpace.TYPE_FCLR, 15 + }; + for (int i = 0; i < lookup.length; i += 2) + if (header.getColorSpace() == lookup[i]) + return lookup[i + 1]; + return 3; // should never happen. + } + + /** + * After deserializing we must determine if the class we want + * is really one of the more specialized ICC_ProfileRGB or + * ICC_ProfileGray classes. + */ + protected Object readResolve() throws ObjectStreamException + { + if (isRGBProfile(header, tagTable)) + return new ICC_ProfileRGB(getData()); + if (isGrayProfile(header, tagTable)) + return new ICC_ProfileGray(getData()); + return this; + } + + /** + * Deserializes an instance + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + String predef = (String) s.readObject(); + byte[] data = (byte[]) s.readObject(); + + if (data != null) + { + header = new ProfileHeader(data); + tagTable = createTagTable(data); + profileID = -1; // Not a predefined color space + } + + if (predef != null) + { + predef = predef.intern(); + if (predef.equals("CS_sRGB")) + createProfile(ColorSpace.CS_sRGB); + if (predef.equals("CS_LINEAR_RGB")) + createProfile(ColorSpace.CS_LINEAR_RGB); + if (predef.equals("CS_CIEXYZ")) + createProfile(ColorSpace.CS_CIEXYZ); + if (predef.equals("CS_GRAY")) + createProfile(ColorSpace.CS_GRAY); + if (predef.equals("CS_PYCC")) + createProfile(ColorSpace.CS_PYCC); + } + } + + /** + * Serializes an instance + * The format is a String and a byte array, + * The string is non-null if the instance is one of the built-in profiles. + * Otherwise the byte array is non-null and represents the profile data. + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + if (profileID == ColorSpace.CS_sRGB) + s.writeObject("CS_sRGB"); + else if (profileID == ColorSpace.CS_LINEAR_RGB) + s.writeObject("CS_LINEAR_RGB"); + else if (profileID == ColorSpace.CS_CIEXYZ) + s.writeObject("CS_CIEXYZ"); + else if (profileID == ColorSpace.CS_GRAY) + s.writeObject("CS_GRAY"); + else if (profileID == ColorSpace.CS_PYCC) + s.writeObject("CS_PYCC"); + else + { + s.writeObject(null); // null string + s.writeObject(getData()); // data + return; + } + s.writeObject(null); // null data + } + + /** + * Sorts a ICC profile byte array into TagEntry objects stored in + * a hash table. + */ + private static Hashtable createTagTable(byte[] data) + throws IllegalArgumentException + { + ByteBuffer buf = ByteBuffer.wrap(data); + int nTags = buf.getInt(tagTableOffset); + + Hashtable tagTable = new Hashtable(); + for (int i = 0; i < nTags; i++) + { + TagEntry te = new TagEntry(buf.getInt(tagTableOffset + + i * TagEntry.entrySize + 4), + buf.getInt(tagTableOffset + + i * TagEntry.entrySize + 8), + buf.getInt(tagTableOffset + + i * TagEntry.entrySize + 12), + data); + + if (tagTable.put(te.hashKey(), te) != null) + throw new IllegalArgumentException("Duplicate tag in profile:" + te); + } + return tagTable; + } + + /** + * Returns the total size of the padded, stored data + * Note: Tags must be stored on 4-byte aligned offsets. + */ + private int getSize() + { + int totalSize = ProfileHeader.HEADERSIZE; // size of header + + int tagTableSize = 4 + tagTable.size() * TagEntry.entrySize; // size of tag table + if ((tagTableSize & 0x0003) != 0) + tagTableSize += 4 - (tagTableSize & 0x0003); // pad + totalSize += tagTableSize; + + Enumeration e = tagTable.elements(); + while (e.hasMoreElements()) + { // tag data + int tagSize = ((TagEntry) e.nextElement()).getSize(); + if ((tagSize & 0x0003) != 0) + tagSize += 4 - (tagSize & 0x0003); // pad + totalSize += tagSize; + } + return totalSize; + } + + /** + * Generates the tag index table + */ + private byte[] getTagTable() + { + int tagTableSize = 4 + tagTable.size() * TagEntry.entrySize; + if ((tagTableSize & 0x0003) != 0) + tagTableSize += 4 - (tagTableSize & 0x0003); // pad + + int offset = 4; + int tagOffset = ProfileHeader.HEADERSIZE + tagTableSize; + ByteBuffer buf = ByteBuffer.allocate(tagTableSize); + buf.putInt(tagTable.size()); // number of tags + + Enumeration e = tagTable.elements(); + while (e.hasMoreElements()) + { + TagEntry tag = (TagEntry) e.nextElement(); + buf.putInt(offset, tag.getSignature()); + buf.putInt(offset + 4, tagOffset); + buf.putInt(offset + 8, tag.getSize()); + tag.setOffset(tagOffset); + int tagSize = tag.getSize(); + if ((tagSize & 0x0003) != 0) + tagSize += 4 - (tagSize & 0x0003); // pad + tagOffset += tagSize; + offset += 12; + } + return buf.array(); + } + + /** + * Returns if the criteria for an ICC_ProfileRGB are met. + * This means: + * Color space is TYPE_RGB + * (r,g,b)ColorantTags included + * (r,g,b)TRCTags included + * mediaWhitePointTag included + */ + private static boolean isRGBProfile(ProfileHeader header, Hashtable tags) + { + if (header.getColorSpace() != ColorSpace.TYPE_RGB) + return false; + if (tags.get(TagEntry.tagHashKey(icSigRedColorantTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigGreenColorantTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigBlueColorantTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigRedTRCTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigGreenTRCTag)) == null) + return false; + if (tags.get(TagEntry.tagHashKey(icSigBlueTRCTag)) == null) + return false; + return (tags.get(TagEntry.tagHashKey(icSigMediaWhitePointTag)) != null); + } + + /** + * Returns if the criteria for an ICC_ProfileGray are met. + * This means: + * Colorspace is TYPE_GRAY + * grayTRCTag included + * mediaWhitePointTag included + */ + private static boolean isGrayProfile(ProfileHeader header, Hashtable tags) + { + if (header.getColorSpace() != ColorSpace.TYPE_GRAY) + return false; + if (tags.get(TagEntry.tagHashKey(icSigGrayTRCTag)) == null) + return false; + return (tags.get(TagEntry.tagHashKey(icSigMediaWhitePointTag)) != null); + } + + /** + * Returns curve data for a 'curv'-type tag + * If it's a gamma curve, a single entry will be returned with the + * gamma value (including 1.0 for linear response) + * Otherwise the TRC table is returned. + * + * (Package private - used by ICC_ProfileRGB and ICC_ProfileGray) + */ + short[] getCurve(int signature) + { + byte[] data = getData(signature); + short[] curve; + + // can't find tag? + if (data == null) + return null; + + // not an curve type tag? + ByteBuffer buf = ByteBuffer.wrap(data); + if (buf.getInt(0) != 0x63757276) // 'curv' type + return null; + int count = buf.getInt(8); + if (count == 0) + { + curve = new short[1]; + curve[0] = 0x0100; // 1.00 in u8fixed8 + return curve; + } + if (count == 1) + { + curve = new short[1]; + curve[0] = buf.getShort(12); // other u8fixed8 gamma + return curve; + } + curve = new short[count]; + for (int i = 0; i < count; i++) + curve[i] = buf.getShort(12 + i * 2); + return curve; + } + + /** + * Returns XYZ tristimulus values for an 'XYZ ' type tag + * @return the XYZ values, or null if the tag was not an 'XYZ ' type tag. + * + * (Package private - used by ICC_ProfileXYZ and ICC_ProfileGray) + */ + float[] getXYZData(int signature) + { + byte[] data = getData(signature); + + // can't find tag? + if (data == null) + return null; + + // not an XYZData type tag? + ByteBuffer buf = ByteBuffer.wrap(data); + if (buf.getInt(0) != icSigXYZData) // 'XYZ ' type + return null; + + float[] point = new float[3]; + + // get the X,Y,Z tristimulus values + point[0] = ((float) buf.getInt(8)) / 65536f; + point[1] = ((float) buf.getInt(12)) / 65536f; + point[2] = ((float) buf.getInt(16)) / 65536f; + return point; + } + + /** + * Returns the profile ID if it's a predefined profile + * Or -1 for a profile loaded from an ICC profile + * + * (Package private - used by ICC_ColorSpace) + */ + int isPredefined() + { + return profileID; + } + + /** + * Creates a tag of XYZ-value type. + */ + private byte[] makeXYZData(float[] values) + { + ByteBuffer buf = ByteBuffer.allocate(20); + buf.putInt(0, icSigXYZData); // 'XYZ ' + buf.putInt(4, 0); + buf.putInt(8, (int) (values[0] * 65536.0)); + buf.putInt(12, (int) (values[1] * 65536.0)); + buf.putInt(16, (int) (values[2] * 65536.0)); + return buf.array(); + } + + /** + * Creates a tag of text type + */ + private byte[] makeTextTag(String text) + { + int length = text.length(); + ByteBuffer buf = ByteBuffer.allocate(8 + length + 1); + byte[] data; + try + { + data = text.getBytes("US-ASCII"); + } + catch (UnsupportedEncodingException e) + { + data = new byte[length]; // shouldn't happen + } + + buf.putInt(0, (int) 0x74657874); // 'text' + buf.putInt(4, 0); + for (int i = 0; i < length; i++) + buf.put(8 + i, data[i]); + buf.put(8 + length, (byte) 0); // null-terminate + return buf.array(); + } + + /** + * Creates a tag of textDescriptionType + */ + private byte[] makeDescTag(String text) + { + int length = text.length(); + ByteBuffer buf = ByteBuffer.allocate(90 + length + 1); + buf.putInt(0, (int) 0x64657363); // 'desc' + buf.putInt(4, 0); // reserved + buf.putInt(8, length + 1); // ASCII length, including null termination + byte[] data; + + try + { + data = text.getBytes("US-ASCII"); + } + catch (UnsupportedEncodingException e) + { + data = new byte[length]; // shouldn't happen + } + + for (int i = 0; i < length; i++) + buf.put(12 + i, data[i]); + buf.put(12 + length, (byte) 0); // null-terminate + + for (int i = 0; i < 39; i++) + buf.putShort(13 + length + (i * 2), (short) 0); // 78 bytes we can ignore + + return buf.array(); + } + + /** + * Creates a tag of TRC type (linear curve) + */ + private byte[] makeTRC() + { + ByteBuffer buf = ByteBuffer.allocate(12); + buf.putInt(0, 0x63757276); // 'curv' type + buf.putInt(4, 0); // reserved + buf.putInt(8, 0); + return buf.array(); + } + + /** + * Creates a tag of TRC type (single gamma value) + */ + private byte[] makeTRC(float gamma) + { + short gammaValue = (short) (gamma * 256f); + ByteBuffer buf = ByteBuffer.allocate(14); + buf.putInt(0, 0x63757276); // 'curv' type + buf.putInt(4, 0); // reserved + buf.putInt(8, 1); + buf.putShort(12, gammaValue); // 1.00 in u8fixed8 + return buf.array(); + } + + /** + * Creates a tag of TRC type (TRC curve points) + */ + private byte[] makeTRC(float[] trc) + { + ByteBuffer buf = ByteBuffer.allocate(12 + 2 * trc.length); + buf.putInt(0, 0x63757276); // 'curv' type + buf.putInt(4, 0); // reserved + buf.putInt(8, trc.length); // number of points + + // put the curve values + for (int i = 0; i < trc.length; i++) + buf.putShort(12 + i * 2, (short) (trc[i] * 65535f)); + + return buf.array(); + } + + /** + * Creates an identity color lookup table. + */ + private byte[] makeIdentityClut() + { + final int nIn = 3; + final int nOut = 3; + final int nInEntries = 256; + final int nOutEntries = 256; + final int gridpoints = 16; + + // gridpoints**nIn + final int clutSize = 2 * nOut * gridpoints * gridpoints * gridpoints; + final int totalSize = clutSize + 2 * nInEntries * nIn + + 2 * nOutEntries * nOut + 52; + + ByteBuffer buf = ByteBuffer.allocate(totalSize); + buf.putInt(0, 0x6D667432); // 'mft2' + buf.putInt(4, 0); // reserved + buf.put(8, (byte) nIn); // number input channels + buf.put(9, (byte) nOut); // number output channels + buf.put(10, (byte) gridpoints); // number gridpoints + buf.put(11, (byte) 0); // padding + + // identity matrix + buf.putInt(12, 65536); // = 1 in s15.16 fixed point + buf.putInt(16, 0); + buf.putInt(20, 0); + buf.putInt(24, 0); + buf.putInt(28, 65536); + buf.putInt(32, 0); + buf.putInt(36, 0); + buf.putInt(40, 0); + buf.putInt(44, 65536); + + buf.putShort(48, (short) nInEntries); // input table entries + buf.putShort(50, (short) nOutEntries); // output table entries + + // write the linear input channels, unsigned 16.16 fixed point, + // from 0.0 to FF.FF + for (int channel = 0; channel < 3; channel++) + for (int i = 0; i < nInEntries; i++) + { + short n = (short) ((i << 8) | i); // assumes 256 entries + buf.putShort(52 + (channel * nInEntries + i) * 2, n); + } + int clutOffset = 52 + nInEntries * nIn * 2; + + for (int x = 0; x < gridpoints; x++) + for (int y = 0; y < gridpoints; y++) + for (int z = 0; z < gridpoints; z++) + { + int offset = clutOffset + z * 2 * nOut + y * gridpoints * 2 * nOut + + x * gridpoints * gridpoints * 2 * nOut; + double xf = ((double) x) / ((double) gridpoints - 1.0); + double yf = ((double) y) / ((double) gridpoints - 1.0); + double zf = ((double) z) / ((double) gridpoints - 1.0); + buf.putShort(offset, (short) (xf * 65535.0)); + buf.putShort(offset + 2, (short) (yf * 65535.0)); + buf.putShort(offset + 4, (short) (zf * 65535.0)); + } + + for (int channel = 0; channel < 3; channel++) + for (int i = 0; i < nOutEntries; i++) + { + short n = (short) ((i << 8) | i); // assumes 256 entries + buf.putShort(clutOffset + clutSize + (channel * nOutEntries + i) * 2, + n); + } + + return buf.array(); + } + + /** + * Creates profile data corresponding to the built-in colorspaces. + */ + private void createProfile(int colorSpace) throws IllegalArgumentException + { + this.profileID = colorSpace; + header = new ProfileHeader(); + tagTable = new Hashtable(); + + switch (colorSpace) + { + case ColorSpace.CS_sRGB: + createRGBProfile(); + return; + case ColorSpace.CS_LINEAR_RGB: + createLinearRGBProfile(); + return; + case ColorSpace.CS_CIEXYZ: + createCIEProfile(); + return; + case ColorSpace.CS_GRAY: + createGrayProfile(); + return; + case ColorSpace.CS_PYCC: + createPyccProfile(); + return; + default: + throw new IllegalArgumentException("Not a predefined color space!"); + } + } + + /** + * Creates an ICC_Profile representing the sRGB color space + */ + private void createRGBProfile() + { + header.setColorSpace( ColorSpace.TYPE_RGB ); + header.setProfileColorSpace( ColorSpace.TYPE_XYZ ); + ICC_ColorSpace cs = new ICC_ColorSpace(this); + + float[] r = { 1f, 0f, 0f }; + float[] g = { 0f, 1f, 0f }; + float[] b = { 0f, 0f, 1f }; + float[] black = { 0f, 0f, 0f }; + + // CIE 1931 D50 white point (in Lab coordinates) + float[] white = D50; + + // Get tristimulus values (matrix elements) + r = cs.toCIEXYZ(r); + g = cs.toCIEXYZ(g); + b = cs.toCIEXYZ(b); + + // Generate the sRGB TRC curve, this is the linear->nonlinear + // RGB transform. + cs = new ICC_ColorSpace(getInstance(ICC_ColorSpace.CS_LINEAR_RGB)); + float[] points = new float[TRC_POINTS]; + float[] in = new float[3]; + for (int i = 0; i < TRC_POINTS; i++) + { + in[0] = in[1] = in[2] = ((float) i) / ((float) TRC_POINTS - 1); + in = cs.fromRGB(in); + // Note this value is the same for all components. + points[i] = in[0]; + } + + setData(icSigRedColorantTag, makeXYZData(r)); + setData(icSigGreenColorantTag, makeXYZData(g)); + setData(icSigBlueColorantTag, makeXYZData(b)); + setData(icSigMediaWhitePointTag, makeXYZData(white)); + setData(icSigMediaBlackPointTag, makeXYZData(black)); + setData(icSigRedTRCTag, makeTRC(points)); + setData(icSigGreenTRCTag, makeTRC(points)); + setData(icSigBlueTRCTag, makeTRC(points)); + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("Generic sRGB")); + this.profileID = ColorSpace.CS_sRGB; + } + + /** + * Creates an linear sRGB profile + */ + private void createLinearRGBProfile() + { + header.setColorSpace(ColorSpace.TYPE_RGB); + header.setProfileColorSpace(ColorSpace.TYPE_XYZ); + ICC_ColorSpace cs = new ICC_ColorSpace(this); + + float[] r = { 1f, 0f, 0f }; + float[] g = { 0f, 1f, 0f }; + float[] b = { 0f, 0f, 1f }; + float[] black = { 0f, 0f, 0f }; + + float[] white = D50; + + // Get tristimulus values (matrix elements) + r = cs.toCIEXYZ(r); + g = cs.toCIEXYZ(g); + b = cs.toCIEXYZ(b); + + setData(icSigRedColorantTag, makeXYZData(r)); + setData(icSigGreenColorantTag, makeXYZData(g)); + setData(icSigBlueColorantTag, makeXYZData(b)); + + setData(icSigMediaWhitePointTag, makeXYZData(white)); + setData(icSigMediaBlackPointTag, makeXYZData(black)); + + setData(icSigRedTRCTag, makeTRC()); + setData(icSigGreenTRCTag, makeTRC()); + setData(icSigBlueTRCTag, makeTRC()); + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("Linear RGB")); + this.profileID = ColorSpace.CS_LINEAR_RGB; + } + + /** + * Creates an CIE XYZ identity profile + */ + private void createCIEProfile() + { + header.setColorSpace( ColorSpace.TYPE_XYZ ); + header.setProfileColorSpace( ColorSpace.TYPE_XYZ ); + header.setProfileClass( CLASS_COLORSPACECONVERSION ); + ICC_ColorSpace cs = new ICC_ColorSpace(this); + + float[] white = D50; + + setData(icSigMediaWhitePointTag, makeXYZData(white)); + setData(icSigAToB0Tag, makeIdentityClut()); + setData(icSigBToA0Tag, makeIdentityClut()); + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("CIE XYZ identity profile")); + this.profileID = ColorSpace.CS_CIEXYZ; + } + + /** + * Creates a linear gray ICC_Profile + */ + private void createGrayProfile() + { + header.setColorSpace(ColorSpace.TYPE_GRAY); + header.setProfileColorSpace(ColorSpace.TYPE_XYZ); + + // CIE 1931 D50 white point (in Lab coordinates) + float[] white = D50; + + setData(icSigMediaWhitePointTag, makeXYZData(white)); + setData(icSigGrayTRCTag, makeTRC(1.0f)); + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("Linear grayscale")); + this.profileID = ColorSpace.CS_GRAY; + } + + /** + * XXX Implement me + */ + private void createPyccProfile() + { + header.setColorSpace(ColorSpace.TYPE_3CLR); + header.setProfileColorSpace(ColorSpace.TYPE_XYZ); + + // Create CLUTs here. :-) + + setData(icSigCopyrightTag, makeTextTag(copyrightNotice)); + setData(icSigProfileDescriptionTag, makeDescTag("Photo YCC")); + this.profileID = ColorSpace.CS_PYCC; + } +} // class ICC_Profile diff --git a/libjava/classpath/java/awt/color/ICC_ProfileGray.java b/libjava/classpath/java/awt/color/ICC_ProfileGray.java new file mode 100644 index 000000000..f85533cec --- /dev/null +++ b/libjava/classpath/java/awt/color/ICC_ProfileGray.java @@ -0,0 +1,133 @@ +/* ICC_ProfileGray.java -- the ICC profile for a Gray colorspace + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +/** + * ICC_ProfileGray - a special case of ICC_Profiles. + * + * The ICC_Profile.getInstance() method will return an instance of the + * ICC_ProfileGray subclass when all the following conditions are met: + * The device color space of the profile is TYPE_GRAY. + * The profile contains a gray TRCTag. + * The profile contains a mediaWhitePointTag. + * + * As per the ICC specification, the color space conversion can then + * be done through the following method: + * linearGray = grayTRC[deviceGray] + * + * Note that if the profile contains a CLUT for the color space conversion, + * it should be used instead, and the TRC information ignored. + * + * @author Sven de Marothy + * @since 1.2 + */ +public class ICC_ProfileGray extends ICC_Profile +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -1124721290732002649L; + private transient float[] whitePoint; + + /** + * Package-private constructor used by ICC_ColorSpace for creating an + * ICC_ProfileGray from a predefined ColorSpace (CS_GRAY) + */ + ICC_ProfileGray(int cspace) + { + super(cspace); + whitePoint = getXYZData(icSigMediaWhitePointTag); + } + + /** + * Package-private constructor used by ICC_ColorSpace for creating an + * ICC_ProfileGray from profile data. + */ + ICC_ProfileGray(byte[] data) + { + super(data); + whitePoint = getXYZData(icSigMediaWhitePointTag); + } + + + /** + * Returns the media white point of the profile. + */ + public float[] getMediaWhitePoint() + { + float[] wp = new float[3]; + wp[0] = whitePoint[0]; + wp[1] = whitePoint[1]; + wp[2] = whitePoint[2]; + return wp; + } + + /** + * Returns the TRC gamma value. + * @throws ProfileDataException if the TRC is described by a lookup + * table and not a gamma value. + */ + public float getGamma() + { + short[] data = getCurve(icSigGrayTRCTag); + if (data == null) + throw new IllegalArgumentException("Couldn't read Gray TRC data."); + if (data.length != 1) + throw new ProfileDataException("TRC is a table, not a gamma value."); + + // convert the unsigned 7.8 fixed-point gamma to a float. + double gamma = (double) (data[0] & (0xFFFF)) / 256.0; + return (float) gamma; + } + + /** + * Returns the TRC lookup table. + * @throws ProfileDataException if the TRC is described by a gamma value + * and not a lookup table. + */ + public short[] getTRC() + { + short[] data = getCurve(icSigGrayTRCTag); + if (data == null) + throw new IllegalArgumentException("Couldn't read Gray TRC data."); + if (data.length <= 1) + throw new ProfileDataException("Gamma value, not a TRC table."); + return data; + } +} // class ICC_ProfileGray diff --git a/libjava/classpath/java/awt/color/ICC_ProfileRGB.java b/libjava/classpath/java/awt/color/ICC_ProfileRGB.java new file mode 100644 index 000000000..cfdbf6e54 --- /dev/null +++ b/libjava/classpath/java/awt/color/ICC_ProfileRGB.java @@ -0,0 +1,227 @@ +/* ICC_ProfileRGB.java -- the ICC profile for a RGB colorspace + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +/** + * ICC_ProfileRGB - a special case of ICC_Profiles. + * + * The ICC_Profile.getInstance() method will return an instance of the + * ICC_ProfileRGB subclass when all the following conditions are met: + * The device color space of the profile is TYPE_RGB. + * The profile contains red, green and blue ColorantTags. + * The profile contains red, green and blue TRCTags. + * The profile contains a mediaWhitePointTag included. + * + * As per the ICC specification, the color space conversion can then + * be done through the following method: + * linearR = redTRC[deviceR] + * linearG = greenTRC[deviceG] + * linearB = blueTRC[deviceB] + * TRC curves are either a single gamma value, or a 1-dimensional lookup table. + * + * Followed by the matrix transform: + * PCS = M*linear + * + * Where PCS is the vector of profile color space (must be XYZ) coordinates, + * linear is the vector of linear RGB coordinates, and the matrix M is + * constructed from the ColorantTags, where the columns are red, green and + * blue respectively, and the rows are X, Y and Z. + * + * Note that if the profile contains a CLUT for the color space conversion, + * it should be used instead, and the TRC information ignored. + * + * @author Sven de Marothy + * @since 1.2 + */ +public class ICC_ProfileRGB extends ICC_Profile +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 8505067385152579334L; + + public static final int REDCOMPONENT = 0; + + public static final int GREENCOMPONENT = 1; + + public static final int BLUECOMPONENT = 2; + + private transient float[][] matrix; + + private transient float[] gamma; + + private transient float[] whitePoint; + + + /** + * Package-private constructor used by ICC_ColorSpace for creating an + * ICC_ProfileRGB from a predefined ColorSpace (CS_LINEAR_RGB and CS_sRGB) + */ + ICC_ProfileRGB(int cspace) + { + super(cspace); + matrix = createMatrix(); + whitePoint = getXYZData(icSigMediaWhitePointTag); + } + + /** + * Package-private constructor used by ICC_ColorSpace for creating an + * ICC_ProfileRGB from profile data. + */ + ICC_ProfileRGB(byte[] data) + { + super(data); + matrix = createMatrix(); + whitePoint = getXYZData(icSigMediaWhitePointTag); + } + + /** + * Returns the media white point of the profile. + */ + public float[] getMediaWhitePoint() + { + float[] wp = new float[3]; + wp[0] = whitePoint[0]; + wp[1] = whitePoint[1]; + wp[2] = whitePoint[2]; + return wp; + } + + /** + * Returns the colorant matrix of the conversion. + */ + public float[][] getMatrix() + { + float[][] mat = new float[3][3]; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + mat[i][j] = matrix[i][j]; + return mat; + } + + /** + * Returns the gamma value of a component + * @throws ProfileDataException if the TRC is described by a lookup + * table and not a gamma value. + */ + public float getGamma(int component) + { + short[] data; + switch (component) + { + case REDCOMPONENT: + data = getCurve(icSigRedTRCTag); + break; + case GREENCOMPONENT: + data = getCurve(icSigGreenTRCTag); + break; + case BLUECOMPONENT: + data = getCurve(icSigBlueTRCTag); + break; + default: + throw new IllegalArgumentException("Not a valid component"); + } + if (data == null) + throw new IllegalArgumentException("Error reading TRC"); + + if (data.length != 1) + throw new ProfileDataException("Not a single-gamma TRC"); + + // convert the unsigned 7.8 fixed-point gamma to a float. + float gamma = (float) (((int) data[0] & 0xFF00) >> 8); + double fraction = ((int) data[0] & 0x00FF) / 256.0; + gamma += (float) fraction; + return gamma; + } + + /** + * Returns the TRC lookup table for a component + * @throws ProfileDataException if the TRC is described by a gamma + * value and not a lookup table. + */ + public short[] getTRC(int component) + { + short[] data; + switch (component) + { + case REDCOMPONENT: + data = getCurve(icSigRedTRCTag); + break; + case GREENCOMPONENT: + data = getCurve(icSigGreenTRCTag); + break; + case BLUECOMPONENT: + data = getCurve(icSigBlueTRCTag); + break; + default: + throw new IllegalArgumentException("Not a valid component"); + } + if (data == null) + throw new IllegalArgumentException("Error reading TRC"); + + if (data.length <= 1) + throw new ProfileDataException("Gamma value, not a TRC table."); + + return data; + } + + /** + * Creates the colorspace conversion matrix from the RGB tristimulus + * values. + */ + private float[][] createMatrix() throws IllegalArgumentException + { + float[][] mat = new float[3][3]; + float[] r; + float[] g; + float[] b; + r = getXYZData(icSigRedColorantTag); + g = getXYZData(icSigGreenColorantTag); + b = getXYZData(icSigBlueColorantTag); + if (r == null || g == null || b == null) + throw new IllegalArgumentException("Error reading colorant tags!"); + for (int i = 0; i < 3; i++) + { + mat[i][0] = r[i]; + mat[i][1] = g[i]; + mat[i][2] = b[i]; + } + return mat; + } +} // class ICC_ProfileRGB diff --git a/libjava/classpath/java/awt/color/ProfileDataException.java b/libjava/classpath/java/awt/color/ProfileDataException.java new file mode 100644 index 000000000..1af23b183 --- /dev/null +++ b/libjava/classpath/java/awt/color/ProfileDataException.java @@ -0,0 +1,64 @@ +/* ProfileDataException.java -- error in processing an ICC_Profile + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.color; + +/** + * Thrown when there is an error accessing or processing an + * ICC_Profile. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class ProfileDataException extends RuntimeException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7286140888240322498L; + + /** + * Create a new instance with a specified detailed error message. + * + * @param message the message + */ + public ProfileDataException(String message) + { + super(message); + } +} // class ProfileDataException diff --git a/libjava/classpath/java/awt/color/package.html b/libjava/classpath/java/awt/color/package.html new file mode 100644 index 000000000..9a08577c1 --- /dev/null +++ b/libjava/classpath/java/awt/color/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.color + + +

Classes to represent color spaces and profiles.

+ + + diff --git a/libjava/classpath/java/awt/datatransfer/Clipboard.java b/libjava/classpath/java/awt/datatransfer/Clipboard.java new file mode 100644 index 000000000..d0a1d3a3f --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/Clipboard.java @@ -0,0 +1,213 @@ +/* Clipboard.java -- Class for transferring data via cut and paste. + Copyright (C) 1999, 2001, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * This class allows data to be transferred using a cut and paste type + * mechanism. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Mark J. Wielaard (mark@klomp.org) + */ +public class Clipboard +{ + /** + * The data currently on this clipboard. For use by + * subclasses. Also returned by the public method getContents(). + */ + protected Transferable contents; + + /** + * The owner of this clipboard. + */ + protected ClipboardOwner owner; + + // The clipboard name + private final String name; + + // The flavor listeners (most likely small). + private final ArrayList listeners = new ArrayList(3); + + /** + * Initializes a new instance of Clipboard with the + * specified name. + * + * @param name The clipboard name. + */ + public Clipboard(String name) + { + this.name = name; + } + + /** + * Returns the name of the clipboard. + */ + public String getName() + { + return name; + } + + /** + * Returns the contents of the clipboard. + * + * @param requestor The object requesting the contents. This + * implementation ignores this parameter. + * + * @exception IllegalStateException If the clipboard is currently unavailable + */ + public synchronized Transferable getContents(Object requestor) + { + return contents; + } + + /** + * Sets the content and owner of this clipboard. If the given owner + * is different from the current owner then lostOwnership() + * is called on the current owner with the old contents of the given + * clipboard. + * + * @param contents The new clipboard contents. + * @param owner The new clipboard owner + * + * @exception IllegalStateException If the clipboard is currently unavailable + */ + public synchronized void setContents(Transferable contents, + ClipboardOwner owner) + { + Transferable oldContents = getContents(null); + this.contents = contents; + if (this.owner != owner) + { + ClipboardOwner oldOwner = this.owner; + this.owner = owner; + if (oldOwner != null) + oldOwner.lostOwnership(this, oldContents); + } + + FlavorListener[] fs = getFlavorListeners(); + if (fs.length > 0) + { + // We are a bit optimistic here. We assume DataFlavors will be + // given in the same order. If the number of flavors is + // different or the order of the DataFlavors in the list then + // fire a change event. + boolean newFlavors = ((contents != null && oldContents == null) + || (contents == null && oldContents != null)); + if (!newFlavors && contents != null && oldContents != null) + { + DataFlavor[] df1 = contents.getTransferDataFlavors(); + DataFlavor[] df2 = oldContents.getTransferDataFlavors(); + newFlavors = df1.length != df2.length; + + for (int i = 0; !newFlavors && i < df1.length; i++) + newFlavors = !df1[i].equals(df2[i]); + } + + if (newFlavors) + { + FlavorEvent e = new FlavorEvent(this); + for (int i = 0; i < fs.length; i++) + fs[i].flavorsChanged(e); + } + } + } + + public DataFlavor[] getAvailableDataFlavors() + { + Transferable c = getContents(null); + if (c == null) + return new DataFlavor[0]; + else + return c.getTransferDataFlavors(); + } + + public boolean isDataFlavorAvailable(DataFlavor flavor) + { + DataFlavor[] fs = getAvailableDataFlavors(); + for (int i = 0; i < fs.length; i++) + if (flavor.equals(fs[i])) + return true; + + return false; + } + + public Object getData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException + { + Transferable c = getContents(null); + if (c == null) + throw new UnsupportedFlavorException(flavor); + else + return c.getTransferData(flavor); + } + + public void addFlavorListener(FlavorListener listener) + { + if (listener == null) + return; + + synchronized(listeners) + { + listeners.add(listener); + } + } + + public void removeFlavorListener(FlavorListener listener) + { + if (listener == null) + return; + + synchronized(listeners) + { + listeners.remove(listener); + } + } + + public FlavorListener[] getFlavorListeners() + { + synchronized(listeners) + { + return (FlavorListener[]) + listeners.toArray(new FlavorListener[listeners.size()]); + } + } +} diff --git a/libjava/classpath/java/awt/datatransfer/ClipboardOwner.java b/libjava/classpath/java/awt/datatransfer/ClipboardOwner.java new file mode 100644 index 000000000..b98059a0e --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/ClipboardOwner.java @@ -0,0 +1,56 @@ +/* ClipboardOwner.java -- Interface for clipboard providers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +/** + * This interface is for classes that will own a clipboard object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface ClipboardOwner +{ + /** + * This method is called to notify this object that it no longer + * has ownership of the specified Clipboard. + * + * @param clipboard The clipboard for which ownership was lost. + * @param contents The contents of the clipboard which are no longer owned. + */ + void lostOwnership (Clipboard clipboard, Transferable contents); +} diff --git a/libjava/classpath/java/awt/datatransfer/DataFlavor.java b/libjava/classpath/java/awt/datatransfer/DataFlavor.java new file mode 100644 index 000000000..e54770434 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/DataFlavor.java @@ -0,0 +1,1026 @@ +/* DataFlavor.java -- A type of data to transfer via the clipboard. + Copyright (C) 1999, 2001, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.OptionalDataException; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.rmi.Remote; + +/** + * This class represents a particular data format used for transferring + * data via the clipboard. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DataFlavor implements java.io.Externalizable, Cloneable +{ + static final long serialVersionUID = 8367026044764648243L; + + // FIXME: Serialization: Need to write methods for. + + /** + * This is the data flavor used for tranferring plain text. The MIME + * type is "text/plain; charset=unicode". The representation class + * is java.io.InputStream. + * + * @deprecated The charset unicode is platform specific and InputStream + * deals with bytes not chars. Use getRederForText(). + */ + public static final DataFlavor plainTextFlavor = + new DataFlavor("text/plain; charset=unicode; class=java.io.InputStream", + "plain unicode text"); + + /** + * This is the data flavor used for transferring Java strings. The + * MIME type is "application/x-java-serialized-object" and the + * representation class is java.lang.String. + */ + public static final DataFlavor stringFlavor = + new DataFlavor(java.lang.String.class, "Java Unicode String"); + + /** + * This is a data flavor used for transferring lists of files. The + * representation type is a java.util.List, with each + * element of the list being a java.io.File. + */ + public static final DataFlavor javaFileListFlavor = + new DataFlavor("application/x-java-file-list; class=java.util.List", + "Java File List"); + + /** + * This is an image flavor used for transferring images. The + * representation type is a java.awt.Image. + */ + public static final DataFlavor imageFlavor = + new DataFlavor(java.awt.Image.class, "Java Image"); + + /** + * This is the MIME type used for transferring a serialized object. + * The representation class is the type of object be deserialized. + */ + public static final String javaSerializedObjectMimeType = + "application/x-java-serialized-object"; + + /** + * This is the MIME type used to transfer a Java object reference within + * the same JVM. The representation class is the class of the object + * being transferred. + */ + public static final String javaJVMLocalObjectMimeType = + "application/x-java-jvm-local-objectref"; + + /** + * This is the MIME type used to transfer a link to a remote object. + * The representation class is the type of object being linked to. + */ + public static final String javaRemoteObjectMimeType = + "application/x-java-remote-object"; + + /* + * Instance Variables + */ + + // The MIME type for this flavor + private MimeType mimeType; + + // The representation class for this flavor + private Class representationClass; + + // The human readable name of this flavor + private String humanPresentableName; + + /* + * Static Methods + */ + + /** + * This method attempts to load the named class. The following class + * loaders are searched in order: the bootstrap class loader, the + * system class loader, the context class loader (if it exists), and + * the specified fallback class loader. + * + * @param className The name of the class to load. + * @param classLoader The class loader to use if all others fail, which + * may be null. + * + * @exception ClassNotFoundException If the class cannot be loaded. + */ + protected static final Class tryToLoadClass(String className, + ClassLoader classLoader) + throws ClassNotFoundException + { + // Bootstrap + try + { + return Class.forName(className); + } + catch(ClassNotFoundException cnfe) + { + // Ignored. + } + + // System + try + { + ClassLoader loader = ClassLoader.getSystemClassLoader(); + return Class.forName(className, true, loader); + } + catch(ClassNotFoundException cnfe) + { + // Ignored. + } + + // Context + try + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + return Class.forName(className, true, loader); + } + catch(ClassNotFoundException cnfe) + { + // Ignored. + } + + if (classLoader != null) + return Class.forName(className, true, classLoader); + + throw new ClassNotFoundException(className); + } + + /** + * XXX - Currently returns plainTextFlavor. + */ + public static final DataFlavor getTextPlainUnicodeFlavor() + { + return plainTextFlavor; + } + + /** + * Selects the best supported text flavor on this implementation. + * Returns null when none of the given flavors is liked. + * + * The DataFlavor returned the first data flavor in the + * array that has either a representation class which is (a subclass of) + * Reader or String, or has a representation + * class which is (a subclass of) InputStream and has a + * primary MIME type of "text" and has an supported encoding. + */ + public static final DataFlavor + selectBestTextFlavor(DataFlavor[] availableFlavors) + { + for(int i = 0; i < availableFlavors.length; i++) + { + DataFlavor df = availableFlavors[i]; + Class c = df.representationClass; + + // A Reader or String is good. + if ((Reader.class.isAssignableFrom(c)) + || (String.class.isAssignableFrom(c))) + return df; + + // A InputStream is good if the mime primary type is "text" + if ((InputStream.class.isAssignableFrom(c)) + && ("text".equals(df.getPrimaryType()))) + { + String encoding = availableFlavors[i].getParameter("charset"); + if (encoding == null) + encoding = "us-ascii"; + Reader r = null; + try + { + // Try to construct a dummy reader with the found encoding + r = new InputStreamReader + (new ByteArrayInputStream(new byte[0]), encoding); + } + catch(UnsupportedEncodingException uee) { /* ignore */ } + + if (r != null) + return df; + } + } + + // Nothing found + return null; + } + + + /* + * Constructors + */ + + /** + * Empty public constructor needed for externalization. + * Should not be used for normal instantiation. + */ + public DataFlavor() + { + // Used for deserialization only, nothing to do here. + } + + /** + * Initializes a new instance of DataFlavor. The class + * and human readable name are specified, the MIME type will be + * "application/x-java-serialized-object". If the human readable name + * is not specified (null) then the human readable name + * will be the same as the MIME type. + * + * @param representationClass The representation class for this object. + * @param humanPresentableName The display name of the object. + */ + public DataFlavor(Class representationClass, String humanPresentableName) + { + if (representationClass == null) + throw new NullPointerException("representationClass must not be null"); + try + { + mimeType = new MimeType(javaSerializedObjectMimeType); + } + catch (MimeTypeParseException ex) + { + // Must not happen as we use a constant string. + assert false; + } + if (humanPresentableName == null) + humanPresentableName = javaSerializedObjectMimeType; + this.humanPresentableName = humanPresentableName; + this.representationClass = representationClass; + } + + /** + * Initializes a new instance of DataFlavor with the + * specified MIME type and description. If the MIME type has a + * "class=<rep class>" parameter then the representation class will + * be the class name specified. Otherwise the class defaults to + * java.io.InputStream. If the human readable name + * is not specified (null) then the human readable name + * will be the same as the MIME type. + * + * @param mimeType The MIME type for this flavor. + * @param humanPresentableName The display name of this flavor. + * @param classLoader The class loader for finding classes if the default + * class loaders do not work. + * + * @exception IllegalArgumentException If the representation class + * specified cannot be loaded. + * @exception ClassNotFoundException If the class is not loaded. + */ + public DataFlavor(String mimeType, String humanPresentableName, + ClassLoader classLoader) + throws ClassNotFoundException + { + init(mimeType, humanPresentableName, classLoader); + } + + /** + * Initializes a new instance of DataFlavor with the + * specified MIME type and description. If the MIME type has a + * "class=<rep class>" parameter then the representation class will + * be the class name specified. Otherwise the class defaults to + * java.io.InputStream. If the human readable name + * is not specified (null) then the human readable name + * will be the same as the MIME type. This is the same as calling + * new DataFlavor(mimeType, humanPresentableName, null). + * + * @param mimeType The MIME type for this flavor. + * @param humanPresentableName The display name of this flavor. + * + * @exception IllegalArgumentException If the representation class + * specified cannot be loaded. + */ + public DataFlavor(String mimeType, String humanPresentableName) + { + try + { + init(mimeType, humanPresentableName, getClass().getClassLoader()); + } + catch (ClassNotFoundException ex) + { + IllegalArgumentException iae = + new IllegalArgumentException("Class not found: " + ex.getMessage()); + iae.initCause(ex); + throw iae; + } + } + + /** + * Initializes a new instance of DataFlavor with the specified + * MIME type. This type can have a "class=" parameter to specify the + * representation class, and then the class must exist or an exception will + * be thrown. If there is no "class=" parameter then the representation class + * will be java.io.InputStream. This is the same as calling + * new DataFlavor(mimeType, null). + * + * @param mimeType The MIME type for this flavor. + * + * @exception IllegalArgumentException If a class is not specified in + * the MIME type. + * @exception ClassNotFoundException If the class cannot be loaded. + */ + public DataFlavor(String mimeType) throws ClassNotFoundException + { + init(mimeType, null, getClass().getClassLoader()); + } + + /** + * Called by various constructors to initialize this object. + * + * @param mime the mime string + * @param humanPresentableName the human presentable name + * @param loader the class loader to use for loading the representation + * class + */ + private void init(String mime, String humanPresentableName, + ClassLoader loader) + throws ClassNotFoundException + { + if (mime == null) + throw new NullPointerException("The mime type must not be null"); + try + { + mimeType = new MimeType(mime); + } + catch (MimeTypeParseException ex) + { + IllegalArgumentException iae = + new IllegalArgumentException("Invalid mime type"); + iae.initCause(ex); + throw iae; + } + String className = mimeType.getParameter("class"); + if (className == null) + { + if (mimeType.getBaseType().equals(javaSerializedObjectMimeType)) + throw new IllegalArgumentException("Serialized object type must have" + + " a representation class parameter"); + else + representationClass = java.io.InputStream.class; + } + else + representationClass = tryToLoadClass(className, loader); + mimeType.addParameter("class", representationClass.getName()); + + if (humanPresentableName == null) + { + humanPresentableName = mimeType.getParameter("humanPresentableName"); + if (humanPresentableName == null) + humanPresentableName = mimeType.getBaseType(); + } + this.humanPresentableName = humanPresentableName; + } + + /** + * Returns the MIME type of this flavor. + * + * @return The MIME type for this flavor. + */ + public String getMimeType() + { + return(mimeType.toString()); + } + + /** + * Returns the representation class for this flavor. + * + * @return The representation class for this flavor. + */ + public Class getRepresentationClass() + { + return(representationClass); + } + + /** + * Returns the human presentable name for this flavor. + * + * @return The human presentable name for this flavor. + */ + public String getHumanPresentableName() + { + return(humanPresentableName); + } + + /** + * Returns the primary MIME type for this flavor. + * + * @return The primary MIME type for this flavor. + */ + public String getPrimaryType() + { + return(mimeType.getPrimaryType()); + } + + /** + * Returns the MIME subtype for this flavor. + * + * @return The MIME subtype for this flavor. + */ + public String getSubType() + { + return mimeType.getSubType(); + } + + /** + * Returns the value of the named MIME type parameter, or null + * if the parameter does not exist. + * + * @param paramName The name of the paramter. + * + * @return The value of the parameter. + */ + public String getParameter(String paramName) + { + if ("humanPresentableName".equals(paramName)) + return getHumanPresentableName(); + + return mimeType.getParameter(paramName); + } + + /** + * Sets the human presentable name to the specified value. + * + * @param humanPresentableName The new display name. + */ + public void setHumanPresentableName(String humanPresentableName) + { + this.humanPresentableName = humanPresentableName; + } + + /** + * Tests the MIME type of this object for equality against the specified + * MIME type. Ignores parameters. + * + * @param mimeType The MIME type to test against. + * + * @return true if the MIME type is equal to this object's + * MIME type (ignoring parameters), false otherwise. + * + * @exception NullPointerException If mimeType is null. + */ + public boolean isMimeTypeEqual(String mimeType) + { + if (mimeType == null) + throw new NullPointerException("mimeType must not be null"); + boolean equal = false; + try + { + if (this.mimeType != null) + { + MimeType other = new MimeType(mimeType); + equal = this.mimeType.matches(other); + } + } + catch (MimeTypeParseException ex) + { + // Return false in this case. + } + return equal; + } + + /** + * Tests the MIME type of this object for equality against the specified + * data flavor's MIME type + * + * @param flavor The flavor to test against. + * + * @return true if the flavor's MIME type is equal to this + * object's MIME type, false otherwise. + */ + public final boolean isMimeTypeEqual(DataFlavor flavor) + { + return isMimeTypeEqual(flavor.getMimeType()); + } + + /** + * Tests whether or not this flavor represents a serialized object. + * + * @return true if this flavor represents a serialized + * object, false otherwise. + */ + public boolean isMimeTypeSerializedObject() + { + return isMimeTypeEqual(javaSerializedObjectMimeType); + } + + /** + * Tests whether or not this flavor has a representation class of + * java.io.InputStream. + * + * @return true if the representation class of this flavor + * is java.io.InputStream, false otherwise. + */ + public boolean isRepresentationClassInputStream() + { + return InputStream.class.isAssignableFrom(representationClass); + } + + /** + * Tests whether the representation class for this flavor is + * serializable. + * + * @return true if the representation class is serializable, + * false otherwise. + */ + public boolean isRepresentationClassSerializable() + { + return Serializable.class.isAssignableFrom(representationClass); + } + + /** + * Tests whether the representation class for his flavor is remote. + * + * @return true if the representation class is remote, + * false otherwise. + */ + public boolean isRepresentationClassRemote() + { + return Remote.class.isAssignableFrom (representationClass); + } + + /** + * Tests whether or not this flavor represents a serialized object. + * + * @return true if this flavor represents a serialized + * object, false otherwise. + */ + public boolean isFlavorSerializedObjectType() + { + return isRepresentationClassSerializable() + && isMimeTypeEqual(javaSerializedObjectMimeType); + } + + /** + * Tests whether or not this flavor represents a remote object. + * + * @return true if this flavor represents a remote object, + * false otherwise. + */ + public boolean isFlavorRemoteObjectType() + { + return isRepresentationClassRemote() + && isRepresentationClassSerializable() + && isMimeTypeEqual(javaRemoteObjectMimeType); + } + + /** + * Tests whether or not this flavor represents a list of files. + * + * @return true if this flavor represents a list of files, + * false otherwise. + */ + public boolean isFlavorJavaFileListType() + { + if (getPrimaryType().equals(javaFileListFlavor.getPrimaryType()) + && getSubType().equals(javaFileListFlavor.getSubType()) + && javaFileListFlavor.representationClass + .isAssignableFrom(representationClass)) + return true; + + return false ; + } + + /** + * Returns a copy of this object. + * + * @return A copy of this object. + * + * @exception CloneNotSupportedException If the object's class does not support + * the Cloneable interface. Subclasses that override the clone method can also + * throw this exception to indicate that an instance cannot be cloned. + */ + public Object clone () throws CloneNotSupportedException + { + // FIXME - This cannot be right. + try + { + return super.clone(); + } + catch(Exception e) + { + return null; + } + } + + /** + * This method test the specified DataFlavor for equality + * against this object. This will be true if the MIME type and + * representation class are the equal. If the primary type is 'text' + * then also the value of the charset parameter is compared. In such a + * case when the charset parameter isn't given then the charset is + * assumed to be equal to the default charset of the platform. All + * other parameters are ignored. + * + * @param flavor The DataFlavor to test against. + * + * @return true if the flavor is equal to this object, + * false otherwise. + */ + public boolean equals(DataFlavor flavor) + { + if (flavor == null) + return false; + + String primary = getPrimaryType(); + if (! primary.equals(flavor.getPrimaryType())) + return false; + + String sub = getSubType(); + if (! sub.equals(flavor.getSubType())) + return false; + + if (! this.representationClass.equals(flavor.representationClass)) + return false; + + if (primary.equals("text")) + if (! isRepresentationClassCharBuffer() + && ! isRepresentationClassReader() + && representationClass != java.lang.String.class + && ! (representationClass.isArray() + && representationClass.getComponentType() == Character.TYPE)) + { + String charset = getParameter("charset"); + String otherset = flavor.getParameter("charset"); + String defaultset = Charset.defaultCharset().name(); + + if (charset == null || charset.equals(defaultset)) + return (otherset == null || otherset.equals(defaultset)); + + return charset.equals(otherset); + } + + return true; + } + + /** + * This method test the specified Object for equality + * against this object. This will be true if the following conditions + * are met: + *

+ *

    + *
  • The object is not null.
  • + *
  • The object is an instance of DataFlavor.
  • + *
  • The object's MIME type and representation class are equal to + * this object's.
  • + *
+ * + * @param obj The Object to test against. + * + * @return true if the flavor is equal to this object, + * false otherwise. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof DataFlavor)) + return false; + + return equals((DataFlavor) obj); + } + + /** + * Tests whether or not the specified string is equal to the MIME type + * of this object. + * + * @param str The string to test against. + * + * @return true if the string is equal to this object's MIME + * type, false otherwise. + * + * @deprecated Not compatible with hashCode(). + * Use isMimeTypeEqual() + */ + public boolean equals(String str) + { + return isMimeTypeEqual(str); + } + + /** + * Returns the hash code for this data flavor. + * The hash code is based on the (lower case) mime type and the + * representation class. + */ + public int hashCode() + { + return mimeType.toString().hashCode() ^ representationClass.hashCode(); + } + + /** + * Returns true when the given DataFlavor + * matches this one. + */ + public boolean match(DataFlavor dataFlavor) + { + // XXX - How is this different from equals? + return equals(dataFlavor); + } + + /** + * This method exists for backward compatibility. It simply returns + * the same name/value pair passed in. + * + * @param name The parameter name. + * @param value The parameter value. + * + * @return The name/value pair. + * + * @deprecated + */ + protected String normalizeMimeTypeParameter(String name, String value) + { + return name + "=" + value; + } + + /** + * This method exists for backward compatibility. It simply returns + * the MIME type string unchanged. + * + * @param type The MIME type. + * + * @return The MIME type. + * + * @deprecated + */ + protected String normalizeMimeType(String type) + { + return type; + } + + /** + * Serialize this class. + * + * @param stream The ObjectOutput stream to serialize to. + * + * @exception IOException If an error occurs. + */ + public void writeExternal(ObjectOutput stream) + throws IOException + { + if (mimeType != null) + { + mimeType.addParameter("humanPresentableName", humanPresentableName); + stream.writeObject(mimeType); + mimeType.removeParameter("humanPresentableName"); + } + else + stream.writeObject(null); + stream.writeObject(representationClass); + } + + + /** + * De-serialize this class. + * + * @param stream The ObjectInput stream to deserialize from. + * + * @exception IOException If an error ocurs. + * @exception ClassNotFoundException If the class for an object being restored + * cannot be found. + */ + public void readExternal(ObjectInput stream) + throws IOException, ClassNotFoundException + { + mimeType = (MimeType) stream.readObject(); + String className = null; + if (mimeType != null) + { + humanPresentableName = + mimeType.getParameter("humanPresentableName"); + mimeType.removeParameter("humanPresentableName"); + className = mimeType.getParameter("class"); + if (className == null) + throw new IOException("No class in mime type"); + } + try + { + representationClass = (Class) stream.readObject(); + } + catch (OptionalDataException ex) + { + if (ex.eof && ex.length == 0) + { + if (className != null) + representationClass = tryToLoadClass(className, + getClass().getClassLoader()); + } + else + throw ex; + } + } + + /** + * Returns a string representation of this DataFlavor. Including the + * representation class name, MIME type and human presentable name. + */ + public String toString() + { + return (getClass().getName() + + "[representationClass=" + getRepresentationClass().getName() + + ",mimeType=" + getMimeType() + + ",humanPresentableName=" + getHumanPresentableName() + + "]"); + } + + /** + * XXX - Currently returns java.io.InputStream. + * + * @since 1.3 + */ + public final Class getDefaultRepresentationClass() + { + return java.io.InputStream.class; + } + + /** + * XXX - Currently returns java.io.InputStream. + */ + public final String getDefaultRepresentationClassAsString() + { + return getDefaultRepresentationClass().getName(); + } + + /** + * Creates a Reader for a given Transferable. + * + * If the representation class is a (subclass of) Reader + * then an instance of the representation class is returned. If the + * representatation class is a String then a + * StringReader is returned. And if the representation class + * is a (subclass of) InputStream and the primary MIME type + * is "text" then a InputStreamReader for the correct charset + * encoding is returned. + * + * @param transferable The Transferable for which a text + * Reader is requested. + * + * @exception IllegalArgumentException If the representation class is not one + * of the seven listed above or the Transferable has null data. + * @exception NullPointerException If the Transferable is null. + * @exception UnsupportedFlavorException when the transferable doesn't + * support this DataFlavor. Or if the representable class + * isn't a (subclass of) Reader, String, + * InputStream and/or the primary MIME type isn't "text". + * @exception IOException when any IOException occurs. + * @exception UnsupportedEncodingException if the "charset" isn't supported + * on this platform. + */ + public Reader getReaderForText(Transferable transferable) + throws UnsupportedFlavorException, IOException + { + if (!transferable.isDataFlavorSupported(this)) + throw new UnsupportedFlavorException(this); + + if (Reader.class.isAssignableFrom(representationClass)) + return (Reader)transferable.getTransferData(this); + + if (String.class.isAssignableFrom(representationClass)) + return new StringReader((String)transferable.getTransferData(this)); + + if (InputStream.class.isAssignableFrom(representationClass) + && "text".equals(getPrimaryType())) + { + InputStream in = (InputStream)transferable.getTransferData(this); + String encoding = getParameter("charset"); + if (encoding == null) + encoding = "us-ascii"; + return new InputStreamReader(in, encoding); + } + + throw new UnsupportedFlavorException(this); + } + + /** + * Returns whether the representation class for this DataFlavor is + * @see java.nio.ByteBuffer or a subclass thereof. + * + * @since 1.4 + */ + public boolean isRepresentationClassByteBuffer() + { + return ByteBuffer.class.isAssignableFrom(representationClass); + } + + /** + * Returns whether the representation class for this DataFlavor is + * @see java.nio.CharBuffer or a subclass thereof. + * + * @since 1.4 + */ + public boolean isRepresentationClassCharBuffer() + { + return CharBuffer.class.isAssignableFrom(representationClass); + } + + /** + * Returns whether the representation class for this DataFlavor is + * @see java.io.Reader or a subclass thereof. + * + * @since 1.4 + */ + public boolean isRepresentationClassReader() + { + return Reader.class.isAssignableFrom(representationClass); + } + + /** + * Returns whether this DataFlavor is a valid text flavor for + * this implementation of the Java platform. Only flavors equivalent to + * DataFlavor.stringFlavor and DataFlavors with + * a primary MIME type of "text" can be valid text flavors. + *

+ * If this flavor supports the charset parameter, it must be equivalent to + * DataFlavor.stringFlavor, or its representation must be + * java.io.Reader, java.lang.String, + * java.nio.CharBuffer, java.io.InputStream or + * java.nio.ByteBuffer, + * If the representation is java.io.InputStream or + * java.nio.ByteBuffer, then this flavor's charset + * parameter must be supported by this implementation of the Java platform. + * If a charset is not specified, then the platform default charset, which + * is always supported, is assumed. + *

+ * If this flavor does not support the charset parameter, its + * representation must be java.io.InputStream, + * java.nio.ByteBuffer. + *

+ * See selectBestTextFlavor for a list of text flavors which + * support the charset parameter. + * + * @return true if this DataFlavor is a valid + * text flavor as described above; false otherwise + * @see #selectBestTextFlavor + * @since 1.4 + */ + public boolean isFlavorTextType() { + // FIXME: I'm not 100% sure if this implementation does the same like sun's does + if(equals(DataFlavor.stringFlavor) || getPrimaryType().equals("text")) + { + String charset = getParameter("charset"); + Class c = getRepresentationClass(); + if(charset != null) + { + if(Reader.class.isAssignableFrom(c) + || CharBuffer.class.isAssignableFrom(c) + || String.class.isAssignableFrom(c)) + { + return true; + } + else if(InputStream.class.isAssignableFrom(c) + || ByteBuffer.class.isAssignableFrom(c)) + { + return Charset.isSupported(charset); + } + } + else if(InputStream.class.isAssignableFrom(c) + || ByteBuffer.class.isAssignableFrom(c)) + { + return true; + } + } + return false; + } +} // class DataFlavor diff --git a/libjava/classpath/java/awt/datatransfer/FlavorEvent.java b/libjava/classpath/java/awt/datatransfer/FlavorEvent.java new file mode 100644 index 000000000..8a292f850 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/FlavorEvent.java @@ -0,0 +1,57 @@ +/* FlavorEvent -- Event indicating a ClipBoard has different flavors available. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.util.EventObject; + +/** + * Event indicating a Clipboard has different flavors available. + * Fired by a ClipBoard for registered FlavorListeners. + * + * @author Mark J. Wielaard (mark@klomp.org) + * + * @since 1.5 + */ +public class FlavorEvent extends EventObject +{ + public FlavorEvent(Clipboard board) + { + super(board); + } +} diff --git a/libjava/classpath/java/awt/datatransfer/FlavorListener.java b/libjava/classpath/java/awt/datatransfer/FlavorListener.java new file mode 100644 index 000000000..eb388aa84 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/FlavorListener.java @@ -0,0 +1,54 @@ +/* FlavorListener -- Interface for tagging an interest in FlavorEvents. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.util.EventListener; + +/** + * Interface for tagging an interest in FlavorEvents by a class. The + * flavorsChanged() method will be called with a FlavorEvent pointing + * to the Clipboard which has content in different Flavors available. + * + * @author Mark J. Wielaard (mark@klomp.org) + */ +public interface FlavorListener + extends EventListener +{ + void flavorsChanged(FlavorEvent event); +} diff --git a/libjava/classpath/java/awt/datatransfer/FlavorMap.java b/libjava/classpath/java/awt/datatransfer/FlavorMap.java new file mode 100644 index 000000000..8842c8e55 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/FlavorMap.java @@ -0,0 +1,75 @@ +/* FlavorMap.java -- Maps between flavor names and MIME types. + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.util.Map; + +/** + * This interface maps between native platform type names and DataFlavors. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface FlavorMap +{ + /** + * Maps the specified DataFlavor objects to the native + * data type name. The returned Map has keys that are + * the data flavors and values that are strings. The returned map + * may be modified. This can be useful for implementing nested mappings. + * + * @param flavors An array of data flavors to map + * or null for all data flavors. + * + * @return A Map of native data types. + */ + Map getNativesForFlavors (DataFlavor[] flavors); + + /** + * Maps the specified native type names to DataFlavor's. + * The returned Map has keys that are strings and values + * that are DataFlavor's. The returned map may be + * modified. This can be useful for implementing nested mappings. + * + * @param natives An array of native types to map + * or null for all native types. + * + * @return A Map of data flavors. + */ + Map getFlavorsForNatives (String[] natives); +} diff --git a/libjava/classpath/java/awt/datatransfer/FlavorTable.java b/libjava/classpath/java/awt/datatransfer/FlavorTable.java new file mode 100644 index 000000000..f6c43af83 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/FlavorTable.java @@ -0,0 +1,73 @@ +/* FlavorTable.java -- A relaxed mapping between flavors + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.util.List; + +/** + * A FlavorMap which no longer requires a 1-to-1 mapping between flavors. Any + * native can map to multiple flavors, and any flavor can map to multiple + * natives; although the mappings are usually symmetric. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public interface FlavorTable extends FlavorMap +{ + /** + * Returns a list of String natives corresponding to the given flavor. The + * list should be sorted from best to worst. The list must be modifiable + * without affecting this table. + * + * @param flavor the flavor to look up, or null to return all natives + * @return the sorted list of natives + */ + List getNativesForFlavor(DataFlavor flavor); + + /** + * Returns a list of flavors corresponding to the given String native. The + * list should be sorted from best to worst. The list must be modifiable + * without affecting this table. + * + * @param name the native name to look up, or null to return all flavors + * @return the sorted list of flavors + */ + List getFlavorsForNative(String name); +} diff --git a/libjava/classpath/java/awt/datatransfer/MimeType.java b/libjava/classpath/java/awt/datatransfer/MimeType.java new file mode 100644 index 000000000..143e8700b --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/MimeType.java @@ -0,0 +1,283 @@ +/* MimeType.java -- A helper class for mime handling in DataFlavor + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * A helper class for mime handling in DataFlavor. + * + * A Mauve test for DataFlavor.writeExternal() shows that a non-public + * class java.awt.datatransfer.MimeType gets serialized. This class + * is mainly here for serialization compatibility. Of course, + * now that we have it here, we can just as well implement some + * mime handling facility here. + */ +class MimeType + implements Externalizable +{ + + /** + * The primary type. + */ + private String primaryType; + + /** + * The subtype. + */ + private String subType; + + /** + * Additional parameters to be appended to the mime string. + */ + private HashMap parameters; + + /** + * This is only here for deserialization. + */ + public MimeType() + { + parameters = new HashMap(); + } + + /** + * Creates a new MimeType object. + * + * @param mime the mime type + */ + MimeType(String mime) + throws MimeTypeParseException + { + this(); + parse(mime); + } + + /** + * Adds a mime parameter. + * + * @param param the parameter key + * @param value the parameter value + */ + void addParameter(String param, String value) + { + parameters.put(param, value); + } + + /** + * Removes the parameter with the specified key. + * + * @param param the parameter to remove + */ + void removeParameter(String param) + { + parameters.remove(param); + } + + /** + * Returns the parameter for the key. + * + * @param key the parameter key + * + * @return the parameter for the key + */ + String getParameter(String key) + { + return (String) parameters.get(key); + } + + /** + * Returns the primary type. + * + * @return the primary type + */ + String getPrimaryType() + { + return primaryType; + } + + String getSubType() + { + return subType; + } + + /** + * Returns the base type of this mime type. This is the primary + * type plus the subtype, separated by '/'. + * + * @return the base type of this mime type + */ + String getBaseType() + { + return primaryType + '/' + subType; + } + + /** + * Returns true if this mime type and another mime type + * match. This will be true when their primary types are equal, and their + * subtypes are equal (or when either subtype is * ). + * + * @param other the other mime type + * + * @return true if the mime types match, false + * otherwise + */ + boolean matches(MimeType other) + { + boolean match = false; + if (other != null) + { + match = primaryType.equals(other.primaryType) + && (subType.equals("*") || other.subType.equals("*") + || subType.equals(other.subType)); + } + return match; + } + + /** + * Serializes the mime type. + * + * @param in the input stream to read from + * + * @throws ClassNotFoundException not thrown here + * @throws IOException when something goes wrong on the input stream, + * or when the mime type can't be parsed + */ + public void readExternal(ObjectInput in) + throws ClassNotFoundException, IOException + { + String mime = in.readUTF(); + parameters.clear(); + try + { + parse(mime); + } + catch (MimeTypeParseException ex) + { + IOException ioEx = new IOException(); + ioEx.initCause(ex); + throw ioEx; + } + } + + /** + * Serializes this mime type. + * + * @param out the output stream + * + * @throws IOException when something goes wrong on the output stream + */ + public void writeExternal(ObjectOutput out) + throws IOException + { + out.writeUTF(toString()); + } + + /** + * Creates a string representation of this mime type. + * + * @return a string representation of this mime type + */ + public String toString() + { + CPStringBuilder s = new CPStringBuilder(); + s.append(primaryType); + s.append('/'); + s.append(subType); + if (parameters.size() > 0) + { + Set entries = parameters.entrySet(); + for (Iterator i = entries.iterator(); i.hasNext();) + { + s.append("; "); + Map.Entry entry = (Map.Entry) i.next(); + s.append(entry.getKey()); + s.append('='); + s.append(entry.getValue()); + } + } + return s.toString(); + } + + /** + * Parses the specified mime type string and initializes the fields + * of this object. + * + * @param mime the mime type string + */ + private void parse(String mime) + throws MimeTypeParseException + { + // FIXME: Maybe implement more sophisticated mime string parsing according + // to RFC 2045 and 2046. + StringTokenizer tokenizer = new StringTokenizer(mime); + try + { + primaryType = tokenizer.nextToken("/"); + subType = tokenizer.nextToken("/;"); + } + catch (NoSuchElementException ex) + { + throw new MimeTypeParseException("Expected / separator"); + } + + // Add any parameters. + while (tokenizer.hasMoreTokens()) + { + String keyValuePair = tokenizer.nextToken(";"); + int i = keyValuePair.indexOf('='); + if (i == -1) + throw new MimeTypeParseException("Expected = as parameter separator"); + String key = keyValuePair.substring(0, i).trim(); + String value = keyValuePair.substring(i + 1).trim(); + parameters.put(key, value); + } + } + +} diff --git a/libjava/classpath/java/awt/datatransfer/MimeTypeParseException.java b/libjava/classpath/java/awt/datatransfer/MimeTypeParseException.java new file mode 100644 index 000000000..6113ab760 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/MimeTypeParseException.java @@ -0,0 +1,70 @@ +/* MimeTypeParseException.java -- thrown when MIME string couldn't be parsed + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +/** + * MIME string couldn't be parsed correctly. + * + * @author Mark Wielaard (mark@klomp.org) + * @status updated to 1.4 + */ +public class MimeTypeParseException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5604407764691570741L; + + /** + * Create a new instance without any message. + */ + public MimeTypeParseException() + { + } + + /** + * Create a new instance with a specified detailed error message. + * + * @param message the message + */ + public MimeTypeParseException(String message) + { + super(message); + } +} // class MimeTypeParseException diff --git a/libjava/classpath/java/awt/datatransfer/StringSelection.java b/libjava/classpath/java/awt/datatransfer/StringSelection.java new file mode 100644 index 000000000..4b64cda57 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/StringSelection.java @@ -0,0 +1,157 @@ +/* StringSelection.java -- Clipboard handler for text. + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.io.IOException; +import java.io.StringReader; + +/** + * This class transfers a string as plain text using the clipboard. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class StringSelection implements Transferable, ClipboardOwner +{ + +/* + * Class Variables + */ + +// List of flavors we support +// XXX: DataFlavor.plainTextFlavor is deprecated. +static final DataFlavor[] supported_flavors + = { DataFlavor.stringFlavor, + DataFlavor.plainTextFlavor }; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +// This is the data to transfer +private String data; + + /** + * Transfer the specfied string as text. + * + * @param data the data for the string selection + */ + public StringSelection(String data) + { + this.data = data; + } + +/** + * Returns a list of supported data flavors. + * + * @return A list of supported data flavors. + */ +public DataFlavor[] +getTransferDataFlavors() +{ + return(supported_flavors); +} + +/*************************************************************************/ + +/** + * Tests whether or not the specified data flavor is supported. + * + * @param flavor The data flavor to test. + * + * @return true if the data flavor is supported, + * false otherwise. + */ +public boolean +isDataFlavorSupported(DataFlavor flavor) +{ + for (int i = 0; i < supported_flavors.length; i++) + if (supported_flavors[i].equals(flavor)) + return(true); + + return(false); +} + +/*************************************************************************/ + +/** + * This method returns the data in the requested format. + * + * @param flavor The desired data flavor. + * + * @return The transferred data. + * + * @exception UnsupportedFlavorException If the specified flavor is not + * supported. + * @exception IOException If any other error occurs. + */ +public Object +getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, + IOException +{ + if (!isDataFlavorSupported(flavor)) + throw new UnsupportedFlavorException(flavor); + + if (DataFlavor.plainTextFlavor == flavor) + /* The behavior of this method for DataFlavor.plainTextFlavor and + equivalent DataFlavors is inconsistent with the definition of + DataFlavor.plainTextFlavor. We choose to do like Sun's implementation + and return a Reader instead of an InputString. */ + /* return(new StringBufferInputStream(data)); */ + return(new StringReader(data)); + else // DataFlavor.stringFlavor + return data; +} + +/*************************************************************************/ + +/** + * Called when ownership of the clipboard object is lost. + * + * @param clipboard The affected clipboard. + * @param contents The clipboard contents. + */ +public void +lostOwnership(Clipboard clipboard, Transferable contents) +{ + // FIXME: What does this do? +} + +} // class StringSelection diff --git a/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java b/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java new file mode 100644 index 000000000..65c14c1ba --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/SystemFlavorMap.java @@ -0,0 +1,561 @@ +/* SystemFlavorMap.java -- Maps between native flavor names and MIME types. + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.awt.Toolkit; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.WeakHashMap; + +/** + * This class maps between native platform type names and DataFlavors. + * + * XXX - The current implementation does no mapping at all. + * + * @author Mark Wielaard (mark@klomp.org) + * + * @since 1.2 + */ +public final class SystemFlavorMap implements FlavorMap, FlavorTable +{ + /** + * The map which maps the thread's ClassLoaders to + * SystemFlavorMaps. + */ + private static final Map systemFlavorMaps = new WeakHashMap(); + + /** + * Constant which is used to prefix encode Java MIME types. + */ + private static final String GNU_JAVA_MIME_PREFIX = "gnu.java:"; + + /** + * This map maps native Strings to lists of + * DataFlavors + */ + private HashMap> nativeToFlavorMap = + new HashMap>(); + + /** + * This map maps DataFlavors to lists of native + * Strings + */ + private HashMap> flavorToNativeMap = + new HashMap>(); + + /** + * Private constructor. + */ + private SystemFlavorMap () + { + AccessController.doPrivileged + (new PrivilegedAction() + { + public Object run() + { + try + { + // Load installed flavormap.properties first. + String sep = File.separator; + File propsFile = + new File(System.getProperty("gnu.classpath.home.url") + + sep + "accessibility.properties"); + InputStream in = new FileInputStream(propsFile); + Properties props = new Properties(); + props.load(in); + in.close(); + + String augmented = Toolkit.getProperty("AWT.DnD.flavorMapFileURL", + null); + if (augmented != null) + { + URL url = new URL(augmented); + in = url.openStream(); + props.load(in); + } + setupMapping(props); + } + catch (IOException ex) + { + // Can't do anything about it. + } + return null; + } + }); + } + + /** + * Sets up the mapping from native to mime types and vice versa as specified + * in the flavormap.properties file. + * + * This is package private to avoid an accessor method. + * + * @param props the properties file + */ + void setupMapping(Properties props) + { + Enumeration propNames = props.propertyNames(); + while (propNames.hasMoreElements()) + { + try + { + String nat = (String) propNames.nextElement(); + String mime = (String) props.getProperty(nat); + // Check valid mime type. + MimeType type = new MimeType(mime); + DataFlavor flav = new DataFlavor(mime); + + List flavs = nativeToFlavorMap.get(nat); + if (flavs == null) + { + flavs = new ArrayList(); + nativeToFlavorMap.put(nat, flavs); + } + List nats = flavorToNativeMap.get(flav); + if (nats == null) + { + nats = new ArrayList(); + flavorToNativeMap.put(flav, nats); + } + flavs.add(flav); + nats.add(nat); + } + catch (ClassNotFoundException ex) + { + // Skip. + } + catch (MimeTypeParseException ex) + { + // Skip. + } + } + } + + /** + * Maps the specified DataFlavor objects to the native + * data type name. The returned Map has keys that are + * the data flavors and values that are strings. The returned map + * may be modified. This can be useful for implementing nested mappings. + * + * @param flavors An array of data flavors to map + * or null for all data flavors. + * + * @return A Map of native data types to data flavors. + */ + public Map getNativesForFlavors (DataFlavor[] flavors) + { + return new HashMap(); + } + + /** + * Maps the specified native type names to DataFlavor's. + * The returned Map has keys that are strings and values + * that are DataFlavor's. The returned map may be + * modified. This can be useful for implementing nested mappings. + * + * @param natives An array of native types to map + * or null for all native types. + * + * @return A Map of data flavors to native type names. + */ + public Map getFlavorsForNatives (String[] natives) + { + return new HashMap(); + } + + /** + * Returns the (System)FlavorMap for the current thread's + * ClassLoader. + */ + public static FlavorMap getDefaultFlavorMap () + { + ClassLoader classLoader = Thread.currentThread() + .getContextClassLoader(); + + //if ContextClassLoader not set, use system default + if (classLoader == null) + { + classLoader = ClassLoader.getSystemClassLoader(); + } + + synchronized(systemFlavorMaps) + { + FlavorMap map = (FlavorMap) + systemFlavorMaps.get(classLoader); + if (map == null) + { + map = new SystemFlavorMap(); + systemFlavorMaps.put(classLoader, map); + } + return map; + } + } + + /** + * Encodes a MIME type for use as a String native. The format + * of an encoded representation of a MIME type is implementation-dependent. + * The only restrictions are: + *
    + *
  • The encoded representation is null if and only if the + * MIME type String is null.
  • + *
  • The encoded representations for two non-null MIME type + * Strings are equal if and only if these Strings + * are equal according to String.equals(Object).
  • + *
+ *

+ * The present implementation of this method returns the specified MIME + * type String prefixed with gnu.java:. + * + * @param mime the MIME type to encode + * @return the encoded String, or null if + * mimeType is null + */ + public static String encodeJavaMIMEType (String mime) + { + if (mime != null) + return GNU_JAVA_MIME_PREFIX + mime; + else + return null; + } + + /** + * Encodes a DataFlavor for use as a String + * native. The format of an encoded DataFlavor is + * implementation-dependent. The only restrictions are: + *

    + *
  • The encoded representation is null if and only if the + * specified DataFlavor is null or its MIME type + * String is null.
  • + *
  • The encoded representations for two non-null + * DataFlavors with non-null MIME type + * Strings are equal if and only if the MIME type + * Strings of these DataFlavors are equal + * according to String.equals(Object).
  • + *
+ *

+ * The present implementation of this method returns the MIME type + * String of the specified DataFlavor prefixed + * with gnu.java:. + * + * @param df the DataFlavor to encode + * @return the encoded String, or null if + * flav is null or has a null MIME type + */ + public static String encodeDataFlavor (DataFlavor df) + { + if (df != null) + { + return encodeJavaMIMEType(df.getMimeType()); + } + else + return null; + } + + /** + * Returns true if the native type name can be represented as + * a java mime type. Returns false if parameter is + * null. + */ + public static boolean isJavaMIMEType (String name) + { + return (name != null && name.startsWith(GNU_JAVA_MIME_PREFIX)); + } + + /** + * Decodes a String native for use as a Java MIME type. + * + * @param name the String to decode + * @return the decoded Java MIME type, or null if nat + * is not an encoded String native + */ + public static String decodeJavaMIMEType (String name) + { + if (isJavaMIMEType(name)) + { + return name.substring(GNU_JAVA_MIME_PREFIX.length()); + } + else + return null; + } + + /** + * Returns the data flavor given the native type name + * or null when no such data flavor exists. + */ + public static DataFlavor decodeDataFlavor (String name) + throws ClassNotFoundException + { + String javaMIMEType = decodeJavaMIMEType (name); + + if (javaMIMEType != null) + return new DataFlavor (javaMIMEType); + else + return null; + } + + /** + * Returns a List of DataFlavors to which the specified + * String native can be translated by the data transfer + * subsystem. The List will be sorted from best + * DataFlavor to worst. That is, the first DataFlavor + * will best reflect data in the specified native to a Java + * application. + *

+ * If the specified native is previously unknown to the data transfer + * subsystem, and that native has been properly encoded, then invoking + * this method will establish a mapping in both directions between the + * specified native and a DataFlavor whose MIME type is a decoded + * version of the native. + */ + public List getFlavorsForNative(String nat) + { + List ret = new ArrayList(); + if (nat == null) + { + Collection> all = nativeToFlavorMap.values(); + for (List list : all) + { + for (DataFlavor flav : list) + { + if (! ret.contains(flav)) + ret.add(flav); + } + } + } + else + { + List list = nativeToFlavorMap.get(nat); + if (list != null) + ret.addAll(list); + } + return ret; + } + + public List getNativesForFlavor (DataFlavor flav) + { + List ret = new ArrayList(); + if (flav == null) + { + Collection> all = flavorToNativeMap.values(); + for (List list : all) + { + for (String nat : list) + { + if (! ret.contains(nat)) + ret.add(nat); + } + } + } + else + { + List list = flavorToNativeMap.get(flav); + if (list != null) + ret.addAll(list); + } + return ret; + } + + /** + * Adds a mapping from a single String native to a single + * DataFlavor. Unlike getFlavorsForNative, the + * mapping will only be established in one direction, and the native will + * not be encoded. To establish a two-way mapping, call + * addUnencodedNativeForFlavor as well. The new mapping will + * be of lower priority than any existing mapping. + * This method has no effect if a mapping from the specified + * String native to the specified or equal + * DataFlavor already exists. + * + * @param nativeStr the String native key for the mapping + * @param flavor the DataFlavor value for the mapping + * @throws NullPointerException if nat or flav is null + * + * @see #addUnencodedNativeForFlavor + * @since 1.4 + */ + public synchronized void addFlavorForUnencodedNative(String nativeStr, + DataFlavor flavor) + { + if ((nativeStr == null) || (flavor == null)) + throw new NullPointerException(); + List flavors = nativeToFlavorMap.get(nativeStr); + if (flavors == null) + { + flavors = new ArrayList(); + nativeToFlavorMap.put(nativeStr, flavors); + } + else + { + if (! flavors.contains(flavor)) + flavors.add(flavor); + } + } + + /** + * Adds a mapping from the specified DataFlavor (and all + * DataFlavors equal to the specified DataFlavor) + * to the specified String native. + * Unlike getNativesForFlavor, the mapping will only be + * established in one direction, and the native will not be encoded. To + * establish a two-way mapping, call + * addFlavorForUnencodedNative as well. The new mapping will + * be of lower priority than any existing mapping. + * This method has no effect if a mapping from the specified or equal + * DataFlavor to the specified String native + * already exists. + * + * @param flavor the DataFlavor key for the mapping + * @param nativeStr the String native value for the mapping + * @throws NullPointerException if flav or nat is null + * + * @see #addFlavorForUnencodedNative + * @since 1.4 + */ + public synchronized void addUnencodedNativeForFlavor(DataFlavor flavor, + String nativeStr) + { + if ((nativeStr == null) || (flavor == null)) + throw new NullPointerException(); + List natives = flavorToNativeMap.get(flavor); + if (natives == null) + { + natives = new ArrayList(); + flavorToNativeMap.put(flavor, natives); + } + else + { + if (! natives.contains(nativeStr)) + natives.add(nativeStr); + } + } + + /** + * Discards the current mappings for the specified DataFlavor + * and all DataFlavors equal to the specified + * DataFlavor, and creates new mappings to the + * specified String natives. + * Unlike getNativesForFlavor, the mappings will only be + * established in one direction, and the natives will not be encoded. To + * establish two-way mappings, call setFlavorsForNative + * as well. The first native in the array will represent the highest + * priority mapping. Subsequent natives will represent mappings of + * decreasing priority. + *

+ * If the array contains several elements that reference equal + * String natives, this method will establish new mappings + * for the first of those elements and ignore the rest of them. + *

+ * It is recommended that client code not reset mappings established by the + * data transfer subsystem. This method should only be used for + * application-level mappings. + * + * @param flavor the DataFlavor key for the mappings + * @param natives the String native values for the mappings + * @throws NullPointerException if flav or natives is null + * or if natives contains null elements + * + * @see #setFlavorsForNative + * @since 1.4 + */ + public synchronized void setNativesForFlavor(DataFlavor flavor, + String[] natives) + { + if ((natives == null) || (flavor == null)) + throw new NullPointerException(); + + flavorToNativeMap.remove(flavor); + for (int i = 0; i < natives.length; i++) + { + addUnencodedNativeForFlavor(flavor, natives[i]); + } + } + + /** + * Discards the current mappings for the specified String + * native, and creates new mappings to the specified + * DataFlavors. Unlike getFlavorsForNative, the + * mappings will only be established in one direction, and the natives need + * not be encoded. To establish two-way mappings, call + * setNativesForFlavor as well. The first + * DataFlavor in the array will represent the highest priority + * mapping. Subsequent DataFlavors will represent mappings of + * decreasing priority. + *

+ * If the array contains several elements that reference equal + * DataFlavors, this method will establish new mappings + * for the first of those elements and ignore the rest of them. + *

+ * It is recommended that client code not reset mappings established by the + * data transfer subsystem. This method should only be used for + * application-level mappings. + * + * @param nativeStr the String native key for the mappings + * @param flavors the DataFlavor values for the mappings + * @throws NullPointerException if nat or flavors is null + * or if flavors contains null elements + * + * @see #setNativesForFlavor + * @since 1.4 + */ + public synchronized void setFlavorsForNative(String nativeStr, + DataFlavor[] flavors) + { + if ((nativeStr == null) || (flavors == null)) + throw new NullPointerException(); + + nativeToFlavorMap.remove(nativeStr); + for (int i = 0; i < flavors.length; i++) + { + addFlavorForUnencodedNative(nativeStr, flavors[i]); + } + } + +} // class SystemFlavorMap diff --git a/libjava/classpath/java/awt/datatransfer/Transferable.java b/libjava/classpath/java/awt/datatransfer/Transferable.java new file mode 100644 index 000000000..99239fcc6 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/Transferable.java @@ -0,0 +1,82 @@ +/* Transferable.java -- Data transfer source + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.io.IOException; + +/** + * This interface is implemented by classes that can transfer data. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public interface Transferable +{ + /** + * This method returns a list of available data flavors for the data being + * transferred. The array returned will be sorted from most preferred + * flavor at the beginning to least preferred at the end. + * + * @return adA list of data flavors for this data + */ + DataFlavor[] getTransferDataFlavors(); + + /** + * Tests whether or not this data can be delivered in the specified data + * flavor. + * + * @param flavor the data flavor to test + * @return true if the data flavor is supported + */ + boolean isDataFlavorSupported(DataFlavor flavor); + + /** + * Returns the data in the specified DataFlavor. + * + * @param flavor the data flavor to return + * @return the data in the appropriate flavor + * @throws UnsupportedFlavorException if the flavor is not supported + * @throws IOException if the data is not available + * @see DataFlavor#getRepresentationClass + */ + Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException; + +} // interface Transferable diff --git a/libjava/classpath/java/awt/datatransfer/UnsupportedFlavorException.java b/libjava/classpath/java/awt/datatransfer/UnsupportedFlavorException.java new file mode 100644 index 000000000..0ca17b2e9 --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/UnsupportedFlavorException.java @@ -0,0 +1,65 @@ +/* UnsupportedFlavorException.java -- ata flavor is not valid + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +/** + * The data flavor requested is not supported for the transfer data. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Transferable#getTransferData(DataFlavor) + * @status updated to 1.4 + */ +public class UnsupportedFlavorException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5383814944251665601L; + + /** + * Initializes a new instance of UnsupportedDataFlavor + * for the specified data flavor. + * + * @param flavor the data flavor that is not supported + */ + public UnsupportedFlavorException(DataFlavor flavor) + { + super(flavor == null ? null : flavor.getHumanPresentableName()); + } +} // class UnsupportedFlavorException diff --git a/libjava/classpath/java/awt/datatransfer/package.html b/libjava/classpath/java/awt/datatransfer/package.html new file mode 100644 index 000000000..5ab860cdc --- /dev/null +++ b/libjava/classpath/java/awt/datatransfer/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.awt.datatransfer + + +

Classes to represent different flavors of data for transferring native +and system types through for example a clipboard.

+ + + diff --git a/libjava/classpath/java/awt/dnd/Autoscroll.java b/libjava/classpath/java/awt/dnd/Autoscroll.java new file mode 100644 index 000000000..094063ce1 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/Autoscroll.java @@ -0,0 +1,69 @@ +/* Autoscroll.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Insets; +import java.awt.Point; + +/** + * During DnD operations it is possible that a user may wish to drop the + * subject of the operation on a region of a scrollable GUI control that + * is not currently visible to the user. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.2 + * @status updated to 1.4 + */ +public interface Autoscroll +{ + /** + * This method returns the Insets describing the autoscrolling region or + * border relative to the geometry of the implementing Component + */ + Insets getAutoscrollInsets (); + + /** + * Notify the Component to autoscroll + * + * @param location A Point indicating the location of the cursor that + * triggered this operation + */ + void autoscroll (Point location); + +} // interface Autoscroll diff --git a/libjava/classpath/java/awt/dnd/DnDConstants.java b/libjava/classpath/java/awt/dnd/DnDConstants.java new file mode 100644 index 000000000..85c9c0528 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DnDConstants.java @@ -0,0 +1,77 @@ +/* DnDConstants.java -- constants for drag-and-drop operations + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +/** + * This class contains various constants used in drag-and-drop operations. + * Why it is not an interface is beyond me. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public final class DnDConstants +{ + /** No action takes place. */ + public static final int ACTION_NONE = 0; + + /** The copy action. */ + public static final int ACTION_COPY = 1; + + /** The move action. */ + public static final int ACTION_MOVE = 2; + + /** Either a copy or a move. */ + public static final int ACTION_COPY_OR_MOVE = 3; + + /** + * A link action. This does not copy or move, but creates a reference back + * to the original. However, since platforms differ on how a reference should + * behave, this action is not recommended for common use. + */ + public static final int ACTION_LINK = 1073741824; + + /** A synonym for {@link #ACTION_LINK}. */ + public static final int ACTION_REFERENCE = ACTION_LINK; + + private DnDConstants() + { + // Do nothing here. + } +} diff --git a/libjava/classpath/java/awt/dnd/DnDEventMulticaster.java b/libjava/classpath/java/awt/dnd/DnDEventMulticaster.java new file mode 100644 index 000000000..30594c2c7 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DnDEventMulticaster.java @@ -0,0 +1,74 @@ +/* DnDEventMulticaster.java -- helper class for listener chains in java.awt.dnd + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.AWTEventMulticaster; +import java.util.EventListener; + +class DnDEventMulticaster extends AWTEventMulticaster +{ + protected DnDEventMulticaster (EventListener a, EventListener b) + { + super (a, b); + } + + public static DragSourceListener add (DragSourceListener a, + DragSourceListener b) + { + return (DragSourceListener) addInternal (a, b); + } + + public static DragSourceMotionListener add (DragSourceMotionListener a, + DragSourceMotionListener b) + { + return (DragSourceMotionListener) addInternal (a, b); + } + + public static DragSourceListener remove (DragSourceListener a, + DragSourceListener b) + { + return (DragSourceListener) removeInternal (a, b); + } + + public static DragSourceMotionListener remove (DragSourceMotionListener a, + DragSourceMotionListener b) + { + return (DragSourceMotionListener) removeInternal (a, b); + } +} diff --git a/libjava/classpath/java/awt/dnd/DragGestureEvent.java b/libjava/classpath/java/awt/dnd/DragGestureEvent.java new file mode 100644 index 000000000..e127a7dc1 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragGestureEvent.java @@ -0,0 +1,219 @@ +/* DragGestureEvent.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; +import java.awt.datatransfer.Transferable; +import java.awt.event.InputEvent; +import java.util.EventObject; +import java.util.Iterator; +import java.util.List; + +public class DragGestureEvent extends EventObject +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 9080172649166731306L; + + private DragSource dragSource; + private Component component; + private final Point origin; + private final int action; + private List events; + private DragGestureRecognizer dgr; + + /** + * Constructs a new DragGestureEvent. + * @param dgr - DragGestureRecognizer firing this event + * @param action - user's preferred action + * @param origin - origin of the drag + * @param events - List of events that make up the gesture + * @throws IllegalArgumentException - if input parameters are null + */ + public DragGestureEvent(DragGestureRecognizer dgr, int action, Point origin, + List events) + { + super(dgr); + if (origin == null || events == null || dgr == null) + throw new IllegalArgumentException(); + + this.origin = origin; + this.action = action; + this.events = (List) events; + this.dgr = dgr; + this.component = dgr.getComponent(); + this.dragSource = dgr.getDragSource(); + } + + /** + * Returns the source casted as a DragGestureRecognizer. + * + * @return the source casted as a DragGestureRecognizer. + */ + public DragGestureRecognizer getSourceAsDragGestureRecognizer() + { + return (DragGestureRecognizer) getSource(); + } + + /** + * Returns the Component corresponding to this. + * + * @return the Component corresponding to this. + */ + public Component getComponent() + { + return component; + } + + /** + * Gets the DragSource corresponding to this. + * + * @return the DragSource corresponding to this. + */ + public DragSource getDragSource() + { + return dragSource; + } + + /** + * Returns the origin of the drag. + * + * @return the origin of the drag. + */ + public Point getDragOrigin() + { + return origin; + } + + /** + * Gets an iterator representation of the List of events. + * + * @return an iterator representation of the List of events. + */ + public Iterator iterator() + { + return events.iterator(); + } + + /** + * Gets an array representation of the List of events. + * + * @return an array representation of the List of events. + */ + public Object[] toArray() + { + return events.toArray(); + } + + /** + * Gets an array representation of the List of events. + * + * @param array - the array to store the events in. + * @return an array representation of the List of events. + */ + public Object[] toArray(Object[] array) + { + return events.toArray(array); + } + + /** + * Gets the user's preferred action. + * + * @return the user's preferred action. + */ + public int getDragAction() + { + return action; + } + + /** + * Get the event that triggered this gesture. + * + * @return the event that triggered this gesture. + */ + public InputEvent getTriggerEvent() + { + return dgr.getTriggerEvent(); + } + + /** + * Starts the drag given the initial Cursor to display, the Transferable + * object, and the DragSourceListener to use. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(Cursor dragCursor, Transferable trans) + { + startDrag(dragCursor, null, null, trans, null); + } + + /** + * Starts the drag given the initial Cursor to display, the Transferable + * object, and the DragSourceListener to use. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(Cursor dragCursor, Transferable trans, + DragSourceListener l) + { + startDrag(dragCursor, null, null, trans, l); + } + + /** + * Starts the drag given the initial Cursor to display, the Transferable + * object, and the DragSourceListener to use. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, + Transferable trans, DragSourceListener l) + { + dragSource.startDrag(this, dragCursor, dragImage, imageOffset, trans, l); + } +} // class DragGestureEvent diff --git a/libjava/classpath/java/awt/dnd/DragGestureListener.java b/libjava/classpath/java/awt/dnd/DragGestureListener.java new file mode 100644 index 000000000..e8befe80f --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragGestureListener.java @@ -0,0 +1,63 @@ +/* DragGestureListener.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.util.EventListener; + +/** + * This is a listener for starting a drag-and-drop gesture. Upon receiving + * notification, the implementor then starts the drag operation. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see DragGestureRecognizer + * @see DragGestureEvent + * @see DragSource + * @since 1.2 + * @status updated to 1.4 + */ +public interface DragGestureListener extends EventListener +{ + /** + * Called when the native platform notifies the virtual machine that a + * drag-and-drop has been initiated. + * + * @param e the event + */ + void dragGestureRecognized(DragGestureEvent e); +} // interface DragGestureListener diff --git a/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java b/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java new file mode 100644 index 000000000..c41402ca7 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragGestureRecognizer.java @@ -0,0 +1,191 @@ +/* DragGestureRecognizer.java -- + Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.Point; +import java.awt.event.InputEvent; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.TooManyListenersException; + +/** + * STUBBED + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.2 + */ +public abstract class DragGestureRecognizer implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 8996673345831063337L; + + protected DragSource dragSource; + protected Component component; + protected transient DragGestureListener dragGestureListener; + protected int sourceActions; + protected ArrayList events = new ArrayList(); + + protected DragGestureRecognizer(DragSource ds, Component c, int sa, + DragGestureListener dgl) + { + if (ds == null) + throw new IllegalArgumentException(); + dragSource = ds; + component = c; + sourceActions = sa; + dragGestureListener = dgl; + } + + protected DragGestureRecognizer(DragSource ds, Component c, int sa) + { + this(ds, c, sa, null); + } + + protected DragGestureRecognizer(DragSource ds, Component c) + { + this(ds, c, 0, null); + } + + protected DragGestureRecognizer(DragSource ds) + { + this(ds, null, 0, null); + } + + protected abstract void registerListeners(); + + protected abstract void unregisterListeners(); + + public DragSource getDragSource() + { + return dragSource; + } + + public Component getComponent() + { + return component; + } + + public void setComponent(Component c) + { + component = c; + } + + public int getSourceActions() + { + return sourceActions; + } + + public void setSourceActions(int sa) + { + sourceActions = sa; + } + + public InputEvent getTriggerEvent() + { + return events.size() > 0 ? events.get(0) : null; + } + + /** + * Resets the recognizer. If a gesture is currently recognize, discard it. + */ + public void resetRecognizer() + { + events.clear(); + } + + /** + * Register a new DragGestureListener. + * + * @exception TooManyListenersException If a DragGestureListener has already + * been added. + */ + public void addDragGestureListener(DragGestureListener dgl) + throws TooManyListenersException + { + if (dragGestureListener != null) + throw new TooManyListenersException(); + dragGestureListener = dgl; + } + + public void removeDragGestureListener(DragGestureListener dgl) + { + if (dragGestureListener != dgl) + throw new IllegalArgumentException(); + dragGestureListener = null; + } + + /** + * Fires a DragGestureEvent to the DragGestureListener + * associated with this object, if there is one. + */ + protected void fireDragGestureRecognized(int dragAction, Point p) + { + if(dragGestureListener != null) + dragGestureListener.dragGestureRecognized + (new DragGestureEvent(this, dragAction, p, events)); + resetRecognizer(); + } + + protected void appendEvent(InputEvent e) + { + if (e == null) + return; + events.add(e); + } + + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + dragGestureListener = (DragGestureListener) s.readObject(); + } + + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeObject(dragGestureListener instanceof Serializable + ? dragGestureListener : null); + } +} // class DragGestureRecognizer diff --git a/libjava/classpath/java/awt/dnd/DragSource.java b/libjava/classpath/java/awt/dnd/DragSource.java new file mode 100644 index 000000000..af8b37668 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSource.java @@ -0,0 +1,326 @@ +/* DragSource.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import gnu.classpath.NotImplementedException; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.datatransfer.FlavorMap; +import java.awt.datatransfer.SystemFlavorMap; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.io.Serializable; +import java.util.EventListener; + +/** + * @since 1.2 + */ +public class DragSource implements Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 6236096958971414066L; + + public static final Cursor DefaultCopyDrop = null; + public static final Cursor DefaultMoveDrop = null; + public static final Cursor DefaultLinkDrop = null; + public static final Cursor DefaultCopyNoDrop = null; + public static final Cursor DefaultMoveNoDrop = null; + public static final Cursor DefaultLinkNoDrop = null; + + private transient FlavorMap flavorMap = SystemFlavorMap.getDefaultFlavorMap (); + private transient DragSourceListener dragSourceListener; + private transient DragSourceMotionListener dragSourceMotionListener; + + private static DragSource ds; + private DragSourceContextPeer peer; + private DragSourceContext context; + + /** + * Initializes the drag source. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public DragSource() + { + if (GraphicsEnvironment.isHeadless()) + { + ds = null; + throw new HeadlessException(); + } + } + + /** + * Gets the default drag source. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. + */ + public static DragSource getDefaultDragSource() + { + if (GraphicsEnvironment.isHeadless()) + { + ds = null; + throw new HeadlessException(); + } + + if (ds == null) + ds = new DragSource(); + return ds; + } + + public static boolean isDragImageSupported() + { + // In all cases, Sun returns false here. + return false; + } + + /** + * Start a drag, given the DragGestureEvent that initiated the drag. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(DragGestureEvent trigger, Cursor dragCursor, + Image dragImage, Point imageOffset, + Transferable trans, DragSourceListener dsl, + FlavorMap map) + { + // http://www.javaworld.com/javaworld/jw-03-1999/jw-03-dragndrop.html + + // This function creates a DragSourceContext object. This object tracks the + // state of the operation by listening to a native peer. In this situation, + // the DragSource may be obtained from the event or by an instance variable. + // This function also creates a new DragSourceContextPeer. + + // This function sends the same message to the context, which then forwards + // it to the peer, passing itself as a parameter. Now, the native system has + // access to the Transferable through the context. + + try + { + flavorMap = map; + + if (peer == null) + peer = Toolkit.getDefaultToolkit().createDragSourceContextPeer(trigger); + + if (context == null) + context = createDragSourceContext(peer, trigger, + dragCursor, + dragImage, + imageOffset, trans, + dsl); + + if (peer == null) + throw new InvalidDnDOperationException(); + + peer.startDrag(context, dragCursor, dragImage, imageOffset); + } + catch (Exception e) + { + throw new InvalidDnDOperationException("Drag and Drop system is " + + "unable to initiate a drag operation."); + } + } + + /** + * Start a drag, given the DragGestureEvent that initiated the drag. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(DragGestureEvent trigger, Cursor dragCursor, + Transferable trans, DragSourceListener dsl, + FlavorMap map) + { + startDrag(trigger, dragCursor, null, null, trans, dsl, map); + } + + /** + * Start a drag, given the DragGestureEvent that initiated the drag. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(DragGestureEvent trigger, Cursor dragCursor, + Image dragImage, Point imageOffset, + Transferable trans, DragSourceListener dsl) + { + startDrag(trigger, dragCursor, dragImage, imageOffset, trans, dsl, null); + } + + /** + * Start a drag, given the DragGestureEvent that initiated the drag. + * + * @exception InvalidDnDOperationException If the Drag and Drop system is + * unable to initiate a drag operation, or if the user attempts to start + * a drag while an existing drag operation is still executing. + */ + public void startDrag(DragGestureEvent trigger, Cursor dragCursor, + Transferable trans, DragSourceListener dsl) + { + startDrag(trigger, dragCursor, null, null, trans, dsl, null); + } + + /** + * Creates the DragSourceContext to handle this drag. + * + * @exception IllegalArgumentException + * @exception NullPointerException If dscp, dgl, dragImage or t is null. + */ + protected DragSourceContext + createDragSourceContext(DragSourceContextPeer peer, DragGestureEvent dge, + Cursor cursor, Image image, Point offset, + Transferable t, DragSourceListener dsl) + { + return new DragSourceContext(peer, dge, cursor, image, offset, t, dsl); + } + + public FlavorMap getFlavorMap() + { + return flavorMap; + } + + public T + createDragGestureRecognizer(Class recognizer, + Component c, + int actions, + DragGestureListener dgl) + { + return (T) Toolkit.getDefaultToolkit().createDragGestureRecognizer(recognizer, + this, c, + actions, dgl); + } + + public DragGestureRecognizer createDefaultDragGestureRecognizer(Component c, + int actions, + DragGestureListener dgl) + { + return createDragGestureRecognizer(MouseDragGestureRecognizer.class, c, + actions, dgl); + } + + /** + * @since 1.4 + */ + public void addDragSourceListener(DragSourceListener l) + { + DnDEventMulticaster.add (dragSourceListener, l); + } + + /** + * @since 1.4 + */ + public void removeDragSourceListener(DragSourceListener l) + { + DnDEventMulticaster.remove (dragSourceListener, l); + } + + /** + * @since 1.4 + */ + public DragSourceListener[] getDragSourceListeners() + { + return (DragSourceListener[]) getListeners (DragSourceListener.class); + } + + /** + * @since 1.4 + */ + public void addDragSourceMotionListener(DragSourceMotionListener l) + { + DnDEventMulticaster.add (dragSourceMotionListener, l); + } + + /** + * @since 1.4 + */ + public void removeDragSourceMotionListener(DragSourceMotionListener l) + { + DnDEventMulticaster.remove (dragSourceMotionListener, l); + } + + /** + * @since 1.4 + */ + public DragSourceMotionListener[] getDragSourceMotionListeners () + { + return (DragSourceMotionListener[]) getListeners + (DragSourceMotionListener.class); + } + + /** + * @since 1.4 + */ + public T[] getListeners (Class listenerType) + { + if (listenerType == DragSourceListener.class) + return DnDEventMulticaster.getListeners (dragSourceListener, + listenerType); + + if (listenerType == DragSourceMotionListener.class) + return DnDEventMulticaster.getListeners (dragSourceMotionListener, + listenerType); + + // Return an empty EventListener array. + return (T[]) new EventListener [0]; + } + + /** + * TODO + * @return TODO + * + * @since 1.5 + */ + public static int getDragThreshold() + throws NotImplementedException + { + // FIXME: Not implemented. + return 8; + } +} // class DragSource diff --git a/libjava/classpath/java/awt/dnd/DragSourceAdapter.java b/libjava/classpath/java/awt/dnd/DragSourceAdapter.java new file mode 100644 index 000000000..90d9a6983 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceAdapter.java @@ -0,0 +1,126 @@ +/* DragSourceAdapter.java -- drag-and-drop listener adapter + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +/** + * This class implements DragSourceListener and + * DragSourceMotionListener, and implements all methods + * with empty bodies. This allows a listener interested in implementing only + * a subset of these interfaces to extend this class and override only the + * desired methods. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see DragSourceEvent + * @see DragSourceListener + * @see DragSourceMotionListener + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class DragSourceAdapter + implements DragSourceListener, DragSourceMotionListener +{ + /** + * Default constructor. + */ + public DragSourceAdapter() + { + } + + /** + * Called when the cursor hotspot enters a drop site which will accept the + * drag. + * + * @param e the event + */ + public void dragEnter(DragSourceDragEvent e) + { + } + + /** + * Called when the cursor hotspot moves inside of a drop site which will + * accept the drag. + * + * @param e the event + */ + public void dragOver(DragSourceDragEvent e) + { + } + + /** + * Called whenever the mouse is moved during a drag-and-drop operation. + * + * @param e the event + */ + public void dragMouseMoved(DragSourceDragEvent e) + { + } + + /** + * Called when the user modifies the drop gesture. This is often the case + * when additional mouse or key events are received during the drag. + * + * @param e the event + */ + public void dropActionChanged(DragSourceDragEvent e) + { + } + + /** + * Called when the cursor hotspot moves outside of a drop site which will + * accept the drag. This could also happen if the drop site is no longer + * active, or no longer accepts the drag. + * + * @param e the event + */ + public void dragExit(DragSourceEvent e) + { + } + + /** + * Called when the drag and drop operation is complete. After this event, + * getDropSuccess of the event is valid, and + * getDropAction holds the action requested by the drop site. + * Furthermore, the DragSourceContext is invalidated. + * + * @param e the event + */ + public void dragDropEnd(DragSourceDropEvent e) + { + } +} // class DragSourceAdapter diff --git a/libjava/classpath/java/awt/dnd/DragSourceContext.java b/libjava/classpath/java/awt/dnd/DragSourceContext.java new file mode 100644 index 000000000..e21b4fdae --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceContext.java @@ -0,0 +1,383 @@ +/* DragSourceContext.java -- + Copyright (C) 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.peer.DragSourceContextPeer; +import java.io.Serializable; +import java.util.TooManyListenersException; + +/** + * @since 1.2 + */ +public class DragSourceContext + implements DragSourceListener, DragSourceMotionListener, Serializable +{ + /** + * Compatible with JDK 1.2+ + */ + static final long serialVersionUID = -115407898692194719L; + + protected static final int DEFAULT = 0; + protected static final int ENTER = 1; + protected static final int OVER = 2; + protected static final int CHANGED = 3; + + private DragSourceContextPeer peer; + private Cursor cursor; + private Transferable transferable; + private DragGestureEvent trigger; + private DragSourceListener dragSourceListener; + private boolean useCustomCursor; + private int sourceActions; + private Image image; + private Point offset; + + /** + * Initializes a drag source context. + * + * @exception IllegalArgumentException If Component or DragSource of trigger + * are null, the drag action for the trigger event is DnDConstants.ACTION_NONE + * or if the source actions for the DragGestureRecognizer associated with the + * trigger event are equal to DnDConstants.ACTION_NONE. + * @exception NullPointerException If peer, trans or trigger is null or if the + * image is not null but the offset is. + */ + public DragSourceContext (DragSourceContextPeer peer, + DragGestureEvent trigger, Cursor cursor, + Image image, Point offset, Transferable trans, + DragSourceListener dsl) + { + if (peer == null + || trigger == null || trans == null + || (image != null && offset == null)) + throw new NullPointerException (); + + if (trigger.getComponent () == null + || trigger.getDragSource () == null + || trigger.getDragAction () == DnDConstants.ACTION_NONE + || trigger.getSourceAsDragGestureRecognizer () + .getSourceActions () == DnDConstants.ACTION_NONE) + throw new IllegalArgumentException (); + + this.peer = peer; + this.trigger = trigger; + this.cursor = cursor; + this.image = image; + this.offset = offset; + this.transferable = trans; + this.dragSourceListener = dsl; + this.sourceActions = trigger.getSourceAsDragGestureRecognizer().getSourceActions(); + + setCursor(cursor); + updateCurrentCursor(trigger.getDragAction(), sourceActions, DEFAULT); + } + + /** + * Returns the DragSource object associated with the + * DragGestureEvent. + * + * @return the DragSource associated with the trigger. + */ + public DragSource getDragSource() + { + return trigger.getDragSource (); + } + + /** + * Returns the component associated with this. + * + * @return the component associated with the trigger. + */ + public Component getComponent() + { + return trigger.getComponent (); + } + + /** + * Gets the trigger associated with this. + * + * @return the trigger. + */ + public DragGestureEvent getTrigger() + { + return trigger; + } + + /** + * Returns the source actions for the DragGestureRecognizer. + * + * @return the source actions for DragGestureRecognizer. + */ + public int getSourceActions() + { + if (sourceActions == 0) + sourceActions = trigger.getSourceAsDragGestureRecognizer().getSourceActions(); + return sourceActions; + } + + /** + * Sets the cursor for this drag operation to the specified cursor. + * + * @param cursor c - the Cursor to use, or null to use the default drag + * cursor. + */ + public void setCursor(Cursor cursor) + { + if (cursor == null) + useCustomCursor = false; + else + useCustomCursor = true; + this.cursor = cursor; + peer.setCursor(cursor); + } + + /** + * Returns the current cursor or null if the default + * drag cursor is used. + * + * @return the current cursor or null. + */ + public Cursor getCursor() + { + return cursor; + } + + /** + * Adds a DragSourceListener. + * + * @exception TooManyListenersException If a DragSourceListener + * has already been added. + */ + public void addDragSourceListener (DragSourceListener dsl) + throws TooManyListenersException + { + if (dragSourceListener != null) + throw new TooManyListenersException (); + + dragSourceListener = dsl; + } + + public void removeDragSourceListener (DragSourceListener dsl) + { + if (dragSourceListener == dsl) + dragSourceListener = null; + } + + /** + * This function tells the peer that the DataFlavors have been modified. + */ + public void transferablesFlavorsChanged() + { + peer.transferablesFlavorsChanged(); + } + + /** + * Calls dragEnter on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceDragEvent + */ + public void dragEnter(DragSourceDragEvent e) + { + if (dragSourceListener != null) + dragSourceListener.dragEnter(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dragEnter(e); + + updateCurrentCursor(e.getDropAction(), e.getTargetActions(), ENTER); + } + + /** + * Calls dragOver on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceDragEvent + */ + public void dragOver(DragSourceDragEvent e) + { + if (dragSourceListener != null) + dragSourceListener.dragOver(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dragOver(e); + + updateCurrentCursor(e.getDropAction(), e.getTargetActions(), OVER); + } + + /** + * Calls dragExit on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceEvent + */ + public void dragExit(DragSourceEvent e) + { + if (dragSourceListener != null) + dragSourceListener.dragExit(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dragExit(e); + + updateCurrentCursor(DnDConstants.ACTION_NONE, DnDConstants.ACTION_NONE, + DEFAULT); + } + + /** + * Calls dropActionChanged on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceDragEvent + */ + public void dropActionChanged(DragSourceDragEvent e) + { + if (dragSourceListener != null) + dragSourceListener.dropActionChanged(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dropActionChanged(e); + + updateCurrentCursor(e.getDropAction(), e.getTargetActions(), CHANGED); + } + + /** + * Calls dragDropEnd on the listeners registered with this + * and with the DragSource. + * + * @param e - the DragSourceDropEvent + */ + public void dragDropEnd(DragSourceDropEvent e) + { + if (dragSourceListener != null) + dragSourceListener.dragDropEnd(e); + + DragSource ds = getDragSource(); + DragSourceListener[] dsl = ds.getDragSourceListeners(); + for (int i = 0; i < dsl.length; i++) + dsl[i].dragDropEnd(e); + } + + /** + * Calls dragMouseMoved on the listeners registered with the DragSource. + * + * @param e - the DragSourceDragEvent + */ + public void dragMouseMoved(DragSourceDragEvent e) + { + DragSource ds = getDragSource(); + DragSourceMotionListener[] dsml = ds.getDragSourceMotionListeners(); + for (int i = 0; i < dsml.length; i++) + dsml[i].dragMouseMoved(e); + } + + /** + * Returns the Transferable set with this object. + * + * @return the transferable. + */ + public Transferable getTransferable() + { + return transferable; + } + + /** + * This function sets the drag cursor for the specified operation, actions and + * status if the default drag cursor is active. Otherwise, the cursor is not + * updated in any way. + * + * @param dropOp - the current operation. + * @param targetAct - the supported actions. + * @param status - the status of the cursor (constant). + */ + protected void updateCurrentCursor(int dropOp, int targetAct, int status) + { + if (! useCustomCursor) + { + Cursor newCursor = null; + switch (status) + { + default: + targetAct = DnDConstants.ACTION_NONE; + case ENTER: + case CHANGED: + case OVER: + int action = dropOp & targetAct; + if (action == DnDConstants.ACTION_NONE) + { + if ((dropOp & DnDConstants.ACTION_LINK) != 0) + newCursor = DragSource.DefaultLinkNoDrop; + else if ((dropOp & DnDConstants.ACTION_MOVE) != 0) + newCursor = DragSource.DefaultMoveNoDrop; + else + newCursor = DragSource.DefaultCopyNoDrop; + } + else + { + if ((dropOp & DnDConstants.ACTION_LINK) != 0) + newCursor = DragSource.DefaultLinkDrop; + else if ((dropOp & DnDConstants.ACTION_MOVE) != 0) + newCursor = DragSource.DefaultMoveDrop; + else + newCursor = DragSource.DefaultCopyDrop; + } + } + + if (cursor == null || ! cursor.equals(newCursor)) + { + cursor = newCursor; + DragSourceContextPeer p = peer; + if (p != null) + p.setCursor(cursor); + } + } + } +} // class DragSourceContext diff --git a/libjava/classpath/java/awt/dnd/DragSourceDragEvent.java b/libjava/classpath/java/awt/dnd/DragSourceDragEvent.java new file mode 100644 index 000000000..511700b61 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceDragEvent.java @@ -0,0 +1,102 @@ +/* DragSourceDragEvent.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import gnu.java.awt.EventModifier; + +/** + * @author Michael Koch + * @since 1.2 + */ +public class DragSourceDragEvent extends DragSourceEvent +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = 481346297933902471L; + + private final int dropAction; + private final int targetActions; + private final int gestureModifiers; + + public DragSourceDragEvent(DragSourceContext context, int dropAction, + int actions, int modifiers) + { + super(context); + this.dropAction = dropAction; + targetActions = actions; + gestureModifiers = EventModifier.extend(modifiers); + } + + public DragSourceDragEvent(DragSourceContext context, int dropAction, + int actions, int modifiers, int x, int y) + { + super(context, x, y); + this.dropAction = dropAction; + targetActions = actions; + gestureModifiers = EventModifier.extend(modifiers); + } + + public int getTargetActions() + { + return targetActions; + } + + public int getGestureModifiers() + { + return EventModifier.revert(gestureModifiers); + } + + public int getGestureModifiersEx() + { + return gestureModifiers; + } + + public int getUserAction() + { + return dropAction; + } + + public int getDropAction() + { + return (dropAction + & targetActions + & ((DragSourceContext) source).getSourceActions()); + } +} // class DragSourceDragEvent diff --git a/libjava/classpath/java/awt/dnd/DragSourceDropEvent.java b/libjava/classpath/java/awt/dnd/DragSourceDropEvent.java new file mode 100644 index 000000000..4df984994 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceDropEvent.java @@ -0,0 +1,89 @@ +/* DragSourceDragEvent.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.2 + * + * Written using JDK 1.4.1 Online API + * Status: JDK 1.4 complete + */ +public class DragSourceDropEvent extends DragSourceEvent +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = -5571321229470821891L; + + private final int dropAction; + private final boolean dropSuccess; + + public DragSourceDropEvent (DragSourceContext context) + { + super (context); + this.dropAction = 0; + this.dropSuccess = false; + } + + public DragSourceDropEvent (DragSourceContext context, int dropAction, + boolean dropSuccess) + { + super (context); + this.dropAction = dropAction; + this.dropSuccess = dropSuccess; + } + + public DragSourceDropEvent (DragSourceContext context, int dropAction, + boolean dropSuccess, int x, int y) + { + super (context, x, y); + this.dropAction = dropAction; + this.dropSuccess = dropSuccess; + } + + public int getDropAction() + { + return dropAction & ((DragSourceContext) source).getSourceActions(); + } + + public boolean getDropSuccess() + { + return dropSuccess; + } +} // class DragSourceDropEvent diff --git a/libjava/classpath/java/awt/dnd/DragSourceEvent.java b/libjava/classpath/java/awt/dnd/DragSourceEvent.java new file mode 100644 index 000000000..c9e18d712 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceEvent.java @@ -0,0 +1,93 @@ +/* DragSourceEvent.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Point; +import java.util.EventObject; + +/** + * @since 1.2 + */ +public class DragSourceEvent extends EventObject +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = -763287114604032641L; + + private final boolean locationSpecified; + private final int x; + private final int y; + + public DragSourceEvent(DragSourceContext context) + { + super(context); + locationSpecified = false; + x = 0; + y = 0; + } + + public DragSourceEvent(DragSourceContext context, int x, int y) + { + super(context); + locationSpecified = true; + this.x = x; + this.y = y; + } + + public DragSourceContext getDragSourceContext() + { + return (DragSourceContext) source; + } + + public Point getLocation() + { + return locationSpecified ? new Point(x, y) : null; + } + + public int getX() + { + return x; + } + + public int getY() + { + return y; + } +} // class DragSourceEvent diff --git a/libjava/classpath/java/awt/dnd/DragSourceListener.java b/libjava/classpath/java/awt/dnd/DragSourceListener.java new file mode 100644 index 000000000..aac6e94eb --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceListener.java @@ -0,0 +1,97 @@ +/* DragSourceListener.java -- listen to events during the drag + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.util.EventListener; + +/** + * This class allows an object to listen for drag and drop events. It can + * be used to provide appropriate feedback for "drag over" actions. You can + * also use a DragSourceAdapter to filter the events you are + * interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public interface DragSourceListener extends EventListener +{ + /** + * Called when the cursor hotspot enters a drop site which will accept the + * drag. + * + * @param e the drag source drag event + */ + void dragEnter(DragSourceDragEvent e); + + /** + * Called when the cursor hotspot moves inside of a drop site which will + * accept the drag. + * + * @param e the drag source drag event + */ + void dragOver(DragSourceDragEvent e); + + /** + * Called when the user modifies the drop gesture. This is often the case + * when additional mouse or key events are received during the drag. + * + * @param e the drag source drag event + */ + void dropActionChanged(DragSourceDragEvent e); + + /** + * Called when the cursor hotspot moves outside of a drop site which will + * accept the drag. This could also happen if the drop site is no longer + * active, or no longer accepts the drag. + * + * @param e the drag source drag event + */ + void dragExit(DragSourceEvent e); + + /** + * Called when the drag and drop operation is complete. After this event, + * getDropSuccess of the event is valid, and + * getDropAction holds the action requested by the drop site. + * Furthermore, the DragSourceContext is invalidated. + * + * @param e the drag source drag event + */ + void dragDropEnd(DragSourceDropEvent e); +} // interface DragSourceListener diff --git a/libjava/classpath/java/awt/dnd/DragSourceMotionListener.java b/libjava/classpath/java/awt/dnd/DragSourceMotionListener.java new file mode 100644 index 000000000..5d04c2271 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DragSourceMotionListener.java @@ -0,0 +1,64 @@ +/* DragSourceMotionListener.java -- tracks motion in the drag source + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.util.EventListener; + +/** + * This is a listener for mouse motion in the drag source before the drop + * event occurs. You can also use a DragSourceAdapter to filter + * the events you are interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see DragSourceDragEvent + * @see DragSource + * @see DragSourceListener + * @see DragSourceAdapter + * @since 1.4 + * @status updated to 1.4 + */ +public interface DragSourceMotionListener extends EventListener +{ + /** + * Called whenever the mouse is moved during a drag-and-drop operation. + * + * @param e the event + */ + void dragMouseMoved(DragSourceDragEvent e); +} // interface DragSourceMotionListener diff --git a/libjava/classpath/java/awt/dnd/DropTarget.java b/libjava/classpath/java/awt/dnd/DropTarget.java new file mode 100644 index 000000000..e5180d074 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTarget.java @@ -0,0 +1,438 @@ +/* DropTarget.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.GraphicsEnvironment; +import java.awt.HeadlessException; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.datatransfer.FlavorMap; +import java.awt.datatransfer.SystemFlavorMap; +import java.awt.dnd.peer.DropTargetPeer; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.peer.ComponentPeer; +import java.awt.peer.LightweightPeer; +import java.io.Serializable; +import java.util.EventListener; +import java.util.TooManyListenersException; + +import javax.swing.Timer; + +/** + * @author Michael Koch + * @since 1.2 + */ +public class DropTarget + implements DropTargetListener, EventListener, Serializable +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = -6283860791671019047L; + + protected static class DropTargetAutoScroller + implements ActionListener + { + /** + * The threshold that keeps the autoscroller running. + */ + private static final int HYSTERESIS = 10; + + /** + * The initial timer delay. + */ + private static final int DELAY = 100; + + private Component component; + private Point point; + + /** + * The timer that triggers autoscrolling. + */ + private Timer timer; + + /** + * The outer region of the scroller. This is the component's size. + */ + private Rectangle outer; + + /** + * The inner region of the scroller. This is the component size without + * the autoscroll insets. + */ + private Rectangle inner; + + protected DropTargetAutoScroller (Component c, Point p) + { + component = c; + point = p; + timer = new Timer(DELAY, this); + timer.setCoalesce(true); + timer.start(); + } + + protected void updateLocation (Point newLocn) + { + Point previous = point; + point = newLocn; + if (Math.abs(point.x - previous.x) > HYSTERESIS + || Math.abs(point.y - previous.y) > HYSTERESIS) + { + if (timer.isRunning()) + timer.stop(); + } + else + { + if (! timer.isRunning()) + timer.start(); + } + } + + protected void stop () + { + timer.start(); + } + + public void actionPerformed (ActionEvent e) + { + Autoscroll autoScroll = (Autoscroll) component; + + // First synchronize the inner and outer rectangles. + Insets i = autoScroll.getAutoscrollInsets(); + int width = component.getWidth(); + int height = component.getHeight(); + if (width != outer.width || height != outer.height) + outer.setBounds(0, 0, width, height); + if (inner.x != i.left || inner.y != i.top) + inner.setLocation(i.left, i.top); + int inWidth = width - i.left - i.right; + int inHeight = height - i.top - i.bottom; + if (inWidth != inner.width || inHeight != inner.height) + inner.setSize(inWidth, inHeight); + + // Scroll if the outer rectangle contains the location, but the + // inner doesn't. + if (outer.contains(point) && ! inner.contains(point)) + autoScroll.autoscroll(point); + } + } + + private Component component; + private FlavorMap flavorMap; + private int actions; + private DropTargetPeer peer; + private DropTargetContext dropTargetContext; + private DropTargetListener dropTargetListener; + private DropTarget.DropTargetAutoScroller autoscroller; + private boolean active = true; + + /** + * Creates a DropTarget object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget () + { + this (null, DnDConstants.ACTION_COPY_OR_MOVE, null, true, null); + } + + /** + * Creates a DropTarget object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget (Component c, DropTargetListener dtl) + { + this (c, DnDConstants.ACTION_COPY_OR_MOVE, dtl, true, null); + } + + /** + * Creates a DropTarget object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget (Component c, int i, DropTargetListener dtl) + { + this (c, i, dtl, true, null); + } + + /** + * Creates a DropTarget object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget (Component c, int i, DropTargetListener dtl, boolean b) + { + this (c, i, dtl, b, null); + } + + /** + * Creates a DropTarget object. + * + * @exception HeadlessException If GraphicsEnvironment.isHeadless() + * returns true. + */ + public DropTarget (Component c, int i, DropTargetListener dtl, boolean b, + FlavorMap fm) + { + if (GraphicsEnvironment.isHeadless ()) + throw new HeadlessException (); + + setComponent(c); + setDefaultActions(i); + dropTargetListener = dtl; + + if (fm == null) + flavorMap = SystemFlavorMap.getDefaultFlavorMap(); + else + flavorMap = fm; + + setActive (b); + + if (c != null) + c.setDropTarget(this); + } + + /** + * Sets the component associated with this drop target object. + */ + public void setComponent (Component c) + { + if (component != null) + clearAutoscroll(); + component = c; + } + + /** + * Returns the component associated with this drop target object. + */ + public Component getComponent () + { + return component; + } + + /** + * Sets the default actions. + */ + public void setDefaultActions (int ops) + { + actions = ops; + } + + /** + * Returns the default actions. + */ + public int getDefaultActions () + { + return actions; + } + + public void setActive (boolean active) + { + this.active = active; + if (! active) + clearAutoscroll(); + } + + public boolean isActive() + { + return active; + } + + /** + * Adds a new DropTargetListener. + * + * @exception TooManyListenersException Sun's JDK does not, despite + * documentation, throw this exception here when you install an additional + * DropTargetListener. So to be compatible, we do the same + * thing. + */ + public void addDropTargetListener (DropTargetListener dtl) + throws TooManyListenersException + { + if (dtl == null) + return; + + if (dtl.equals(this)) + throw new IllegalArgumentException(); + + if (dropTargetListener != null) + throw new TooManyListenersException(); + + dropTargetListener = dtl; + } + + public void removeDropTargetListener(DropTargetListener dtl) + { + if (dropTargetListener != null) + dropTargetListener = null; + } + + public void dragEnter(DropTargetDragEvent dtde) + { + if (active) + { + if (dropTargetListener != null) + dropTargetListener.dragEnter(dtde); + initializeAutoscrolling(dtde.getLocation()); + } + } + + public void dragOver(DropTargetDragEvent dtde) + { + if (active) + { + if (dropTargetListener != null) + dropTargetListener.dragOver(dtde); + updateAutoscroll(dtde.getLocation()); + } + } + + public void dropActionChanged(DropTargetDragEvent dtde) + { + if (active) + { + if (dropTargetListener != null) + dropTargetListener.dropActionChanged(dtde); + updateAutoscroll(dtde.getLocation()); + } + } + + public void dragExit(DropTargetEvent dte) + { + if (active) + { + if (dropTargetListener != null) + dropTargetListener.dragExit(dte); + clearAutoscroll(); + } + } + + public void drop(DropTargetDropEvent dtde) + { + clearAutoscroll(); + if (dropTargetListener != null) + dropTargetListener.drop(dtde); + } + + public FlavorMap getFlavorMap() + { + return flavorMap; + } + + public void setFlavorMap(FlavorMap fm) + { + flavorMap = fm; + } + + public void addNotify(ComponentPeer p) + { + Component c = component; + while (c != null && p instanceof LightweightPeer) + { + p = c.getPeer(); + c = c.getParent(); + } + + if (p instanceof DropTargetPeer) + { + peer = ((DropTargetPeer) p); + peer.addDropTarget(this); + } + else + peer = null; + } + + public void removeNotify(ComponentPeer p) + { + ((DropTargetPeer) peer).removeDropTarget(this); + peer = null; + p = null; + } + + public DropTargetContext getDropTargetContext() + { + if (dropTargetContext == null) + dropTargetContext = createDropTargetContext (); + + return dropTargetContext; + } + + protected DropTargetContext createDropTargetContext() + { + if (dropTargetContext == null) + dropTargetContext = new DropTargetContext (this); + + return dropTargetContext; + } + + protected DropTarget.DropTargetAutoScroller createDropTargetAutoScroller + (Component c, Point p) + { + return new DropTarget.DropTargetAutoScroller (c, p); + } + + protected void initializeAutoscrolling(Point p) + { + if (component instanceof Autoscroll) // Checks for null too. + autoscroller = createDropTargetAutoScroller (component, p); + } + + protected void updateAutoscroll(Point dragCursorLocn) + { + if (autoscroller != null) + autoscroller.updateLocation(dragCursorLocn); + } + + protected void clearAutoscroll() + { + if (autoscroller != null) + { + autoscroller.stop(); + autoscroller = null; + } + } +} // class DropTarget diff --git a/libjava/classpath/java/awt/dnd/DropTargetAdapter.java b/libjava/classpath/java/awt/dnd/DropTargetAdapter.java new file mode 100644 index 000000000..13c6b9f4b --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetAdapter.java @@ -0,0 +1,100 @@ +/* DragSourceAdapter.java -- drag-and-drop listener adapter + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +/** + * This class implements DropTargetListener, and implements all methods + * with empty bodies. This allows a listener interested in implementing only + * a subset of these interfaces to extend this class and override only the + * desired methods. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class DropTargetAdapter + implements DropTargetListener +{ + /** + * Default constructor. + */ + public DropTargetAdapter() + { + } + + /** + * Called when the cursor hotspot enters a drop site which will accept the + * drag. + * + * @param e the event + */ + public void dragEnter (DropTargetDragEvent e) + { + } + + /** + * Called when the cursor hotspot moves inside of a drop site which will + * accept the drag. + * + * @param e the event + */ + public void dragOver (DropTargetDragEvent e) + { + } + + /** + * Called when the user modifies the drop gesture. This is often the case + * when additional mouse or key events are received during the drag. + * + * @param e the event + */ + public void dropActionChanged (DropTargetDragEvent e) + { + } + + /** + * Called when the cursor hotspot moves outside of a drop site which will + * accept the drag. This could also happen if the drop site is no longer + * active, or no longer accepts the drag. + * + * @param e the event + */ + public void dragExit(DropTargetEvent e) + { + } +} // class DropTargetAdapter diff --git a/libjava/classpath/java/awt/dnd/DropTargetContext.java b/libjava/classpath/java/awt/dnd/DropTargetContext.java new file mode 100644 index 000000000..4c21a6e8a --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetContext.java @@ -0,0 +1,197 @@ +/* DropTargetContext.java -- + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.peer.DropTargetContextPeer; +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.2 + */ +public class DropTargetContext implements Serializable +{ + static final long serialVersionUID = -634158968993743371L; + + protected class TransferableProxy implements Transferable + { + protected boolean isLocal; + protected Transferable transferable; + + TransferableProxy(Transferable t, boolean local) + { + this.transferable = t; + this.isLocal = local; + } + + public DataFlavor[] getTransferDataFlavors() + { + return transferable.getTransferDataFlavors(); + } + + public boolean isDataFlavorSupported(DataFlavor flavor) + { + return transferable.isDataFlavorSupported(flavor); + } + + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException + { + return transferable.getTransferData (flavor); + } + } + + private DropTarget dropTarget; + private int targetActions; + private DropTargetContextPeer dtcp; + + // package private + DropTargetContext(DropTarget dropTarget) + { + this.dropTarget = dropTarget; + } + + public DropTarget getDropTarget() + { + return dropTarget; + } + + public Component getComponent() + { + return dropTarget.getComponent(); + } + + public void addNotify(DropTargetContextPeer dtcp) + { + this.dtcp = dtcp; + } + + public void removeNotify() + { + this.dtcp = null; + } + + protected void setTargetActions(int actions) + { + targetActions = actions; + } + + protected int getTargetActions() + { + return targetActions; + } + + /** + * Signals that the drop is completed. + * + * @exception InvalidDnDOperationException If a drop is not outstanding. + */ + public void dropComplete (boolean success) + { + if (dtcp != null) + dtcp.dropComplete(success); + } + + protected void acceptDrag (int dragOperation) + { + if (dtcp != null) + dtcp.acceptDrag(dragOperation); + } + + protected void rejectDrag () + { + if (dtcp != null) + dtcp.rejectDrag(); + } + + protected void acceptDrop (int dropOperation) + { + if (dtcp != null) + dtcp.acceptDrop(dropOperation); + } + + protected void rejectDrop () + { + if (dtcp != null) + dtcp.rejectDrop(); + } + + protected DataFlavor[] getCurrentDataFlavors () + { + if (dtcp != null) + dtcp.getTransferDataFlavors(); + return null; + } + + protected List getCurrentDataFlavorsAsList () + { + return Arrays.asList(getCurrentDataFlavors ()); + } + + protected boolean isDataFlavorSupported (DataFlavor flavor) + { + return getCurrentDataFlavorsAsList().contains (flavor); + } + + /** + * Return the Transferable operandof this operation. + * + * @exception InvalidDnDOperationException If a drag is not outstanding. + */ + protected Transferable getTransferable() + throws InvalidDnDOperationException + { + // FIXME: Implement this + if (dtcp != null) + return dtcp.getTransferable(); + return null; + } + + protected Transferable createTransferableProxy(Transferable t, boolean local) + { + return new TransferableProxy(t, local); + } +} // class DropTargetContext diff --git a/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java b/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java new file mode 100644 index 000000000..21c9e2b35 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetDragEvent.java @@ -0,0 +1,152 @@ +/* DropTargetDragEvent.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.awt.Point; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.util.List; + +/** + * @since 1.2 + */ +public class DropTargetDragEvent extends DropTargetEvent +{ + /** + * Compatible with 1.2+ + */ + private static final long serialVersionUID = -8422265619058953682L; + + private final int dropAction; + private final int srcActions; + private final Point location; + + /** + * Initializes a DropTargetDragEvent. + * + * @exception IllegalArgumentException If dropAction is not one of DnDConstants, + * srcActions is not a bitwise mask of DnDConstants, or dtc is null. + * @exception NullPointerException If location is null. + */ + public DropTargetDragEvent (DropTargetContext context, Point location, + int dropAction, int srcActions) + { + super (context); + + if (location == null) + throw new NullPointerException (); + + if (context == null) + throw new IllegalArgumentException (); + + if (dropAction != DnDConstants.ACTION_NONE + && dropAction != DnDConstants.ACTION_COPY + && dropAction != DnDConstants.ACTION_MOVE + && dropAction != DnDConstants.ACTION_COPY_OR_MOVE + && dropAction != DnDConstants.ACTION_LINK + && dropAction != DnDConstants.ACTION_REFERENCE) + throw new IllegalArgumentException (); + + int srcActionsMask = DnDConstants.ACTION_NONE + | DnDConstants.ACTION_COPY + | DnDConstants.ACTION_MOVE + | DnDConstants.ACTION_COPY_OR_MOVE + | DnDConstants.ACTION_LINK + | DnDConstants.ACTION_REFERENCE; + + if (~(srcActions ^ srcActionsMask) != 0) + throw new IllegalArgumentException (); + + this.dropAction = dropAction; + this.srcActions = srcActions; + this.location = location; + } + + public void acceptDrag (int dragOperation) + { + context.acceptDrag (dragOperation); + } + + public DataFlavor[] getCurrentDataFlavors () + { + return context.getCurrentDataFlavors (); + } + + public List getCurrentDataFlavorsAsList () + { + return context.getCurrentDataFlavorsAsList (); + } + + public int getDropAction() + { + return dropAction & ((DropTargetContext) source).getTargetActions(); + } + + public Point getLocation () + { + return location; + } + + public int getSourceActions () + { + return srcActions; + } + + public boolean isDataFlavorSupported (DataFlavor df) + { + return context.isDataFlavorSupported (df); + } + + public void rejectDrag () + { + context.rejectDrag (); + } + + /** + * TODO + * + * @return + * + * @since 1.5 + */ + public Transferable getTransferable() + { + return context.getTransferable(); + } +} // class DropTargetDragEvent diff --git a/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java b/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java new file mode 100644 index 000000000..333f8c6cc --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetDropEvent.java @@ -0,0 +1,170 @@ +/* DropTargetDropEvent.java -- + Copyright (C) 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +import java.awt.Point; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.util.List; + +/** + * @since 1.2 + */ +public class DropTargetDropEvent extends DropTargetEvent +{ + /** + * Compatible with JDK 1.2+ + */ + private static final long serialVersionUID = -1721911170440459322L; + + private final int dropAction; + private final int actions; + private final Point location; + private final boolean isLocalTx; + + /** + * Initializes a DropTargetDropEvent. By default this constructor + * assumes that the target is not int same JVM. + * + * @exception IllegalArgumentException If dropAction is not one of DnDConstants, + * actions is not a bitwise mask of DnDConstants, or dtc is null. + * @exception NullPointerException If location is null. + */ + public DropTargetDropEvent(DropTargetContext dtc, Point location, + int dropAction, int actions) + { + this(dtc, location, dropAction, actions, false); + } + + /** + * Initializes a DropTargetDropEvent. + * + * @exception IllegalArgumentException If dropAction is not one of DnDConstants, + * actions is not a bitwise mask of DnDConstants, or dtc is null. + * @exception NullPointerException If location is null. + */ + public DropTargetDropEvent(DropTargetContext dtc, Point location, + int dropAction, int actions, boolean isLocalTx) + { + super(dtc); + + if (location == null) + throw new NullPointerException(); + + if (dtc == null) + throw new IllegalArgumentException(); + + if (dropAction != DnDConstants.ACTION_NONE + && dropAction != DnDConstants.ACTION_COPY + && dropAction != DnDConstants.ACTION_MOVE + && dropAction != DnDConstants.ACTION_COPY_OR_MOVE + && dropAction != DnDConstants.ACTION_LINK + && dropAction != DnDConstants.ACTION_REFERENCE) + throw new IllegalArgumentException(); + + int actionsMask = DnDConstants.ACTION_NONE + | DnDConstants.ACTION_COPY + | DnDConstants.ACTION_MOVE + | DnDConstants.ACTION_COPY_OR_MOVE + | DnDConstants.ACTION_LINK + | DnDConstants.ACTION_REFERENCE; + + if (~(actions ^ actionsMask) != 0) + throw new IllegalArgumentException(); + + this.dropAction = dropAction; + this.actions = actions; + this.location = location; + this.isLocalTx = isLocalTx; + } + + public Point getLocation() + { + return location; + } + + public DataFlavor[] getCurrentDataFlavors() + { + return context.getCurrentDataFlavors(); + } + + public List getCurrentDataFlavorsAsList() + { + return context.getCurrentDataFlavorsAsList(); + } + + public boolean isDataFlavorSupported(DataFlavor flavor) + { + return context.isDataFlavorSupported(flavor); + } + + public int getSourceActions() + { + return actions; + } + + public int getDropAction() + { + return dropAction; + } + + public Transferable getTransferable() + { + return context.getTransferable (); + } + + public void acceptDrop(int dropAction) + { + context.acceptDrop(dropAction); + } + + public void rejectDrop() + { + context.rejectDrop(); + } + + public void dropComplete(boolean success) + { + context.dropComplete(success); + } + + public boolean isLocalTransfer() + { + return isLocalTx; + } +} // class DropTargetDropEvent diff --git a/libjava/classpath/java/awt/dnd/DropTargetEvent.java b/libjava/classpath/java/awt/dnd/DropTargetEvent.java new file mode 100644 index 000000000..fc599a9e2 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetEvent.java @@ -0,0 +1,62 @@ +/* DropTarget.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +import java.util.EventObject; + +public class DropTargetEvent extends EventObject +{ + + /** + * Serialization identifier for Sun 1.5 compatability + */ + private static final long serialVersionUID = 2821229066521922993L; + + protected DropTargetContext context; + + public DropTargetEvent (DropTargetContext context) + { + super (context); + this.context = context; + } + + public DropTargetContext getDropTargetContext () + { + return context; + } +} diff --git a/libjava/classpath/java/awt/dnd/DropTargetListener.java b/libjava/classpath/java/awt/dnd/DropTargetListener.java new file mode 100644 index 000000000..ceb839bac --- /dev/null +++ b/libjava/classpath/java/awt/dnd/DropTargetListener.java @@ -0,0 +1,89 @@ +/* DropTargetListener.java -- listen to events during the drop + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +import java.util.EventListener; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.2 + * @status updated to 1.4 + */ +public interface DropTargetListener extends EventListener +{ + /** + * Called when the cursor hotspot enters a drop site which will accept the + * drag. + * + * @param e the drag source drag event + */ + void dragEnter (DropTargetDragEvent e); + + /** + * Called when the cursor hotspot moves inside of a drop site which will + * accept the drag. + * + * @param e the drag source drag event + */ + void dragOver (DropTargetDragEvent e); + + /** + * Called when the user modifies the drop gesture. This is often the case + * when additional mouse or key events are received during the drag. + * + * @param e the drag source drag event + */ + void dropActionChanged (DropTargetDragEvent e); + + /** + * Called when the cursor hotspot moves outside of a drop site which will + * accept the drag. This could also happen if the drop site is no longer + * active, or no longer accepts the drag. + * + * @param e the drag source drag event + */ + void dragExit (DropTargetEvent e); + + /** + * Called when the drag operation has terminated with a drop. + * + * @param e the drag source drag event + */ + void drop (DropTargetDropEvent e); +} // interface DropTargetListener diff --git a/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java b/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java new file mode 100644 index 000000000..4a75610bf --- /dev/null +++ b/libjava/classpath/java/awt/dnd/InvalidDnDOperationException.java @@ -0,0 +1,74 @@ +/* InvalidDnDOperationException.java -- thrown when drag-and-drop fails + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd; + +/** + * Thrown when a method in the java.awt.dnd package is unable to perform a + * requested operation, usually because the underlying DnD system is in the + * wrong state. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public class InvalidDnDOperationException extends IllegalStateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -6062568741193956678L; + + /** + * Create an exception without a message. + */ + public InvalidDnDOperationException() + { + super(); + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public InvalidDnDOperationException(String s) + { + super(s); + } +} // class InvalidDnDOperationException diff --git a/libjava/classpath/java/awt/dnd/MouseDragGestureRecognizer.java b/libjava/classpath/java/awt/dnd/MouseDragGestureRecognizer.java new file mode 100644 index 000000000..08a2ac085 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/MouseDragGestureRecognizer.java @@ -0,0 +1,131 @@ +/* MouseDragGestureRecognizer.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +/** + * @author Michael Koch (konqueror@gmx.de) + */ +public abstract class MouseDragGestureRecognizer + extends DragGestureRecognizer + implements MouseListener, MouseMotionListener +{ + /** + * Creates a MouseDragGestureRecognizer object. + */ + protected MouseDragGestureRecognizer (DragSource ds, Component c, int act, + DragGestureListener dgl) + { + super (ds, c, act, dgl); + } + + /** + * Creates a MouseDragGestureRecognizer object. + */ + protected MouseDragGestureRecognizer (DragSource ds, Component c, int act) + { + super (ds, c, act); + } + + /** + * Creates a MouseDragGestureRecognizer object. + */ + protected MouseDragGestureRecognizer (DragSource ds, Component c) + { + super (ds, c); + } + + /** + * Creates a MouseDragGestureRecognizer object. + */ + protected MouseDragGestureRecognizer (DragSource ds) + { + super (ds); + } + + protected void registerListeners () + { + component.addMouseListener (this); + component.addMouseMotionListener (this); + } + + protected void unregisterListeners () + { + component.removeMouseListener (this); + component.removeMouseMotionListener (this); + } + + public void mouseClicked (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mousePressed (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseReleased (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseEntered (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseExited (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseDragged (MouseEvent e) + { + // Do nothing in here by default. + } + + public void mouseMoved (MouseEvent e) + { + // Do nothing in here by default. + } +} // class MouseDragGestureRecognizer diff --git a/libjava/classpath/java/awt/dnd/package.html b/libjava/classpath/java/awt/dnd/package.html new file mode 100644 index 000000000..d1ae5215c --- /dev/null +++ b/libjava/classpath/java/awt/dnd/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.dnd + + +

Events and listeners for drag and drop sources and targets.

+ + + diff --git a/libjava/classpath/java/awt/dnd/peer/DragSourceContextPeer.java b/libjava/classpath/java/awt/dnd/peer/DragSourceContextPeer.java new file mode 100644 index 000000000..8c134b623 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/peer/DragSourceContextPeer.java @@ -0,0 +1,57 @@ +/* DragSourceContextPeer.java -- interface for drag-and-drop peers + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd.peer; + +import java.awt.Cursor; +import java.awt.Image; +import java.awt.Point; +import java.awt.dnd.DragSourceContext; +import java.awt.dnd.InvalidDnDOperationException; + +/** + * STUBBED + */ +public interface DragSourceContextPeer +{ + void startDrag(DragSourceContext context, Cursor c, Image i, Point p) + throws InvalidDnDOperationException; + Cursor getCursor(); + void setCursor(Cursor c) throws InvalidDnDOperationException; + void transferablesFlavorsChanged(); +} // interface DragSourceContextPeer diff --git a/libjava/classpath/java/awt/dnd/peer/DropTargetContextPeer.java b/libjava/classpath/java/awt/dnd/peer/DropTargetContextPeer.java new file mode 100644 index 000000000..2f4da5ff3 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/peer/DropTargetContextPeer.java @@ -0,0 +1,68 @@ +/* DropTargetContextPeer.java -- interface for drag-and-drop peers + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.dnd.peer; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.dnd.DropTarget; +import java.awt.dnd.InvalidDnDOperationException; + + +/** + * Used to control state of recipient protocol from the + * DropTargetListener. Occurs when a Component + * with an associated DropTarget and visible geometry is first + * intersected by a logical cursor. + * + * @author Michael Koch (konqueror@gmx.de) + */ +public interface DropTargetContextPeer +{ + void setTargetActions(int actions); + int getTargetActions(); + DropTarget getDropTarget(); + DataFlavor[] getTransferDataFlavors(); + Transferable getTransferable() throws InvalidDnDOperationException; + boolean isTransferableJVMLocal(); + void acceptDrag(int dragAction); + void rejectDrag(); + void acceptDrop(int dropAction); + void rejectDrop(); + void dropComplete(boolean success); +} diff --git a/libjava/classpath/java/awt/dnd/peer/DropTargetPeer.java b/libjava/classpath/java/awt/dnd/peer/DropTargetPeer.java new file mode 100644 index 000000000..ec17cbe4b --- /dev/null +++ b/libjava/classpath/java/awt/dnd/peer/DropTargetPeer.java @@ -0,0 +1,48 @@ +/* DropTargetPeer.java -- interface for drag-and-drop peers + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.dnd.peer; + +import java.awt.dnd.DropTarget; + +/** + */ +public interface DropTargetPeer +{ + void addDropTarget (DropTarget target); + void removeDropTarget (DropTarget target); +} // interface DropTargetContextPeer diff --git a/libjava/classpath/java/awt/dnd/peer/package.html b/libjava/classpath/java/awt/dnd/peer/package.html new file mode 100644 index 000000000..52ec19cb4 --- /dev/null +++ b/libjava/classpath/java/awt/dnd/peer/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.dnd.peer + + +

Interfaces for using native interface components.

+ + + diff --git a/libjava/classpath/java/awt/doc-files/capjoin.png b/libjava/classpath/java/awt/doc-files/capjoin.png new file mode 100644 index 000000000..555dca901 Binary files /dev/null and b/libjava/classpath/java/awt/doc-files/capjoin.png differ diff --git a/libjava/classpath/java/awt/event/AWTEventListener.java b/libjava/classpath/java/awt/event/AWTEventListener.java new file mode 100644 index 000000000..3f30df46e --- /dev/null +++ b/libjava/classpath/java/awt/event/AWTEventListener.java @@ -0,0 +1,65 @@ +/* AWTEventListener.java -- listen for all events in the AWT system + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.util.EventListener; + +/** + * This listener is for classes that need to listen to all events in the AWT + * system. In general, this should not be used except for classes like + * javax.accessibility or by event recorders. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see AWTEvent + * @see Toolkit#addAWTEventListener(AWTEventListener, long) + * @see Toolkit#removeAWTEventListener(AWTEventListener) + * @since 1.2 + * @status updated to 1.4 + */ +public interface AWTEventListener extends EventListener +{ + /** + * This method is called when any event in the AWT system is dispatched. + * + * @param event the AWTEvent that was dispatched + */ + void eventDispatched(AWTEvent event); +} // interface AWTEventListener diff --git a/libjava/classpath/java/awt/event/AWTEventListenerProxy.java b/libjava/classpath/java/awt/event/AWTEventListenerProxy.java new file mode 100644 index 000000000..55a4bfe37 --- /dev/null +++ b/libjava/classpath/java/awt/event/AWTEventListenerProxy.java @@ -0,0 +1,95 @@ +/* AWTEventListenerProxy.java -- wrapper/filter for AWTEventListener + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.util.EventListenerProxy; + +/** + * This class allows adding an AWTEventListener which only pays attention to + * a specific event mask. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Toolkit + * @see EventListenerProxy + * @since 1.4 + * @status updated to 1.4 + */ +public class AWTEventListenerProxy extends EventListenerProxy + implements AWTEventListener +{ + /** The event mask. */ + private final long mask; + + /** + * Construct an AWT Event Listener which only listens to events in the given + * mask, passing the work on to the real listener. + * + * @param eventMask the mask of events to listen to + * @param listener the wrapped listener + */ + public AWTEventListenerProxy(long eventMask, AWTEventListener listener) + { + super(listener); + mask = eventMask; + } + + /** + * Forwards events on to the delegate. + * + * @param event the to forward to the delagate listener + * + * @throws NullPointerException if the delegate this was created with is null + */ + public void eventDispatched(AWTEvent event) + { + ((AWTEventListener) getListener()).eventDispatched(event); + } + + /** + * This returns the event mask associated with this listener. + * + * @return the event mask + */ + public long getEventMask() + { + return mask; + } +} // class AWTEventListenerProxy diff --git a/libjava/classpath/java/awt/event/ActionEvent.java b/libjava/classpath/java/awt/event/ActionEvent.java new file mode 100644 index 000000000..776ab04be --- /dev/null +++ b/libjava/classpath/java/awt/event/ActionEvent.java @@ -0,0 +1,228 @@ +/* ActionEvent.java -- an action has been triggered + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.AWTEvent; +import java.awt.EventQueue; + +/** + * This event is generated when an action on a component (such as a + * button press) occurs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ActionListener + * @since 1.1 + * @status updated to 1.4 + */ +public class ActionEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7671078796273832149L; + + /** Bit mask indicating the shift key was pressed. */ + public static final int SHIFT_MASK = InputEvent.SHIFT_MASK; + + /** Bit mask indicating the control key was pressed. */ + public static final int CTRL_MASK = InputEvent.CTRL_MASK; + + /** Bit mask indicating the that meta key was pressed. */ + public static final int META_MASK = InputEvent.META_MASK; + + /** Bit mask indicating that the alt key was pressed. */ + public static final int ALT_MASK = InputEvent.ALT_MASK; + + /** The first id number in the range of action id's. */ + public static final int ACTION_FIRST = 1001; + + /** The last id number in the range of action id's. */ + public static final int ACTION_LAST = 1001; + + /** An event id indicating that an action has occurred. */ + public static final int ACTION_PERFORMED = 1001; + + /** + * A nonlocalized string that gives more specific details of the event cause. + * + * @see #getActionCommand() + * @serial the command for this event + */ + private final String actionCommand; + + /** + * The bitmask of the modifiers that were pressed during the action. + * + * @see #getModifiers() + * @serial modifiers for this event + */ + private final int modifiers; + + /** + * The timestamp of this event; usually the same as the underlying input + * event. + * + * @see #getWhen() + * @serial the timestamp of the event + * @since 1.4 + */ + private final long when; + + /** + * Initializes a new instance of ActionEvent with the + * specified source, id, and command. Note that an invalid id leads to + * unspecified results. + * + * @param source the event source + * @param id the event id + * @param command the command string for this action + * @throws IllegalArgumentException if source is null + */ + public ActionEvent(Object source, int id, String command) + { + this(source, id, command, EventQueue.getMostRecentEventTime(), 0); + } + + /** + * Initializes a new instance of ActionEvent with the + * specified source, id, command, and modifiers. Note that an invalid id + * leads to unspecified results. + * + * @param source the event source + * @param id the event id + * @param command the command string for this action + * @param modifiers the bitwise or of modifier keys down during the action + * @throws IllegalArgumentException if source is null + */ + public ActionEvent(Object source, int id, String command, int modifiers) + { + this(source, id, command, EventQueue.getMostRecentEventTime(), modifiers); + } + + /** + * Initializes a new instance of ActionEvent with the + * specified source, id, command, and modifiers, and timestamp. Note that + * an invalid id leads to unspecified results. + * + * @param source the event source + * @param id the event id + * @param command the command string for this action + * @param when the timestamp of the event + * @param modifiers the bitwise or of modifier keys down during the action + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public ActionEvent(Object source, int id, String command, long when, + int modifiers) + { + super(source, id); + actionCommand = command; + this.when = when; + this.modifiers = modifiers; + } + + /** + * Returns the command string associated with this action. + * + * @return the command string associated with this action + */ + public String getActionCommand() + { + return actionCommand; + } + + /** + * Gets the timestamp of when this action took place. Usually, this + * corresponds to the timestamp of the underlying InputEvent. + * + * @return the timestamp of this action + * @since 1.4 + */ + public long getWhen() + { + return when; + } + + /** + * Returns the keys held down during the action. This value will be a + * combination of the bit mask constants defined in this class, or 0 if no + * modifiers were pressed. + * + * @return the modifier bits + */ + public int getModifiers() + { + return modifiers; + } + + /** + * Returns a string that identifies the action event. This is in the format + * "ACTION_PERFORMED,cmd=" + getActionCommand() + ",when=" + getWhen() + * + ",modifiers=" + <modifier string>, where the modifier + * string is in the order "Meta", "Ctrl", "Alt", "Shift", "Alt Graph", and + * "Button1", separated by '+', according to the bits set in getModifiers(). + * + * @return a string identifying the event + */ + public String paramString() + { + CPStringBuilder s = new CPStringBuilder(id == ACTION_PERFORMED + ? "ACTION_PERFORMED,cmd=" + : "unknown type,cmd="); + s.append(actionCommand).append(",when=").append(when).append(",modifiers"); + int len = s.length(); + s.setLength(len + 1); + if ((modifiers & META_MASK) != 0) + s.append("+Meta"); + if ((modifiers & CTRL_MASK) != 0) + s.append("+Ctrl"); + if ((modifiers & ALT_MASK) != 0) + s.append("+Alt"); + if ((modifiers & SHIFT_MASK) != 0) + s.append("+Shift"); + if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) + s.append("+Alt Graph"); + if ((modifiers & InputEvent.BUTTON1_MASK) != 0) + s.append("+Button1"); + s.setCharAt(len, '='); + return s.toString(); + } +} // class ActionEvent diff --git a/libjava/classpath/java/awt/event/ActionListener.java b/libjava/classpath/java/awt/event/ActionListener.java new file mode 100644 index 000000000..4c302cca3 --- /dev/null +++ b/libjava/classpath/java/awt/event/ActionListener.java @@ -0,0 +1,59 @@ +/* ActionListener.java -- listens for action events + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that listen for action events. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ActionEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface ActionListener extends EventListener +{ + /** + * This method is invoked when an action occurs. + * + * @param event the ActionEvent that occurred + */ + void actionPerformed(ActionEvent event); +} diff --git a/libjava/classpath/java/awt/event/AdjustmentEvent.java b/libjava/classpath/java/awt/event/AdjustmentEvent.java new file mode 100644 index 000000000..867c577d3 --- /dev/null +++ b/libjava/classpath/java/awt/event/AdjustmentEvent.java @@ -0,0 +1,222 @@ +/* AdjustmentEvent.java -- an adjustable value was changed + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.Adjustable; + +/** + * This class represents an event that is generated when an adjustable + * value is changed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Adjustable + * @see AdjustmentListener + * @since 1.1 + * @status updated to 1.4 + */ +public class AdjustmentEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5700290645205279921L; + + /** This is the first id in the range of ids used by adjustment events. */ + public static final int ADJUSTMENT_FIRST = 601; + + /** This is the last id in the range of ids used by adjustment events. */ + public static final int ADJUSTMENT_LAST = 601; + + /** This is the id indicating an adjustment value changed. */ + public static final int ADJUSTMENT_VALUE_CHANGED = 601; + + /** Adjustment type for unit increments. */ + public static final int UNIT_INCREMENT = 1; + + /** Adjustment type for unit decrements. */ + public static final int UNIT_DECREMENT = 2; + + /** Adjustment type for block decrements. */ + public static final int BLOCK_DECREMENT = 3; + + /** Adjustment type for block increments. */ + public static final int BLOCK_INCREMENT = 4; + + /** Adjustment type for tracking adjustments. */ + public static final int TRACK = 5; + + /** + * The adjustable object that caused the event. + * + * @see #getAdjustable() + * @serial the cause + */ + private final Adjustable adjustable; + + /** + * The type of adjustment, one of {@link #UNIT_INCREMENT}, + * {@link #UNIT_DECREMENT}, {@link #BLOCK_INCREMENT}, + * {@link #BLOCK_DECREMENT}, or {@link #TRACK}. + * + * @see #getAdjustmentType() + * @serial the adjustment type + */ + private final int adjustmentType; + + /** + * The new value of the adjustable; it should be in the range of the + * adjustable cause. + * + * @see #getValue() + * @serial the adjustment value + */ + private final int value; + + /** + * True if this is in a series of multiple adjustment events. + * + * @see #getValueIsAdjusting() + * @serial true if this is not the last adjustment + * @since 1.4 + */ + private final boolean isAdjusting; + + /** + * Initializes an instance of AdjustmentEvent with the + * specified source, id, type, and value. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param type the event type, one of the constants of this class + * @param value the value of the adjustment + * @throws IllegalArgumentException if source is null + */ + public AdjustmentEvent(Adjustable source, int id, int type, int value) + { + this(source, id, type, value, false); + } + + /** + * Initializes an instance of AdjustmentEvent with the + * specified source, id, type, and value. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param type the event type, one of the constants of this class + * @param value the value of the adjustment + * @param isAdjusting if this event is in a chain of adjustments + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public AdjustmentEvent(Adjustable source, int id, int type, int value, + boolean isAdjusting) + { + super(source, id); + this.adjustmentType = type; + this.value = value; + adjustable = source; + this.isAdjusting = isAdjusting; + } + + /** + * This method returns the source of the event as an Adjustable. + * + * @return the Adjustable source of the event + */ + public Adjustable getAdjustable() + { + return adjustable; + } + + /** + * Returns the new value of the adjustable object. + * + * @return the value of the event + */ + public int getValue() + { + return value; + } + + /** + * Returns the type of the event, which will be one of + * {@link #UNIT_INCREMENT}, {@link #UNIT_DECREMENT}, + * {@link #BLOCK_INCREMENT}, {@link #BLOCK_DECREMENT}, or {@link #TRACK}. + * + * @return the type of the event + */ + public int getAdjustmentType() + { + return adjustmentType; + } + + /** + * Test if this event is part of a sequence of multiple adjustements. + * + * @return true if this is not the last adjustment + * @since 1.4 + */ + public boolean getValueIsAdjusting() + { + return isAdjusting; + } + + /** + * Returns a string that describes the event. This is in the format + * "ADJUSTMENT_VALUE_CHANGED,adjType=" + <type> + ",value=" + * + getValue() + ",isAdjusting=" + getValueIsAdjusting(), where + * type is the name of the constant returned by getAdjustmentType(). + * + * @return a string that describes the event + */ + public String paramString() + { + return (id == ADJUSTMENT_VALUE_CHANGED + ? "ADJUSTMENT_VALUE_CHANGED,adjType=" : "unknown type,adjType=") + + (adjustmentType == UNIT_INCREMENT ? "UNIT_INCREMENT,value=" + : adjustmentType == UNIT_DECREMENT ? "UNIT_DECREMENT,value=" + : adjustmentType == BLOCK_INCREMENT ? "BLOCK_INCREMENT,value=" + : adjustmentType == BLOCK_DECREMENT ? "BLOCK_DECREMENT,value=" + : adjustmentType == TRACK ? "TRACK,value=" : "unknown type,value=") + + value + ",isAdjusting=" + isAdjusting; + } +} // class AdjustmentEvent diff --git a/libjava/classpath/java/awt/event/AdjustmentListener.java b/libjava/classpath/java/awt/event/AdjustmentListener.java new file mode 100644 index 000000000..1eb2e3bcf --- /dev/null +++ b/libjava/classpath/java/awt/event/AdjustmentListener.java @@ -0,0 +1,58 @@ +/* AdjustmentListener.java -- listen for adjustment events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * Interface for classes that listen for adjustment events. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public interface AdjustmentListener extends EventListener +{ + /** + * This method is called when an adjustable value changes. + * + * @param event the AdjustmentEvent that occurred + */ + void adjustmentValueChanged(AdjustmentEvent event); +} // interface AdjustmentListener diff --git a/libjava/classpath/java/awt/event/ComponentAdapter.java b/libjava/classpath/java/awt/event/ComponentAdapter.java new file mode 100644 index 000000000..cb1c0dae9 --- /dev/null +++ b/libjava/classpath/java/awt/event/ComponentAdapter.java @@ -0,0 +1,97 @@ +/* ComponentAdapter.java -- convenience class for writing component listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements ComponentListener and implements + * all methods with empty bodies. This allows a listener interested in + * implementing only a subset of the ComponentListener + * interface to extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ComponentEvent + * @see ComponentListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ComponentAdapter implements ComponentListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public ComponentAdapter() + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentResized(ComponentEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentMoved(ComponentEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentShown(ComponentEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentHidden(ComponentEvent event) + { + } +} // class ComponentAdapter diff --git a/libjava/classpath/java/awt/event/ComponentEvent.java b/libjava/classpath/java/awt/event/ComponentEvent.java new file mode 100644 index 000000000..3fd86853b --- /dev/null +++ b/libjava/classpath/java/awt/event/ComponentEvent.java @@ -0,0 +1,142 @@ +/* ComponentEvent.java -- notification of events for components + Copyright (C) 1999, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.AWTEvent; +import java.awt.Component; + +/** + * This class is for events generated when a component is moved, resized, + * hidden, or shown. These events normally do not need to be handled by the + * application, since the AWT system automatically takes care of them. This + * is also the superclass for other events on components, but + * ComponentListeners ignore such subclasses. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ComponentAdapter + * @see ComponentListener + * @since 1.1 + * @status updated to 1.4 + */ +public class ComponentEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8101406823902992965L; + + /** This is the first id in the range of ids used by this class. */ + public static final int COMPONENT_FIRST = 100; + + /** This is the last id in the range of ids used by this class. */ + public static final int COMPONENT_LAST = 103; + + /** This id indicates that a component was moved. */ + public static final int COMPONENT_MOVED = 100; + + /** This id indicates that a component was resized. */ + public static final int COMPONENT_RESIZED = 101; + + /** This id indicates that a component was shown. */ + public static final int COMPONENT_SHOWN = 102; + + /** This id indicates that a component was hidden. */ + public static final int COMPONENT_HIDDEN = 103; + + /** + * Initializes a new instance of ComponentEvent with the + * specified source and id. Note that an invalid id leads to unspecified + * results. + * + * @param source the source of the event + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public ComponentEvent(Component source, int id) + { + super(source, id); + } + + /** + * This method returns the event source as a Component. If the + * source has subsequently been modified to a non-Component, this returns + * null. + * + * @return the event source as a Component, or null + */ + public Component getComponent() + { + return source instanceof Component ? (Component) source : null; + } + + /** + * This method returns a string identifying this event. This is the field + * name of the id type, and for COMPONENT_MOVED or COMPONENT_RESIZED, the + * new bounding box of the component. + * + * @return a string identifying this event + */ + public String paramString() + { + CPStringBuilder s = new CPStringBuilder(); + + // Unlike Sun, we don't throw NullPointerException or ClassCastException + // when source was illegally changed. + if (id == COMPONENT_MOVED) + s.append("COMPONENT_MOVED "); + else if (id == COMPONENT_RESIZED) + s.append("COMPONENT_RESIZED "); + else if (id == COMPONENT_SHOWN) + s.append("COMPONENT_SHOWN "); + else if (id == COMPONENT_HIDDEN) + s.append("COMPONENT_HIDDEN "); + else + return "unknown type"; + + s.append("(").append(getComponent().getX()).append(",") + .append(getComponent().getY()).append(" ") + .append(getComponent().getWidth()).append("x") + .append(getComponent().getHeight()).append(")"); + + return s.toString(); + } + +} // class ComponentEvent diff --git a/libjava/classpath/java/awt/event/ComponentListener.java b/libjava/classpath/java/awt/event/ComponentListener.java new file mode 100644 index 000000000..b43faaed7 --- /dev/null +++ b/libjava/classpath/java/awt/event/ComponentListener.java @@ -0,0 +1,84 @@ +/* ComponentListener.java -- receive all events for a component + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that receive all events from a component. + * Normally it is not necessary to process these events since the AWT + * handles them internally, taking all appropriate actions. To watch a subset + * of these events, use a ComponentAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ComponentAdapter + * @see ComponentEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface ComponentListener extends EventListener +{ + /** + * This method is called when the component is resized. + * + * @param event the ComponentEvent indicating the resize + */ + void componentResized(ComponentEvent event); + + /** + * This method is called when the component is moved. + * + * @param event the ComponentEvent indicating the move + */ + void componentMoved(ComponentEvent event); + + /** + * This method is called when the component is made visible. + * + * @param event the ComponentEvent indicating the visibility + */ + void componentShown(ComponentEvent event); + + /** + * This method is called when the component is hidden. + * + * @param event the ComponentEvent indicating the visibility + */ + void componentHidden(ComponentEvent event); +} // interface ComponentListener diff --git a/libjava/classpath/java/awt/event/ContainerAdapter.java b/libjava/classpath/java/awt/event/ContainerAdapter.java new file mode 100644 index 000000000..c847adfa2 --- /dev/null +++ b/libjava/classpath/java/awt/event/ContainerAdapter.java @@ -0,0 +1,79 @@ +/* ContainerAdapter.java -- convenience class for writing container listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements ContainerListener and implements + * all methods with empty bodies. This allows a listener interested in + * implementing only a subset of the ContainerListener + * interface to extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ContainerEvent + * @see ContainerListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ContainerAdapter implements ContainerListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public ContainerAdapter() + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentAdded(ContainerEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void componentRemoved(ContainerEvent event) + { + } +} // class ContainerAdapter diff --git a/libjava/classpath/java/awt/event/ContainerEvent.java b/libjava/classpath/java/awt/event/ContainerEvent.java new file mode 100644 index 000000000..3c401fe1a --- /dev/null +++ b/libjava/classpath/java/awt/event/ContainerEvent.java @@ -0,0 +1,135 @@ +/* ContainerEvent.java -- components added/removed from a container + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Component; +import java.awt.Container; + +/** + * This event is generated when a component is added or removed from a + * container. Applications do not ordinarily need to handle these events + * since the AWT system handles them internally. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ContainerAdapter + * @see ContainerListener + * @since 1.1 + * @status updated to 1.4 + */ +public class ContainerEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4114942250539772041L; + + /** This is the first id in the id range used by this class. */ + public static final int CONTAINER_FIRST = 300; + + /** This is the last id in the id range used by this class. */ + public static final int CONTAINER_LAST = 301; + + /** This id indicates a component was added to the container. */ + public static final int COMPONENT_ADDED = 300; + + /** This id indicates a component was removed from the container. */ + public static final int COMPONENT_REMOVED = 301; + + /** + * The non-null child component that was added or removed. + * + * @serial the child component that changed + */ + private final Component child; + + /** + * Initializes a new instance of ContainerEvent with the + * specified source and id. Additionally, the affected child component + * is also passed as a parameter. Note that an invalid id leads to + * unspecified results. + * + * @param source the source container of the event + * @param id the event id + * @param child the child component affected by this event + * @throws IllegalArgumentException if source is null + */ + public ContainerEvent(Component source, int id, Component child) + { + super(source, id); + this.child = child; + } + + /** + * Returns the source of this event as a Container. + * + * @return the source of the event + * @throws ClassCastException if the source is changed to a non-Container + */ + public Container getContainer() + { + return (Container) source; + } + + /** + * This method returns the child object that was added or removed from + * the container. + * + * @return the child object added or removed + */ + public Component getChild() + { + return child; + } + + /** + * This method returns a string identifying this event. It is formatted as: + * (getID() == COMPONENT_ADDED ? "COMPONENT_ADDED" + * : "COMPONENT_REMOVED") + ",child=" + getChild().getName(). + * + * @return a string identifying this event + */ + public String paramString() + { + // Unlike Sun, we don't throw NullPointerException if child is illegally + // null. + return (id == COMPONENT_ADDED ? "COMPONENT_ADDED,child=" + : id == COMPONENT_REMOVED ? "COMPONENT_REMOVED,child=" + : "unknown type,child=") + (child == null ? "" : child.getName()); + } +} // class ContainerEvent diff --git a/libjava/classpath/java/awt/event/ContainerListener.java b/libjava/classpath/java/awt/event/ContainerListener.java new file mode 100644 index 000000000..b37d43408 --- /dev/null +++ b/libjava/classpath/java/awt/event/ContainerListener.java @@ -0,0 +1,70 @@ +/* ContainerListener.java -- listen for container events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to listen for all events from + * container objects. This is normally not necessary since the AWT system + * listens for and processes these events. To watch a subset of these events, + * use a ContainerAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ContainerAdapter + * @see ContainerEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface ContainerListener extends EventListener +{ + /** + * This method is called when a component is added to the container. + * + * @param event the ContainerEvent indicating component addition + */ + void componentAdded(ContainerEvent event); + + /** + * This method is called when a component is removed from the container. + * + * @param event the ContainerEvent indicating component removal + */ + void componentRemoved(ContainerEvent event); +} // interface ContainerListener diff --git a/libjava/classpath/java/awt/event/FocusAdapter.java b/libjava/classpath/java/awt/event/FocusAdapter.java new file mode 100644 index 000000000..fb0532a3a --- /dev/null +++ b/libjava/classpath/java/awt/event/FocusAdapter.java @@ -0,0 +1,79 @@ +/* FocusAdapter.java -- convenience class for writing focus listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements FocusListener and implements all + * methods with empty bodies. This allows a listener interested in + * implementing only a subset of the FocusListener interface to + * extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see FocusEvent + * @see FocusListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class FocusAdapter implements FocusListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public FocusAdapter() + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void focusGained(FocusEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void focusLost(FocusEvent event) + { + } +} // class FocusAdapter diff --git a/libjava/classpath/java/awt/event/FocusEvent.java b/libjava/classpath/java/awt/event/FocusEvent.java new file mode 100644 index 000000000..a44284aea --- /dev/null +++ b/libjava/classpath/java/awt/event/FocusEvent.java @@ -0,0 +1,181 @@ +/* FocusEvent.java -- generated for a focus change + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Component; + +/** + * This class represents an event generated when a focus change occurs for a + * component. There are both temporary changes, such as when focus is stolen + * during a sroll then returned, and permanent changes, such as when the user + * TABs through focusable components. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see FocusAdapter + * @see FocusListener + * @since 1.1 + * @status updated to 1.4 + */ +public class FocusEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 523753786457416396L; + + /** This is the first id in the range of ids used by this class. */ + public static final int FOCUS_FIRST = 1004; + + /** This is the last id in the range of ids used by this class. */ + public static final int FOCUS_LAST = 1005; + + /** This is the event id for a focus gained event. */ + public static final int FOCUS_GAINED = 1004; + + /** This is the event id for a focus lost event. */ + public static final int FOCUS_LOST = 1005; + + /** + * Indicates whether or not the focus change is temporary. + * + * @see #isTemporary() + * @serial true if the focus change is temporary + */ + private final boolean temporary; + + /** + * The other component which is giving up or stealing focus from this + * component, if known. + * + * @see #getOppositeComponent() + * @serial the component with the opposite focus event, or null + * @since 1.4 + */ + private final Component opposite; + + /** + * Initializes a new instance of FocusEvent with the + * specified source, id, temporary status, and opposite counterpart. Note + * that an invalid id leads to unspecified results. + * + * @param source the component that is gaining or losing focus + * @param id the event id + * @param temporary true if the focus change is temporary + * @param opposite the component receiving the opposite focus event, or null + * @throws IllegalArgumentException if source is null + */ + public FocusEvent(Component source, int id, boolean temporary, + Component opposite) + { + super(source, id); + this.temporary = temporary; + this.opposite = opposite; + } + + /** + * Initializes a new instance of FocusEvent with the + * specified source, id, and temporary status. Note that an invalid id + * leads to unspecified results. + * + * @param source the component that is gaining or losing focus + * @param id the event id + * @param temporary true if the focus change is temporary + * @throws IllegalArgumentException if source is null + */ + public FocusEvent(Component source, int id, boolean temporary) + { + this(source, id, temporary, null); + } + + /** + * Initializes a new instance of FocusEvent with the + * specified source and id. Note that an invalid id leads to unspecified + * results. + * + * @param source the component that is gaining or losing focus + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public FocusEvent(Component source, int id) + { + this(source, id, false, null); + } + + /** + * This method tests whether or not the focus change is temporary or + * permanent. + * + * @return true if the focus change is temporary + */ + public boolean isTemporary() + { + return temporary; + } + + /** + * Returns the component which received the opposite focus event. If this + * component gained focus, the opposite lost focus; likewise if this + * component is giving up focus, the opposite is gaining it. If this + * information is unknown, perhaps because the opposite is a native + * application, this returns null. + * + * @return the component with the focus opposite, or null + * @since 1.4 + */ + public Component getOppositeComponent() + { + return opposite; + } + + /** + * Returns a string identifying this event. This is formatted as: + * (getID() == FOCUS_GAINED ? "FOCUS_GAINED" : "FOCUS_LOST") + * + (isTemporary() ? ",temporary," : ",permanent,") + "opposite=" + * + getOppositeComponent(). + * + * @return a string identifying this event + */ + public String paramString() + { + return (id == FOCUS_GAINED ? "FOCUS_GAINED" + : id == FOCUS_LOST ? "FOCUS_LOST" : "unknown type") + + (temporary ? ",temporary,opposite=" : ",permanent,opposite=") + + opposite; + } +} // class FocusEvent diff --git a/libjava/classpath/java/awt/event/FocusListener.java b/libjava/classpath/java/awt/event/FocusListener.java new file mode 100644 index 000000000..1f7201825 --- /dev/null +++ b/libjava/classpath/java/awt/event/FocusListener.java @@ -0,0 +1,69 @@ +/* FocusListener.java -- listen for focus changes + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to be notified of changes of + * keyboard focus for a component. To watch a subset of these events, use a + * FocusAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see FocusAdapter + * @see FocusEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface FocusListener extends EventListener +{ + /** + * This method is called when a component gains the keyboard focus. + * + * @param event the FocusEvent indicating that focus was gained + */ + void focusGained(FocusEvent event); + + /** + * This method is invoked when a component loses the keyboard focus. + * + * @param event the FocusEvent indicating that focus was lost + */ + void focusLost(FocusEvent event); +} // interface FocusListener diff --git a/libjava/classpath/java/awt/event/HierarchyBoundsAdapter.java b/libjava/classpath/java/awt/event/HierarchyBoundsAdapter.java new file mode 100644 index 000000000..340cf01ed --- /dev/null +++ b/libjava/classpath/java/awt/event/HierarchyBoundsAdapter.java @@ -0,0 +1,78 @@ +/* HierarchyBoundsAdapter.java -- convenience class for writing listeners + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.event; + +/** + * This class implements HierarchyBoundsListener and implements + * all methods with empty bodies. This allows a listener interested in + * implementing only a subset of the HierarchyBoundsListener + * interface to extend this class and override only the desired methods. + * + * @author Bryce McKinlay + * @see HierarchyBoundsListener + * @see HierarchyEvent + * @since 1.3 + * @status updated to 1.4 + */ +public abstract class HierarchyBoundsAdapter implements HierarchyBoundsListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public HierarchyBoundsAdapter() + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void ancestorMoved(HierarchyEvent event) + { + } + + /** + * Implements this method from the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void ancestorResized(HierarchyEvent event) + { + } +} diff --git a/libjava/classpath/java/awt/event/HierarchyBoundsListener.java b/libjava/classpath/java/awt/event/HierarchyBoundsListener.java new file mode 100644 index 000000000..689623744 --- /dev/null +++ b/libjava/classpath/java/awt/event/HierarchyBoundsListener.java @@ -0,0 +1,70 @@ +/* HierarchyBoundsListener.java -- listens to bounds changes of parents + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This listens for changes in an ancestors size or location. Normally it is + * not necessary to process these events since the AWT handles them + * internally, taking all appropriate actions. To watch a subset of these + * events, use a HierarchyBoundsAdapter. + * + * @author Bryce McKinlay + * @see HierarchyBoundsAdapter + * @see HierarchyEvent + * @since 1.3 + * @status updated to 1.4 + */ +public interface HierarchyBoundsListener extends EventListener +{ + /** + * Called when an ancestor component of the source is moved. + * + * @param e the event describing the ancestor's motion + */ + void ancestorMoved(HierarchyEvent e); + + /** + * Called when an ancestor component is resized. + * + * @param e the event describing the ancestor's resizing + */ + void ancestorResized(HierarchyEvent e); +} // interface HierarchyBoundsListener diff --git a/libjava/classpath/java/awt/event/HierarchyEvent.java b/libjava/classpath/java/awt/event/HierarchyEvent.java new file mode 100644 index 000000000..3237978f9 --- /dev/null +++ b/libjava/classpath/java/awt/event/HierarchyEvent.java @@ -0,0 +1,255 @@ +/* HierarchyEvent.java -- generated for a change in hierarchy + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Container; + +/** + * This class represents an event generated for an ancestor component which + * may affect this component. These events normally do not need to be handled + * by the application, since the AWT system automatically takes care of them. + * + *

There are two types of hierarchy events. The first type is handled by + * HierarchyListener, and includes addition or removal of an ancestor, or + * an ancestor changing its on-screen status (visible and/or displayble). The + * second type is handled by HierarchyBoundsListener, and includes resizing + * or moving of an ancestor. + * + * @author Bryce McKinlay + * @see HierarchyListener + * @see HierarchyBoundsAdapter + * @see HierarchyBoundsListener + * @since 1.3 + * @status updated to 1.4 + */ +public class HierarchyEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = -5337576970038043990L; + + /** This is the first id in the range of ids used by this class. */ + public static final int HIERARCHY_FIRST = 1400; + + /** This id indicates that the hierarchy tree changed. */ + public static final int HIERARCHY_CHANGED = 1400; + + /** This id indicates that an ancestor was moved. */ + public static final int ANCESTOR_MOVED = 1401; + + /** This id indicates that an ancestor was resized. */ + public static final int ANCESTOR_RESIZED = 1402; + + /** This is the last id in the range of ids used by this class. */ + public static final int HIERARCHY_LAST = 1402; + + /** This indicates that the HIERARCHY_CHANGED is a changed parent. */ + public static final int PARENT_CHANGED = 1; + + /** + * This indicates that the HIERARCHY_CHANGED is caused by a change in + * displayability. + * + * @see Component#isDisplayable() + * @see Component#addNotify() + * @see Component#removeNotify() + */ + public static final int DISPLAYABILITY_CHANGED = 2; + + /** + * This indicates that the HIERARCHY_CHANGED is a changed visibility. + * + * @see Component#isShowing() + * @see Component#addNotify() + * @see Component#removeNotify() + * @see Component#show() + * @see Component#hide() + */ + public static final int SHOWING_CHANGED = 4; + + /** + * The component at the top of the changed hierarchy. + * + * @serial the top component changed + */ + private final Component changed; + + /** + * The parent of this component, either before or after the change depending + * on the type of change. + * + * @serial the parent component changed + */ + private final Container changedParent; + + /** + * The bitmask of HIERARCHY_CHANGED event types. + * + * @serial the change flags + */ + private final long changeFlags; + + /** + * Initializes a new instance of HierarchyEvent with the + * specified parameters. Note that an invalid id leads to unspecified + * results. + * + * @param source the component whose hierarchy changed + * @param id the event id + * @param changed the top component in the tree of changed hierarchy + * @param changedParent the updated parent of this object + * @throws IllegalArgumentException if source is null + */ + public HierarchyEvent(Component source, int id, Component changed, + Container changedParent) + { + this(source, id, changed, changedParent, 0); + } + + /** + * Initializes a new instance of HierarchyEvent with the + * specified parameters. Note that an invalid id leads to unspecified + * results. + * + * @param source the component whose hierarchy changed + * @param id the event id + * @param changed the top component in the tree of changed hierarchy + * @param changedParent the updated parent of this object + * @param changeFlags the bitmask of specific HIERARCHY_CHANGED events + * @throws IllegalArgumentException if source is null + */ + public HierarchyEvent(Component source, int id, Component changed, + Container changedParent, long changeFlags) + { + super(source, id); + this.changed = changed; + this.changedParent = changedParent; + this.changeFlags = changeFlags; + } + + /** + * This method returns the event source as a Component. If the + * source has subsequently been modified to a non-Component, this returns + * null. + * + * @return the event source as a Component, or null + */ + public Component getComponent() + { + return source instanceof Component ? (Component) source : null; + } + + /** + * Returns the component at the top of the hierarchy which changed. + * + * @return the top changed component + */ + public Component getChanged() + { + return changed; + } + + /** + * Returns the parent of the component listed in getChanged(). + * If the cause of this event was Container.add, this is the + * new parent; if the cause was Container.remove, this is the + * old parent; otherwise it is the unchanged parent. + * + * @return the parent container of the changed component + */ + public Container getChangedParent() + { + return changedParent; + } + + /** + * If this is a HIERARCHY_CHANGED event, this returns a bitmask of the + * types of changes that took place. + * + * @return the bitwise or of hierarchy change types, or 0 + * @see #PARENT_CHANGED + * @see #DISPLAYABILITY_CHANGED + * @see #SHOWING_CHANGED + */ + public long getChangeFlags() + { + return changeFlags; + } + + /** + * This method returns a string identifying this event. This is the field + * name of the id type, followed by a parenthesized listing of the changed + * component and its parent container. In addition, if the type is + * HIERARCHY_CHANGED, the flags preceed the changed component, in the + * order PARENT_CHANGED, DISPLAYABILITY_CHANGED, and SHOWING_CHANGED. + * + * @return a string identifying this event + */ + public String paramString() + { + CPStringBuilder r = new CPStringBuilder(); + switch (id) + { + case HIERARCHY_CHANGED: + r.append("HIERARCHY_CHANGED ("); + if ((changeFlags & PARENT_CHANGED) != 0) + r.append("PARENT_CHANGED,"); + if ((changeFlags & DISPLAYABILITY_CHANGED) != 0) + r.append("DISPLAYABILITY_CHANGED,"); + if ((changeFlags & SHOWING_CHANGED) != 0) + r.append("SHOWING_CHANGED,"); + break; + case ANCESTOR_MOVED: + r.append("ANCESTOR_MOVED ("); + break; + case ANCESTOR_RESIZED: + r.append("ANCESTOR_RESIZED ("); + break; + default: + return "unknown type"; + } + r.append(changed).append(',').append(changedParent).append(')'); + return r.toString(); + } +} // class HierarchyEvent diff --git a/libjava/classpath/java/awt/event/HierarchyListener.java b/libjava/classpath/java/awt/event/HierarchyListener.java new file mode 100644 index 000000000..f90414b86 --- /dev/null +++ b/libjava/classpath/java/awt/event/HierarchyListener.java @@ -0,0 +1,62 @@ +/* HierarchyListener.java -- listens to changes in the component hierarchy + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This listens for changes in the hierarchy tree of components. Normally it is + * not necessary to process these events since the AWT handles them + * internally, taking all appropriate actions. + * + * @author Bryce McKinlay + * @see HierarchyEvent + * @since 1.3 + * @status updated to 1.4 + */ +public interface HierarchyListener extends EventListener +{ + /** + * Called when the hierarchy of this component changes. Use + * getChangeFlags() on the event to see what exactly changed. + * + * @param e the event describing the change + */ + void hierarchyChanged(HierarchyEvent e); +} // interface HierarchyListener diff --git a/libjava/classpath/java/awt/event/InputEvent.java b/libjava/classpath/java/awt/event/InputEvent.java new file mode 100644 index 000000000..241630ca3 --- /dev/null +++ b/libjava/classpath/java/awt/event/InputEvent.java @@ -0,0 +1,399 @@ +/* InputEvent.java -- common superclass of component input events + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.awt.EventModifier; +import gnu.java.lang.CPStringBuilder; + +import java.awt.Component; + +/** + * This is the common superclass for all component input classes. These are + * passed to listeners before the component, so that listeners can consume + * the event before it does its default behavior. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see KeyEvent + * @see KeyAdapter + * @see MouseEvent + * @see MouseAdapter + * @see MouseMotionAdapter + * @see MouseWheelEvent + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class InputEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2482525981698309786L; + + /** + * This is the bit mask which indicates the shift key is down. It is + * recommended that SHIFT_DOWN_MASK be used instead. + * + * @see #SHIFT_DOWN_MASK + */ + public static final int SHIFT_MASK = 1; + + /** + * This is the bit mask which indicates the control key is down. It is + * recommended that CTRL_DOWN_MASK be used instead. + * + * @see #CTRL_DOWN_MASK + */ + public static final int CTRL_MASK = 2; + + /** + * This is the bit mask which indicates the meta key is down. It is + * recommended that META_DOWN_MASK be used instead. + * + * @see #META_DOWN_MASK + */ + public static final int META_MASK = 4; + + /** + * This is the bit mask which indicates the alt key is down. It is + * recommended that ALT_DOWN_MASK be used instead. + * + * @see #ALT_DOWN_MASK + */ + public static final int ALT_MASK = 8; + + /** + * This is the bit mask which indicates the alt-graph modifier is in effect. + * It is recommended that ALT_GRAPH_DOWN_MASK be used instead. + * + * @see #ALT_GRAPH_DOWN_MASK + */ + public static final int ALT_GRAPH_MASK = 0x20; + + /** + * This bit mask indicates mouse button one is down. It is recommended that + * BUTTON1_DOWN_MASK be used instead. + * + * @see #BUTTON1_DOWN_MASK + */ + public static final int BUTTON1_MASK = 0x10; + + /** + * This bit mask indicates mouse button two is down. It is recommended that + * BUTTON2_DOWN_MASK be used instead. + * + * @see #BUTTON2_DOWN_MASK + */ + public static final int BUTTON2_MASK = 8; + + /** + * This bit mask indicates mouse button three is down. It is recommended + * that BUTTON3_DOWN_MASK be used instead. + * + * @see #BUTTON3_DOWN_MASK + */ + public static final int BUTTON3_MASK = 4; + + /** + * The SHIFT key extended modifier. + * + * @since 1.4 + */ + public static final int SHIFT_DOWN_MASK = 0x0040; + + /** + * The CTRL key extended modifier. + * + * @since 1.4 + */ + public static final int CTRL_DOWN_MASK = 0x0080; + + /** + * The META key extended modifier. + * + * @since 1.4 + */ + public static final int META_DOWN_MASK = 0x0100; + + /** + * The ALT key extended modifier. + * + * @since 1.4 + */ + public static final int ALT_DOWN_MASK = 0x0200; + + /** + * The mouse button1 key extended modifier. + * + * @since 1.4 + */ + public static final int BUTTON1_DOWN_MASK = 0x0400; + + /** + * The mouse button2 extended modifier. + * + * @since 1.4 + */ + public static final int BUTTON2_DOWN_MASK = 0x0800; + + /** + * The mouse button3 extended modifier. + * + * @since 1.4 + */ + public static final int BUTTON3_DOWN_MASK = 0x1000; + + /** + * The ALT_GRAPH key extended modifier. + * + * @since 1.4 + */ + public static final int ALT_GRAPH_DOWN_MASK = 0x2000; + + /** The mask to convert new to old, package visible for use in subclasses. */ + static final int CONVERT_MASK + = EventModifier.NEW_MASK & ~(BUTTON2_DOWN_MASK | BUTTON3_DOWN_MASK); + + /** + * The timestamp when this event occurred. + * + * @see #getWhen() + * @serial the timestamp + */ + private final long when; + + /** + * The old-style modifiers in effect for this event. Package visible + * for use by subclasses. The old style (bitmask 0x3f) should not be + * mixed with the new style (bitmasks 0xffffffc0). + * + * @see #getModifiers() + * @see MouseEvent + * @serial the modifier state, stored in the old style + */ + int modifiers; + + /** + * The new-style modifiers in effect for this event. Package visible + * for use by subclasses. The old style (bitmask 0x3f) should not be + * mixed with the new style (bitmasks 0xffffffc0). + * + * @see #getModifiersEx() + * @see MouseEvent + * @serial the modifier state, stored in the new style + */ + int modifiersEx; + + /** + * Initializes a new instance of InputEvent with the specified + * source, id, timestamp, and modifiers. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp when the event occurred + * @param modifiers the modifiers in effect for this event, old or new style + * @throws IllegalArgumentException if source is null + */ + InputEvent(Component source, int id, long when, int modifiers) + { + super(source, id); + this.when = when; + this.modifiers = modifiers & EventModifier.OLD_MASK; + this.modifiersEx = modifiers & EventModifier.NEW_MASK; + } + + /** + * This method tests whether or not the shift key was down during the event. + * + * @return true if the shift key is down + */ + public boolean isShiftDown() + { + return ((modifiers & SHIFT_MASK) != 0) + || ((modifiersEx & SHIFT_DOWN_MASK) != 0); + } + + /** + * This method tests whether or not the control key was down during the + * event. + * + * @return true if the control key is down + */ + public boolean isControlDown() + { + return ((modifiers & CTRL_MASK) != 0) + || ((modifiersEx & CTRL_DOWN_MASK) != 0); + } + + /** + * This method tests whether or not the meta key was down during the event. + * + * @return true if the meta key is down + */ + public boolean isMetaDown() + { + return ((modifiers & META_MASK) != 0) + || ((modifiersEx & META_DOWN_MASK) != 0); + } + + /** + * This method tests whether or not the alt key was down during the event. + * + * @return true if the alt key is down + */ + public boolean isAltDown() + { + return ((modifiers & ALT_MASK) != 0) + || ((modifiersEx & ALT_DOWN_MASK) != 0); + } + + /** + * This method tests whether or not the alt-graph modifier was in effect + * during the event. + * + * @return true if the alt-graph modifier is down + */ + public boolean isAltGraphDown() + { + return ((modifiers & ALT_GRAPH_MASK) != 0) + || ((modifiersEx & ALT_GRAPH_DOWN_MASK) != 0); + } + + /** + * This method returns the timestamp when this event occurred. + * + * @return the timestamp when this event occurred + */ + public long getWhen() + { + return when; + } + + /** + * This method returns the old-style modifiers in effect for this event. + * Note that this is ambiguous between button2 and alt, and between + * button3 and meta. Also, code which generated these modifiers tends to + * only list the modifier that just changed, even if others were down at + * the time. Consider using getModifiersEx instead. This will be a union + * of the bit masks defined in this class that are applicable to the event. + * + * @return the modifiers in effect for this event + * @see #getModifiersEx() + */ + public int getModifiers() + { + return modifiers; + } + + /** + * Returns the extended modifiers (new-style) for this event. This represents + * the state of all modal keys and mouse buttons at the time of the event, + * and does not suffer from the problems mentioned in getModifiers. + * + *

For an example of checking multiple modifiers, this code will return + * true only if SHIFT and BUTTON1 were pressed and CTRL was not: + *

+   * int onmask = InputEvent.SHIFT_DOWN_MASK | InputEvent.BUTTON1_DOWN_MASK;
+   * int offmask = InputEvent.CTRL_DOWN_MASK;
+   * return (event.getModifiersEx() & (onmask | offmask)) == onmask;
+   * 
+ * + * @return the bitwise or of all modifiers pressed during the event + * @since 1.4 + */ + public int getModifiersEx() + { + return modifiersEx; + } + + /** + * Consumes this event. A consumed event is not processed further by the AWT + * system. + */ + public void consume() + { + consumed = true; + } + + /** + * This method tests whether or not this event has been consumed. + * + * @return true if this event has been consumed + */ + public boolean isConsumed() + { + return consumed; + } + + /** + * Convert the extended modifier bitmask into a String, such as "Shift" or + * "Ctrl+Button1". + * + * XXX Sun claims this can be localized via the awt.properties file - how + * do we implement that? + * + * @param modifiers the modifiers + * @return a string representation of the modifiers in this bitmask + * @since 1.4 + */ + public static String getModifiersExText(int modifiers) + { + modifiers &= EventModifier.NEW_MASK; + if (modifiers == 0) + return ""; + CPStringBuilder s = new CPStringBuilder(); + if ((modifiers & META_DOWN_MASK) != 0) + s.append("Meta+"); + if ((modifiers & CTRL_DOWN_MASK) != 0) + s.append("Ctrl+"); + if ((modifiers & ALT_DOWN_MASK) != 0) + s.append("Alt+"); + if ((modifiers & SHIFT_DOWN_MASK) != 0) + s.append("Shift+"); + if ((modifiers & ALT_GRAPH_DOWN_MASK) != 0) + s.append("Alt Graph+"); + if ((modifiers & BUTTON1_DOWN_MASK) != 0) + s.append("Button1+"); + if ((modifiers & BUTTON2_DOWN_MASK) != 0) + s.append("Button2+"); + if ((modifiers & BUTTON3_DOWN_MASK) != 0) + s.append("Button3+"); + return s.substring(0, s.length() - 1); + } +} // class InputEvent diff --git a/libjava/classpath/java/awt/event/InputMethodEvent.java b/libjava/classpath/java/awt/event/InputMethodEvent.java new file mode 100644 index 000000000..1542bcc9f --- /dev/null +++ b/libjava/classpath/java/awt/event/InputMethodEvent.java @@ -0,0 +1,305 @@ +/* InputMethodEvent.java -- events from a text input method + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.font.TextHitInfo; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.text.AttributedCharacterIterator; + +/** + * This class is for event generated by change in a text input method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see InputMethodListener + * @since 1.2 + * @status updated to 1.4 + */ +public class InputMethodEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 4727190874778922661L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int INPUT_METHOD_FIRST = 1100; + + /** This event id indicates that the text in the input method has changed. */ + public static final int INPUT_METHOD_TEXT_CHANGED = 1100; + + /** This event id indicates that the input method curor point has changed. */ + public static final int CARET_POSITION_CHANGED = 1101; + + /** This is the last id in the range of event ids used by this class. */ + public static final int INPUT_METHOD_LAST = 1101; + + /** + * The timestamp when this event was created. + * + * @serial the timestamp + * @since 1.4 + */ + private long when; + + /** The input method text. */ + private final transient AttributedCharacterIterator text; + + /** The number of committed characters in the text. */ + private final transient int committedCharacterCount; + + /** The caret. */ + private final transient TextHitInfo caret; + + /** The most important position to be visible. */ + private final transient TextHitInfo visiblePosition; + + /** + * Initializes a new instance of InputMethodEvent with the + * specified source, id, timestamp, text, char count, caret, and visible + * position. + * + * @param source the source that generated the event + * @param id the event id + * @param when the timestamp of the event + * @param text the input text + * @param committedCharacterCount the number of committed characters + * @param caret the caret position + * @param visiblePosition the position most important to make visible + * @throws IllegalArgumentException if source is null, id is invalid, id is + * CARET_POSITION_CHANGED and text is non-null, or if + * committedCharacterCount is out of range + * @since 1.4 + */ + public InputMethodEvent(Component source, int id, long when, + AttributedCharacterIterator text, + int committedCharacterCount, TextHitInfo caret, + TextHitInfo visiblePosition) + { + super(source, id); + this.when = when; + this.text = text; + this.committedCharacterCount = committedCharacterCount; + this.caret = caret; + this.visiblePosition = visiblePosition; + if (id < INPUT_METHOD_FIRST || id > INPUT_METHOD_LAST + || (id == CARET_POSITION_CHANGED && text != null) + || committedCharacterCount < 0 + || (committedCharacterCount + > (text == null ? 0 : text.getEndIndex() - text.getBeginIndex()))) + throw new IllegalArgumentException(); + } + + /** + * Initializes a new instance of InputMethodEvent with the + * specified source, id, text, char count, caret, and visible position. + * + * @param source the source that generated the event + * @param id the event id + * @param text the input text + * @param committedCharacterCount the number of committed characters + * @param caret the caret position + * @param visiblePosition the position most important to make visible + * @throws IllegalArgumentException if source is null, id is invalid, id is + * CARET_POSITION_CHANGED and text is non-null, or if + * committedCharacterCount is out of range + * @since 1.4 + */ + public InputMethodEvent(Component source, int id, + AttributedCharacterIterator text, + int committedCharacterCount, TextHitInfo caret, + TextHitInfo visiblePosition) + { + this(source, id, EventQueue.getMostRecentEventTime(), text, + committedCharacterCount, caret, visiblePosition); + } + + /** + * Initializes a new instance of InputMethodEvent with the + * specified source, id, caret, and visible position, and with a null + * text and char count. + * + * @param source the source that generated the event + * @param id the event id + * @param caret the caret position + * @param visiblePosition the position most important to make visible + * @throws IllegalArgumentException if source is null or id is invalid + * @since 1.4 + */ + public InputMethodEvent(Component source, int id, TextHitInfo caret, + TextHitInfo visiblePosition) + { + this(source, id, EventQueue.getMostRecentEventTime(), null, 0, caret, + visiblePosition); + } + + /** + * This method returns the input method text. This can be null, + * and will always be null for CARET_POSITION_CHANGED events. + * Characters from 0 to getCommittedCharacterCount()-1 have + * been committed, the remaining characters are composed text. + * + * @return the input method text, or null + */ + public AttributedCharacterIterator getText() + { + return text; + } + + /** + * Returns the number of committed characters in the input method text. + * + * @return the number of committed characters in the input method text + */ + public int getCommittedCharacterCount() + { + return committedCharacterCount; + } + + /** + * Returns the caret position. The caret offset is relative to the composed + * text of the most recent INPUT_METHOD_TEXT_CHANGED event. + * + * @return the caret position, or null + */ + public TextHitInfo getCaret() + { + return caret; + } + + /** + * Returns the position that is most important to be visible, or null if + * such a hint is not necessary. The caret offset is relative to the composed + * text of the most recent INPUT_METHOD_TEXT_CHANGED event. + * + * @return the position that is most important to be visible + */ + public TextHitInfo getVisiblePosition() + { + return visiblePosition; + } + + /** + * This method consumes the event. A consumed event is not processed + * in the default manner by the component that generated it. + */ + public void consume() + { + consumed = true; + } + + /** + * This method tests whether or not this event has been consumed. + * + * @return true if the event has been consumed + */ + public boolean isConsumed() + { + return consumed; + } + + /** + * Return the timestamp of this event. + * + * @return the timestamp + * @since 1.4 + */ + public long getWhen() + { + return when; + } + + /** + * This method returns a string identifying the event. This contains the + * event ID, the committed and composed characters separated by '+', the + * number of committed characters, the caret, and the visible position. + * + * @return a string identifying the event + */ + public String paramString() + { + CPStringBuilder s + = new CPStringBuilder(80 + (text == null ? 0 + : text.getEndIndex() - text.getBeginIndex())); + s.append(id == INPUT_METHOD_TEXT_CHANGED ? "INPUT_METHOD_TEXT_CHANGED, " + : "CARET_POSITION_CHANGED, "); + if (text == null) + s.append("no text, 0 characters committed, caret: "); + else + { + s.append('"'); + int i = text.getBeginIndex(); + int j = committedCharacterCount; + while (--j >= 0) + s.append(text.setIndex(i++)); + s.append("\" + \""); + j = text.getEndIndex() - i; + while (--j >= 0) + s.append(text.setIndex(i++)); + s.append("\", ").append(committedCharacterCount) + .append(" characters committed, caret: "); + } + s.append(caret == null ? (Object) "no caret" : caret).append(", ") + .append(visiblePosition == null ? (Object) "no visible position" + : visiblePosition); + return s.toString(); + } + + /** + * Reads in the object from a serial stream, updating when to + * {@link EventQueue#getMostRecentEventTime()} if necessary. + * + * @param s the stream to read from + * @throws IOException if deserialization fails + * @throws ClassNotFoundException if deserialization fails + * @serialData default, except for updating when + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + if (when == 0) + when = EventQueue.getMostRecentEventTime(); + } +} // class InputMethodEvent diff --git a/libjava/classpath/java/awt/event/InputMethodListener.java b/libjava/classpath/java/awt/event/InputMethodListener.java new file mode 100644 index 000000000..e2f6a4e67 --- /dev/null +++ b/libjava/classpath/java/awt/event/InputMethodListener.java @@ -0,0 +1,70 @@ +/* InputMethodListener.java -- listen for input method events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.im.InputMethodRequests; +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive events from an input + * method. For a text component to use input methods, it must also install + * an InputMethodRequests handler. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see InputMethodEvent + * @see InputMethodRequests + * @since 1.2 + * @status updated to 1.4 + */ +public interface InputMethodListener extends EventListener +{ + /** + * This method is called when the text is changed. + * + * @param event the InputMethodEvent indicating the text change + */ + void inputMethodTextChanged(InputMethodEvent event); + + /** + * This method is called when the cursor position within the text is changed. + * + * @param event the InputMethodEvent indicating the change + */ + void caretPositionChanged(InputMethodEvent event); +} // interface InputMethodListener diff --git a/libjava/classpath/java/awt/event/InvocationEvent.java b/libjava/classpath/java/awt/event/InvocationEvent.java new file mode 100644 index 000000000..afa09de13 --- /dev/null +++ b/libjava/classpath/java/awt/event/InvocationEvent.java @@ -0,0 +1,258 @@ +/* InvocationEvent.java -- call a runnable when dispatched + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.ActiveEvent; +import java.awt.EventQueue; + +/** + * This event executes {@link Runnable#run()} of a target object when it is + * dispatched. This class is used by calls to invokeLater and + * invokeAndWait, so client code can use this fact to avoid + * writing special-casing AWTEventListener objects. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ActiveEvent + * @see EventQueue#invokeLater(Runnable) + * @see EventQueue#invokeAndWait(Runnable) + * @see AWTEventListener + * @since 1.2 + * @status updated to 1.4 + */ +public class InvocationEvent extends AWTEvent implements ActiveEvent +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 436056344909459450L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int INVOCATION_FIRST = 1200; + + /** This is the default id for this event type. */ + public static final int INVOCATION_DEFAULT = 1200; + + /** This is the last id in the range of event ids used by this class. */ + public static final int INVOCATION_LAST = 1200; + + /** + * This is the Runnable object to call when dispatched. + * + * @serial the runnable to execute + */ + protected Runnable runnable; + + /** + * This is the object to call notifyAll() on when + * the call to run() returns, or null if no + * object is to be notified. + * + * @serial the object to notify + */ + protected Object notifier; + + /** + * This variable is set to true if exceptions are caught + * and stored in a variable during the call to run(), otherwise + * exceptions are ignored and propagate up. + * + * @serial true to catch exceptions + */ + protected boolean catchExceptions; + + /** + * This is the caught exception thrown in the run() method. It + * is null if exceptions are ignored, the run method hasn't completed, or + * there were no exceptions. + * + * @serial the caught exception, if any + */ + private Exception exception; + + /** + * This is the caught Throwable thrown in the run() method. + * It is null if throwables are ignored, the run method hasn't completed, + * or there were no throwables thrown. + */ + private Throwable throwable; + + /** + * The timestamp when this event was created. + * + * @see #getWhen() + * @serial the timestamp + * @since 1.4 + */ + private final long when = EventQueue.getMostRecentEventTime(); + + /** + * Initializes a new instance of InvocationEvent with the + * specified source and runnable. + * + * @param source the source of the event + * @param runnable the Runnable object to invoke + * @throws IllegalArgumentException if source is null + */ + public InvocationEvent(Object source, Runnable runnable) + { + this(source, INVOCATION_DEFAULT, runnable, null, false); + } + + /** + * Initializes a new instance of InvocationEvent with the + * specified source, runnable, and notifier. It will also catch exceptions + * if specified. If notifier is non-null, this will call notifyAll() on + * the object when the runnable is complete. If catchExceptions is true, + * this traps any exception in the runnable, otherwise it lets the exception + * propagate up the Event Dispatch thread. + * + * @param source the source of the event + * @param runnable the Runnable object to invoke + * @param notifier the object to notify, or null + * @param catchExceptions true to catch exceptions from the runnable + */ + public InvocationEvent(Object source, Runnable runnable, Object notifier, + boolean catchExceptions) + { + this(source, INVOCATION_DEFAULT, runnable, notifier, catchExceptions); + } + + /** + * Initializes a new instance of InvocationEvent with the + * specified source, runnable, and notifier. It will also catch exceptions + * if specified. If notifier is non-null, this will call notifyAll() on + * the object when the runnable is complete. If catchExceptions is true, + * this traps any exception in the runnable, otherwise it lets the exception + * propagate up the Event Dispatch thread. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param runnable the Runnable object to invoke + * @param notifier the object to notify, or null + * @param catchExceptions true to catch exceptions from the runnable + */ + protected InvocationEvent(Object source, int id, Runnable runnable, + Object notifier, boolean catchExceptions) + { + super(source, id); + this.runnable = runnable; + this.notifier = notifier; + this.catchExceptions = catchExceptions; + } + + /** + * This method calls the run() method of the runnable, traps + * exceptions if instructed to do so, and calls notifyAll() + * on any notifier if all worked successfully. + */ + public void dispatch() + { + if (catchExceptions) + try + { + runnable.run(); + } + catch (Throwable t) + { + throwable = t; + if (t instanceof Exception) + exception = (Exception)t; + } + else + runnable.run(); + + Object o = notifier; + if (o != null) + synchronized(o) + { + o.notifyAll(); + } + } + + /** + * This method returns the exception that occurred during the execution of + * the runnable, or null if not exception was thrown or + * exceptions were not caught. + * + * @return the exception thrown by the runnable + */ + public Exception getException() + { + return exception; + } + + /** + * Returns a throwable caught while executing the Runnable's run() method. + * Null if none was thrown or if this InvocationEvent doesn't catch + * throwables. + * @return the caught Throwable + * @since 1.5 + */ + public Throwable getThrowable() + { + return throwable; + } + + /** + * Gets the timestamp of when this event was created. + * + * @return the timestamp of this event + * @since 1.4 + */ + public long getWhen() + { + return when; + } + + /** + * This method returns a string identifying this event. This is formatted as: + * "INVOCATION_DEFAULT,runnable=" + runnable + ",notifier=" + notifier + * + ",catchExceptions=" + catchExceptions + ",when=" + getWhen(). + * + * @return a string identifying this event + */ + public String paramString() + { + return (id == INVOCATION_DEFAULT ? "INVOCATION_DEFAULT,runnable=" + : "unknown type,runnable=") + runnable + ",notifier=" + notifier + + ",catchExceptions=" + catchExceptions + ",when=" + when; + } +} // class InvocationEvent diff --git a/libjava/classpath/java/awt/event/ItemEvent.java b/libjava/classpath/java/awt/event/ItemEvent.java new file mode 100644 index 000000000..467815b16 --- /dev/null +++ b/libjava/classpath/java/awt/event/ItemEvent.java @@ -0,0 +1,155 @@ +/* ItemEvent.java -- event for item state changes + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.ItemSelectable; + +/** + * This event is generated when a selection item changes state. This is an + * abstraction that distills a large number of individual mouse or keyboard + * events into a simpler "item selected" and "item deselected" events. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ItemSelectable + * @see ItemListener + * @since 1.1 + * @status updated to 1.4 + */ +public class ItemEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -608708132447206933L; + + /** This is the first id in the event id range used by this class. */ + public static final int ITEM_FIRST = 701; + + /** This is the last id in the event id range used by this class. */ + public static final int ITEM_LAST = 701; + + /** This event id indicates a state change occurred. */ + public static final int ITEM_STATE_CHANGED = 701; + + /** This type indicates that the item was selected. */ + public static final int SELECTED = 1; + + /** This type indicates that the item was deselected. */ + public static final int DESELECTED = 2; + + /** + * The item affected by this event. + * + * @serial the item of the selection + */ + private final Object item; + + /** + * The state change direction, one of {@link #SELECTED} or + * {@link #DESELECTED}. + * + * @serial the selection state + */ + private final int stateChange; + + /** + * Initializes a new instance of ItemEvent with the specified + * source, id, and state change constant. Note that an invalid id leads to + * unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param item the item affected by the state change + * @param stateChange one of {@link #SELECTED} or {@link #DESELECTED} + */ + public ItemEvent(ItemSelectable source, int id, Object item, int stateChange) + { + super(source, id); + this.item = item; + this.stateChange = stateChange; + } + + /** + * This method returns the event source as an ItemSelectable. + * + * @return the event source as an ItemSelected + * @throws ClassCastException if source is changed to a non-ItemSelectable + */ + public ItemSelectable getItemSelectable() + { + return (ItemSelectable) source; + } + + /** + * Returns the item affected by this state change. + * + * @return the item affected by this state change + */ + public Object getItem() + { + return item; + } + + /** + * Returns the type of state change, either {@link #SELECTED} or + * {@link #DESELECTED}. + * + * @return the type of state change + */ + public int getStateChange() + { + return stateChange; + } + + /** + * Returns a string identifying this event. This is in the format: + * "ITEM_STATE_CHANGED,item=" + item + ",stateChange=" + * + (getStateChange() == DESELECTED ? "DESELECTED" : "SELECTED"). + * + * @return a string identifying this event + */ + public String paramString() + { + return (id == ITEM_STATE_CHANGED ? "ITEM_STATE_CHANGED,item=" + : "unknown type,item=") + item + ",stateChange=" + + (stateChange == SELECTED ? "SELECTED" + : stateChange == DESELECTED ? "DESELECTED" : "unknown type"); + } +} // class ItemEvent diff --git a/libjava/classpath/java/awt/event/ItemListener.java b/libjava/classpath/java/awt/event/ItemListener.java new file mode 100644 index 000000000..fa5f3aad3 --- /dev/null +++ b/libjava/classpath/java/awt/event/ItemListener.java @@ -0,0 +1,62 @@ +/* ItemListener.java -- listen for item events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.ItemSelectable; +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive events when an + * item's selection state changes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ItemSelectable + * @see ItemEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface ItemListener extends EventListener +{ + /** + * This method is called when an item's state is changed. + * + * @param event the ItemEvent indicating the change + */ + void itemStateChanged(ItemEvent event); +} // interface ItemListener diff --git a/libjava/classpath/java/awt/event/KeyAdapter.java b/libjava/classpath/java/awt/event/KeyAdapter.java new file mode 100644 index 000000000..c01d61ff3 --- /dev/null +++ b/libjava/classpath/java/awt/event/KeyAdapter.java @@ -0,0 +1,88 @@ +/* KeyAdapter.java -- convenience class for writing key listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements KeyListener and implements all methods + * with empty bodies. This allows a listener interested in implementing only + * a subset of the KeyListener interface to extend this class + * and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see KeyEvent + * @see KeyListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class KeyAdapter implements KeyListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public KeyAdapter() + { + } + + /** + * Implements this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void keyTyped(KeyEvent event) + { + } + + /** + * Implements this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void keyPressed(KeyEvent event) + { + } + + /** + * Implements this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void keyReleased(KeyEvent event) + { + } +} // class KeyAdapter diff --git a/libjava/classpath/java/awt/event/KeyEvent.java b/libjava/classpath/java/awt/event/KeyEvent.java new file mode 100644 index 000000000..a2b1dc94c --- /dev/null +++ b/libjava/classpath/java/awt/event/KeyEvent.java @@ -0,0 +1,1762 @@ +/* KeyEvent.java -- event for key presses + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.awt.EventModifier; +import gnu.java.lang.CPStringBuilder; + +import java.awt.Component; +import java.io.IOException; +import java.io.ObjectInputStream; + +/** + * This event is generated when a key is pressed or released. There are two + * categories of key events: + * + *

"Key typed" events are higher-level, and have already + * compensated for modifiers and keyboard layout to generate a single Unicode + * character. It may take several key press events to generate one key typed. + * The getKeyCode method will return VK_UNDEFINED, + * and getKeyChar will return a valid Unicode character or + * CHAR_UNDEFINED. + * + *

"Key pressed" and "key released" events are lower-level, and + * are platform and keyboard dependent. They correspond to the actaul motion + * on a keyboard, and return a virtual key code which labels the key that was + * pressed. The getKeyCode method will return one of the + * VK_* constants (except VK_UNDEFINED), and the + * getKeyChar method is undefined. + * + *

Some keys do not generate key typed events, such as the F1 or HELP keys. + * Not all keyboards can generate all virtual keys, and no attempt is made to + * simulate the ones that can't be typed. Virtual keys correspond to the + * keyboard layout, so for example, VK_Q in English is VK_A in French. Also, + * there are some additional virtual keys to ease handling of actions, such + * as VK_ALL_CANDIDATES in place of ALT+VK_CONVERT. Do not rely on the value + * of the VK_* constants, except for VK_ENTER, VK_BACK_SPACE, and VK_TAB. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see KeyAdapter + * @see KeyListener + * @since 1.1 + * @status updated to 1.4 + */ +public class KeyEvent extends InputEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2352130953028126954L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int KEY_FIRST = 400; + + /** This is the last id in the range of event ids used by this class. */ + public static final int KEY_LAST = 402; + + /** + * This event id indicates a key was typed, which is a key press followed + * by a key release to generate an actual Unicode character. It may take + * several key presses to generate one key typed event, and some action + * keys have no corresponding key typed. + */ + public static final int KEY_TYPED = 400; + + /** This event id indicates a key was pressed. */ + public static final int KEY_PRESSED = 401; + + /** This event it indicates a key was released. */ + public static final int KEY_RELEASED = 402; + + /** The virtual key Enter, which will always map to '\n'. */ + public static final int VK_ENTER = '\n'; + + /** The virtual key Backspace, which will always map to '\b'. */ + public static final int VK_BACK_SPACE = '\b'; + + /** The virtual key Tab, which will always map to '\t'. */ + public static final int VK_TAB = '\t'; + + /** The virtual key Cancel. */ + public static final int VK_CANCEL = 3; + + /** The virtual key VK_CLEAR. */ + public static final int VK_CLEAR = 12; + + /** The virtual key VK_SHIFT. */ + public static final int VK_SHIFT = 16; + + /** The virtual key VK_CONTROL. */ + public static final int VK_CONTROL = 17; + + /** The virtual key VK_ALT. */ + public static final int VK_ALT = 18; + + /** The virtual key VK_PAUSE. */ + public static final int VK_PAUSE = 19; + + /** The virtual key VK_CAPS_LOCK. */ + public static final int VK_CAPS_LOCK = 20; + + /** The virtual key VK_ESCAPE. */ + public static final int VK_ESCAPE = 27; + + /** The virtual key VK_SPACE. */ + public static final int VK_SPACE = ' '; + + /** The virtual key VK_PAGE_UP. */ + public static final int VK_PAGE_UP = 33; + + /** The virtual key VK_PAGE_DOWN. */ + public static final int VK_PAGE_DOWN = 34; + + /** The virtual key VK_END. */ + public static final int VK_END = 35; + + /** The virtual key VK_HOME. */ + public static final int VK_HOME = 36; + + /** + * The virtual key for the non-numpad VK_LEFT. + * + * @see #VK_KP_LEFT + */ + public static final int VK_LEFT = 37; + + /** + * The virtual key for the non-numpad VK_UP. + * + * @see #VK_KP_UP + */ + public static final int VK_UP = 38; + + /** + * The virtual key for the non-numpad VK_RIGHT. + * + * @see #VK_KP_RIGHT + */ + public static final int VK_RIGHT = 39; + + /** + * The virtual key for the non-numpad VK_DOWN. + * + * @see #VK_KP_DOWN + */ + public static final int VK_DOWN = 40; + + /** The virtual key VK_COMMA. */ + public static final int VK_COMMA = ','; + + /** + * The virtual key VK_MINUS. + * + * @since 1.2 + */ + public static final int VK_MINUS = '-'; + + /** The virtual key VK_PERIOD. */ + public static final int VK_PERIOD = '.'; + + /** The virtual key VK_SLASH. */ + public static final int VK_SLASH = '/'; + + /** The virtual key VK_0. */ + public static final int VK_0 = '0'; + + /** The virtual key VK_1. */ + public static final int VK_1 = '1'; + + /** The virtual key VK_2. */ + public static final int VK_2 = '2'; + + /** The virtual key VK_3. */ + public static final int VK_3 = '3'; + + /** The virtual key VK_4. */ + public static final int VK_4 = '4'; + + /** The virtual key VK_5. */ + public static final int VK_5 = '5'; + + /** The virtual key VK_6. */ + public static final int VK_6 = '6'; + + /** The virtual key VK_7. */ + public static final int VK_7 = '7'; + + /** The virtual key VK_8. */ + public static final int VK_8 = '8'; + + /** The virtual key VK_9. */ + public static final int VK_9 = '9'; + + /** The virtual key VK_SEMICOLON. */ + public static final int VK_SEMICOLON = ';'; + + /** The virtual key VK_EQUALS. */ + public static final int VK_EQUALS = '='; + + /** The virtual key VK_A. */ + public static final int VK_A = 'A'; + + /** The virtual key VK_B. */ + public static final int VK_B = 'B'; + + /** The virtual key VK_C. */ + public static final int VK_C = 'C'; + + /** The virtual key VK_D. */ + public static final int VK_D = 'D'; + + /** The virtual key VK_E. */ + public static final int VK_E = 'E'; + + /** The virtual key VK_F. */ + public static final int VK_F = 'F'; + + /** The virtual key VK_G. */ + public static final int VK_G = 'G'; + + /** The virtual key VK_H. */ + public static final int VK_H = 'H'; + + /** The virtual key VK_I. */ + public static final int VK_I = 'I'; + + /** The virtual key VK_J. */ + public static final int VK_J = 'J'; + + /** The virtual key VK_K. */ + public static final int VK_K = 'K'; + + /** The virtual key VK_L. */ + public static final int VK_L = 'L'; + + /** The virtual key VK_M. */ + public static final int VK_M = 'M'; + + /** The virtual key VK_N. */ + public static final int VK_N = 'N'; + + /** The virtual key VK_O. */ + public static final int VK_O = 'O'; + + /** The virtual key VK_P. */ + public static final int VK_P = 'P'; + + /** The virtual key VK_Q. */ + public static final int VK_Q = 'Q'; + + /** The virtual key VK_R. */ + public static final int VK_R = 'R'; + + /** The virtual key VK_S. */ + public static final int VK_S = 'S'; + + /** The virtual key VK_T. */ + public static final int VK_T = 'T'; + + /** The virtual key VK_U. */ + public static final int VK_U = 'U'; + + /** The virtual key VK_V. */ + public static final int VK_V = 'V'; + + /** The virtual key VK_W. */ + public static final int VK_W = 'W'; + + /** The virtual key VK_X. */ + public static final int VK_X = 'X'; + + /** The virtual key VK_Y. */ + public static final int VK_Y = 'Y'; + + /** The virtual key VK_Z. */ + public static final int VK_Z = 'Z'; + + /** The virtual key VK_OPEN_BRACKET. */ + public static final int VK_OPEN_BRACKET = '['; + + /** The virtual key VK_BACK_SLASH. */ + public static final int VK_BACK_SLASH = '\\'; + + /** The virtual key VK_CLOSE_BRACKET. */ + public static final int VK_CLOSE_BRACKET = ']'; + + /** The virtual key VK_NUMPAD0. */ + public static final int VK_NUMPAD0 = 96; + + /** The virtual key VK_NUMPAD1. */ + public static final int VK_NUMPAD1 = 97; + + /** The virtual key VK_NUMPAD2. */ + public static final int VK_NUMPAD2 = 98; + + /** The virtual key VK_NUMPAD3. */ + public static final int VK_NUMPAD3 = 99; + + /** The virtual key VK_NUMPAD4. */ + public static final int VK_NUMPAD4 = 100; + + /** The virtual key VK_NUMPAD5. */ + public static final int VK_NUMPAD5 = 101; + + /** The virtual key VK_NUMPAD6. */ + public static final int VK_NUMPAD6 = 102; + + /** The virtual key VK_NUMPAD7. */ + public static final int VK_NUMPAD7 = 103; + + /** The virtual key VK_NUMPAD8. */ + public static final int VK_NUMPAD8 = 104; + + /** The virtual key VK_NUMPAD9. */ + public static final int VK_NUMPAD9 = 105; + + /** The virtual key VK_MULTIPLY. */ + public static final int VK_MULTIPLY = 106; + + /** The virtual key VK_ADD. */ + public static final int VK_ADD = 107; + + /** + * The virtual key VK_SEPARATOR, handily mispelled for those who can't + * figure it out. + * + * @deprecated use {@link #VK_SEPARATOR} + */ + public static final int VK_SEPARATER = 108; + + /** + * The virtual key VK_SEPARATOR. + * + * @since 1.4 + */ + public static final int VK_SEPARATOR = 108; + + /** The virtual key VK_SUBTRACT. */ + public static final int VK_SUBTRACT = 109; + + /** The virtual key VK_DECIMAL. */ + public static final int VK_DECIMAL = 110; + + /** The virtual key VK_DIVIDE. */ + public static final int VK_DIVIDE = 111; + + /** The virtual key VK_DELETE. */ + public static final int VK_DELETE = 127; + + /** The virtual key VK_NUM_LOCK. */ + public static final int VK_NUM_LOCK = 144; + + /** The virtual key VK_SCROLL_LOCK. */ + public static final int VK_SCROLL_LOCK = 145; + + /** The virtual key VK_F1. */ + public static final int VK_F1 = 112; + + /** The virtual key VK_F2. */ + public static final int VK_F2 = 113; + + /** The virtual key VK_F3. */ + public static final int VK_F3 = 114; + + /** The virtual key VK_F4. */ + public static final int VK_F4 = 115; + + /** The virtual key VK_F5. */ + public static final int VK_F5 = 116; + + /** The virtual key VK_F6. */ + public static final int VK_F6 = 117; + + /** The virtual key VK_F7. */ + public static final int VK_F7 = 118; + + /** The virtual key VK_F8. */ + public static final int VK_F8 = 119; + + /** The virtual key VK_F9. */ + public static final int VK_F9 = 120; + + /** The virtual key VK_F10. */ + public static final int VK_F10 = 121; + + /** The virtual key VK_F11. */ + public static final int VK_F11 = 122; + + /** The virtual key VK_F12. */ + public static final int VK_F12 = 123; + + /** + * The virtual key VK_F13. + * + * @since 1.2 + */ + public static final int VK_F13 = 61440; + + /** + * The virtual key VK_F14. + * + * @since 1.2 + */ + public static final int VK_F14 = 61441; + + /** + * The virtual key VK_F15. + * + * @since 1.2 + */ + public static final int VK_F15 = 61442; + + /** + * The virtual key VK_F16. + * + * @since 1.2 + */ + public static final int VK_F16 = 61443; + + /** + * The virtual key VK_F17. + * + * @since 1.2 + */ + public static final int VK_F17 = 61444; + + /** + * The virtual key VK_F18. + * + * @since 1.2 + */ + public static final int VK_F18 = 61445; + + /** + * The virtual key VK_F19. + * + * @since 1.2 + */ + public static final int VK_F19 = 61446; + + /** + * The virtual key VK_F20. + * + * @since 1.2 + */ + public static final int VK_F20 = 61447; + + /** + * The virtual key VK_F21. + * + * @since 1.2 + */ + public static final int VK_F21 = 61448; + + /** + * The virtual key VK_F22. + * + * @since 1.2 + */ + public static final int VK_F22 = 61449; + + /** + * The virtual key VK_F23. + * + * @since 1.2 + */ + public static final int VK_F23 = 61450; + + /** + * The virtual key VK_F24. + * + * @since 1.2 + */ + public static final int VK_F24 = 61451; + + /** The virtual key VK_PRINTSCREEN. */ + public static final int VK_PRINTSCREEN = 154; + + /** The virtual key VK_INSERT. */ + public static final int VK_INSERT = 155; + + /** The virtual key VK_HELP. */ + public static final int VK_HELP = 156; + + /** The virtual key VK_META. */ + public static final int VK_META = 157; + + /** The virtual key VK_BACK_QUOTE. */ + public static final int VK_BACK_QUOTE = 192; + + /** The virtual key VK_QUOTE. */ + public static final int VK_QUOTE = 222; + + /** + * The virtual key for the numpad VK_KP_UP. + * + * @see #VK_UP + * @since 1.2 + */ + public static final int VK_KP_UP = 224; + + /** + * The virtual key for the numpad VK_KP_DOWN. + * + * @see #VK_DOWN + * @since 1.2 + */ + public static final int VK_KP_DOWN = 225; + + /** + * The virtual key for the numpad VK_KP_LEFT. + * + * @see #VK_LEFT + * @since 1.2 + */ + public static final int VK_KP_LEFT = 226; + + /** + * The virtual key for the numpad VK_KP_RIGHT. + * + * @see #VK_RIGHT + * @since 1.2 + */ + public static final int VK_KP_RIGHT = 227; + + /** + * The virtual key VK_DEAD_GRAVE. + * + * @since 1.2 + */ + public static final int VK_DEAD_GRAVE = 128; + + /** + * The virtual key VK_DEAD_ACUTE. + * + * @since 1.2 + */ + public static final int VK_DEAD_ACUTE = 129; + + /** + * The virtual key VK_DEAD_CIRCUMFLEX. + * + * @since 1.2 + */ + public static final int VK_DEAD_CIRCUMFLEX = 130; + + /** + * The virtual key VK_DEAD_TILDE. + * + * @since 1.2 + */ + public static final int VK_DEAD_TILDE = 131; + + /** + * The virtual key VK_DEAD_MACRON. + * + * @since 1.2 + */ + public static final int VK_DEAD_MACRON = 132; + + /** + * The virtual key VK_DEAD_BREVE. + * + * @since 1.2 + */ + public static final int VK_DEAD_BREVE = 133; + + /** + * The virtual key VK_DEAD_ABOVEDOT. + * + * @since 1.2 + */ + public static final int VK_DEAD_ABOVEDOT = 134; + + /** + * The virtual key VK_DEAD_DIAERESIS. + * + * @since 1.2 + */ + public static final int VK_DEAD_DIAERESIS = 135; + + /** + * The virtual key VK_DEAD_ABOVERING. + * + * @since 1.2 + */ + public static final int VK_DEAD_ABOVERING = 136; + + /** + * The virtual key VK_DEAD_DOUBLEACUTE. + * + * @since 1.2 + */ + public static final int VK_DEAD_DOUBLEACUTE = 137; + + /** + * The virtual key VK_DEAD_CARON. + * + * @since 1.2 + */ + public static final int VK_DEAD_CARON = 138; + + /** + * The virtual key VK_DEAD_CEDILLA. + * + * @since 1.2 + */ + public static final int VK_DEAD_CEDILLA = 139; + + /** + * The virtual key VK_DEAD_OGONEK. + * + * @since 1.2 + */ + public static final int VK_DEAD_OGONEK = 140; + + /** + * The virtual key VK_DEAD_IOTA. + * + * @since 1.2 + */ + public static final int VK_DEAD_IOTA = 141; + + /** + * The virtual key VK_DEAD_VOICED_SOUND. + * + * @since 1.2 + */ + public static final int VK_DEAD_VOICED_SOUND = 142; + + /** + * The virtual key VK_DEAD_SEMIVOICED_SOUND. + * + * @since 1.2 + */ + public static final int VK_DEAD_SEMIVOICED_SOUND = 143; + + /** + * The virtual key VK_AMPERSAND. + * + * @since 1.2 + */ + public static final int VK_AMPERSAND = 150; + + /** + * The virtual key VK_ASTERISK. + * + * @since 1.2 + */ + public static final int VK_ASTERISK = 151; + + /** + * The virtual key VK_QUOTEDBL. + * + * @since 1.2 + */ + public static final int VK_QUOTEDBL = 152; + + /** + * The virtual key VK_LESS. + * + * @since 1.2 + */ + public static final int VK_LESS = 153; + + /** + * The virtual key VK_GREATER. + * + * @since 1.2 + */ + public static final int VK_GREATER = 160; + + /** + * The virtual key VK_BRACELEFT. + * + * @since 1.2 + */ + public static final int VK_BRACELEFT = 161; + + /** + * The virtual key VK_BRACERIGHT. + * + * @since 1.2 + */ + public static final int VK_BRACERIGHT = 162; + + /** + * The virtual key VK_AT. + * + * @since 1.2 + */ + public static final int VK_AT = 512; + + /** + * The virtual key VK_COLON. + * + * @since 1.2 + */ + public static final int VK_COLON = 513; + + /** + * The virtual key VK_CIRCUMFLEX. + * + * @since 1.2 + */ + public static final int VK_CIRCUMFLEX = 514; + + /** + * The virtual key VK_DOLLAR. + * + * @since 1.2 + */ + public static final int VK_DOLLAR = 515; + + /** + * The virtual key VK_EURO_SIGN. + * + * @since 1.2 + */ + public static final int VK_EURO_SIGN = 516; + + /** + * The virtual key VK_EXCLAMATION_MARK. + * + * @since 1.2 + */ + public static final int VK_EXCLAMATION_MARK = 517; + + /** + * The virtual key VK_INVERTED_EXCLAMATION_MARK. + * + * @since 1.2 + */ + public static final int VK_INVERTED_EXCLAMATION_MARK = 518; + + /** + * The virtual key VK_LEFT_PARENTHESIS. + * + * @since 1.2 + */ + public static final int VK_LEFT_PARENTHESIS = 519; + + /** + * The virtual key VK_NUMBER_SIGN. + * + * @since 1.2 + */ + public static final int VK_NUMBER_SIGN = 520; + + /** + * The virtual key VK_PLUS. + * + * @since 1.2 + */ + public static final int VK_PLUS = 521; + + /** + * The virtual key VK_RIGHT_PARENTHESIS. + * + * @since 1.2 + */ + public static final int VK_RIGHT_PARENTHESIS = 522; + + /** + * The virtual key VK_UNDERSCORE. + * + * @since 1.2 + */ + public static final int VK_UNDERSCORE = 523; + + /** The virtual key VK_FINAL. */ + public static final int VK_FINAL = 24; + + /** The virtual key VK_CONVERT. */ + public static final int VK_CONVERT = 28; + + /** The virtual key VK_NONCONVERT. */ + public static final int VK_NONCONVERT = 29; + + /** The virtual key VK_ACCEPT. */ + public static final int VK_ACCEPT = 30; + + /** The virtual key VK_MODECHANGE. */ + public static final int VK_MODECHANGE = 31; + + /** The virtual key VK_KANA. */ + public static final int VK_KANA = 21; + + /** The virtual key VK_KANJI. */ + public static final int VK_KANJI = 25; + + /** + * The virtual key VK_ALPHANUMERIC. + * + * @since 1.2 + */ + public static final int VK_ALPHANUMERIC = 240; + + /** + * The virtual key VK_KATAKANA. + * + * @since 1.2 + */ + public static final int VK_KATAKANA = 241; + + /** + * The virtual key VK_HIRAGANA. + * + * @since 1.2 + */ + public static final int VK_HIRAGANA = 242; + + /** + * The virtual key VK_FULL_WIDTH. + * + * @since 1.2 + */ + public static final int VK_FULL_WIDTH = 243; + + /** + * The virtual key VK_HALF_WIDTH. + * + * @since 1.2 + */ + public static final int VK_HALF_WIDTH = 244; + + /** + * The virtual key VK_ROMAN_CHARACTERS. + * + * @since 1.2 + */ + public static final int VK_ROMAN_CHARACTERS = 245; + + /** + * The virtual key VK_ALL_CANDIDATES. + * + * @since 1.2 + */ + public static final int VK_ALL_CANDIDATES = 256; + + /** + * The virtual key VK_PREVIOUS_CANDIDATE. + * + * @since 1.2 + */ + public static final int VK_PREVIOUS_CANDIDATE = 257; + + /** + * The virtual key VK_CODE_INPUT. + * + * @since 1.2 + */ + public static final int VK_CODE_INPUT = 258; + + /** + * The virtual key VK_JAPANESE_KATAKANA. + * + * @since 1.2 + */ + public static final int VK_JAPANESE_KATAKANA = 259; + + /** + * The virtual key VK_JAPANESE_HIRAGANA. + * + * @since 1.2 + */ + public static final int VK_JAPANESE_HIRAGANA = 260; + + /** + * The virtual key VK_JAPANESE_ROMAN. + * + * @since 1.2 + */ + public static final int VK_JAPANESE_ROMAN = 261; + + /** + * The virtual key VK_KANA_LOCK. + * + * @since 1.3 + */ + public static final int VK_KANA_LOCK = 262; + + /** + * The virtual key VK_INPUT_METHOD_ON_OFF. + * + * @since 1.3 + */ + public static final int VK_INPUT_METHOD_ON_OFF = 263; + + /** + * The virtual key VK_CUT. + * + * @since 1.2 + */ + public static final int VK_CUT = 65489; + + /** + * The virtual key VK_COPY. + * + * @since 1.2 + */ + public static final int VK_COPY = 65485; + + /** + * The virtual key VK_PASTE. + * + * @since 1.2 + */ + public static final int VK_PASTE = 65487; + + /** + * The virtual key VK_UNDO. + * + * @since 1.2 + */ + public static final int VK_UNDO = 65483; + + /** + * The virtual key VK_AGAIN. + * + * @since 1.2 + */ + public static final int VK_AGAIN = 65481; + + /** + * The virtual key VK_FIND. + * + * @since 1.2 + */ + public static final int VK_FIND = 65488; + + /** + * The virtual key VK_PROPS. + * + * @since 1.2 + */ + public static final int VK_PROPS = 65482; + + /** + * The virtual key VK_STOP. + * + * @since 1.2 + */ + public static final int VK_STOP = 65480; + + /** + * The virtual key VK_COMPOSE. + * + * @since 1.2 + */ + public static final int VK_COMPOSE = 65312; + + /** + * The virtual key VK_ALT_GRAPH. + * + * @since 1.2 + */ + public static final int VK_ALT_GRAPH = 65406; + + /** + * The 'begin' key VK_BEGIN + * + * @since 1.5 + */ + public static final int VK_BEGIN = 65368; + + /** + * The context-menu key VK_CONTEXT_MENU + * + * @since 1.5 + */ + public static final int VK_CONTEXT_MENU = 525; + + /** + * The 'Windows' key VK_WINDOWS + * + * @since 1.5 + */ + public static final int VK_WINDOWS = 524; + + /** + * The virtual key VK_UNDEFINED. This is used for key typed events, which + * do not have a virtual key. + */ + public static final int VK_UNDEFINED = 0; + + /** + * The only char with no valid Unicode interpretation. This is used for + * key pressed and key released events which do not have a valid keyChar. + */ + public static final char CHAR_UNDEFINED = '\uffff'; + + /** + * Indicates unknown or irrelavent key location. This is also used for + * key typed events, which do not need a location. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_UNKNOWN = 0; + + /** + * Indicates a standard key location, with no left/right variants and not + * on the numeric pad. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_STANDARD = 1; + + /** + * Indicates the key is on the left side of the keyboard, such as the left + * shift. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_LEFT = 2; + + /** + * Indicates the key is on the right side of the keyboard, such as the right + * shift. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_RIGHT = 3; + + /** + * Indicates the key is on the numeric pad, such as the numpad 0. + * + * @since 1.4 + */ + public static final int KEY_LOCATION_NUMPAD = 4; + + /** + * The code assigned to the physical keyboard location (as adjusted by the + * keyboard layout). Use the symbolic VK_* names instead of numbers. + * + * @see #getKeyCode() + * @serial the VK_ code for this key + */ + private int keyCode; + + /** + * The Unicode character produced by the key type event. This has no meaning + * for key pressed and key released events. + * + * @see #getKeyChar() + * @serial the Unicode value for this key + */ + private char keyChar; + + /** + * The keyboard location of the key. One of {@link #KEY_LOCATION_UNKNOWN}, + * {@link #KEY_LOCATION_STANDARD}, {@link #KEY_LOCATION_LEFT}, + * {@link #KEY_LOCATION_RIGHT}, or {@link #KEY_LOCATION_NUMPAD}. + * + * @see #getKeyLocation() + * @serial the key location + * @since 1.4 + */ + private final int keyLocation; + + /** + * Stores the state of the native event dispatching system, to correctly + * dispatch in Component#dispatchEventImpl when a proxy is active. + * + * XXX Does this matter in Classpath? + * + * @serial whether the proxy is active + */ + private boolean isProxyActive; + + + /** + * Initializes a new instance of KeyEvent with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the component that generated this event + * @param id the event id + * @param when the timestamp when the even occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param keyCode the integer constant for the virtual key type + * @param keyChar the Unicode value of the key + * @param keyLocation the location of the key + * @throws IllegalArgumentException if source is null, if keyLocation is + * invalid, or if (id == KEY_TYPED && (keyCode != VK_UNDEFINED + * || keyChar == CHAR_UNDEFINED)) + */ + public KeyEvent(Component source, int id, long when, int modifiers, + int keyCode, char keyChar, int keyLocation) + { + super(source, id, when, modifiers); + this.keyCode = keyCode; + this.keyChar = keyChar; + this.keyLocation = keyLocation; + if ((id == KEY_TYPED && (keyCode != VK_UNDEFINED + || keyChar == CHAR_UNDEFINED)) + || keyLocation < KEY_LOCATION_UNKNOWN + || keyLocation > KEY_LOCATION_NUMPAD) + throw new IllegalArgumentException(); + } + + /** + * Initializes a new instance of KeyEvent with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the component that generated this event + * @param id the event id + * @param when the timestamp when the even occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param keyCode the integer constant for the virtual key type + * @param keyChar the Unicode value of the key + * @throws IllegalArgumentException if source is null, or if + * (id == KEY_TYPED && (keyCode != VK_UNDEFINED + * || keyChar == CHAR_UNDEFINED)) + */ + public KeyEvent(Component source, int id, long when, int modifiers, + int keyCode, char keyChar) + { + this(source, id, when, modifiers, keyCode, keyChar, KEY_LOCATION_UNKNOWN); + } + + /** + * Initializes a new instance of KeyEvent with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the component that generated this event + * @param id the event id + * @param when the timestamp when the even occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param keyCode the integer constant for the virtual key type + * @throws IllegalArgumentException if source is null, or if + * id == KEY_TYPED but keyCode != VK_UNDEFINED + * + * @deprecated + */ + public KeyEvent(Component source, int id, long when, int modifiers, + int keyCode) + { + this(source, id, when, modifiers, keyCode, '\0', KEY_LOCATION_UNKNOWN); + } + + /** + * Returns the key code for the event key. This will be one of the + * VK_* constants defined in this class. If the event type is + * KEY_TYPED, the result will be VK_UNDEFINED. + * + * @return the key code for this event + */ + public int getKeyCode() + { + return keyCode; + } + + /** + * Sets the key code for this event. This must be one of the + * VK_* constants defined in this class. + * + * @param keyCode the new key code for this event + */ + public void setKeyCode(int keyCode) + { + this.keyCode = keyCode; + } + + /** + * Returns the Unicode value for the event key. This will be + * CHAR_UNDEFINED if there is no Unicode equivalent for + * this key, usually when this is a KEY_PRESSED or KEY_RELEASED event. + * + * @return the Unicode character for this event + */ + public char getKeyChar() + { + return keyChar; + } + + /** + * Sets the Unicode character for this event to the specified value. + * + * @param keyChar the new Unicode character for this event + */ + public void setKeyChar(char keyChar) + { + this.keyChar = keyChar; + } + + /** + * Sets the modifier keys to the specified value. This should be a union + * of the bit mask constants from InputEvent. The use of this + * method is not recommended, particularly for KEY_TYPED events, which do + * not check if the modifiers were changed. + * + * @param modifiers the new modifier value, in either old or new style + * @see InputEvent + * + * @deprecated + */ + public void setModifiers(int modifiers) + { + this.modifiers = EventModifier.extend(modifiers); + } + + /** + * Returns the keyboard location of the key that generated this event. This + * provides a way to distinguish between keys like left and right shift + * which share a common key code. The result will be one of + * {@link #KEY_LOCATION_UNKNOWN}, {@link #KEY_LOCATION_STANDARD}, + * {@link #KEY_LOCATION_LEFT}, {@link #KEY_LOCATION_RIGHT}, or + * {@link #KEY_LOCATION_NUMPAD}. + * + * @return the key location + * @since 1.4 + */ + public int getKeyLocation() + { + return keyLocation; + } + + /** + * Returns the text name of key code, such as "HOME", "F1", or "A". + * + * XXX Sun claims this can be localized via the awt.properties file - how + * do we implement that? + * + * @return the text name of the key code + */ + public static String getKeyText(int keyCode) + { + switch (keyCode) + { + case VK_CANCEL: + return "Cancel"; + case VK_BACK_SPACE: + return "Backspace"; + case VK_TAB: + return "Tab"; + case VK_ENTER: + return "Enter"; + case VK_CLEAR: + return "Clear"; + case VK_SHIFT: + return "Shift"; + case VK_CONTROL: + return "Ctrl"; + case VK_ALT: + return "Alt"; + case VK_PAUSE: + return "Pause"; + case VK_CAPS_LOCK: + return "Caps Lock"; + case VK_KANA: + return "Kana"; + case VK_FINAL: + return "Final"; + case VK_KANJI: + return "Kanji"; + case VK_ESCAPE: + return "Escape"; + case VK_CONVERT: + return "Convert"; + case VK_NONCONVERT: + return "No Convert"; + case VK_ACCEPT: + return "Accept"; + case VK_MODECHANGE: + return "Mode Change"; + case VK_SPACE: + return "Space"; + case VK_PAGE_UP: + return "Page Up"; + case VK_PAGE_DOWN: + return "Page Down"; + case VK_END: + return "End"; + case VK_HOME: + return "Home"; + case VK_LEFT: + case VK_KP_LEFT: + return "Left"; + case VK_UP: + case VK_KP_UP: + return "Up"; + case VK_RIGHT: + case VK_KP_RIGHT: + return "Right"; + case VK_DOWN: + case VK_KP_DOWN: + return "Down"; + case VK_MINUS: + return "Minus"; + case VK_MULTIPLY: + return "NumPad *"; + case VK_ADD: + return "NumPad +"; + case VK_SEPARATOR: + return "NumPad ,"; + case VK_SUBTRACT: + return "NumPad -"; + case VK_DECIMAL: + return "NumPad ."; + case VK_DIVIDE: + return "NumPad /"; + case VK_DELETE: + return "Delete"; + case VK_DEAD_GRAVE: + return "Dead Grave"; + case VK_DEAD_ACUTE: + return "Dead Acute"; + case VK_DEAD_CIRCUMFLEX: + return "Dead Circumflex"; + case VK_DEAD_TILDE: + return "Dead Tilde"; + case VK_DEAD_MACRON: + return "Dead Macron"; + case VK_DEAD_BREVE: + return "Dead Breve"; + case VK_DEAD_ABOVEDOT: + return "Dead Above Dot"; + case VK_DEAD_DIAERESIS: + return "Dead Diaeresis"; + case VK_DEAD_ABOVERING: + return "Dead Above Ring"; + case VK_DEAD_DOUBLEACUTE: + return "Dead Double Acute"; + case VK_DEAD_CARON: + return "Dead Caron"; + case VK_DEAD_CEDILLA: + return "Dead Cedilla"; + case VK_DEAD_OGONEK: + return "Dead Ogonek"; + case VK_DEAD_IOTA: + return "Dead Iota"; + case VK_DEAD_VOICED_SOUND: + return "Dead Voiced Sound"; + case VK_DEAD_SEMIVOICED_SOUND: + return "Dead Semivoiced Sound"; + case VK_NUM_LOCK: + return "Num Lock"; + case VK_SCROLL_LOCK: + return "Scroll Lock"; + case VK_AMPERSAND: + return "Ampersand"; + case VK_ASTERISK: + return "Asterisk"; + case VK_QUOTEDBL: + return "Double Quote"; + case VK_LESS: + return "Less"; + case VK_PRINTSCREEN: + return "Print Screen"; + case VK_INSERT: + return "Insert"; + case VK_HELP: + return "Help"; + case VK_META: + return "Meta"; + case VK_GREATER: + return "Greater"; + case VK_BRACELEFT: + return "Left Brace"; + case VK_BRACERIGHT: + return "Right Brace"; + case VK_BACK_QUOTE: + return "Back Quote"; + case VK_QUOTE: + return "Quote"; + case VK_ALPHANUMERIC: + return "Alphanumeric"; + case VK_KATAKANA: + return "Katakana"; + case VK_HIRAGANA: + return "Hiragana"; + case VK_FULL_WIDTH: + return "Full-Width"; + case VK_HALF_WIDTH: + return "Half-Width"; + case VK_ROMAN_CHARACTERS: + return "Roman Characters"; + case VK_ALL_CANDIDATES: + return "All Candidates"; + case VK_PREVIOUS_CANDIDATE: + return "Previous Candidate"; + case VK_CODE_INPUT: + return "Code Input"; + case VK_JAPANESE_KATAKANA: + return "Japanese Katakana"; + case VK_JAPANESE_HIRAGANA: + return "Japanese Hiragana"; + case VK_JAPANESE_ROMAN: + return "Japanese Roman"; + case VK_KANA_LOCK: + return "Kana Lock"; + case VK_INPUT_METHOD_ON_OFF: + return "Input Method On/Off"; + case VK_AT: + return "At"; + case VK_COLON: + return "Colon"; + case VK_CIRCUMFLEX: + return "Circumflex"; + case VK_DOLLAR: + return "Dollar"; + case VK_EURO_SIGN: + return "Euro"; + case VK_EXCLAMATION_MARK: + return "Exclamation Mark"; + case VK_INVERTED_EXCLAMATION_MARK: + return "Inverted Exclamation Mark"; + case VK_LEFT_PARENTHESIS: + return "Left Parenthesis"; + case VK_NUMBER_SIGN: + return "Number Sign"; + case VK_PLUS: + return "Plus"; + case VK_RIGHT_PARENTHESIS: + return "Right Parenthesis"; + case VK_UNDERSCORE: + return "Underscore"; + case VK_COMPOSE: + return "Compose"; + case VK_ALT_GRAPH: + return "Alt Graph"; + case VK_STOP: + return "Stop"; + case VK_AGAIN: + return "Again"; + case VK_PROPS: + return "Props"; + case VK_UNDO: + return "Undo"; + case VK_COPY: + return "Copy"; + case VK_PASTE: + return "Paste"; + case VK_FIND: + return "Find"; + case VK_CUT: + return "Cut"; + case VK_COMMA: + case VK_PERIOD: + case VK_SLASH: + case VK_0: + case VK_1: + case VK_2: + case VK_3: + case VK_4: + case VK_5: + case VK_6: + case VK_7: + case VK_8: + case VK_9: + case VK_SEMICOLON: + case VK_EQUALS: + case VK_A: + case VK_B: + case VK_C: + case VK_D: + case VK_E: + case VK_F: + case VK_G: + case VK_H: + case VK_I: + case VK_J: + case VK_K: + case VK_L: + case VK_M: + case VK_N: + case VK_O: + case VK_P: + case VK_Q: + case VK_R: + case VK_S: + case VK_T: + case VK_U: + case VK_V: + case VK_W: + case VK_X: + case VK_Y: + case VK_Z: + case VK_OPEN_BRACKET: + case VK_BACK_SLASH: + case VK_CLOSE_BRACKET: + return "" + (char) keyCode; + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + return "NumPad-" + (keyCode - VK_NUMPAD0); + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + case VK_F11: + case VK_F12: + return "F" + (keyCode - (VK_F1 - 1)); + case VK_F13: + case VK_F14: + case VK_F15: + case VK_F16: + case VK_F17: + case VK_F18: + case VK_F19: + case VK_F20: + case VK_F21: + case VK_F22: + case VK_F23: + case VK_F24: + return "F" + (keyCode - (VK_F13 - 13)); + default: + // This is funky on negative numbers, but that's Sun's fault. + return "Unknown keyCode: 0x" + (keyCode < 0 ? "-" : "") + + Integer.toHexString(Math.abs(keyCode)); + } + } + + /** + * Returns a string describing the modifiers, such as "Shift" or + * "Ctrl+Button1". + * + * XXX Sun claims this can be localized via the awt.properties file - how + * do we implement that? + * + * @param modifiers the old-style modifiers to convert to text + * @return a string representation of the modifiers in this bitmask + */ + public static String getKeyModifiersText(int modifiers) + { + return getModifiersExText(EventModifier.extend(modifiers + & EventModifier.OLD_MASK)); + } + + /** + * Tests whether or not this key is an action key. An action key typically + * does not fire a KEY_TYPED event, and is not a modifier. + * + * @return true if this is an action key + */ + public boolean isActionKey() + { + switch (keyCode) + { + case VK_PAUSE: + case VK_CAPS_LOCK: + case VK_KANA: + case VK_FINAL: + case VK_KANJI: + case VK_CONVERT: + case VK_NONCONVERT: + case VK_ACCEPT: + case VK_MODECHANGE: + case VK_PAGE_UP: + case VK_PAGE_DOWN: + case VK_END: + case VK_HOME: + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + case VK_F1: + case VK_F2: + case VK_F3: + case VK_F4: + case VK_F5: + case VK_F6: + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + case VK_F11: + case VK_F12: + case VK_NUM_LOCK: + case VK_SCROLL_LOCK: + case VK_PRINTSCREEN: + case VK_INSERT: + case VK_HELP: + case VK_KP_UP: + case VK_KP_DOWN: + case VK_KP_LEFT: + case VK_KP_RIGHT: + case VK_ALPHANUMERIC: + case VK_KATAKANA: + case VK_HIRAGANA: + case VK_FULL_WIDTH: + case VK_HALF_WIDTH: + case VK_ROMAN_CHARACTERS: + case VK_ALL_CANDIDATES: + case VK_PREVIOUS_CANDIDATE: + case VK_CODE_INPUT: + case VK_JAPANESE_KATAKANA: + case VK_JAPANESE_HIRAGANA: + case VK_JAPANESE_ROMAN: + case VK_KANA_LOCK: + case VK_INPUT_METHOD_ON_OFF: + case VK_F13: + case VK_F14: + case VK_F15: + case VK_F16: + case VK_F17: + case VK_F18: + case VK_F19: + case VK_F20: + case VK_F21: + case VK_F22: + case VK_F23: + case VK_F24: + case VK_STOP: + case VK_AGAIN: + case VK_PROPS: + case VK_UNDO: + case VK_COPY: + case VK_PASTE: + case VK_FIND: + case VK_CUT: + return true; + default: + return false; + } + } + + /** + * Returns a string identifying the event. This is formatted as the + * field name of the id type, followed by the keyCode, then the + * keyChar, modifiers (if any), extModifiers (if any), and + * keyLocation. + * + * @return a string identifying the event + */ + public String paramString() + { + CPStringBuilder s = new CPStringBuilder(); + + switch (id) + { + case KEY_PRESSED: + s.append("KEY_PRESSED"); + break; + case KEY_RELEASED: + s.append("KEY_RELEASED"); + break; + case KEY_TYPED: + s.append("KEY_TYPED"); + break; + default: + s.append("unknown type"); + } + + s.append(",keyCode=").append(keyCode); + + s.append(",keyText=").append(getKeyText(keyCode)); + + s.append(",keyChar="); + if (isActionKey() + || keyCode == VK_SHIFT + || keyCode == VK_CONTROL + || keyCode == VK_ALT) + s.append("Undefined keyChar"); + else + { + /* This output string must be selected by examining keyChar + * rather than keyCode, because key code information is not + * included in KEY_TYPED events. + */ + if (keyChar == VK_BACK_SPACE + || keyChar == VK_TAB + || keyChar == VK_ENTER + || keyChar == VK_ESCAPE + || keyChar == VK_DELETE) + s.append(getKeyText(keyChar)); + else + s.append("'").append(keyChar).append("'"); + } + + if ((modifiers & CONVERT_MASK) != 0) + s.append(",modifiers=").append(getModifiersExText(modifiers + & CONVERT_MASK)); + if (modifiers != 0) + s.append(",extModifiers=").append(getModifiersExText(modifiers)); + + s.append(",keyLocation=KEY_LOCATION_"); + switch (keyLocation) + { + case KEY_LOCATION_UNKNOWN: + s.append("UNKNOWN"); + break; + case KEY_LOCATION_STANDARD: + s.append("STANDARD"); + break; + case KEY_LOCATION_LEFT: + s.append("LEFT"); + break; + case KEY_LOCATION_RIGHT: + s.append("RIGHT"); + break; + case KEY_LOCATION_NUMPAD: + s.append("NUMPAD"); + } + + return s.toString(); + } + + /** + * Reads in the object from a serial stream. + * + * @param s the stream to read from + * @throws IOException if deserialization fails + * @throws ClassNotFoundException if deserialization fails + * @serialData default, except that the modifiers are converted to new style + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + modifiersEx = EventModifier.extend(modifiers) & EventModifier.NEW_MASK; + } +} // class KeyEvent diff --git a/libjava/classpath/java/awt/event/KeyListener.java b/libjava/classpath/java/awt/event/KeyListener.java new file mode 100644 index 000000000..5c0a640f6 --- /dev/null +++ b/libjava/classpath/java/awt/event/KeyListener.java @@ -0,0 +1,77 @@ +/* KeyListener.java -- listen for keyboard presses + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive keyboard events. To + * watch a subset of these events, use a KeyAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see KeyAdapter + * @see KeyEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface KeyListener extends EventListener +{ + /** + * This method is called when a key is typed. A key is considered typed + * when it and all modifiers have been pressed and released, mapping to + * a single virtual key. + * + * @param event the KeyEvent indicating that a key was typed + */ + void keyTyped(KeyEvent event); + + /** + * This method is called when a key is pressed. + * + * @param event the KeyEvent indicating the key press + */ + void keyPressed(KeyEvent event); + + /** + * This method is called when a key is released. + * + * @param event the KeyEvent indicating the key release + */ + void keyReleased(KeyEvent event); +} // interface KeyListener diff --git a/libjava/classpath/java/awt/event/MouseAdapter.java b/libjava/classpath/java/awt/event/MouseAdapter.java new file mode 100644 index 000000000..9f40c285a --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseAdapter.java @@ -0,0 +1,106 @@ +/* MouseAdapter.java -- convenience class for writing mouse listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements MouseListener and implements all methods + * with empty bodies. This allows a listener interested in implementing only + * a subset of the MouseListener interface to extend this class + * and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see MouseEvent + * @see MouseListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class MouseAdapter implements MouseListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public MouseAdapter() + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mouseClicked(MouseEvent event) + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mousePressed(MouseEvent event) + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mouseReleased(MouseEvent event) + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mouseEntered(MouseEvent event) + { + } + + /** + * Implements this method in the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void mouseExited(MouseEvent event) + { + } +} // class MouseAdapter diff --git a/libjava/classpath/java/awt/event/MouseEvent.java b/libjava/classpath/java/awt/event/MouseEvent.java new file mode 100644 index 000000000..64142ecc6 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseEvent.java @@ -0,0 +1,502 @@ +/* MouseEvent.java -- a mouse event + Copyright (C) 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.awt.EventModifier; +import gnu.java.lang.CPStringBuilder; + +import java.awt.Component; +import java.awt.Point; +import java.awt.PopupMenu; +import java.io.IOException; +import java.io.ObjectInputStream; + +/** + * This event is generated for a mouse event. There are three main categories + * of mouse events: Regular events include pressing, releasing, and clicking + * buttons, as well as moving over the boundary of the unobscured portion of + * a component. Motion events include movement and dragging. Wheel events are + * covered separately by the subclass MouseWheelEvent. + * + *

A mouse event is tied to the unobstructed visible component that the + * mouse cursor was over at the time of the action. The button that was + * most recently pressed is the only one that shows up in + * getModifiers, and is returned by getButton, + * while all buttons that are down show up in getModifiersEx. + * + *

Drag events may be cut short if native drag-and-drop operations steal + * the event. Likewise, if a mouse drag exceeds the bounds of a window or + * virtual device, some platforms may clip the path to fit in the bounds of + * the component. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see MouseAdapter + * @see MouseListener + * @see MouseMotionAdapter + * @see MouseMotionListener + * @see MouseWheelListener + * @since 1.1 + * @status updated to 1.4 + */ +public class MouseEvent extends InputEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -991214153494842848L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int MOUSE_FIRST = 500; + + /** This is the last id in the range of event ids used by this class. */ + public static final int MOUSE_LAST = 507; + + /** This event id indicates that the mouse was clicked. */ + public static final int MOUSE_CLICKED = 500; + + /** This event id indicates that the mouse was pressed. */ + public static final int MOUSE_PRESSED = 501; + + /** This event id indicates that the mouse was released. */ + public static final int MOUSE_RELEASED = 502; + + /** This event id indicates that the mouse was moved. */ + public static final int MOUSE_MOVED = 503; + + /** This event id indicates that the mouse entered a component. */ + public static final int MOUSE_ENTERED = 504; + + /** This event id indicates that the mouse exited a component. */ + public static final int MOUSE_EXITED = 505; + + /** + * This indicates that no button changed state. + * + * @see #getButton() + * @since 1.4 + */ + public static final int NOBUTTON = 0; + + /** + * This indicates that button 1 changed state. + * + * @see #getButton() + * @since 1.4 + */ + public static final int BUTTON1 = 1; + + /** + * This indicates that button 2 changed state. + * + * @see #getButton() + * @since 1.4 + */ + public static final int BUTTON2 = 2; + + /** + * This indicates that button 3 changed state. + * + * @see #getButton() + * @since 1.4 + */ + public static final int BUTTON3 = 3; + + /** This event id indicates that the mouse was dragged over a component. */ + public static final int MOUSE_DRAGGED = 506; + + /** + * This event id indicates that the mouse wheel was rotated. + * + * @since 1.4 + */ + public static final int MOUSE_WHEEL = 507; + + /** + * The X coordinate of the mouse cursor at the time of the event. + * + * @see #getX() + * @serial the x coordinate + */ + private int x; + + /** + * The Y coordinate of the mouse cursor at the time of the event. + * + * @see #getY() + * @serial the y coordinate + */ + private int y; + + /** + * The screen position of that mouse event, X coordinate. + */ + private int absX; + + /** + * The screen position of that mouse event, Y coordinate. + */ + private int absY; + + /** + * The number of clicks that took place. For MOUSE_CLICKED, MOUSE_PRESSED, + * and MOUSE_RELEASED, this will be at least 1; otherwise it is 0. + * + * see #getClickCount() + * @serial the number of clicks + */ + private final int clickCount; + + /** + * Indicates which mouse button changed state. Can only be one of + * {@link #NOBUTTON}, {@link #BUTTON1}, {@link #BUTTON2}, or + * {@link #BUTTON3}. + * + * @see #getButton() + * @since 1.4 + */ + private int button; + + /** + * Whether or not this event should trigger a popup menu. + * + * @see PopupMenu + * @see #isPopupTrigger() + * @serial true if this is a popup trigger + */ + private final boolean popupTrigger; + + /** + * Initializes a new instance of MouseEvent with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp of when the event occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param x the X coordinate of the mouse point + * @param y the Y coordinate of the mouse point + * @param clickCount the number of mouse clicks for this event + * @param popupTrigger true if this event triggers a popup menu + * @param button the most recent mouse button to change state + * @throws IllegalArgumentException if source is null or button is invalid + * @since 1.4 + */ + public MouseEvent(Component source, int id, long when, int modifiers, + int x, int y, int clickCount, boolean popupTrigger, + int button) + { + this(source, id, when, modifiers, x, y, 0, 0, clickCount, popupTrigger, + button); + } + + /** + * Initializes a new instance of MouseEvent with the specified + * information. Note that an invalid id leads to unspecified results. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp of when the event occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param x the X coordinate of the mouse point + * @param y the Y coordinate of the mouse point + * @param clickCount the number of mouse clicks for this event + * @param popupTrigger true if this event triggers a popup menu + * @throws IllegalArgumentException if source is null + */ + public MouseEvent(Component source, int id, long when, int modifiers, + int x, int y, int clickCount, boolean popupTrigger) + { + this(source, id, when, modifiers, x, y, clickCount, popupTrigger, + NOBUTTON); + } + + /** + * Creates a new MouseEvent. This is like the other constructors and adds + * specific absolute coordinates. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp of when the event occurred + * @param modifiers the modifier keys during the event, in old or new style + * @param x the X coordinate of the mouse point + * @param y the Y coordinate of the mouse point + * @param absX the absolute X screen coordinate of this event + * @param absY the absolute Y screen coordinate of this event + * @param clickCount the number of mouse clicks for this event + * @param popupTrigger true if this event triggers a popup menu + * @param button the most recent mouse button to change state + * + * @throws IllegalArgumentException if source is null or button is invalid + * + * @since 1.6 + */ + public MouseEvent(Component source, int id, long when, int modifiers, + int x, int y, int absX, int absY, int clickCount, + boolean popupTrigger, int button) + { + super(source, id, when, modifiers); + + this.x = x; + this.y = y; + this.clickCount = clickCount; + this.popupTrigger = popupTrigger; + this.button = button; + if (button < NOBUTTON || button > BUTTON3) + throw new IllegalArgumentException(); + if ((modifiers & EventModifier.OLD_MASK) != 0) + { + if ((modifiers & BUTTON1_MASK) != 0) + this.button = BUTTON1; + else if ((modifiers & BUTTON2_MASK) != 0) + this.button = BUTTON2; + else if ((modifiers & BUTTON3_MASK) != 0) + this.button = BUTTON3; + } + // clear the mouse button modifier masks if this is a button + // release event. + if (id == MOUSE_RELEASED) + this.modifiersEx &= ~(BUTTON1_DOWN_MASK + | BUTTON2_DOWN_MASK + | BUTTON3_DOWN_MASK); + + this.absX = absX; + this.absY = absY; + } + + /** + * This method returns the X coordinate of the mouse position. This is + * relative to the source component. + * + * @return the x coordinate + */ + public int getX() + { + return x; + } + + /** + * This method returns the Y coordinate of the mouse position. This is + * relative to the source component. + * + * @return the y coordinate + */ + public int getY() + { + return y; + } + + /** + * @since 1.6 + */ + public Point getLocationOnScreen() + { + return new Point(absX, absY); + } + + /** + * @since 1.6 + */ + public int getXOnScreen() + { + return absX; + } + + /** + * @since 1.6 + */ + public int getYOnScreen() + { + return absY; + } + + /** + * This method returns a Point for the x,y position of + * the mouse pointer. This is relative to the source component. + * + * @return a Point for the event position + */ + public Point getPoint() + { + return new Point(x, y); + } + + /** + * Translates the event coordinates by the specified x and y offsets. + * + * @param dx the value to add to the X coordinate of this event + * @param dy the value to add to the Y coordiante of this event + */ + public void translatePoint(int dx, int dy) + { + x += dx; + y += dy; + } + + /** + * This method returns the number of mouse clicks associated with this + * event. + * + * @return the number of mouse clicks for this event + */ + public int getClickCount() + { + return clickCount; + } + + /** + * Returns which button, if any, was the most recent to change state. This + * will be one of {@link #NOBUTTON}, {@link #BUTTON1}, {@link #BUTTON2}, or + * {@link #BUTTON3}. + * + * @return the button that changed state + * @since 1.4 + */ + public int getButton() + { + return button; + } + + /** + * This method tests whether or not the event is a popup menu trigger. This + * should be checked in both MousePressed and MouseReleased to be + * cross-platform compatible, as different systems have different popup + * triggers. + * + * @return true if the event is a popup menu trigger + */ + public boolean isPopupTrigger() + { + return popupTrigger; + } + + /** + * Returns a string describing the modifiers, such as "Shift" or + * "Ctrl+Button1". + * + * XXX Sun claims this can be localized via the awt.properties file - how + * do we implement that? + * + * @param modifiers the old-style modifiers to convert to text + * @return a string representation of the modifiers in this bitmask + */ + public static String getMouseModifiersText(int modifiers) + { + modifiers &= EventModifier.OLD_MASK; + if ((modifiers & BUTTON2_MASK) != 0) + modifiers |= BUTTON2_DOWN_MASK; + if ((modifiers & BUTTON3_MASK) != 0) + modifiers |= BUTTON3_DOWN_MASK; + return getModifiersExText(EventModifier.extend(modifiers)); + } + + /** + * Returns a string identifying this event. This is formatted as the field + * name of the id type, followed by the (x,y) point, the most recent button + * changed, modifiers (if any), extModifiers (if any), and clickCount. + * + * @return a string identifying this event + */ + public String paramString() + { + CPStringBuilder s = new CPStringBuilder(); + switch (id) + { + case MOUSE_CLICKED: + s.append("MOUSE_CLICKED,("); + break; + case MOUSE_PRESSED: + s.append("MOUSE_PRESSED,("); + break; + case MOUSE_RELEASED: + s.append("MOUSE_RELEASED,("); + break; + case MOUSE_MOVED: + s.append("MOUSE_MOVED,("); + break; + case MOUSE_ENTERED: + s.append("MOUSE_ENTERED,("); + break; + case MOUSE_EXITED: + s.append("MOUSE_EXITED,("); + break; + case MOUSE_DRAGGED: + s.append("MOUSE_DRAGGED,("); + break; + case MOUSE_WHEEL: + s.append("MOUSE_WHEEL,("); + break; + default: + s.append("unknown type,("); + } + s.append(x).append(',').append(y).append("),button=").append(button); + // FIXME: need a mauve test for this method + if (modifiersEx != 0) + s.append(",extModifiers=").append(getModifiersExText(modifiersEx)); + + s.append(",clickCount=").append(clickCount); + s.append(",consumed=").append(consumed); + + return s.toString(); + } + + /** + * Reads in the object from a serial stream. + * + * @param s the stream to read from + * @throws IOException if deserialization fails + * @throws ClassNotFoundException if deserialization fails + * @serialData default, except that the modifiers are converted to new style + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + if ((modifiers & EventModifier.OLD_MASK) != 0) + { + if ((modifiers & BUTTON1_MASK) != 0) + button = BUTTON1; + else if ((modifiers & BUTTON2_MASK) != 0) + button = BUTTON2; + else if ((modifiers & BUTTON3_MASK) != 0) + button = BUTTON3; + modifiersEx = EventModifier.extend(modifiers) & EventModifier.NEW_MASK; + } + } +} // class MouseEvent diff --git a/libjava/classpath/java/awt/event/MouseListener.java b/libjava/classpath/java/awt/event/MouseListener.java new file mode 100644 index 000000000..735ca6b5a --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseListener.java @@ -0,0 +1,94 @@ +/* MouseListener.java -- listen for mouse clicks and crossing component edges + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive mouse events other than + * simple motion events. This includes clicks (but not mouse wheel events), + * and crossing component boundaries without change in button status. To + * track moves and drags, use MouseMotionListener, and to track wheel events, + * use MouseWheelListener. To watch a subset of these events, use a + * MouseAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see MouseAdapter + * @see MouseEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface MouseListener extends EventListener +{ + /** + * This method is called when the mouse is clicked (pressed and released + * in short succession) on a component. + * + * @param event the MouseEvent indicating the click + */ + void mouseClicked(MouseEvent event); + + /** + * This method is called when the mouse is pressed over a component. + * + * @param event the MouseEvent for the press + */ + void mousePressed(MouseEvent event); + + /** + * This method is called when the mouse is released over a component. + * + * @param event the MouseEvent for the release + */ + void mouseReleased(MouseEvent event); + + /** + * This method is called when the mouse enters a component. + * + * @param event the MouseEvent for the entry + */ + void mouseEntered(MouseEvent event); + + /** + * This method is called when the mouse exits a component. + * + * @param event the MouseEvent for the exit + */ + void mouseExited(MouseEvent event); +} // interface MouseListener diff --git a/libjava/classpath/java/awt/event/MouseMotionAdapter.java b/libjava/classpath/java/awt/event/MouseMotionAdapter.java new file mode 100644 index 000000000..8a295f66c --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseMotionAdapter.java @@ -0,0 +1,79 @@ +/* MouseMotionAdapter.java -- convenience class for mouse motion listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements MouseMotionListener and implements all + * methods with empty bodies. This allows a listener interested in + * implementing only a subset of the MouseMotionListener + * interface to extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see MouseEvent + * @see MouseMotionListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class MouseMotionAdapter implements MouseMotionListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public MouseMotionAdapter() + { + } + + /** + * Implement this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void mouseDragged(MouseEvent event) + { + } + + /** + * Implement this method in the interface with an empty body. + * + * @param event the event, ignored in this implementation + */ + public void mouseMoved(MouseEvent event) + { + } +} // class MouseMotionAdapter diff --git a/libjava/classpath/java/awt/event/MouseMotionListener.java b/libjava/classpath/java/awt/event/MouseMotionListener.java new file mode 100644 index 000000000..ba2c5698b --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseMotionListener.java @@ -0,0 +1,72 @@ +/* MouseMotionListener.java -- listen to mouse motion events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to be notified of mouse movements. + * This includes moves and drags, but not crossing component boundaries. To + * track other mouse events, use MouseListener or MouseWheelListener. To + * watch a subset of these events, use a MouseMotionAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see MouseMotionAdapter + * @see MouseEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface MouseMotionListener extends EventListener +{ + /** + * This method is called when the mouse is moved over a component + * while a button has been pressed. + * + * @param event the MouseEvent indicating the motion + */ + void mouseDragged(MouseEvent event); + + /** + * This method is called when the mouse is moved over a component + * while no button is pressed. + * + * @param event the MouseEvent indicating the motion + */ + void mouseMoved(MouseEvent event); +} // interface MouseMotionListener diff --git a/libjava/classpath/java/awt/event/MouseWheelEvent.java b/libjava/classpath/java/awt/event/MouseWheelEvent.java new file mode 100644 index 000000000..1ca946582 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseWheelEvent.java @@ -0,0 +1,232 @@ +/* MouseWheelEvent.java -- a mouse wheel event + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Adjustable; +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.ScrollPane; + +import javax.swing.JScrollPane; +import javax.swing.Scrollable; + +/** + * This event is generated for a mouse wheel rotation. The wheel (the middle + * mouse button on most modern mice) can be rotated towards or away from the + * user, and is often used for scrolling. + * + *

Because of the special use for scrolling components, MouseWheelEvents + * often affect a different component than the one located at the point of + * the event. If the component under the mouse cursor does not accept wheel + * events, the event is passed to the first ancestor container which does. This + * is often a ScrollPane, which knows how to scroll. If an AWT component is + * built from a native widget that knows how to use mouse wheel events, that + * component will consume the event. + * + *

The two most common scroll types are "units" (lines at a time) or + * "blocks" (pages at a time). The initial setting is taken from the platform, + * although the user can adjust the setting at any time. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see MouseWheelListener + * @see ScrollPane + * @see ScrollPane#setWheelScrollingEnabled(boolean) + * @see JScrollPane + * @see JScrollPane#setWheelScrollingEnabled(boolean) + * @since 1.4 + * @status updated to 1.4 + */ +public class MouseWheelEvent extends MouseEvent +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 6459879390515399677L; + + /** + * Indicates scrolling by units (lines). + * + * @see #getScrollType() + */ + public static final int WHEEL_UNIT_SCROLL = 0; + + /** + * Indicates scrolling by blocks (pages). + * + * @see #getScrollType() + */ + public static final int WHEEL_BLOCK_SCROLL = 1; + + /** + * Indicates what scroll type should take place. This should be limited + * to {@link #WHEEL_UNIT_SCROLL} and {@link #WHEEL_BLOCK_SCROLL}. + * + * @serial the scroll type + */ + private final int scrollType; + + /** + * Indicates the scroll amount. This is only meaningful if scrollType is + * WHEEL_UNIT_SCROLL. + * + * @serial the number of lines to scroll + */ + private final int scrollAmount; + + /** + * Indicates how far the mouse wheel was rotated. + * + * @serial the rotation amount + */ + private final int wheelRotation; + + /** + * Initializes a new instance of MouseWheelEvent with the + * specified information. Note that an invalid id leads to unspecified + * results. + * + * @param source the source of the event + * @param id the event id + * @param when the timestamp of when the event occurred + * @param modifiers any modifier bits for this event + * @param x the X coordinate of the mouse point + * @param y the Y coordinate of the mouse point + * @param clickCount the number of mouse clicks for this event + * @param popupTrigger true if this event triggers a popup menu + * @param scrollType one of {@link #WHEEL_UNIT_SCROLL}, + * {@link #WHEEL_BLOCK_SCROLL} + * @param scrollAmount the number of units to scroll, ignored for block type + * @param wheelRotation the number of rotation "clicks" + * @throws IllegalArgumentException if source is null + * @see MouseEvent#MouseEvent(Component, int, long, int, int, int, int, + * boolean) + */ + public MouseWheelEvent(Component source, int id, long when, int modifiers, + int x, int y, int clickCount, boolean popupTrigger, + int scrollType, int scrollAmount, int wheelRotation) + { + super(source, id, when, modifiers, x, y, clickCount, popupTrigger); + this.scrollType = scrollType; + this.scrollAmount = scrollAmount; + this.wheelRotation = wheelRotation; + } + + /** + * This method returns the scrolling pattern this event requests. Legal + * values are {@link #WHEEL_UNIT_SCROLL} and {@link #WHEEL_BLOCK_SCROLL}. + * + * @return the scroll type + * @see Adjustable#getUnitIncrement() + * @see Adjustable#getBlockIncrement() + * @see Scrollable#getScrollableUnitIncrement(Rectangle, int, int) + * @see Scrollable#getScrollableBlockIncrement(Rectangle, int, int) + */ + public int getScrollType() + { + return scrollType; + } + + /** + * Returns the number of units to scroll in response to this event. This + * only makes sense when the scroll type is WHEEL_UNIT_SCROLL. + * + * @return the number of scroll units, if defined + * @see #getScrollType() + */ + public int getScrollAmount() + { + return scrollAmount; + } + + /** + * Gets the number of "clicks" the wheel was rotated. Negative values move + * up (away) from the user, positive values move down (towards) the user. + * + * @return the number of rotation clicks + */ + public int getWheelRotation() + { + return wheelRotation; + } + + /** + * This is a convenience method which aids in a common listener for scrolling + * a scrollpane (although this is already built into ScrollPane and + * JScrollPane). This method only makes sense when getScrollType() returns + * WHEEL_UNIT_SCROLL. + * + *

This accounts for direction of scroll and amount of wheel movement, as + * interpreted by the platform settings. + * + * @return the number of units to scroll + * @see #getScrollType() + * @see #getScrollAmount() + * @see MouseWheelListener + * @see Adjustable + * @see Adjustable#getUnitIncrement() + * @see Scrollable + * @see Scrollable#getScrollableUnitIncrement(Rectangle, int, int) + * @see ScrollPane + * @see ScrollPane#setWheelScrollingEnabled(boolean) + * @see JScrollPane + * @see JScrollPane#setWheelScrollingEnabled(boolean) + */ + public int getUnitsToScroll() + { + return wheelRotation * scrollAmount; + } + + /** + * Returns a string identifying this event. For mouse wheel events, this + * is super.paramString() + ",scrollType=WHEEL_" + + * (getScrollType() == WHEEL_UNIT_SCROLL ? "UNIT" : "BLOCK") + * + "_SCROLL,scrollAmount=" + getScrollAmount() + ",wheelRotation=" + * + getWheelRotation(). + * + * @return a string identifying this event + */ + public String paramString() + { + return super.paramString() + ",scrollType=" + + (scrollType == WHEEL_UNIT_SCROLL ? "WHEEL_UNIT_SCROLL" + : scrollType == WHEEL_BLOCK_SCROLL ? "WHEEL_BLOCK_SCROLL" + : "unknown scroll type") + + ",scrollAmount=" + scrollAmount + ",wheelRotation=" + wheelRotation; + } +} // class MouseWheelEvent diff --git a/libjava/classpath/java/awt/event/MouseWheelListener.java b/libjava/classpath/java/awt/event/MouseWheelListener.java new file mode 100644 index 000000000..1125582e1 --- /dev/null +++ b/libjava/classpath/java/awt/event/MouseWheelListener.java @@ -0,0 +1,60 @@ +/* MouseWheelListener.java -- listen for mouse wheel events + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to receive mouse wheel events. For + * other events, use MouseListener or MouseMotionListener. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see MouseWheelEvent + * @since 1.4 + * @status updated to 1.4 + */ +public interface MouseWheelListener extends EventListener +{ + /** + * This method is called when the mouse wheel is rotated. + * + * @param event the MouseWheelEvent indicating the rotation + */ + void mouseWheelMoved(MouseWheelEvent event); +} // interface MouseWheelListener diff --git a/libjava/classpath/java/awt/event/PaintEvent.java b/libjava/classpath/java/awt/event/PaintEvent.java new file mode 100644 index 000000000..bb89c3722 --- /dev/null +++ b/libjava/classpath/java/awt/event/PaintEvent.java @@ -0,0 +1,127 @@ +/* PaintEvent.java -- an area of the screen needs to be repainted + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Component; +import java.awt.Rectangle; + +/** + * This event is generated when an area of the screen needs to be painted. + * This event is not meant for users, but exists to allow proper serialization + * behavior in the EventQueue with user-accessible events. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class PaintEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 1267492026433337593L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int PAINT_FIRST = 800; + + /** This is the last id in the range of event ids used by this class. */ + public static final int PAINT_LAST = 801; + + /** This id is for paint event types. */ + public static final int PAINT = 800; + + /** This id is for update event types. */ + public static final int UPDATE = 801; + + /** + * This is the rectange to be painted or updated. + * + * @see #getUpdateRect() + * @see #setUpdateRect(Rectangle) + * @serial the non-null rectangle to be painted + */ + private Rectangle updateRect; + + /** + * Initializes a new instance of PaintEvent with the specified + * source, id, and update region. Note that an invalid id leads to + * unspecified results. + * + * @param source the event source + * @param id the event id + * @param updateRect the rectangle to repaint + * @throws IllegalArgumentException if source is null + */ + public PaintEvent(Component source, int id, Rectangle updateRect) + { + super(source, id); + this.updateRect = updateRect; + } + + /** + * Returns the rectange to be updated for this event. + * + * @return the rectangle to update + */ + public Rectangle getUpdateRect() + { + return updateRect; + } + + /** + * Sets the rectangle to be updated for this event. + * + * @param updateRect the new update rectangle for this event + */ + public void setUpdateRect(Rectangle updateRect) + { + this.updateRect = updateRect; + } + + /** + * Returns a string identifying this event. + * + * @return a string identifying this event + */ + public String paramString() + { + return (id == PAINT ? "PAINT,updateRect=" : id == UPDATE + ? "UPDATE,updateRect=" : "unknown type,updateRect=") + updateRect; + } +} // class PaintEvent diff --git a/libjava/classpath/java/awt/event/TextEvent.java b/libjava/classpath/java/awt/event/TextEvent.java new file mode 100644 index 000000000..0288abbb7 --- /dev/null +++ b/libjava/classpath/java/awt/event/TextEvent.java @@ -0,0 +1,93 @@ +/* TextEvent.java -- event for text changes + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.AWTEvent; +import java.awt.TextComponent; + +/** + * This event is generated when a text box changes contents. This is an + * abstraction that distills a large number of individual mouse or keyboard + * events into a simpler "text changed" event. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see TextComponent + * @see TextListener + * @since 1.1 + * @status updated to 1.4 + */ +public class TextEvent extends AWTEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6269902291250941179L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int TEXT_FIRST = 900; + + /** This is the last id in the range of event ids used by this class. */ + public static final int TEXT_LAST = 900; + + /** This event id indicates that the text of an object has changed. */ + public static final int TEXT_VALUE_CHANGED = 900; + + /** + * Initializes a new instance of TextEvent with the specified + * source and id. Note that an invalid id leads to unspecified results. + * + * @param source the (TextComponent) object that generated this event + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public TextEvent(Object source, int id) + { + super(source, id); + } + + /** + * Returns a string identifying this event. This is "TEXT_VALUE_CHANGED". + * + * @return a string identifying this event + */ + public String paramString() + { + return id == TEXT_VALUE_CHANGED ? "TEXT_VALUE_CHANGED" : "unknown type"; + } +} // class TextEvent diff --git a/libjava/classpath/java/awt/event/TextListener.java b/libjava/classpath/java/awt/event/TextListener.java new file mode 100644 index 000000000..bcdd7fa7a --- /dev/null +++ b/libjava/classpath/java/awt/event/TextListener.java @@ -0,0 +1,60 @@ +/* TextListener.java -- listen for text changes + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to be notified when text changes + * in a component. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see TextEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface TextListener extends EventListener +{ + /** + * This method is called when the text being monitored changes. + * + * @param event the TextEvent indicating the change + */ + void textValueChanged(TextEvent event); +} // interface TextListener diff --git a/libjava/classpath/java/awt/event/WindowAdapter.java b/libjava/classpath/java/awt/event/WindowAdapter.java new file mode 100644 index 000000000..708de588c --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowAdapter.java @@ -0,0 +1,156 @@ +/* WindowAdapter.java -- convenience class for writing window listeners + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +/** + * This class implements WindowListener, + * WindowStateListener, and WindowFocusListener, and + * implements all methods with empty bodies. This allows a listener + * interested in listening to only a subset of any WindowEvent + * actions to extend this class and override only the desired methods. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see ComponentEvent + * @see ComponentListener + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class WindowAdapter + implements WindowListener, WindowStateListener, WindowFocusListener +{ + /** + * Do nothing default constructor for subclasses. + */ + public WindowAdapter() + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowOpened(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowClosing(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowClosed(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowIconified(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowDeiconified(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowActivated(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + */ + public void windowDeactivated(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + * @since 1.4 + */ + public void windowStateChanged(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + * @since 1.4 + */ + public void windowGainedFocus(WindowEvent event) + { + } + + /** + * Implements this method from the interface with an empty method body. + * + * @param event the event, ignored in this implementation + * @since 1.4 + */ + public void windowLostFocus(WindowEvent event) + { + } +} // class WindowAdapter diff --git a/libjava/classpath/java/awt/event/WindowEvent.java b/libjava/classpath/java/awt/event/WindowEvent.java new file mode 100644 index 000000000..b52fefcdc --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowEvent.java @@ -0,0 +1,314 @@ +/* WindowEvent.java -- window change event + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.Frame; +import java.awt.Window; + +/** + * This event is generated when there is a change in a window. This includes + * creation, closing, iconification, activation, and focus changes. There + * are three listeners, for three types of events: WindowListeners deal with + * the lifecycle of a window, WindowStateListeners deal with window state + * like maximization, and WindowFocusListeners deal with focus switching to + * or from a window. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see WindowAdapter + * @see WindowListener + * @see WindowFocusListener + * @see WindowStateListener + * @since 1.1 + * @status updated to 1.4 + */ +public class WindowEvent extends ComponentEvent +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1567959133147912127L; + + /** This is the first id in the range of event ids used by this class. */ + public static final int WINDOW_FIRST = 200; + + /** This is the id for a window that is opened. */ + public static final int WINDOW_OPENED = 200; + + /** This is the id for a window that is about to close. */ + public static final int WINDOW_CLOSING = 201; + + /** This is the id for a window that finished closing. */ + public static final int WINDOW_CLOSED = 202; + + /** This is the id for a window that is iconified. */ + public static final int WINDOW_ICONIFIED = 203; + + /** This is the id for a window that is de-iconified. */ + public static final int WINDOW_DEICONIFIED = 204; + + /** This is the id for a window that is activated. */ + public static final int WINDOW_ACTIVATED = 205; + + /** This is the id for a window that is de-activated. */ + public static final int WINDOW_DEACTIVATED = 206; + + /** + * This is the id for a window becoming the focused window. + * + * @since 1.4 + */ + public static final int WINDOW_GAINED_FOCUS = 207; + + /** + * This is the id for a window losing all focus. + * + * @since 1.4 + */ + public static final int WINDOW_LOST_FOCUS = 208; + + /** + * This is the id for a window state change, such as maximization. + * + * @since 1.4 + */ + public static final int WINDOW_STATE_CHANGED = 209; + + /** This is the last id in the range of event ids used by this class. */ + public static final int WINDOW_LAST = 209; + + /** + * The other Window involved in a focus or activation change. For + * WINDOW_ACTIVATED and WINDOW_GAINED_FOCUS events, this is the window that + * lost focus; for WINDOW_DEACTIVATED and WINDOW_LOST_FOCUS, this is the + * window that stole focus; and for other events (or when native + * implementation does not have the data available), this is null. + * + * @see #getOppositeWindow() + * @serial the opposite window, or null + * @since 1.4 + */ + private final Window opposite; + + /** + * The former state of the window. + * + * @serial bitmask of the old window state + * @since 1.4 + */ + private final int oldState; + + /** + * The present state of the window. + * + * @serial bitmask of the new window state + * @since 1.4 + */ + private final int newState; + + /** + * Initializes a new instance of WindowEvent with the specified + * parameters. Note that an invalid id leads to unspecified results. + * + * @param source the window that generated this event + * @param id the event id + * @param opposite the window that received the opposite event, or null + * @param oldState the previous state of this window + * @param newState the new state of this window + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public WindowEvent(Window source, int id, Window opposite, + int oldState, int newState) + { + super(source, id); + this.opposite = opposite; + this.oldState = oldState; + this.newState = newState; + } + + /** + * Initializes a new instance of WindowEvent with the specified + * parameters. Note that an invalid id leads to unspecified results. + * + * @param source the window that generated this event + * @param id the event id + * @param opposite the window that received the opposite event, or null + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public WindowEvent(Window source, int id, Window opposite) + { + this(source, id, opposite, 0, 0); + } + + /** + * Initializes a new instance of WindowEvent with the specified + * parameters. Note that an invalid id leads to unspecified results. + * + * @param source the window that generated this event + * @param id the event id + * @param oldState the previous state of this window + * @param newState the new state of this window + * @throws IllegalArgumentException if source is null + * @since 1.4 + */ + public WindowEvent(Window source, int id, int oldState, int newState) + { + this(source, id, null, oldState, newState); + } + + /** + * Initializes a new instance of WindowEvent with the specified + * parameters. Note that an invalid id leads to unspecified results. + * + * @param source the window that generated this event + * @param id the event id + * @throws IllegalArgumentException if source is null + */ + public WindowEvent(Window source, int id) + { + this(source, id, null, 0, 0); + } + + /** + * Returns the event source as a Window. If the source has + * subsequently been modified to a non-Window, this returns null. + * + * @return the event source as a Window + */ + public Window getWindow() + { + return source instanceof Window ? (Window) source : null; + } + + /** + * Returns the opposite window if this window was involved in an activation + * or focus change. For WINDOW_ACTIVATED and WINDOW_GAINED_FOCUS events, + * this is the window that lost focus; for WINDOW_DEACTIVATED and + * WINDOW_LOST_FOCUS, this is the window that stole focus; and for other + * events (or when native implementation does not have the data available), + * this is null. + * + * @return the opposite window, or null + * @since 1.4 + */ + public Window getOppositeWindow() + { + return opposite; + } + + /** + * Returns the state of this window before the event. This is the bitwise + * or of fields in Frame: NORMAL, ICONIFIED, MAXIMIZED_HORIZ, MAXIMIZED_VERT, + * and MAXIMIZED_BOTH. + * + * @return the former state + * @see Frame#getExtendedState() + * @since 1.4 + */ + public int getOldState() + { + return oldState; + } + + /** + * Returns the state of this window after the event. This is the bitwise + * or of fields in Frame: NORMAL, ICONIFIED, MAXIMIZED_HORIZ, MAXIMIZED_VERT, + * and MAXIMIZED_BOTH. + * + * @return the updated state + * @see Frame#getExtendedState() + * @since 1.4 + */ + public int getNewState() + { + return newState; + } + + /** + * Returns a string that identifies this event. This is formatted as the + * field name of the id, followed by the opposite window, old state, and + * new state. + * + * @return a string that identifies this event + */ + public String paramString() + { + CPStringBuilder s = new CPStringBuilder(); + switch (id) + { + case WINDOW_OPENED: + s.append("WINDOW_OPENED,opposite="); + break; + case WINDOW_CLOSING: + s.append("WINDOW_CLOSING,opposite="); + break; + case WINDOW_CLOSED: + s.append("WINDOW_CLOSED,opposite="); + break; + case WINDOW_ICONIFIED: + s.append("WINDOW_ICONIFIED,opposite="); + break; + case WINDOW_DEICONIFIED: + s.append("WINDOW_DEICONIFIED,opposite="); + break; + case WINDOW_ACTIVATED: + s.append("WINDOW_ACTIVATED,opposite="); + break; + case WINDOW_DEACTIVATED: + s.append("WINDOW_DEACTIVATED,opposite="); + break; + case WINDOW_GAINED_FOCUS: + s.append("WINDOW_GAINED_FOCUS,opposite="); + break; + case WINDOW_LOST_FOCUS: + s.append("WINDOW_LOST_FOCUS,opposite="); + break; + case WINDOW_STATE_CHANGED: + s.append("WINDOW_STATE_CHANGED,opposite="); + break; + default: + s.append("unknown type,opposite="); + } + return s.append(opposite).append(",oldState=").append(oldState) + .append(",newState=").append(newState).toString(); + } +} // class WindowEvent diff --git a/libjava/classpath/java/awt/event/WindowFocusListener.java b/libjava/classpath/java/awt/event/WindowFocusListener.java new file mode 100644 index 000000000..738425353 --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowFocusListener.java @@ -0,0 +1,68 @@ +/* WindowFocusListener.java -- listens for window focus events + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to monitor events for window + * focus changes. To watch a subset of these events, use a WindowAdapter. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see WindowAdapter + * @see WindowEvent + * @since 1.4 + * @status updated to 1.4 + */ +public interface WindowFocusListener extends EventListener +{ + /** + * This method is called when a window gains focus. + * + * @param event the WindowEvent indicating the focus change + */ + void windowGainedFocus(WindowEvent event); + + /** + * This method is called when a window loses focus. + * + * @param event the WindowEvent indicating the focus change + */ + void windowLostFocus(WindowEvent event); +} // interface WindowFocusListener diff --git a/libjava/classpath/java/awt/event/WindowListener.java b/libjava/classpath/java/awt/event/WindowListener.java new file mode 100644 index 000000000..52213eb3d --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowListener.java @@ -0,0 +1,109 @@ +/* WindowListener.java -- listens for window events + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.awt.Frame; +import java.awt.Image; +import java.util.EventListener; + +/** + * This interface is for classes that wish to monitor events for window + * changes. To watch a subset of these events, use a WindowAdapter. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see WindowAdapter + * @see WindowEvent + * @since 1.1 + * @status updated to 1.4 + */ +public interface WindowListener extends EventListener +{ + /** + * This method is called when the window is made visible. + * + * @param event the WindowEvent indicating the change + */ + void windowOpened(WindowEvent event); + + /** + * This method is called when the user calls the system menu close + * function, giving the program a chance to cancel the close. + * + * @param event the WindowEvent indicating the close attempt + */ + void windowClosing(WindowEvent event); + + /** + * This method is called when the window is closed. + * + * @param event the WindowEvent indicating the dispose + */ + void windowClosed(WindowEvent event); + + /** + * This method is called when the window is iconified. + * + * @param event the WindowEvent indicating the iconification + * @see Frame#setIconImage(Image) + */ + void windowIconified(WindowEvent event); + + /** + * This method is called when the window is deiconified. + * + * @param event the WindowEvent indicating the deiconification + */ + void windowDeiconified(WindowEvent event); + + /** + * This method is called when a window is activated. Only Frames and Dialogs + * can be active, and the active window always contains the component with + * focus. + * + * @param event the WindowEvent indicating the activation + */ + void windowActivated(WindowEvent event); + + /** + * This method is called when the window is deactivated. + * + * @param event the WindowEvent indicating the deactivation + */ + void windowDeactivated(WindowEvent event); +} // interface WindowListener diff --git a/libjava/classpath/java/awt/event/WindowStateListener.java b/libjava/classpath/java/awt/event/WindowStateListener.java new file mode 100644 index 000000000..9bc6174fd --- /dev/null +++ b/libjava/classpath/java/awt/event/WindowStateListener.java @@ -0,0 +1,62 @@ +/* WindowStateListener.java -- listens for window state changes + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.event; + +import java.util.EventListener; + +/** + * This interface is for classes that wish to monitor events for window + * state changes. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see WindowAdapter + * @see WindowEvent + * @since 1.4 + * @status updated to 1.4 + */ +public interface WindowStateListener extends EventListener +{ + /** + * This method is called when the window state is changed, because of + * iconification or maximization. + * + * @param event the WindowEvent indicating the change + */ + void windowStateChanged(WindowEvent event); +} // interface WindowStateListener diff --git a/libjava/classpath/java/awt/event/package.html b/libjava/classpath/java/awt/event/package.html new file mode 100644 index 000000000..77662a3fb --- /dev/null +++ b/libjava/classpath/java/awt/event/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.event + + +

Listeners and adapters for different kinds of AWT events.

+ + + diff --git a/libjava/classpath/java/awt/font/FontRenderContext.java b/libjava/classpath/java/awt/font/FontRenderContext.java new file mode 100644 index 000000000..8d530ec5f --- /dev/null +++ b/libjava/classpath/java/awt/font/FontRenderContext.java @@ -0,0 +1,137 @@ +/* FontRenderContext.java + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.geom.AffineTransform; + +/** + * @author Michael Koch + */ +public class FontRenderContext +{ + private AffineTransform affineTransform; + private boolean isAntiAliased; + private boolean usesFractionalMetrics; + + /** + * Construct a new FontRenderContext. + */ + protected FontRenderContext() + { + // Do nothing here. + } + + /** + * Construct a new FontRenderContext. + */ + public FontRenderContext (AffineTransform tx, boolean isAntiAliased, + boolean usesFractionalMetrics) + { + if (tx != null + && !tx.isIdentity ()) + { + this.affineTransform = new AffineTransform (tx); + } + + this.isAntiAliased = isAntiAliased; + this.usesFractionalMetrics = usesFractionalMetrics; + } + + public boolean equals (Object obj) + { + if (! (obj instanceof FontRenderContext)) + return false; + + return equals ((FontRenderContext) obj); + } + + public boolean equals (FontRenderContext rhs) + { + if (rhs == null) + return false; + + if (affineTransform == null && rhs.affineTransform != null + || affineTransform != null && rhs.affineTransform == null) + return false; + + return ((affineTransform == rhs.affineTransform + || affineTransform.equals (rhs.getTransform ())) + && isAntiAliased == rhs.isAntiAliased () + && usesFractionalMetrics == rhs.usesFractionalMetrics ()); + } + + + /** + * Retrieves the affine transform for scaling typographical points + * to raster pixels. + * + * @return a clone of the transform object. + */ + public AffineTransform getTransform () + { + if (affineTransform == null) + return new AffineTransform (); + else + return new AffineTransform (affineTransform); + } + + + /** + * Returns the hash code of the font render context. + */ + public int hashCode () + { + int code = ( isAntiAliased ? 1 : 0 ) + ( usesFractionalMetrics ? 2 : 0 ); + + if( affineTransform != null && !affineTransform.isIdentity() ) + code ^= affineTransform.hashCode(); + + return code; + } + + public boolean isAntiAliased () + { + return isAntiAliased; + } + + public boolean usesFractionalMetrics () + { + return usesFractionalMetrics; + } +} diff --git a/libjava/classpath/java/awt/font/GlyphJustificationInfo.java b/libjava/classpath/java/awt/font/GlyphJustificationInfo.java new file mode 100644 index 000000000..cfa64f05e --- /dev/null +++ b/libjava/classpath/java/awt/font/GlyphJustificationInfo.java @@ -0,0 +1,77 @@ +/* GlyphJustificationInfo.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +/** + * @author Michael Koch + */ +public final class GlyphJustificationInfo +{ + public static final int PRIORITY_KASHIDA = 0; + public static final int PRIORITY_WHITESPACE = 1; + public static final int PRIORITY_INTERCHAR = 2; + public static final int PRIORITY_NONE = 3; + + public final float weight; + public final int growPriority; + public final boolean growAbsorb; + public final float growLeftLimit; + public final float growRightLimit; + public final int shrinkPriority; + public final boolean shrinkAbsorb; + public final float shrinkLeftLimit; + public final float shrinkRightLimit; + + public GlyphJustificationInfo (float weight, boolean growAbsorb, + int growPriority, float growLeftLimit, + float growRightLimit, boolean shrinkAbsorb, + int shrinkPriority, float shrinkLeftLimit, + float shrinkRightLimit) + { + this.weight = weight; + this.growAbsorb = growAbsorb; + this.growPriority = growPriority; + this.growLeftLimit = growLeftLimit; + this.growRightLimit = growRightLimit; + this.shrinkAbsorb = shrinkAbsorb; + this.shrinkPriority = shrinkPriority; + this.shrinkLeftLimit = shrinkLeftLimit; + this.shrinkRightLimit = shrinkRightLimit; + } +} diff --git a/libjava/classpath/java/awt/font/GlyphMetrics.java b/libjava/classpath/java/awt/font/GlyphMetrics.java new file mode 100644 index 000000000..b41b7f45b --- /dev/null +++ b/libjava/classpath/java/awt/font/GlyphMetrics.java @@ -0,0 +1,138 @@ +/* GlyphMetrics.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.geom.Rectangle2D; + +/** + * @author Michael Koch + */ +public final class GlyphMetrics +{ + public static final byte COMBINING = 2; + public static final byte COMPONENT = 3; + public static final byte LIGATURE = 1; + public static final byte STANDARD = 0; + public static final byte WHITESPACE = 4; + + private boolean horizontal; + private float advanceX; + private float advanceY; + private Rectangle2D bounds; + private byte glyphType; + + public GlyphMetrics (boolean horizontal, float advanceX, float advanceY, + Rectangle2D bounds, byte glyphType) + { + this.horizontal = horizontal; + this.advanceX = advanceX; + this.advanceY = advanceY; + this.bounds = bounds; + this.glyphType = glyphType; + } + + public GlyphMetrics (float advance, Rectangle2D bounds, byte glyphType) + { + this (true, advance, advance, bounds, glyphType); + } + + public float getAdvance () + { + return horizontal ? advanceX : advanceY; + } + + public float getAdvanceX () + { + return advanceX; + } + + public float getAdvanceY () + { + return advanceY; + } + + public Rectangle2D getBounds2D () + { + return bounds; + } + + public float getLSB() + { + if (horizontal) + return (float) bounds.getX(); + return (float) bounds.getY(); + } + + public float getRSB() + { + if (horizontal) + return (float) (advanceX - (bounds.getX() + bounds.getWidth())); + return (float) (advanceY - (bounds.getY() + bounds.getHeight())); + } + + public int getType () + { + return glyphType; + } + + public boolean isCombining () + { + return (glyphType == COMBINING); + } + + public boolean isComponent () + { + return (glyphType == COMPONENT); + } + + public boolean isLigature() + { + return (glyphType == LIGATURE); + } + + public boolean isStandard() + { + return (glyphType == STANDARD); + } + + public boolean isWhitespace() + { + return (glyphType == WHITESPACE); + } +} diff --git a/libjava/classpath/java/awt/font/GlyphVector.java b/libjava/classpath/java/awt/font/GlyphVector.java new file mode 100644 index 000000000..4a87f4c62 --- /dev/null +++ b/libjava/classpath/java/awt/font/GlyphVector.java @@ -0,0 +1,174 @@ +/* GlyphVector.java + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * @author Lillian Angel (langel at redhat dot com) + * @author Michael Koch + */ +public abstract class GlyphVector implements Cloneable +{ + public static final int FLAG_COMPLEX_GLYPHS = 8; + public static final int FLAG_HAS_POSITION_ADJUSTMENTS = 2; + public static final int FLAG_HAS_TRANSFORMS = 1; + public static final int FLAG_MASK = 15; + public static final int FLAG_RUN_RTL = 4; + + /** + * Constructs a GlyphVector object. + */ + public GlyphVector () + { + } + + public abstract boolean equals (GlyphVector set); + + public abstract Font getFont (); + + public abstract FontRenderContext getFontRenderContext (); + + public int getGlyphCharIndex (int glyphIndex) + { + return glyphIndex; + } + + public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries, + int[] codeReturn) + { + if (codeReturn == null) + codeReturn = new int[numEntries]; + + int i = 0; + int j = beginGlyphIndex; + while (j < numEntries) + codeReturn[i++] = getGlyphCharIndex(j++); + + return codeReturn; + } + + public abstract int getGlyphCode (int glyphIndex); + + public abstract int[] getGlyphCodes (int beginGlyphIndex, int numEntries, + int[] codeReturn); + + public abstract GlyphJustificationInfo getGlyphJustificationInfo + (int glyphIndex); + + public abstract Shape getGlyphLogicalBounds (int glyphIndex); + + public abstract GlyphMetrics getGlyphMetrics (int glyphIndex); + + public abstract Shape getGlyphOutline (int glyphIndex); + + public Shape getGlyphOutline(int glyphIndex, float x, float y) + { + Shape s = getGlyphOutline(glyphIndex); + + // This is the only way to translate the origin of a shape + AffineTransform at = AffineTransform.getTranslateInstance(x, y); + return at.createTransformedShape(s); + } + + public Rectangle getGlyphPixelBounds(int index, FontRenderContext renderFRC, + float x, float y) + { + Rectangle bounds = new Rectangle(); + Rectangle2D rect = getGlyphVisualBounds(index).getBounds2D(); + + bounds.x = (int) (rect.getX() + x); + bounds.y = (int) (rect.getY() + y); + bounds.width = (int) rect.getMaxX() - bounds.x; + bounds.height = (int) rect.getMaxY() - bounds.y; + + return bounds; + } + + public abstract Point2D getGlyphPosition (int glyphIndex); + + public abstract float[] getGlyphPositions (int beginGlyphIndex, + int numEntries, + float[] positionReturn); + + public abstract AffineTransform getGlyphTransform (int glyphIndex); + + public abstract Shape getGlyphVisualBounds (int glyphIndex); + + public int getLayoutFlags() + { + return 0; + } + + public abstract Rectangle2D getLogicalBounds (); + + public abstract int getNumGlyphs (); + + public abstract Shape getOutline (); + + public abstract Shape getOutline (float x, float y); + + public Rectangle getPixelBounds (FontRenderContext renderFRC, + float x, float y) + { + Rectangle bounds = new Rectangle(); + Rectangle2D rect = getVisualBounds(); + + bounds.x = (int) (rect.getX() + x); + bounds.y = (int) (rect.getY() + y); + bounds.width = (int) rect.getMaxX() - bounds.x; + bounds.height = (int) rect.getMaxY() - bounds.y; + + return bounds; + } + + public abstract Rectangle2D getVisualBounds (); + + public abstract void performDefaultLayout (); + + public abstract void setGlyphPosition (int glyphIndex, Point2D newPos); + + public abstract void setGlyphTransform (int glyphIndex, + AffineTransform newTX); +} diff --git a/libjava/classpath/java/awt/font/GraphicAttribute.java b/libjava/classpath/java/awt/font/GraphicAttribute.java new file mode 100644 index 000000000..edf0c204d --- /dev/null +++ b/libjava/classpath/java/awt/font/GraphicAttribute.java @@ -0,0 +1,137 @@ +/* GraphicAttribute.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + +/** + * This class represents a graphic embedded in text. + * + * @author Michael Koch + * @author Lillian Angel (langel at redhat dot com) + */ +public abstract class GraphicAttribute +{ + public static final int BOTTOM_ALIGNMENT = - 2; + public static final int CENTER_BASELINE = 1; + public static final int HANGING_BASELINE = 2; + public static final int ROMAN_BASELINE = 0; + public static final int TOP_ALIGNMENT = - 1; + + private int alignment; + + /** + * Constructor. + * + * @param alignment - the alignment to use for the graphic + */ + protected GraphicAttribute(int alignment) + { + if (alignment < BOTTOM_ALIGNMENT || alignment > HANGING_BASELINE) + throw new IllegalArgumentException("Invalid alignment"); + this.alignment = alignment; + } + + /** + * Draws the graphic. + * + * @param graphics - the graphics configuration to use + * @param x - the x location + * @param y - the y location + */ + public abstract void draw(Graphics2D graphics, float x, float y); + + /** + * Gets the distance from the origin of its graphic to the right side of the + * bounds of its graphic. + * + * @return the advance + */ + public abstract float getAdvance(); + + /** + * Gets the positive distance from the origin of its graphic to the top of + * bounds. + * + * @return the ascent + */ + public abstract float getAscent(); + + /** + * Gets the distance from the origin of its graphic to the bottom of the bounds. + * + * @return the descent + */ + public abstract float getDescent(); + + /** + * Gets the alignment. + * + * @return the alignment + */ + public final int getAlignment() + { + return alignment; + } + + /** + * Returns a Rectangle2D that encloses the rendered area. + * Default bounds is the rectangle (0, -ascent, advance, ascent+descent). + * + * @return the bounds of the rendered area + */ + public Rectangle2D getBounds() + { + float asc = getAscent(); + return new Rectangle2D.Float(0, - asc, getAdvance(), asc + getDescent()); + } + + /** + * Returns the justification information for this object. + * + * @return the justification information + */ + public GlyphJustificationInfo getJustificationInfo() + { + float adv = getAdvance(); + return new GlyphJustificationInfo(adv, false, 2, adv / 3, adv / 3, false, + 1, 0, 0); + } +} diff --git a/libjava/classpath/java/awt/font/ImageGraphicAttribute.java b/libjava/classpath/java/awt/font/ImageGraphicAttribute.java new file mode 100644 index 000000000..63fff4101 --- /dev/null +++ b/libjava/classpath/java/awt/font/ImageGraphicAttribute.java @@ -0,0 +1,187 @@ +/* ImageGraphicAttribute.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.geom.Rectangle2D; + +/** + * This is an implementation of GraphicAttribute which draws images in a + * TextLayout. + * + * @author Lillian Angel + * @author Michael Koch + */ +public final class ImageGraphicAttribute + extends GraphicAttribute +{ + private Image image; + private float originX; + private float originY; + + /** + * Constucts an instance from the specified Image. The origin is at (0, 0). + * + * @param image - image to construct from. + * @param alignment - the alignment + */ + public ImageGraphicAttribute(Image image, int alignment) + { + this(image, alignment, 0, 0); + } + + /** + * Constucts an instance from the specified Image. The origin is at (originX, + * originY). + * + * @param image - image to construct from + * @param alignment - the alignment + * @param originX - x point of origin + * @param originY - y point of origin + */ + public ImageGraphicAttribute(Image image, int alignment, float originX, + float originY) + { + super(alignment); + this.image = image; + this.originX = originX; + this.originY = originY; + } + + /** + * Draws the image at the specified location, relative to the + * origin. + * + * @param g - the graphics to use to render the image + * @param x - the x location + * @param y - the y location + */ + public void draw(Graphics2D g, float x, float y) + { + g.drawImage(image, (int) (x - originX), (int) (y - originY), null); + } + + /** + * Compares this to the specified Object + * + * @param obj - the object to compare + * @return true if the obj and this are equivalent + */ + public boolean equals(Object obj) + { + if (! (obj instanceof ImageGraphicAttribute)) + return false; + + return equals((ImageGraphicAttribute) obj); + } + + /** + * Compares this to the ImageGraphicAttribute given, by + * comparing all fields and values. + * + * @param rhs - the ImageGraphicAttribute to compare + * @return true if the object given is equivalent to this + */ + public boolean equals(ImageGraphicAttribute rhs) + { + return ((this == rhs) || ((this.getAscent() == rhs.getAscent()) + && (this.getAdvance() == rhs.getAdvance()) + && (this.getAlignment() == rhs.getAlignment()) + && (this.getBounds().equals(rhs.getBounds())) + && (this.getDescent() == rhs.getDescent()) + && (this.hashCode() == rhs.hashCode()) + && (this.image.equals(rhs.image)) + && (this.originX == rhs.originX) + && (this.originY == rhs.originY))); + } + + /** + * Returns distance from the origin to the right edge of the image of this. + * + * @return the advance + */ + public float getAdvance() + { + return Math.max(0, image.getWidth(null) - originX); + } + + /** + * Returns the the distance from the top of the image to the origin of this. + * + * @return the ascent. + */ + public float getAscent() + { + return Math.max(0, originY); + } + + /** + * Gets the bounds of the object rendered, relative to the position. + * + * @return the bounds of the object rendered, relative to the position. + */ + public Rectangle2D getBounds() + { + // This is equivalent to what Sun's JDK returns. + // I am not entirely sure why the origin is negative. + return new Rectangle2D.Float(- originX, - originY, image.getWidth(null), + image.getHeight(null)); + } + + /** + * Returns the distance from the origin to the bottom of the image. + * + * @return the descent + */ + public float getDescent() + { + return Math.max(0, image.getHeight(null) - originY); + } + + /** + * Gets the hash code for this image. + * + * @return the hash code + */ + public int hashCode() + { + return image.hashCode(); + } +} diff --git a/libjava/classpath/java/awt/font/LineBreakMeasurer.java b/libjava/classpath/java/awt/font/LineBreakMeasurer.java new file mode 100644 index 000000000..d11f20d1f --- /dev/null +++ b/libjava/classpath/java/awt/font/LineBreakMeasurer.java @@ -0,0 +1,148 @@ +/* LineBreakMeasurer.java + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.text.AttributedCharacterIterator; +import java.text.BreakIterator; + +public final class LineBreakMeasurer +{ + private AttributedCharacterIterator text; + private int position; + private TextMeasurer tm; + private int numChars; + + public LineBreakMeasurer(AttributedCharacterIterator text, + BreakIterator breakIter, FontRenderContext frc) + { + this( text, frc ); + } + + public LineBreakMeasurer(AttributedCharacterIterator text, + FontRenderContext frc) + { + this.text = text; + position = 0; + numChars = text.getEndIndex(); + tm = new TextMeasurer( text, frc ); + } + + public void deleteChar(AttributedCharacterIterator newParagraph, + int deletePos) + { + tm.deleteChar( newParagraph, deletePos ); + position = 0; + } + + public void insertChar(AttributedCharacterIterator newParagraph, + int insertPos) + { + tm.insertChar( newParagraph, insertPos ); + position = 0; + } + + public TextLayout nextLayout(float wrappingWidth) + { + return nextLayout( wrappingWidth, numChars, false ); + } + + public TextLayout nextLayout(float wrappingWidth, int offsetLimit, + boolean requireNextWord) + { + int next = nextOffset( wrappingWidth, offsetLimit, requireNextWord ); + TextLayout tl = tm.getLayout( position, next ); + position = next; + return tl; + } + + public int nextOffset(float wrappingWidth) + { + return nextOffset( wrappingWidth, numChars, false ); + } + + public int nextOffset(float wrappingWidth, int offsetLimit, + boolean requireNextWord) + { + int guessOffset = tm.getLineBreakIndex(position, wrappingWidth); + if( offsetLimit > numChars ) + offsetLimit = numChars; + + if( guessOffset > offsetLimit ) + { + text.setIndex( offsetLimit ); + return offsetLimit; + } + + text.setIndex( guessOffset ); + + // If we're on a breaking character, return directly + if( Character.isWhitespace( text.current() ) ) + return guessOffset; + + // Otherwise jump forward or backward to the last such char. + if( !requireNextWord ) + while( !Character.isWhitespace( text.previous() ) && + guessOffset > position ) + guessOffset--; + else + while( !Character.isWhitespace( text.next() ) && + guessOffset < offsetLimit ) + guessOffset++; + + if( guessOffset > offsetLimit ) + { + text.setIndex( offsetLimit ); + return offsetLimit; + } + + text.setIndex( guessOffset ); + + return guessOffset; + } + + public void setPosition(int newPosition) + { + position = newPosition; + } + + public int getPosition() + { + return position; + } +} diff --git a/libjava/classpath/java/awt/font/LineMetrics.java b/libjava/classpath/java/awt/font/LineMetrics.java new file mode 100644 index 000000000..d43fd98bb --- /dev/null +++ b/libjava/classpath/java/awt/font/LineMetrics.java @@ -0,0 +1,67 @@ +/* LineMetrics.java -- Information about about a line display characteristics + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +/** + * @author Michael Koch + */ +public abstract class LineMetrics +{ + public abstract float getAscent(); + + public abstract int getBaselineIndex(); + + public abstract float[] getBaselineOffsets(); + + public abstract float getDescent(); + + public abstract float getHeight(); + + public abstract float getLeading(); + + public abstract int getNumChars(); + + public abstract float getStrikethroughOffset(); + + public abstract float getStrikethroughThickness(); + + public abstract float getUnderlineOffset(); + + public abstract float getUnderlineThickness(); +} diff --git a/libjava/classpath/java/awt/font/MultipleMaster.java b/libjava/classpath/java/awt/font/MultipleMaster.java new file mode 100644 index 000000000..1be44bd75 --- /dev/null +++ b/libjava/classpath/java/awt/font/MultipleMaster.java @@ -0,0 +1,61 @@ +/* MultipleMaster.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Font; + +/** + * @author Michael Koch + */ +public interface MultipleMaster +{ + Font deriveMMFont (float[] axes); + + Font deriveMMFont (float[] glyphWidths, float avgStemWidth, + float typicalCapHeight, float typicalXHeight, + float italicAngle); + + float[] getDesignAxisDefaults(); + + String[] getDesignAxisNames(); + + float[] getDesignAxisRanges(); + + int getNumDesignAxes(); +} diff --git a/libjava/classpath/java/awt/font/NumericShaper.java b/libjava/classpath/java/awt/font/NumericShaper.java new file mode 100644 index 000000000..add1c6a44 --- /dev/null +++ b/libjava/classpath/java/awt/font/NumericShaper.java @@ -0,0 +1,425 @@ +/* NumericShaper.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.io.Serializable; +import java.lang.Character.UnicodeBlock; + +/** + * This class handles numeric shaping. A shaper can either be contextual + * or not. A non-contextual shaper will always translate ASCII digits + * in its input into the target Unicode range. A contextual shaper will + * change the target Unicode range depending on the characters it has + * previously processed. + * + * @author Michael Koch + * @author Tom Tromey + * + * @since 1.4 + * @specnote This class does not handle LIMBU or OSMANYA. + * @specnote The JDK does not seem to properly handle ranges without a + * digit zero, such as TAMIL. This implementation does. + */ +public final class NumericShaper implements Serializable +{ + private static final long serialVersionUID = -8022764705923730308L; + + /** Convenience constant representing all the valid Unicode ranges. */ + public static final int ALL_RANGES = 524287; + + /** + * Constant representing the Unicode ARABIC range. Shaping done + * using this range will translate to the arabic decimal characters. + * Use EASTERN_ARABIC if you want to shape to the eastern arabic + * (also known as the extended arabic) decimal characters. + */ + public static final int ARABIC = 2; + + /** Constant representing the Unicode BENGALI range. */ + public static final int BENGALI = 16; + + /** Constant representing the Unicode DEVANAGARI range. */ + public static final int DEVANAGARI = 8; + + /** + * Constant representing the Unicode extended arabic range. + * In Unicode there are two different sets of arabic digits; + * this selects the extended or eastern set. + */ + public static final int EASTERN_ARABIC = 4; + + /** + * Constant representing the Unicode ETHIOPIC range. Note that + * there is no digit zero in this range; an ASCII digit zero + * is left unchanged when shaping to this range. + */ + public static final int ETHIOPIC = 65536; + + /** + * Constant representing the Unicode EUROPEAN range. For + * contextual shaping purposes, characters in the various + * extended Latin character blocks are recognized as EUROPEAN. + */ + public static final int EUROPEAN = 1; + + /** Constant representing the Unicode GUJARATI range. */ + public static final int GUJARATI = 64; + + /** Constant representing the Unicode GURMUKHI range. */ + public static final int GURMUKHI = 32; + + /** Constant representing the Unicode KANNADA range. */ + public static final int KANNADA = 1024; + + /** Constant representing the Unicode KHMER range. */ + public static final int KHMER = 131072; + + /** Constant representing the Unicode LAO range. */ + public static final int LAO = 8192; + + /** Constant representing the Unicode MALAYALAM range. */ + public static final int MALAYALAM = 2048; + + /** Constant representing the Unicode MONGOLIAN range. */ + public static final int MONGOLIAN = 262144; + + /** Constant representing the Unicode MYANMAR range. */ + public static final int MYANMAR = 32768; + + /** Constant representing the Unicode ORIYA range. */ + public static final int ORIYA = 128; + + /** + * Constant representing the Unicode TAMIL range. Note that + * there is no digit zero in this range; an ASCII digit zero + * is left unchanged when shaping to this range. + */ + public static final int TAMIL = 256; + + /** Constant representing the Unicode TELUGU range. */ + public static final int TELUGU = 512; + + /** Constant representing the Unicode THAI range. */ + public static final int THAI = 4096; + + /** Constant representing the Unicode TIBETAN range. */ + public static final int TIBETAN = 16384; + + /** + * This table holds the zero digits for each language. This is hard-coded + * because the values will not change and the table layout is tied to the + * other constants in this class in any case. In the two places where a + * language does not have a zero digit, the character immediately preceeding + * the one digit is used instead. These languages are special-cased in + * the shaping code. + */ + private static final char[] zeroDigits = + { + '0', // EUROPEAN + '\u0660', // ARABIC + '\u06f0', // EASTERN_ARABIC + '\u0966', // DEVANAGARI + '\u09e6', // BENGALI + '\u0a66', // GURMUKHI + '\u0ae6', // GUJARATI + '\u0b66', // ORIYA + '\u0be6', // TAMIL - special case as there is no digit zero + '\u0c66', // TELUGU + '\u0ce6', // KANNADA + '\u0d66', // MALAYALAM + '\u0e50', // THAI + '\u0ed0', // LAO + '\u0f20', // TIBETAN + '\u1040', // MYANMAR + '\u1368', // ETHIOPIC - special case as there is no digit zero + '\u17e0', // KHMER + '\u1810' // MONGOLIAN + }; + + /** + * The default initial context for this shaper, specified as + * an integer from 0 to 18. + */ + private int key; + + /** + * The target ranges handled by this shaper. If the shaper + * is not contextual, the high bit of this field will be set. + * @specnote This was discovered by reading the serialization spec + */ + private int mask; + + /** + * Create a new numeric shaper. The key given is a constant from + * this class, the constructor turns it into its internal form. + * @param key the key to use, as one of the manifest constants + * @param mask a mask of languages to shape for + */ + private NumericShaper (int key, int mask) + { + // This internal form is a bit goofy, but it is specified by + // the serialization spec. + this.key = Integer.numberOfTrailingZeros(key); + this.mask = mask; + } + + /** + * Return an integer representing all the languages for which this + * shaper will shape. The result is taken by "or"ing together + * the constants representing the various languages. + */ + public int getRanges () + { + return mask & ALL_RANGES; + } + + /** + * Return true if this shaper is contextual, false if it is not. + */ + public boolean isContextual () + { + return mask > 0; + } + + /** + * Shape the text in the given array. The starting context will + * be the context passed to the shaper at creation time. + * @param text the text to shape + * @param start the index of the starting character of the array + * @param count the number of characters in the array + */ + public void shape (char[] text, int start, int count) + { + shape (text, start, count, 1 << key); + } + + /** + * Given a unicode block object, return corresponding language constant. + * If the block is not recognized, returns zero. Note that as there + * is no separate ARABIC block in Character, this case must + * be specially handled by the caller; EASTERN_ARABIC is preferred when + * both are specified. + * @param b the unicode block to classify + * @return the language constant, or zero if not recognized + */ + private int classify(UnicodeBlock b) + { + if (b == null) + return 0; + // ARABIC is handled by the caller; from testing we know + // that EASTERN_ARABIC takes precedence. + if (b == UnicodeBlock.ARABIC) + return EASTERN_ARABIC; + if (b == UnicodeBlock.BENGALI) + return BENGALI; + if (b == UnicodeBlock.DEVANAGARI) + return DEVANAGARI; + if (b == UnicodeBlock.ETHIOPIC) + return ETHIOPIC; + if (b == UnicodeBlock.BASIC_LATIN + || b == UnicodeBlock.LATIN_1_SUPPLEMENT + || b == UnicodeBlock.LATIN_EXTENDED_A + || b == UnicodeBlock.LATIN_EXTENDED_ADDITIONAL + || b == UnicodeBlock.LATIN_EXTENDED_B) + return EUROPEAN; + if (b == UnicodeBlock.GUJARATI) + return GUJARATI; + if (b == UnicodeBlock.GURMUKHI) + return GURMUKHI; + if (b == UnicodeBlock.KANNADA) + return KANNADA; + if (b == UnicodeBlock.KHMER) + return KHMER; + if (b == UnicodeBlock.LAO) + return LAO; + if (b == UnicodeBlock.MALAYALAM) + return MALAYALAM; + if (b == UnicodeBlock.MONGOLIAN) + return MONGOLIAN; + if (b == UnicodeBlock.MYANMAR) + return MYANMAR; + if (b == UnicodeBlock.ORIYA) + return ORIYA; + if (b == UnicodeBlock.TAMIL) + return TAMIL; + if (b == UnicodeBlock.TELUGU) + return TELUGU; + if (b == UnicodeBlock.THAI) + return THAI; + if (b == UnicodeBlock.TIBETAN) + return TIBETAN; + return 0; + } + + /** + * Shape the given text, using the indicated initial context. + * If this shaper is not a contextual shaper, then the given context + * will be ignored. + * @param text the text to shape + * @param start the index of the first character of the text to shape + * @param count the number of characters to shape in the text + * @param context the initial context + * @throws IllegalArgumentException if the initial context is invalid + */ + public void shape (char[] text, int start, int count, int context) + { + int currentContext; + if (isContextual()) + { + if (Integer.bitCount(context) != 1 || (context & ~ALL_RANGES) != 0) + throw new IllegalArgumentException("invalid context argument"); + // If the indicated context is not one we are handling, reset it. + if ((context & mask) == 0) + currentContext = -1; + else + currentContext = Integer.numberOfTrailingZeros(context); + } + else + currentContext = key; + + for (int i = 0; i < count; ++i) + { + char c = text[start + i]; + if (c >= '0' && c <= '9') + { + if (currentContext >= 0) + { + // Shape into the current context. + if (c == '0' + && ((1 << currentContext) == TAMIL + || (1 << currentContext) == ETHIOPIC)) + { + // No digit 0 in this context; do nothing. + } + else + text[start + i] + = (char) (zeroDigits[currentContext] + c - '0'); + } + } + else if (isContextual()) + { + // if c is in a group, set currentContext; else reset it. + int group = classify(UnicodeBlock.of(c)); + // Specially handle ARABIC. + if (group == EASTERN_ARABIC && (mask & EASTERN_ARABIC) == 0 + && (mask & ARABIC) != 0) + group = ARABIC; + if ((mask & group) != 0) + { + // The character was classified as being in a group + // we recognize, and it was selected by the shaper. + // So, change the context. + currentContext = Integer.numberOfTrailingZeros(group); + } + } + } + } + + public boolean equals (Object obj) + { + if (! (obj instanceof NumericShaper)) + return false; + NumericShaper tmp = (NumericShaper) obj; + return key == tmp.key && mask == tmp.mask; + } + + public int hashCode () + { + return key ^ mask; + } + + public String toString () + { + // For debugging only. + return "key=" + key + "; mask=" + mask; + } + + /** + * Return a non-contextual shaper which can shape to a single range. + * All ASCII digits in the input text are translated to this language. + * @param singleRange the target language + * @return a non-contextual shaper for this language + * @throws IllegalArgumentException if the argument does not name a + * single language, as specified by the constants declared in this class + */ + public static NumericShaper getShaper (int singleRange) + { + if (Integer.bitCount(singleRange) != 1) + throw new IllegalArgumentException("more than one bit set in argument"); + if ((singleRange & ~ALL_RANGES) != 0) + throw new IllegalArgumentException("argument out of range"); + return new NumericShaper(singleRange, Integer.MIN_VALUE | singleRange); + } + + /** + * Return a contextual shaper which can shape to any of the indicated + * languages. The default initial context for this shaper is EUROPEAN. + * @param ranges the ranges to shape to + * @return a contextual shaper which will target any of these ranges + * @throws IllegalArgumentException if the argument specifies an + * unrecognized range + */ + public static NumericShaper getContextualShaper (int ranges) + { + if ((ranges & ~ALL_RANGES) != 0) + throw new IllegalArgumentException("argument out of range"); + return new NumericShaper(EUROPEAN, ranges); + } + + /** + * Return a contextual shaper which can shape to any of the indicated + * languages. The default initial context for this shaper is given as + * an argument. + * @param ranges the ranges to shape to + * @param defaultContext the default initial context + * @return a contextual shaper which will target any of these ranges + * @throws IllegalArgumentException if the ranges argument specifies an + * unrecognized range, or if the defaultContext argument does not specify + * a single valid range + */ + public static NumericShaper getContextualShaper (int ranges, + int defaultContext) + { + if (Integer.bitCount(defaultContext) != 1) + throw new IllegalArgumentException("more than one bit set in context"); + if ((ranges & ~ALL_RANGES) != 0 || (defaultContext & ~ALL_RANGES) != 0) + throw new IllegalArgumentException("argument out of range"); + return new NumericShaper(defaultContext, ranges); + } +} diff --git a/libjava/classpath/java/awt/font/OpenType.java b/libjava/classpath/java/awt/font/OpenType.java new file mode 100644 index 000000000..e992d0700 --- /dev/null +++ b/libjava/classpath/java/awt/font/OpenType.java @@ -0,0 +1,111 @@ +/* OpenType.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +/** + * @author Michael Koch + */ +public interface OpenType +{ + int TAG_ACNT = 1633906292; + int TAG_AVAR = 1635148146; + int TAG_BASE = 1111577413; + int TAG_BDAT = 1650745716; + int TAG_BLOC = 1651273571; + int TAG_BSLN = 1651731566; + int TAG_CFF = 1128678944; + int TAG_CMAP = 1668112752; + int TAG_CVAR = 1668702578; + int TAG_CVT = 1668707360; + int TAG_DSIG = 1146308935; + int TAG_EBDT = 1161970772; + int TAG_EBLC = 1161972803; + int TAG_EBSC = 1161974595; + int TAG_FDSC = 1717859171; + int TAG_FEAT = 1717920116; + int TAG_FMTX = 1718449272; + int TAG_FPGM = 1718642541; + int TAG_FVAR = 1719034226; + int TAG_GASP = 1734439792; + int TAG_GDEF = 1195656518; + int TAG_GLYF = 1735162214; + int TAG_GPOS = 1196445523; + int TAG_GSUB = 1196643650; + int TAG_GVAR = 1735811442; + int TAG_HDMX = 1751412088; + int TAG_HEAD = 1751474532; + int TAG_HHEA = 1751672161; + int TAG_HMTX = 1752003704; + int TAG_JSTF = 1246975046; + int TAG_JUST = 1786082164; + int TAG_KERN = 1801810542; + int TAG_LCAR = 1818452338; + int TAG_LOCA = 1819239265; + int TAG_LTSH = 1280594760; + int TAG_MAXP = 1835104368; + int TAG_MMFX = 1296909912; + int TAG_MMSD = 1296913220; + int TAG_MORT = 1836020340; + int TAG_NAME = 1851878757; + int TAG_OPBD = 1836020340; + int TAG_OS2 = 1330851634; + int TAG_PCLT = 1346587732; + int TAG_POST = 1886352244; + int TAG_PREP = 1886545264; + int TAG_PROP = 1886547824; + int TAG_TRAK = 1953653099; + int TAG_TYP1 = 1954115633; + int TAG_VDMX = 1447316824; + int TAG_VHEA = 1986553185; + int TAG_VMTX = 1986884728; + + byte[] getFontTable (int sfntTag); + + byte[] getFontTable (int sfntTag, int offset, int count); + + byte[] getFontTable (String strSfntTag); + + byte[] getFontTable (String strSfntTag, int offset, int count); + + int getFontTableSize (int sfntTag); + + int getFontTableSize (String strSfntTag); + + int getVersion (); +} diff --git a/libjava/classpath/java/awt/font/ShapeGraphicAttribute.java b/libjava/classpath/java/awt/font/ShapeGraphicAttribute.java new file mode 100644 index 000000000..8d6891632 --- /dev/null +++ b/libjava/classpath/java/awt/font/ShapeGraphicAttribute.java @@ -0,0 +1,185 @@ +/* ShapeGraphicAttribute.java + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; + +/** + * This is an implementation of GraphicAttribute that draws shapes in a TextLayout. + * + * @author Lillian Angel (langel at redhat dot com) + */ +public final class ShapeGraphicAttribute extends GraphicAttribute +{ + /** True if the shape should be filled. */ + public static final boolean FILL = false; + + /** True if the shape should be stroked with a 1-pixel wide stroke. */ + public static final boolean STROKE = true; + + private Shape shape; + private boolean stroke; + private Rectangle2D bounds; + + /** + * Constructor. + * + * @param shape - the Shape to render. The Shape is rendered with its origin. + * @param alignment - the alignment + * @param stroke - true if the Shape should be stroked; false if the Shape + * should be filled. + */ + public ShapeGraphicAttribute(Shape shape, int alignment, boolean stroke) + { + super(alignment); + this.shape = shape; + this.stroke = stroke; + this.bounds = shape.getBounds2D(); + } + + /** + * Draws the graphic at the given location. + * + * @param graphics - the graphics to use. + * @param x - the x location to draw at. + * @param y - the y location to draw at. + */ + public void draw(Graphics2D graphics, float x, float y) + { + graphics.translate(x, y); + if (stroke == STROKE) + graphics.draw(shape); + else + graphics.fill(shape); + graphics.translate(- x, - y); + } + + /** + * Compares this ShapeGraphicAttribute to obj. + * + * @param obj - the object to compare. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof ShapeGraphicAttribute)) + return false; + + return equals((ShapeGraphicAttribute) obj); + } + + /** + * Compares this ShapeGraphicAttribute to rhs. + * + * @param rhs - the ShapeGraphicAttribute to compare. + */ + public boolean equals(ShapeGraphicAttribute rhs) + { + return (this == rhs || (this.shape.equals(rhs.shape) + && getAlignment() == rhs.getAlignment() + && stroke == rhs.stroke + && getAdvance() == rhs.getAdvance() + && getAscent() == rhs.getAscent() + && getBounds().equals(rhs.getBounds()) + && getDescent() == rhs.getDescent() + && hashCode() == rhs.hashCode())); + } + + /** + * Gets the distance from the origin of its Shape to the right side of the + * bounds of its Shape. + * + * @return the advance + */ + public float getAdvance() + { + return Math.max(0, (float) bounds.getMaxX()); + } + + /** + * Gets the positive distance from the origin of its Shape to the top of + * bounds. + * + * @return the ascent + */ + public float getAscent() + { + return Math.max(0, -(float) bounds.getMinY()); + } + + /** + * Gets the distance from the origin of its Shape to the bottom of the bounds. + * + * @return the descent + */ + public float getDescent() + { + return Math.max(0, (float) bounds.getMaxY()); + } + + /** + * Returns a Rectangle2D that encloses all of the bits drawn by this shape. + * + * @return the bounds of the shape. + */ + public Rectangle2D getBounds() + { + Rectangle2D.Float bounds = new Rectangle2D.Float(); + bounds.setRect(this.bounds); + + if (stroke == STROKE) + { + bounds.width++; + bounds.height++; + } + + return bounds; + } + + /** + * Gets the hash code. + * + * @return the hash code. + */ + public int hashCode() + { + return shape.hashCode(); + } +} diff --git a/libjava/classpath/java/awt/font/TextAttribute.java b/libjava/classpath/java/awt/font/TextAttribute.java new file mode 100644 index 000000000..bfade21bb --- /dev/null +++ b/libjava/classpath/java/awt/font/TextAttribute.java @@ -0,0 +1,309 @@ +/* TextAttribute.java -- + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.io.InvalidObjectException; +import java.text.AttributedCharacterIterator; + +/** + * Attributes (and associated values) that can be used to define an + * {@link java.text.AttributedString}. + */ +public final class TextAttribute extends AttributedCharacterIterator.Attribute +{ + private static final long serialVersionUID = 7744112784117861702L; + + /** A key for the background paint attribute. */ + public static final TextAttribute BACKGROUND = + new TextAttribute("background"); + + /** A key for the BIDI_EMBEDDING attribute. */ + public static final TextAttribute BIDI_EMBEDDING = + new TextAttribute("bidi_embedding"); + + /** A key for the CHAR_REPLACEMENT attribute. */ + public static final TextAttribute CHAR_REPLACEMENT = + new TextAttribute("char_replacement"); + + /** A key for the FAMILY attribute. */ + public static final TextAttribute FAMILY = new TextAttribute("family"); + + /** A key for the font attribute. */ + public static final TextAttribute FONT = new TextAttribute("font"); + + /** A key for the foreground paint attribute. */ + public static final TextAttribute FOREGROUND = + new TextAttribute("foreground"); + + /** A key for the INPUT_METHOD_HIGHLIGHT attribute. */ + public static final TextAttribute INPUT_METHOD_HIGHLIGHT = + new TextAttribute("input method highlight"); + + /** A key for the INPUT_METHOD_UNDERLINE attribute. */ + public static final TextAttribute INPUT_METHOD_UNDERLINE = + new TextAttribute("input method underline"); + + /** A key for the text justification attribute. */ + public static final TextAttribute JUSTIFICATION = + new TextAttribute("justification"); + + /** + * A value that can be used with the {@link #JUSTIFICATION} attribute to + * indicate full justification of the text. + */ + public static final Float JUSTIFICATION_FULL = new Float(1.0); + + /** + * A value that can be used with the {@link #JUSTIFICATION} attribute to + * indicate no justification of the text. + */ + public static final Float JUSTIFICATION_NONE = new Float(0.0); + + /** A key for the NUMERIC_SHAPING attribute. */ + public static final TextAttribute NUMERIC_SHAPING = + new TextAttribute("numeric_shaping"); + + /** A key for the POSTURE attribute. */ + public static final TextAttribute POSTURE = new TextAttribute("posture"); + + /** A value that can be used with the {@link #POSTURE} attribute. */ + public static final Float POSTURE_OBLIQUE = new Float(0.2); + + /** A value that can be used with the {@link #POSTURE} attribute. */ + public static final Float POSTURE_REGULAR = new Float(0.0); + + /** A key for the RUN_DIRECTION attribute. */ + public static final TextAttribute RUN_DIRECTION = + new TextAttribute("run_direction"); + + /** A value that can be used with the {@link #RUN_DIRECTION} attribute. */ + public static final Boolean RUN_DIRECTION_LTR = Boolean.FALSE; + + /** A value that can be used with the {@link #RUN_DIRECTION} attribute. */ + public static final Boolean RUN_DIRECTION_RTL = Boolean.TRUE; + + /** A key for the text size attribute. */ + public static final TextAttribute SIZE = new TextAttribute("size"); + + /** A key for the STRIKETHROUGH attribute. */ + public static final TextAttribute STRIKETHROUGH = + new TextAttribute("strikethrough"); + + /** A value that can be used with the {@link #STRIKETHROUGH} attribute. */ + public static final Boolean STRIKETHROUGH_ON = Boolean.TRUE; + + /** A key for the SUPERSCRIPT attribute. */ + public static final TextAttribute SUPERSCRIPT = + new TextAttribute("superscript"); + + /** A value that can be used with the {@link #SUPERSCRIPT} attribute. */ + public static final Integer SUPERSCRIPT_SUB = new Integer(-1); + + /** A value that can be used with the {@link #SUPERSCRIPT} attribute. */ + public static final Integer SUPERSCRIPT_SUPER = new Integer(1); + + /** A key for the SWAP_COLORS attribute. */ + public static final TextAttribute SWAP_COLORS = + new TextAttribute("swap_colors"); + + /** A value that can be used with the {@link #SWAP_COLORS} attribute. */ + public static final Boolean SWAP_COLORS_ON = Boolean.TRUE; + + /** A key for the TRANFORM attribute. */ + public static final TextAttribute TRANSFORM = new TextAttribute("transform"); + + /** A key for the UNDERLINE attribute. */ + public static final TextAttribute UNDERLINE = new TextAttribute("underline"); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_DASHED = new Integer(5); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_DOTTED = new Integer(3); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_GRAY = new Integer(4); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_ONE_PIXEL = new Integer(1); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_LOW_TWO_PIXEL = new Integer(2); + + /** A value that can be used with the {@link #UNDERLINE} attribute. */ + public static final Integer UNDERLINE_ON = new Integer(0); + + /** A key for the WEIGHT attribute. */ + public static final TextAttribute WEIGHT = new TextAttribute("weight"); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_BOLD = new Float(2.0); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_DEMIBOLD = new Float(1.75); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_DEMILIGHT = new Float(0.875); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_EXTRA_LIGHT = new Float(0.5); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_EXTRABOLD = new Float(2.5); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_HEAVY = new Float(2.25); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_LIGHT = new Float(0.75); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_MEDIUM = new Float(1.5); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_REGULAR = new Float(1.0); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_SEMIBOLD = new Float(1.25); + + /** A value that can be used with the {@link #WEIGHT} attribute. */ + public static final Float WEIGHT_ULTRABOLD = new Float(2.75); + + /** A key for the WIDTH attribute. */ + public static final TextAttribute WIDTH = new TextAttribute("width"); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_CONDENSED = new Float(0.75); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_EXTENDED = new Float(1.5); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_REGULAR = new Float(1.0); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_SEMI_CONDENSED = new Float(0.875); + + /** A value that can be used with the {@link #WIDTH} attribute. */ + public static final Float WIDTH_SEMI_EXTENDED = new Float(1.25); + + /** + * Creates a new attribute. + * + * @param name the name. + */ + protected TextAttribute(String name) + { + super(name); + } + + /** + * After deserialization, this method ensures that only one instance of + * each attribute is used. + * + * @return The (single) attribute instance. + * + * @throws InvalidObjectException if the attribute is not recognised. + */ + protected Object readResolve() + throws InvalidObjectException + { + if (this.getName().equals("background")) + return BACKGROUND; + + if (this.getName().equals("bidi_embedding")) + return BIDI_EMBEDDING; + + if (this.getName().equals("char_replacement")) + return CHAR_REPLACEMENT; + + if (this.getName().equals("family")) + return FAMILY; + + if (this.getName().equals("font")) + return FONT; + + if (this.getName().equals("foreground")) + return FOREGROUND; + + if (this.getName().equals("input method highlight")) + return INPUT_METHOD_HIGHLIGHT; + + if (this.getName().equals("input method underline")) + return INPUT_METHOD_UNDERLINE; + + if (this.getName().equals("justification")) + return JUSTIFICATION; + + if (this.getName().equals("numeric_shaping")) + return NUMERIC_SHAPING; + + if (this.getName().equals("posture")) + return POSTURE; + + if (this.getName().equals("run_direction")) + return RUN_DIRECTION; + + if (this.getName().equals("size")) + return SIZE; + + if (this.getName().equals("strikethrough")) + return STRIKETHROUGH; + + if (this.getName().equals("superscript")) + return SUPERSCRIPT; + + if (this.getName().equals("swap_colors")) + return SWAP_COLORS; + + if (this.getName().equals("transform")) + return TRANSFORM; + + if (this.getName().equals("underline")) + return UNDERLINE; + + if (this.getName().equals("weight")) + return WEIGHT; + + if (this.getName().equals("width")) + return WIDTH; + + throw new InvalidObjectException("Can't resolve Attribute: " + getName()); + } +} diff --git a/libjava/classpath/java/awt/font/TextHitInfo.java b/libjava/classpath/java/awt/font/TextHitInfo.java new file mode 100644 index 000000000..17479b09e --- /dev/null +++ b/libjava/classpath/java/awt/font/TextHitInfo.java @@ -0,0 +1,128 @@ +/* TextHitInfo.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.font; + +/** + * @author John Leuner (jewel@debian.org) + */ +public final class TextHitInfo +{ + private int charIndex; + private boolean leadingEdge; + + TextHitInfo (int charIndex, boolean leadingEdge) + { + this.charIndex = charIndex; + this.leadingEdge = leadingEdge; + } + + public int getCharIndex() + { + return charIndex; + } + + public boolean isLeadingEdge() + { + return leadingEdge; + } + + public int getInsertionIndex() + { + return (leadingEdge ? charIndex : charIndex + 1); + } + + public int hashCode() + { + return charIndex; + } + + public boolean equals(Object obj) + { + if(obj instanceof TextHitInfo) + return this.equals((TextHitInfo) obj); + + return false; + } + + public boolean equals(TextHitInfo hitInfo) + { + if (hitInfo == null) + return false; + + return (charIndex == hitInfo.getCharIndex ()) + && (leadingEdge == hitInfo.isLeadingEdge ()); + } + + public static TextHitInfo leading(int charIndex) + { + return new TextHitInfo (charIndex, true); + } + + public static TextHitInfo trailing(int charIndex) + { + return new TextHitInfo (charIndex, false); + } + + public static TextHitInfo beforeOffset(int offset) + { + return new TextHitInfo ((offset - 1), false); + } + + public static TextHitInfo afterOffset(int offset) + { + return new TextHitInfo (offset, true); + } + + public TextHitInfo getOtherHit() + { + return (leadingEdge ? trailing (charIndex - 1) : leading (charIndex + 1)); + } + + public TextHitInfo getOffsetHit(int offset) + { + return new TextHitInfo (charIndex + offset, leadingEdge); + } + + public String toString() + { + return "TextHitInfo[" + + charIndex + + (leadingEdge ? "L" : "T" ) + + "]"; + } +} diff --git a/libjava/classpath/java/awt/font/TextLayout.java b/libjava/classpath/java/awt/font/TextLayout.java new file mode 100644 index 000000000..c4f174245 --- /dev/null +++ b/libjava/classpath/java/awt/font/TextLayout.java @@ -0,0 +1,1420 @@ +/* TextLayout.java -- + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.text.CharacterIterator; +import java.text.AttributedCharacterIterator; +import java.text.Bidi; +import java.util.ArrayList; +import java.util.Map; + +/** + * @author Sven de Marothy + */ +public final class TextLayout implements Cloneable +{ + /** + * Holds the layout data that belongs to one run of characters. + */ + private class Run + { + /** + * The actual glyph vector. + */ + GlyphVector glyphVector; + + /** + * The font for this text run. + */ + Font font; + + /** + * The start of the run. + */ + int runStart; + + /** + * The end of the run. + */ + int runEnd; + + /** + * The layout location of the beginning of the run. + */ + float location; + + /** + * Initializes the Run instance. + * + * @param gv the glyph vector + * @param start the start index of the run + * @param end the end index of the run + */ + Run(GlyphVector gv, Font f, int start, int end) + { + glyphVector = gv; + font = f; + runStart = start; + runEnd = end; + } + + /** + * Returns true when this run is left to right, + * false otherwise. + * + * @return true when this run is left to right, + * false otherwise + */ + boolean isLeftToRight() + { + return (glyphVector.getLayoutFlags() & GlyphVector.FLAG_RUN_RTL) == 0; + } + } + + /** + * The laid out character runs. + */ + private Run[] runs; + + private FontRenderContext frc; + private char[] string; + private int offset; + private int length; + private Rectangle2D boundsCache; + private LineMetrics lm; + + /** + * The total advance of this text layout. This is cache for maximum + * performance. + */ + private float totalAdvance = -1F; + + /** + * The cached natural bounds. + */ + private Rectangle2D naturalBounds; + + /** + * Character indices. + * Fixt index is the glyphvector, second index is the (first) glyph. + */ + private int[][] charIndices; + + /** + * Base directionality, determined from the first char. + */ + private boolean leftToRight; + + /** + * Whether this layout contains whitespace or not. + */ + private boolean hasWhitespace = false; + + /** + * The {@link Bidi} object that is used for reordering and by + * {@link #getCharacterLevel(int)}. + */ + private Bidi bidi; + + /** + * Mpas the logical position of each individual character in the original + * string to its visual position. + */ + private int[] logicalToVisual; + + /** + * Maps visual positions of a character to its logical position + * in the original string. + */ + private int[] visualToLogical; + + /** + * The cached hashCode. + */ + private int hash; + + /** + * The default caret policy. + */ + public static final TextLayout.CaretPolicy DEFAULT_CARET_POLICY = + new CaretPolicy(); + + /** + * Constructs a TextLayout. + */ + public TextLayout (String str, Font font, FontRenderContext frc) + { + this.frc = frc; + string = str.toCharArray(); + offset = 0; + length = this.string.length; + lm = font.getLineMetrics(this.string, offset, length, frc); + + // Get base direction and whitespace info + getStringProperties(); + + if (Bidi.requiresBidi(string, offset, offset + length)) + { + bidi = new Bidi(str, leftToRight ? Bidi.DIRECTION_LEFT_TO_RIGHT + : Bidi.DIRECTION_RIGHT_TO_LEFT ); + int rc = bidi.getRunCount(); + byte[] table = new byte[ rc ]; + for(int i = 0; i < table.length; i++) + table[i] = (byte)bidi.getRunLevel(i); + + runs = new Run[rc]; + for(int i = 0; i < rc; i++) + { + int start = bidi.getRunStart(i); + int end = bidi.getRunLimit(i); + if(start != end) // no empty runs. + { + GlyphVector gv = font.layoutGlyphVector(frc, + string, start, end, + ((table[i] & 1) == 0) ? Font.LAYOUT_LEFT_TO_RIGHT + : Font.LAYOUT_RIGHT_TO_LEFT ); + runs[i] = new Run(gv, font, start, end); + } + } + Bidi.reorderVisually( table, 0, runs, 0, runs.length ); + // Clean up null runs. + ArrayList cleaned = new ArrayList(rc); + for (int i = 0; i < rc; i++) + { + if (runs[i] != null) + cleaned.add(runs[i]); + } + runs = new Run[cleaned.size()]; + runs = (Run[]) cleaned.toArray(runs); + } + else + { + GlyphVector gv = font.layoutGlyphVector( frc, string, offset, length, + leftToRight ? Font.LAYOUT_LEFT_TO_RIGHT + : Font.LAYOUT_RIGHT_TO_LEFT ); + Run run = new Run(gv, font, 0, length); + runs = new Run[]{ run }; + } + setCharIndices(); + setupMappings(); + layoutRuns(); + } + + public TextLayout (String string, + Map attributes, + FontRenderContext frc) + { + this( string, new Font( attributes ), frc ); + } + + public TextLayout (AttributedCharacterIterator text, FontRenderContext frc) + { + // FIXME: Very rudimentary. + this(getText(text), getFont(text), frc); + } + + /** + * Package-private constructor to make a textlayout from an existing one. + * This is used by TextMeasurer for returning sub-layouts, and it + * saves a lot of time in not having to relayout the text. + */ + TextLayout(TextLayout t, int startIndex, int endIndex) + { + frc = t.frc; + boundsCache = null; + lm = t.lm; + leftToRight = t.leftToRight; + + if( endIndex > t.getCharacterCount() ) + endIndex = t.getCharacterCount(); + string = t.string; + offset = startIndex + offset; + length = endIndex - startIndex; + + int startingRun = t.charIndices[startIndex][0]; + int nRuns = 1 + t.charIndices[endIndex - 1][0] - startingRun; + + runs = new Run[nRuns]; + for( int i = 0; i < nRuns; i++ ) + { + Run run = t.runs[i + startingRun]; + GlyphVector gv = run.glyphVector; + Font font = run.font; + // Copy only the relevant parts of the first and last runs. + int beginGlyphIndex = (i > 0) ? 0 : t.charIndices[startIndex][1]; + int numEntries = ( i < nRuns - 1) ? gv.getNumGlyphs() : + 1 + t.charIndices[endIndex - 1][1] - beginGlyphIndex; + + int[] codes = gv.getGlyphCodes(beginGlyphIndex, numEntries, null); + gv = font.createGlyphVector(frc, codes); + runs[i] = new Run(gv, font, run.runStart - startIndex, + run.runEnd - startIndex); + } + runs[nRuns - 1].runEnd = endIndex - 1; + + setCharIndices(); + setupMappings(); + determineWhiteSpace(); + layoutRuns(); + } + + private void setCharIndices() + { + charIndices = new int[ getCharacterCount() ][2]; + int i = 0; + int currentChar = 0; + for(int run = 0; run < runs.length; run++) + { + currentChar = -1; + Run current = runs[run]; + GlyphVector gv = current.glyphVector; + for( int gi = 0; gi < gv.getNumGlyphs(); gi++) + { + if( gv.getGlyphCharIndex( gi ) != currentChar ) + { + charIndices[ i ][0] = run; + charIndices[ i ][1] = gi; + currentChar = gv.getGlyphCharIndex( gi ); + i++; + } + } + } + } + + /** + * Initializes the logicalToVisual and visualToLogial maps. + */ + private void setupMappings() + { + int numChars = getCharacterCount(); + logicalToVisual = new int[numChars]; + visualToLogical = new int[numChars]; + int lIndex = 0; + int vIndex = 0; + // We scan the runs in visual order and set the mappings accordingly. + for (int i = 0; i < runs.length; i++) + { + Run run = runs[i]; + if (run.isLeftToRight()) + { + for (lIndex = run.runStart; lIndex < run.runEnd; lIndex++) + { + logicalToVisual[lIndex] = vIndex; + visualToLogical[vIndex] = lIndex; + vIndex++; + } + } + else + { + for (lIndex = run.runEnd - 1; lIndex >= run.runStart; lIndex--) + { + logicalToVisual[lIndex] = vIndex; + visualToLogical[vIndex] = lIndex; + vIndex++; + } + } + } + } + + private static String getText(AttributedCharacterIterator iter) + { + CPStringBuilder sb = new CPStringBuilder(); + int idx = iter.getIndex(); + for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) + sb.append(c); + iter.setIndex( idx ); + return sb.toString(); + } + + private static Font getFont(AttributedCharacterIterator iter) + { + Font f = (Font)iter.getAttribute(TextAttribute.FONT); + if( f == null ) + { + int size; + Float i = (Float)iter.getAttribute(TextAttribute.SIZE); + if( i != null ) + size = (int)i.floatValue(); + else + size = 14; + f = new Font("Dialog", Font.PLAIN, size ); + } + return f; + } + + /** + * Scan the character run for the first strongly directional character, + * which in turn defines the base directionality of the whole layout. + */ + private void getStringProperties() + { + boolean gotDirection = false; + int i = offset; + int endOffs = offset + length; + leftToRight = true; + while( i < endOffs && !gotDirection ) + switch( Character.getDirectionality(string[i++]) ) + { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + gotDirection = true; + break; + + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + leftToRight = false; + gotDirection = true; + break; + } + determineWhiteSpace(); + } + + private void determineWhiteSpace() + { + // Determine if there's whitespace in the thing. + // Ignore trailing chars. + int i = offset + length - 1; + hasWhitespace = false; + while( i >= offset && Character.isWhitespace( string[i] ) ) + i--; + // Check the remaining chars + while( i >= offset ) + if( Character.isWhitespace( string[i--] ) ) + hasWhitespace = true; + } + + protected Object clone () + { + return new TextLayout( this, 0, length); + } + + public void draw (Graphics2D g2, float x, float y) + { + for(int i = 0; i < runs.length; i++) + { + Run run = runs[i]; + GlyphVector gv = run.glyphVector; + g2.drawGlyphVector(gv, x, y); + Rectangle2D r = gv.getLogicalBounds(); + x += r.getWidth(); + } + } + + public boolean equals (Object obj) + { + if( !( obj instanceof TextLayout) ) + return false; + + return equals( (TextLayout) obj ); + } + + public boolean equals (TextLayout tl) + { + if( runs.length != tl.runs.length ) + return false; + // Compare all glyph vectors. + for( int i = 0; i < runs.length; i++ ) + if( !runs[i].equals( tl.runs[i] ) ) + return false; + return true; + } + + public float getAdvance () + { + if (totalAdvance == -1F) + { + totalAdvance = 0f; + for(int i = 0; i < runs.length; i++) + { + Run run = runs[i]; + GlyphVector gv = run.glyphVector; + totalAdvance += gv.getLogicalBounds().getWidth(); + } + } + return totalAdvance; + } + + public float getAscent () + { + return lm.getAscent(); + } + + public byte getBaseline () + { + return (byte)lm.getBaselineIndex(); + } + + public float[] getBaselineOffsets () + { + return lm.getBaselineOffsets(); + } + + public Shape getBlackBoxBounds (int firstEndpoint, int secondEndpoint) + { + if( secondEndpoint - firstEndpoint <= 0 ) + return new Rectangle2D.Float(); // Hmm? + + if( firstEndpoint < 0 || secondEndpoint > getCharacterCount()) + return new Rectangle2D.Float(); + + GeneralPath gp = new GeneralPath(); + + int ri = charIndices[ firstEndpoint ][0]; + int gi = charIndices[ firstEndpoint ][1]; + + double advance = 0; + + for( int i = 0; i < ri; i++ ) + { + Run run = runs[i]; + GlyphVector gv = run.glyphVector; + advance += gv.getLogicalBounds().getWidth(); + } + + for( int i = ri; i <= charIndices[ secondEndpoint - 1 ][0]; i++ ) + { + Run run = runs[i]; + GlyphVector gv = run.glyphVector; + int dg; + if( i == charIndices[ secondEndpoint - 1 ][0] ) + dg = charIndices[ secondEndpoint - 1][1]; + else + dg = gv.getNumGlyphs() - 1; + + for( int j = 0; j <= dg; j++ ) + { + Rectangle2D r2 = (gv.getGlyphVisualBounds( j )). + getBounds2D(); + Point2D p = gv.getGlyphPosition( j ); + r2.setRect( advance + r2.getX(), r2.getY(), + r2.getWidth(), r2.getHeight() ); + gp.append(r2, false); + } + + advance += gv.getLogicalBounds().getWidth(); + } + return gp; + } + + public Rectangle2D getBounds() + { + if( boundsCache == null ) + boundsCache = getOutline(new AffineTransform()).getBounds(); + return boundsCache; + } + + public float[] getCaretInfo (TextHitInfo hit) + { + return getCaretInfo(hit, getNaturalBounds()); + } + + public float[] getCaretInfo (TextHitInfo hit, Rectangle2D bounds) + { + float[] info = new float[2]; + int index = hit.getCharIndex(); + boolean leading = hit.isLeadingEdge(); + // For the boundary cases we return the boundary runs. + Run run; + + if (index >= length) + { + info[0] = getAdvance(); + info[1] = 0; + } + else + { + if (index < 0) + { + run = runs[0]; + index = 0; + leading = true; + } + else + run = findRunAtIndex(index); + + int glyphIndex = index - run.runStart; + Shape glyphBounds = run.glyphVector.getGlyphLogicalBounds(glyphIndex); + Rectangle2D glyphRect = glyphBounds.getBounds2D(); + if (isVertical()) + { + if (leading) + info[0] = (float) glyphRect.getMinY(); + else + info[0] = (float) glyphRect.getMaxY(); + } + else + { + if (leading) + info[0] = (float) glyphRect.getMinX(); + else + info[0] = (float) glyphRect.getMaxX(); + } + info[0] += run.location; + info[1] = run.font.getItalicAngle(); + } + return info; + } + + public Shape getCaretShape(TextHitInfo hit) + { + return getCaretShape(hit, getBounds()); + } + + public Shape getCaretShape(TextHitInfo hit, Rectangle2D bounds) + { + // TODO: Handle vertical shapes somehow. + float[] info = getCaretInfo(hit); + float x1 = info[0]; + float y1 = (float) bounds.getMinY(); + float x2 = info[0]; + float y2 = (float) bounds.getMaxY(); + if (info[1] != 0) + { + // Shift x1 and x2 according to the slope. + x1 -= y1 * info[1]; + x2 -= y2 * info[1]; + } + GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 2); + path.moveTo(x1, y1); + path.lineTo(x2, y2); + return path; + } + + public Shape[] getCaretShapes(int offset) + { + return getCaretShapes(offset, getNaturalBounds()); + } + + public Shape[] getCaretShapes(int offset, Rectangle2D bounds) + { + return getCaretShapes(offset, bounds, DEFAULT_CARET_POLICY); + } + + public Shape[] getCaretShapes(int offset, Rectangle2D bounds, + CaretPolicy policy) + { + // The RI returns a 2-size array even when there's only one + // shape in it. + Shape[] carets = new Shape[2]; + TextHitInfo hit1 = TextHitInfo.afterOffset(offset); + int caretHit1 = hitToCaret(hit1); + TextHitInfo hit2 = hit1.getOtherHit(); + int caretHit2 = hitToCaret(hit2); + if (caretHit1 == caretHit2) + { + carets[0] = getCaretShape(hit1); + carets[1] = null; // The RI returns null in this seldom case. + } + else + { + Shape caret1 = getCaretShape(hit1); + Shape caret2 = getCaretShape(hit2); + TextHitInfo strong = policy.getStrongCaret(hit1, hit2, this); + if (strong == hit1) + { + carets[0] = caret1; + carets[1] = caret2; + } + else + { + carets[0] = caret2; + carets[1] = caret1; + } + } + return carets; + } + + public int getCharacterCount () + { + return length; + } + + public byte getCharacterLevel (int index) + { + byte level; + if( bidi == null ) + level = 0; + else + level = (byte) bidi.getLevelAt(index); + return level; + } + + public float getDescent () + { + return lm.getDescent(); + } + + public TextLayout getJustifiedLayout (float justificationWidth) + { + TextLayout newLayout = (TextLayout)clone(); + + if( hasWhitespace ) + newLayout.handleJustify( justificationWidth ); + + return newLayout; + } + + public float getLeading () + { + return lm.getLeading(); + } + + public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint) + { + return getLogicalHighlightShape( firstEndpoint, secondEndpoint, + getBounds() ); + } + + public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint, + Rectangle2D bounds) + { + if( secondEndpoint - firstEndpoint <= 0 ) + return new Rectangle2D.Float(); // Hmm? + + if( firstEndpoint < 0 || secondEndpoint > getCharacterCount()) + return new Rectangle2D.Float(); + + Rectangle2D r = null; + int ri = charIndices[ firstEndpoint ][0]; + int gi = charIndices[ firstEndpoint ][1]; + + double advance = 0; + + for( int i = 0; i < ri; i++ ) + advance += runs[i].glyphVector.getLogicalBounds().getWidth(); + + for( int i = ri; i <= charIndices[ secondEndpoint - 1 ][0]; i++ ) + { + Run run = runs[i]; + GlyphVector gv = run.glyphVector; + int dg; // last index in this run to use. + if( i == charIndices[ secondEndpoint - 1 ][0] ) + dg = charIndices[ secondEndpoint - 1][1]; + else + dg = gv.getNumGlyphs() - 1; + + for(; gi <= dg; gi++ ) + { + Rectangle2D r2 = (gv.getGlyphLogicalBounds( gi )). + getBounds2D(); + if( r == null ) + r = r2; + else + r = r.createUnion(r2); + } + gi = 0; // reset glyph index into run for next run. + + advance += gv.getLogicalBounds().getWidth(); + } + + return r; + } + + public int[] getLogicalRangesForVisualSelection (TextHitInfo firstEndpoint, + TextHitInfo secondEndpoint) + { + // Check parameters. + checkHitInfo(firstEndpoint); + checkHitInfo(secondEndpoint); + + // Convert to visual and order correctly. + int start = hitToCaret(firstEndpoint); + int end = hitToCaret(secondEndpoint); + if (start > end) + { + // Swap start and end so that end >= start. + int temp = start; + start = end; + end = temp; + } + + // Now walk through the visual indices and mark the included pieces. + boolean[] include = new boolean[length]; + for (int i = start; i < end; i++) + { + include[visualToLogical[i]] = true; + } + + // Count included runs. + int numRuns = 0; + boolean in = false; + for (int i = 0; i < length; i++) + { + if (include[i] != in) // At each run in/out point we toggle the in var. + { + in = ! in; + if (in) // At each run start we count up. + numRuns++; + } + } + + // Put together the ranges array. + int[] ranges = new int[numRuns * 2]; + int index = 0; + in = false; + for (int i = 0; i < length; i++) + { + if (include[i] != in) + { + ranges[index] = i; + index++; + in = ! in; + } + } + // If the last run ends at the very end, include that last bit too. + if (in) + ranges[index] = length; + + return ranges; + } + + public TextHitInfo getNextLeftHit(int offset) + { + return getNextLeftHit(offset, DEFAULT_CARET_POLICY); + } + + public TextHitInfo getNextLeftHit(int offset, CaretPolicy policy) + { + if (policy == null) + throw new IllegalArgumentException("Null policy not allowed"); + if (offset < 0 || offset > length) + throw new IllegalArgumentException("Offset out of bounds"); + + TextHitInfo hit1 = TextHitInfo.afterOffset(offset); + TextHitInfo hit2 = hit1.getOtherHit(); + + TextHitInfo strong = policy.getStrongCaret(hit1, hit2, this); + TextHitInfo next = getNextLeftHit(strong); + TextHitInfo ret = null; + if (next != null) + { + TextHitInfo next2 = getVisualOtherHit(next); + ret = policy.getStrongCaret(next2, next, this); + } + return ret; + } + + public TextHitInfo getNextLeftHit (TextHitInfo hit) + { + checkHitInfo(hit); + int index = hitToCaret(hit); + TextHitInfo next = null; + if (index != 0) + { + index--; + next = caretToHit(index); + } + return next; + } + + public TextHitInfo getNextRightHit(int offset) + { + return getNextRightHit(offset, DEFAULT_CARET_POLICY); + } + + public TextHitInfo getNextRightHit(int offset, CaretPolicy policy) + { + if (policy == null) + throw new IllegalArgumentException("Null policy not allowed"); + if (offset < 0 || offset > length) + throw new IllegalArgumentException("Offset out of bounds"); + + TextHitInfo hit1 = TextHitInfo.afterOffset(offset); + TextHitInfo hit2 = hit1.getOtherHit(); + + TextHitInfo next = getNextRightHit(policy.getStrongCaret(hit1, hit2, this)); + TextHitInfo ret = null; + if (next != null) + { + TextHitInfo next2 = getVisualOtherHit(next); + ret = policy.getStrongCaret(next2, next, this); + } + return ret; + } + + public TextHitInfo getNextRightHit(TextHitInfo hit) + { + checkHitInfo(hit); + int index = hitToCaret(hit); + TextHitInfo next = null; + if (index < length) + { + index++; + next = caretToHit(index); + } + return next; + } + + public Shape getOutline (AffineTransform tx) + { + float x = 0f; + GeneralPath gp = new GeneralPath(); + for(int i = 0; i < runs.length; i++) + { + GlyphVector gv = runs[i].glyphVector; + gp.append( gv.getOutline( x, 0f ), false ); + Rectangle2D r = gv.getLogicalBounds(); + x += r.getWidth(); + } + if( tx != null ) + gp.transform( tx ); + return gp; + } + + public float getVisibleAdvance () + { + float totalAdvance = 0f; + + if( runs.length <= 0 ) + return 0f; + + // No trailing whitespace + if( !Character.isWhitespace( string[offset + length - 1]) ) + return getAdvance(); + + // Get length of all runs up to the last + for(int i = 0; i < runs.length - 1; i++) + totalAdvance += runs[i].glyphVector.getLogicalBounds().getWidth(); + + int lastRun = runs[runs.length - 1].runStart; + int j = length - 1; + while( j >= lastRun && Character.isWhitespace( string[j] ) ) j--; + + if( j < lastRun ) + return totalAdvance; // entire last run is whitespace + + int lastNonWSChar = j - lastRun; + j = 0; + while( runs[ runs.length - 1 ].glyphVector.getGlyphCharIndex( j ) + <= lastNonWSChar ) + { + totalAdvance += runs[ runs.length - 1 ].glyphVector + .getGlyphLogicalBounds( j ) + .getBounds2D().getWidth(); + j ++; + } + + return totalAdvance; + } + + public Shape getVisualHighlightShape (TextHitInfo firstEndpoint, + TextHitInfo secondEndpoint) + { + return getVisualHighlightShape( firstEndpoint, secondEndpoint, + getBounds() ); + } + + public Shape getVisualHighlightShape (TextHitInfo firstEndpoint, + TextHitInfo secondEndpoint, + Rectangle2D bounds) + { + GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD); + Shape caret1 = getCaretShape(firstEndpoint, bounds); + path.append(caret1, false); + Shape caret2 = getCaretShape(secondEndpoint, bounds); + path.append(caret2, false); + // Append left (top) bounds to selection if necessary. + int c1 = hitToCaret(firstEndpoint); + int c2 = hitToCaret(secondEndpoint); + if (c1 == 0 || c2 == 0) + { + path.append(left(bounds), false); + } + // Append right (bottom) bounds if necessary. + if (c1 == length || c2 == length) + { + path.append(right(bounds), false); + } + return path.getBounds2D(); + } + + /** + * Returns the shape that makes up the left (top) edge of this text layout. + * + * @param b the bounds + * + * @return the shape that makes up the left (top) edge of this text layout + */ + private Shape left(Rectangle2D b) + { + GeneralPath left = new GeneralPath(GeneralPath.WIND_EVEN_ODD); + left.append(getCaretShape(TextHitInfo.beforeOffset(0)), false); + if (isVertical()) + { + float y = (float) b.getMinY(); + left.append(new Line2D.Float((float) b.getMinX(), y, + (float) b.getMaxX(), y), false); + } + else + { + float x = (float) b.getMinX(); + left.append(new Line2D.Float(x, (float) b.getMinY(), + x, (float) b.getMaxY()), false); + } + return left.getBounds2D(); + } + + /** + * Returns the shape that makes up the right (bottom) edge of this text + * layout. + * + * @param b the bounds + * + * @return the shape that makes up the right (bottom) edge of this text + * layout + */ + private Shape right(Rectangle2D b) + { + GeneralPath right = new GeneralPath(GeneralPath.WIND_EVEN_ODD); + right.append(getCaretShape(TextHitInfo.afterOffset(length)), false); + if (isVertical()) + { + float y = (float) b.getMaxY(); + right.append(new Line2D.Float((float) b.getMinX(), y, + (float) b.getMaxX(), y), false); + } + else + { + float x = (float) b.getMaxX(); + right.append(new Line2D.Float(x, (float) b.getMinY(), + x, (float) b.getMaxY()), false); + } + return right.getBounds2D(); + } + + public TextHitInfo getVisualOtherHit (TextHitInfo hit) + { + checkHitInfo(hit); + int hitIndex = hit.getCharIndex(); + + int index; + boolean leading; + if (hitIndex == -1 || hitIndex == length) + { + // Boundary case. + int visual; + if (isLeftToRight() == (hitIndex == -1)) + visual = 0; + else + visual = length - 1; + index = visualToLogical[visual]; + if (isLeftToRight() == (hitIndex == -1)) + leading = isCharacterLTR(index); // LTR. + else + leading = ! isCharacterLTR(index); // RTL. + } + else + { + // Normal case. + int visual = logicalToVisual[hitIndex]; + boolean b; + if (isCharacterLTR(hitIndex) == hit.isLeadingEdge()) + { + visual--; + b = false; + } + else + { + visual++; + b = true; + } + if (visual >= 0 && visual < length) + { + index = visualToLogical[visual]; + leading = b == isLeftToRight(); + } + else + { + index = b == isLeftToRight() ? length : -1; + leading = index == length; + } + } + return leading ? TextHitInfo.leading(index) : TextHitInfo.trailing(index); + } + + /** + * This is a protected method of a final class, meaning + * it exists only to taunt you. + */ + protected void handleJustify (float justificationWidth) + { + // We assume that the text has non-trailing whitespace. + // First get the change in width to insert into the whitespaces. + double deltaW = justificationWidth - getVisibleAdvance(); + int nglyphs = 0; // # of whitespace chars + + // determine last non-whitespace char. + int lastNWS = offset + length - 1; + while( Character.isWhitespace( string[lastNWS] ) ) lastNWS--; + + // locations of the glyphs. + int[] wsglyphs = new int[length * 10]; + for(int run = 0; run < runs.length; run++ ) + { + Run current = runs[run]; + for(int i = 0; i < current.glyphVector.getNumGlyphs(); i++ ) + { + int cindex = current.runStart + + current.glyphVector.getGlyphCharIndex( i ); + if( Character.isWhitespace( string[cindex] ) ) + // && cindex < lastNWS ) + { + wsglyphs[ nglyphs * 2 ] = run; + wsglyphs[ nglyphs * 2 + 1] = i; + nglyphs++; + } + } + } + deltaW = deltaW / nglyphs; // Change in width per whitespace glyph + double w = 0; + int cws = 0; + // Shift all characters + for(int run = 0; run < runs.length; run++ ) + { + Run current = runs[run]; + for(int i = 0; i < current.glyphVector.getNumGlyphs(); i++ ) + { + if( wsglyphs[ cws * 2 ] == run && wsglyphs[ cws * 2 + 1 ] == i ) + { + cws++; // update 'current whitespace' + w += deltaW; // increment the shift + } + Point2D p = current.glyphVector.getGlyphPosition( i ); + p.setLocation( p.getX() + w, p.getY() ); + current.glyphVector.setGlyphPosition( i, p ); + } + } + } + + public TextHitInfo hitTestChar (float x, float y) + { + return hitTestChar(x, y, getNaturalBounds()); + } + + /** + * Finds the character hit at the specified point. This 'clips' this + * text layout against the specified bounds rectangle. That + * means that in the case where a point is outside these bounds, this method + * returns the leading edge of the first character or the trailing edge of + * the last character. + * + * @param x the X location to test + * @param y the Y location to test + * @param bounds the bounds to test against + * + * @return the character hit at the specified point + */ + public TextHitInfo hitTestChar (float x, float y, Rectangle2D bounds) + { + // Check bounds. + if (isVertical()) + { + if (y < bounds.getMinY()) + return TextHitInfo.leading(0); + else if (y > bounds.getMaxY()) + return TextHitInfo.trailing(getCharacterCount() - 1); + } + else + { + if (x < bounds.getMinX()) + return TextHitInfo.leading(0); + else if (x > bounds.getMaxX()) + return TextHitInfo.trailing(getCharacterCount() - 1); + } + + TextHitInfo hitInfo = null; + if (isVertical()) + { + // Search for the run at the location. + // TODO: Perform binary search for maximum efficiency. However, we + // need the run location laid out statically to do that. + int numRuns = runs.length; + Run hitRun = null; + for (int i = 0; i < numRuns && hitRun == null; i++) + { + Run run = runs[i]; + Rectangle2D lBounds = run.glyphVector.getLogicalBounds(); + if (lBounds.getMinY() + run.location <= y + && lBounds.getMaxY() + run.location >= y) + hitRun = run; + } + // Now we have (hopefully) found a run that hits. Now find the + // right character. + if (hitRun != null) + { + GlyphVector gv = hitRun.glyphVector; + for (int i = hitRun.runStart; + i < hitRun.runEnd && hitInfo == null; i++) + { + int gi = i - hitRun.runStart; + Rectangle2D lBounds = gv.getGlyphLogicalBounds(gi) + .getBounds2D(); + if (lBounds.getMinY() + hitRun.location <= y + && lBounds.getMaxY() + hitRun.location >= y) + { + // Found hit. Now check if we are leading or trailing. + boolean leading = true; + if (lBounds.getCenterY() + hitRun.location <= y) + leading = false; + hitInfo = leading ? TextHitInfo.leading(i) + : TextHitInfo.trailing(i); + } + } + } + } + else + { + // Search for the run at the location. + // TODO: Perform binary search for maximum efficiency. However, we + // need the run location laid out statically to do that. + int numRuns = runs.length; + Run hitRun = null; + for (int i = 0; i < numRuns && hitRun == null; i++) + { + Run run = runs[i]; + Rectangle2D lBounds = run.glyphVector.getLogicalBounds(); + if (lBounds.getMinX() + run.location <= x + && lBounds.getMaxX() + run.location >= x) + hitRun = run; + } + // Now we have (hopefully) found a run that hits. Now find the + // right character. + if (hitRun != null) + { + GlyphVector gv = hitRun.glyphVector; + for (int i = hitRun.runStart; + i < hitRun.runEnd && hitInfo == null; i++) + { + int gi = i - hitRun.runStart; + Rectangle2D lBounds = gv.getGlyphLogicalBounds(gi) + .getBounds2D(); + if (lBounds.getMinX() + hitRun.location <= x + && lBounds.getMaxX() + hitRun.location >= x) + { + // Found hit. Now check if we are leading or trailing. + boolean leading = true; + if (lBounds.getCenterX() + hitRun.location <= x) + leading = false; + hitInfo = leading ? TextHitInfo.leading(i) + : TextHitInfo.trailing(i); + } + } + } + } + return hitInfo; + } + + public boolean isLeftToRight () + { + return leftToRight; + } + + public boolean isVertical () + { + return false; // FIXME: How do you create a vertical layout? + } + + public int hashCode () + { + // This is implemented in sync to equals(). + if (hash == 0 && runs.length > 0) + { + hash = runs.length; + for (int i = 0; i < runs.length; i++) + hash ^= runs[i].glyphVector.hashCode(); + } + return hash; + } + + public String toString () + { + return "TextLayout [string:"+ new String(string, offset, length) + +" Rendercontext:"+ + frc+"]"; + } + + /** + * Returns the natural bounds of that text layout. This is made up + * of the ascent plus descent and the text advance. + * + * @return the natural bounds of that text layout + */ + private Rectangle2D getNaturalBounds() + { + if (naturalBounds == null) + naturalBounds = new Rectangle2D.Float(0.0F, -getAscent(), getAdvance(), + getAscent() + getDescent()); + return naturalBounds; + } + + private void checkHitInfo(TextHitInfo hit) + { + if (hit == null) + throw new IllegalArgumentException("Null hit info not allowed"); + int index = hit.getInsertionIndex(); + if (index < 0 || index > length) + throw new IllegalArgumentException("Hit index out of range"); + } + + private int hitToCaret(TextHitInfo hit) + { + int index = hit.getCharIndex(); + int ret; + if (index < 0) + ret = isLeftToRight() ? 0 : length; + else if (index >= length) + ret = isLeftToRight() ? length : 0; + else + { + ret = logicalToVisual[index]; + if (hit.isLeadingEdge() != isCharacterLTR(index)) + ret++; + } + return ret; + } + + private TextHitInfo caretToHit(int index) + { + TextHitInfo hit; + if (index == 0 || index == length) + { + if ((index == length) == isLeftToRight()) + hit = TextHitInfo.leading(length); + else + hit = TextHitInfo.trailing(-1); + } + else + { + int logical = visualToLogical[index]; + boolean leading = isCharacterLTR(logical); // LTR. + hit = leading ? TextHitInfo.leading(logical) + : TextHitInfo.trailing(logical); + } + return hit; + } + + private boolean isCharacterLTR(int index) + { + byte level = getCharacterLevel(index); + return (level & 1) == 0; + } + + /** + * Finds the run that holds the specified (logical) character index. This + * returns null when the index is not inside the range. + * + * @param index the index of the character to find + * + * @return the run that holds the specified character + */ + private Run findRunAtIndex(int index) + { + Run found = null; + // TODO: Can we do better than linear searching here? + for (int i = 0; i < runs.length && found == null; i++) + { + Run run = runs[i]; + if (run.runStart <= index && run.runEnd > index) + found = run; + } + return found; + } + + /** + * Computes the layout locations for each run. + */ + private void layoutRuns() + { + float loc = 0.0F; + float lastWidth = 0.0F; + for (int i = 0; i < runs.length; i++) + { + runs[i].location = loc; + Rectangle2D bounds = runs[i].glyphVector.getLogicalBounds(); + loc += isVertical() ? bounds.getHeight() : bounds.getWidth(); + } + } + + /** + * Inner class describing a caret policy + */ + public static class CaretPolicy + { + public CaretPolicy() + { + } + + public TextHitInfo getStrongCaret(TextHitInfo hit1, + TextHitInfo hit2, + TextLayout layout) + { + byte l1 = layout.getCharacterLevel(hit1.getCharIndex()); + byte l2 = layout.getCharacterLevel(hit2.getCharIndex()); + TextHitInfo strong; + if (l1 == l2) + { + if (hit2.isLeadingEdge() && ! hit1.isLeadingEdge()) + strong = hit2; + else + strong = hit1; + } + else + { + if (l1 < l2) + strong = hit1; + else + strong = hit2; + } + return strong; + } + } +} diff --git a/libjava/classpath/java/awt/font/TextMeasurer.java b/libjava/classpath/java/awt/font/TextMeasurer.java new file mode 100644 index 000000000..e443e8bc7 --- /dev/null +++ b/libjava/classpath/java/awt/font/TextMeasurer.java @@ -0,0 +1,190 @@ +/* TextMeasurer.java + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.text.AttributedCharacterIterator; +import java.awt.Shape; + +/** + * TextMeasurer is a small utility class for measuring the length of laid-out + * text objects. + * + * @author Sven de Marothy + * @since 1.3 + */ +public final class TextMeasurer implements Cloneable +{ + private AttributedCharacterIterator text; + private FontRenderContext frc; + private TextLayout totalLayout; + private int numChars; + + /** + * Creates a TextMeasurer from a given text in the form of an + * AttributedCharacterIterator and a + * FontRenderContext. + */ + public TextMeasurer (AttributedCharacterIterator text, FontRenderContext frc) + { + this.text = text; + this.frc = frc; + totalLayout = new TextLayout( text, frc ); + numChars = totalLayout.getCharacterCount(); + } + + /** + * Clones the TextMeasurer object + */ + protected Object clone () + { + return new TextMeasurer( text, frc ); + } + + /** + * Update the text if a character is deleted at the position deletePos + * @param newParagraph - the updated paragraph. + * @param deletePos - the deletion position + */ + public void deleteChar (AttributedCharacterIterator newParagraph, + int deletePos) + { + totalLayout = new TextLayout(newParagraph, frc); + if( deletePos < 0 || deletePos > totalLayout.getCharacterCount() ) + throw new NullPointerException("Invalid deletePos:"+deletePos); + numChars = totalLayout.getCharacterCount(); + text = newParagraph; + } + + /** + * Update the text if a character is inserted at the position insertPos + * @param newParagraph - the updated paragraph. + * @param insertPos - the insertion position + */ + public void insertChar (AttributedCharacterIterator newParagraph, + int insertPos) + { + totalLayout = new TextLayout(newParagraph, frc); + if( insertPos < 0 || insertPos > totalLayout.getCharacterCount() ) + throw new NullPointerException("Invalid insertPos:"+insertPos); + numChars = totalLayout.getCharacterCount(); + text = newParagraph; + } + + /*** + * Returns the total advance between two positions in the paragraph. + * Characters from start to limit-1 (inclusive) are included in this count. + * + * @param start - the starting character index. + * @param limit - the limiting index. + */ + public float getAdvanceBetween (int start, int limit) + { + Shape s = totalLayout.getLogicalHighlightShape( start, limit ); + return (float)s.getBounds2D().getWidth(); + } + + /** + * Returns a TextLayout object corresponding to the characters + * from text to limit. + * @param start - the starting character index. + * @param limit - the limiting index. + */ + public TextLayout getLayout (int start, int limit) + { + if( start >= limit ) + throw new IllegalArgumentException("Start position must be < limit."); + return new TextLayout( totalLayout, start, limit ); + } + + /** + * Returns the line-break index from a given starting index and a maximum + * advance. The index returned is the first character outside the given + * advance (or the limit of the string, if all remaining characters fit.) + * + * @param start - the starting index. + * @param maxAdvance - the maximum advance allowed. + * @return the index of the first character beyond maxAdvance, or the + * index of the last character + 1. + */ + public int getLineBreakIndex (int start, float maxAdvance) + { + if( start < 0 ) + throw new IllegalArgumentException("Start parameter must be > 0."); + + double remainingLength = getAdvanceBetween( start, numChars ); + + int guessOffset = (int)( ( (double)maxAdvance / (double)remainingLength) + * ( (double)numChars - (double)start ) ); + guessOffset += start; + if( guessOffset > numChars ) + guessOffset = numChars; + + double guessLength = getAdvanceBetween( start, guessOffset ); + boolean makeSmaller = ( guessLength > maxAdvance ); + int inc = makeSmaller ? -1 : 1; + boolean keepGoing = true; + + do + { + guessOffset = guessOffset + inc; + if( guessOffset <= start || guessOffset > numChars ) + { + keepGoing = false; + } + else + { + guessLength = getAdvanceBetween( start, guessOffset ); + if( makeSmaller && ( guessLength <= maxAdvance) ) + keepGoing = false; + if( !makeSmaller && ( guessLength >= maxAdvance) ) + keepGoing = false; + } + } + while( keepGoing ); + + // Return first index that doesn't fit. + if( !makeSmaller ) + guessOffset--; + + if( guessOffset > numChars ) + return numChars; + + return guessOffset; + } +} diff --git a/libjava/classpath/java/awt/font/TransformAttribute.java b/libjava/classpath/java/awt/font/TransformAttribute.java new file mode 100644 index 000000000..56d15bb0b --- /dev/null +++ b/libjava/classpath/java/awt/font/TransformAttribute.java @@ -0,0 +1,100 @@ +/* TransformAttribute.java -- + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.font; + +import java.awt.geom.AffineTransform; +import java.io.Serializable; + +/** + * This class provides a mechanism for using an {@link AffineTransform} as + * an immutable attribute (for example, in the + * {@link java.text.AttributedString} class). Any transform passed to + * this class is copied before being stored, and any transform handed out + * by this class is a copy of the stored transform. In this way, it is + * not possible to modify the stored transform. + * + * @author Michael Koch + */ +public final class TransformAttribute implements Serializable +{ + private static final long serialVersionUID = 3356247357827709530L; + + private AffineTransform affineTransform; + + /** + * Creates a new attribute that contains a copy of the given transform. + * + * @param transform the transform (null not permitted). + * + * @throws IllegalArgumentException if transform is + * null. + */ + public TransformAttribute (AffineTransform transform) + { + if (transform == null) + { + throw new IllegalArgumentException("Null 'transform' not permitted."); + } + this.affineTransform = new AffineTransform (transform); + } + + /** + * Returns a copy of the transform contained by this attribute. + * + * @return A copy of the transform. + */ + public AffineTransform getTransform () + { + return (AffineTransform) affineTransform.clone(); + } + + /** + * Returns true if the transform contained by this attribute is + * an identity transform, and false otherwise. + * + * @return true if the transform contained by this attribute is + * an identity transform, and false otherwise. + * + * @since 1.4 + */ + public boolean isIdentity () + { + return (affineTransform == null ? false : affineTransform.isIdentity ()); + } +} diff --git a/libjava/classpath/java/awt/font/package.html b/libjava/classpath/java/awt/font/package.html new file mode 100644 index 000000000..8c3c61a40 --- /dev/null +++ b/libjava/classpath/java/awt/font/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.font + + +

Representations of different kind of characters and fonts.

+ + + diff --git a/libjava/classpath/java/awt/geom/AffineTransform.java b/libjava/classpath/java/awt/geom/AffineTransform.java new file mode 100644 index 000000000..42590efce --- /dev/null +++ b/libjava/classpath/java/awt/geom/AffineTransform.java @@ -0,0 +1,1489 @@ +/* AffineTransform.java -- transform coordinates between two 2-D spaces + Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Shape; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +/** + * This class represents an affine transformation between two coordinate + * spaces in 2 dimensions. Such a transform preserves the "straightness" + * and "parallelness" of lines. The transform is built from a sequence of + * translations, scales, flips, rotations, and shears. + * + *

The transformation can be represented using matrix math on a 3x3 array. + * Given (x,y), the transformation (x',y') can be found by: + *

+ * [ x']   [ m00 m01 m02 ] [ x ]   [ m00*x + m01*y + m02 ]
+ * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10*x + m11*y + m12 ]
+ * [ 1 ]   [  0   0   1  ] [ 1 ]   [          1          ]
+ * 
+ * The bottom row of the matrix is constant, so a transform can be uniquely + * represented (as in {@link #toString()}) by + * "[[m00, m01, m02], [m10, m11, m12]]". + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status partially updated to 1.4, still has some problems + */ +public class AffineTransform implements Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 1330973210523860834L; + + /** + * The transformation is the identity (x' = x, y' = y). All other transforms + * have either a combination of the appropriate transform flag bits for + * their type, or the type GENERAL_TRANSFORM. + * + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ + public static final int TYPE_IDENTITY = 0; + + /** + * The transformation includes a translation - shifting in the x or y + * direction without changing length or angles. + * + * @see #TYPE_IDENTITY + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ + public static final int TYPE_TRANSLATION = 1; + + /** + * The transformation includes a uniform scale - length is scaled in both + * the x and y directions by the same amount, without affecting angles. + * This is mutually exclusive with TYPE_GENERAL_SCALE. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_SCALE + * @see #getType() + */ + public static final int TYPE_UNIFORM_SCALE = 2; + + /** + * The transformation includes a general scale - length is scaled in either + * or both the x and y directions, but by different amounts; without + * affecting angles. This is mutually exclusive with TYPE_UNIFORM_SCALE. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_SCALE + * @see #getType() + */ + public static final int TYPE_GENERAL_SCALE = 4; + + /** + * This constant checks if either variety of scale transform is performed. + * + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + */ + public static final int TYPE_MASK_SCALE = 6; + + /** + * The transformation includes a flip about an axis, swapping between + * right-handed and left-handed coordinate systems. In a right-handed + * system, the positive x-axis rotates counter-clockwise to the positive + * y-axis; in a left-handed system it rotates clockwise. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #getType() + */ + public static final int TYPE_FLIP = 64; + + /** + * The transformation includes a rotation of a multiple of 90 degrees (PI/2 + * radians). Angles are rotated, but length is preserved. This is mutually + * exclusive with TYPE_GENERAL_ROTATION. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_ROTATION + * @see #getType() + */ + public static final int TYPE_QUADRANT_ROTATION = 8; + + /** + * The transformation includes a rotation by an arbitrary angle. Angles are + * rotated, but length is preserved. This is mutually exclusive with + * TYPE_QUADRANT_ROTATION. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + * @see #TYPE_MASK_ROTATION + * @see #getType() + */ + public static final int TYPE_GENERAL_ROTATION = 16; + + /** + * This constant checks if either variety of rotation is performed. + * + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + */ + public static final int TYPE_MASK_ROTATION = 24; + + /** + * The transformation is an arbitrary conversion of coordinates which + * could not be decomposed into the other TYPEs. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_FLIP + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #getType() + */ + public static final int TYPE_GENERAL_TRANSFORM = 32; + + /** + * The X coordinate scaling element of the transform matrix. + * + * @serial matrix[0,0] + */ + private double m00; + + /** + * The Y coordinate shearing element of the transform matrix. + * + * @serial matrix[1,0] + */ + private double m10; + + /** + * The X coordinate shearing element of the transform matrix. + * + * @serial matrix[0,1] + */ + private double m01; + + /** + * The Y coordinate scaling element of the transform matrix. + * + * @serial matrix[1,1] + */ + private double m11; + + /** + * The X coordinate translation element of the transform matrix. + * + * @serial matrix[0,2] + */ + private double m02; + + /** + * The Y coordinate translation element of the transform matrix. + * + * @serial matrix[1,2] + */ + private double m12; + + /** The type of this transform. */ + private transient int type; + + /** + * Construct a new identity transform: + *
+   * [ 1 0 0 ]
+   * [ 0 1 0 ]
+   * [ 0 0 1 ]
+   * 
+ */ + public AffineTransform() + { + m00 = m11 = 1; + } + + /** + * Create a new transform which copies the given one. + * + * @param tx the transform to copy + * @throws NullPointerException if tx is null + */ + public AffineTransform(AffineTransform tx) + { + setTransform(tx); + } + + /** + * Construct a transform with the given matrix entries: + *
+   * [ m00 m01 m02 ]
+   * [ m10 m11 m12 ]
+   * [  0   0   1  ]
+   * 
+ * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public AffineTransform(float m00, float m10, + float m01, float m11, + float m02, float m12) + { + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + updateType(); + } + + /** + * Construct a transform from a sequence of float entries. The array must + * have at least 4 entries, which has a translation factor of 0; or 6 + * entries, for specifying all parameters: + *
+   * [ f[0] f[2] (f[4]) ]
+   * [ f[1] f[3] (f[5]) ]
+   * [  0     0    1    ]
+   * 
+ * + * @param f the matrix to copy from, with at least 4 (6) entries + * @throws NullPointerException if f is null + * @throws ArrayIndexOutOfBoundsException if f is too small + */ + public AffineTransform(float[] f) + { + m00 = f[0]; + m10 = f[1]; + m01 = f[2]; + m11 = f[3]; + if (f.length >= 6) + { + m02 = f[4]; + m12 = f[5]; + } + updateType(); + } + + /** + * Construct a transform with the given matrix entries: + *
+   * [ m00 m01 m02 ]
+   * [ m10 m11 m12 ]
+   * [  0   0   1  ]
+   * 
+ * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public AffineTransform(double m00, double m10, double m01, + double m11, double m02, double m12) + { + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + updateType(); + } + + /** + * Construct a transform from a sequence of double entries. The array must + * have at least 4 entries, which has a translation factor of 0; or 6 + * entries, for specifying all parameters: + *
+   * [ d[0] d[2] (d[4]) ]
+   * [ d[1] d[3] (d[5]) ]
+   * [  0     0    1    ]
+   * 
+ * + * @param d the matrix to copy from, with at least 4 (6) entries + * @throws NullPointerException if d is null + * @throws ArrayIndexOutOfBoundsException if d is too small + */ + public AffineTransform(double[] d) + { + m00 = d[0]; + m10 = d[1]; + m01 = d[2]; + m11 = d[3]; + if (d.length >= 6) + { + m02 = d[4]; + m12 = d[5]; + } + updateType(); + } + + /** + * Returns a translation transform: + *
+   * [ 1 0 tx ]
+   * [ 0 1 ty ]
+   * [ 0 0 1  ]
+   * 
+ * + * @param tx the x translation distance + * @param ty the y translation distance + * @return the translating transform + */ + public static AffineTransform getTranslateInstance(double tx, double ty) + { + AffineTransform t = new AffineTransform(); + t.m02 = tx; + t.m12 = ty; + t.type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; + return t; + } + + /** + * Returns a rotation transform. A positive angle (in radians) rotates + * the positive x-axis to the positive y-axis: + *
+   * [ cos(theta) -sin(theta) 0 ]
+   * [ sin(theta)  cos(theta) 0 ]
+   * [     0           0      1 ]
+   * 
+ * + * @param theta the rotation angle + * @return the rotating transform + */ + public static AffineTransform getRotateInstance(double theta) + { + AffineTransform t = new AffineTransform(); + t.setToRotation(theta); + return t; + } + + /** + * Returns a rotation transform about a point. A positive angle (in radians) + * rotates the positive x-axis to the positive y-axis. This is the same + * as calling: + *
+   * AffineTransform tx = new AffineTransform();
+   * tx.setToTranslation(x, y);
+   * tx.rotate(theta);
+   * tx.translate(-x, -y);
+   * 
+ * + *

The resulting matrix is: + *

+   * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
+   * [ sin(theta)  cos(theta) y-x*sin-y*cos ]
+   * [     0           0            1       ]
+   * 
+ * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + * @return the rotating transform + */ + public static AffineTransform getRotateInstance(double theta, + double x, double y) + { + AffineTransform t = new AffineTransform(); + t.setToTranslation(x, y); + t.rotate(theta); + t.translate(-x, -y); + return t; + } + + /** + * Returns a scaling transform: + *
+   * [ sx 0  0 ]
+   * [ 0  sy 0 ]
+   * [ 0  0  1 ]
+   * 
+ * + * @param sx the x scaling factor + * @param sy the y scaling factor + * @return the scaling transform + */ + public static AffineTransform getScaleInstance(double sx, double sy) + { + AffineTransform t = new AffineTransform(); + t.setToScale(sx, sy); + return t; + } + + /** + * Returns a shearing transform (points are shifted in the x direction based + * on a factor of their y coordinate, and in the y direction as a factor of + * their x coordinate): + *
+   * [  1  shx 0 ]
+   * [ shy  1  0 ]
+   * [  0   0  1 ]
+   * 
+ * + * @param shx the x shearing factor + * @param shy the y shearing factor + * @return the shearing transform + */ + public static AffineTransform getShearInstance(double shx, double shy) + { + AffineTransform t = new AffineTransform(); + t.setToShear(shx, shy); + return t; + } + + /** + * Returns the type of this transform. The result is always valid, although + * it may not be the simplest interpretation (in other words, there are + * sequences of transforms which reduce to something simpler, which this + * does not always detect). The result is either TYPE_GENERAL_TRANSFORM, + * or a bit-wise combination of TYPE_TRANSLATION, the mutually exclusive + * TYPE_*_ROTATIONs, and the mutually exclusive TYPE_*_SCALEs. + * + * @return The type. + * + * @see #TYPE_IDENTITY + * @see #TYPE_TRANSLATION + * @see #TYPE_UNIFORM_SCALE + * @see #TYPE_GENERAL_SCALE + * @see #TYPE_QUADRANT_ROTATION + * @see #TYPE_GENERAL_ROTATION + * @see #TYPE_GENERAL_TRANSFORM + */ + public int getType() + { + return type; + } + + /** + * Return the determinant of this transform matrix. If the determinant is + * non-zero, the transform is invertible; otherwise operations which require + * an inverse throw a NoninvertibleTransformException. A result very near + * zero, due to rounding errors, may indicate that inversion results do not + * carry enough precision to be meaningful. + * + *

If this is a uniform scale transformation, the determinant also + * represents the squared value of the scale. Otherwise, it carries little + * additional meaning. The determinant is calculated as: + *

+   * | m00 m01 m02 |
+   * | m10 m11 m12 | = m00 * m11 - m01 * m10
+   * |  0   0   1  |
+   * 
+ * + * @return the determinant + * @see #createInverse() + */ + public double getDeterminant() + { + return m00 * m11 - m01 * m10; + } + + /** + * Return the matrix of values used in this transform. If the matrix has + * fewer than 6 entries, only the scale and shear factors are returned; + * otherwise the translation factors are copied as well. The resulting + * values are: + *
+   * [ d[0] d[2] (d[4]) ]
+   * [ d[1] d[3] (d[5]) ]
+   * [  0     0    1    ]
+   * 
+ * + * @param d the matrix to store the results into; with 4 (6) entries + * @throws NullPointerException if d is null + * @throws ArrayIndexOutOfBoundsException if d is too small + */ + public void getMatrix(double[] d) + { + d[0] = m00; + d[1] = m10; + d[2] = m01; + d[3] = m11; + if (d.length >= 6) + { + d[4] = m02; + d[5] = m12; + } + } + + /** + * Returns the X coordinate scaling factor of the matrix. + * + * @return m00 + * @see #getMatrix(double[]) + */ + public double getScaleX() + { + return m00; + } + + /** + * Returns the Y coordinate scaling factor of the matrix. + * + * @return m11 + * @see #getMatrix(double[]) + */ + public double getScaleY() + { + return m11; + } + + /** + * Returns the X coordinate shearing factor of the matrix. + * + * @return m01 + * @see #getMatrix(double[]) + */ + public double getShearX() + { + return m01; + } + + /** + * Returns the Y coordinate shearing factor of the matrix. + * + * @return m10 + * @see #getMatrix(double[]) + */ + public double getShearY() + { + return m10; + } + + /** + * Returns the X coordinate translation factor of the matrix. + * + * @return m02 + * @see #getMatrix(double[]) + */ + public double getTranslateX() + { + return m02; + } + + /** + * Returns the Y coordinate translation factor of the matrix. + * + * @return m12 + * @see #getMatrix(double[]) + */ + public double getTranslateY() + { + return m12; + } + + /** + * Concatenate a translation onto this transform. This is equivalent, but + * more efficient than + * concatenate(AffineTransform.getTranslateInstance(tx, ty)). + * + * @param tx the x translation distance + * @param ty the y translation distance + * @see #getTranslateInstance(double, double) + * @see #concatenate(AffineTransform) + */ + public void translate(double tx, double ty) + { + m02 += tx * m00 + ty * m01; + m12 += tx * m10 + ty * m11; + updateType(); + } + + /** + * Concatenate a rotation onto this transform. This is equivalent, but + * more efficient than + * concatenate(AffineTransform.getRotateInstance(theta)). + * + * @param theta the rotation angle + * @see #getRotateInstance(double) + * @see #concatenate(AffineTransform) + */ + public void rotate(double theta) + { + double c = Math.cos(theta); + double s = Math.sin(theta); + double n00 = m00 * c + m01 * s; + double n01 = m00 * -s + m01 * c; + double n10 = m10 * c + m11 * s; + double n11 = m10 * -s + m11 * c; + m00 = n00; + m01 = n01; + m10 = n10; + m11 = n11; + updateType(); + } + + /** + * Concatenate a rotation about a point onto this transform. This is + * equivalent, but more efficient than + * concatenate(AffineTransform.getRotateInstance(theta, x, y)). + * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + * @see #getRotateInstance(double, double, double) + * @see #concatenate(AffineTransform) + */ + public void rotate(double theta, double x, double y) + { + translate(x, y); + rotate(theta); + translate(-x, -y); + } + + /** + * Concatenate a scale onto this transform. This is equivalent, but more + * efficient than + * concatenate(AffineTransform.getScaleInstance(sx, sy)). + * + * @param sx the x scaling factor + * @param sy the y scaling factor + * @see #getScaleInstance(double, double) + * @see #concatenate(AffineTransform) + */ + public void scale(double sx, double sy) + { + m00 *= sx; + m01 *= sy; + m10 *= sx; + m11 *= sy; + updateType(); + } + + /** + * Concatenate a shearing onto this transform. This is equivalent, but more + * efficient than + * concatenate(AffineTransform.getShearInstance(sx, sy)). + * + * @param shx the x shearing factor + * @param shy the y shearing factor + * @see #getShearInstance(double, double) + * @see #concatenate(AffineTransform) + */ + public void shear(double shx, double shy) + { + double n00 = m00 + (shy * m01); + double n01 = m01 + (shx * m00); + double n10 = m10 + (shy * m11); + double n11 = m11 + (shx * m10); + m00 = n00; + m01 = n01; + m10 = n10; + m11 = n11; + updateType(); + } + + /** + * Reset this transform to the identity (no transformation): + *
+   * [ 1 0 0 ]
+   * [ 0 1 0 ]
+   * [ 0 0 1 ]
+   * 
+ */ + public void setToIdentity() + { + m00 = m11 = 1; + m01 = m02 = m10 = m12 = 0; + type = TYPE_IDENTITY; + } + + /** + * Set this transform to a translation: + *
+   * [ 1 0 tx ]
+   * [ 0 1 ty ]
+   * [ 0 0 1  ]
+   * 
+ * + * @param tx the x translation distance + * @param ty the y translation distance + */ + public void setToTranslation(double tx, double ty) + { + m00 = m11 = 1; + m01 = m10 = 0; + m02 = tx; + m12 = ty; + type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; + } + + /** + * Set this transform to a rotation. A positive angle (in radians) rotates + * the positive x-axis to the positive y-axis: + *
+   * [ cos(theta) -sin(theta) 0 ]
+   * [ sin(theta)  cos(theta) 0 ]
+   * [     0           0      1 ]
+   * 
+ * + * @param theta the rotation angle + */ + public void setToRotation(double theta) + { + double c = Math.cos(theta); + double s = Math.sin(theta); + m00 = c; + m01 = -s; + m02 = 0; + m10 = s; + m11 = c; + m12 = 0; + type = (c == 1 ? TYPE_IDENTITY + : c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION + : TYPE_GENERAL_ROTATION); + } + + /** + * Set this transform to a rotation about a point. A positive angle (in + * radians) rotates the positive x-axis to the positive y-axis. This is the + * same as calling: + *
+   * tx.setToTranslation(x, y);
+   * tx.rotate(theta);
+   * tx.translate(-x, -y);
+   * 
+ * + *

The resulting matrix is: + *

+   * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
+   * [ sin(theta)  cos(theta) y-x*sin-y*cos ]
+   * [     0           0            1       ]
+   * 
+ * + * @param theta the rotation angle + * @param x the x coordinate of the pivot point + * @param y the y coordinate of the pivot point + */ + public void setToRotation(double theta, double x, double y) + { + double c = Math.cos(theta); + double s = Math.sin(theta); + m00 = c; + m01 = -s; + m02 = x - x * c + y * s; + m10 = s; + m11 = c; + m12 = y - x * s - y * c; + updateType(); + } + + /** + * Set this transform to a scale: + *
+   * [ sx 0  0 ]
+   * [ 0  sy 0 ]
+   * [ 0  0  1 ]
+   * 
+ * + * @param sx the x scaling factor + * @param sy the y scaling factor + */ + public void setToScale(double sx, double sy) + { + m00 = sx; + m01 = m02 = m10 = m12 = 0; + m11 = sy; + type = (sx != sy ? TYPE_GENERAL_SCALE + : sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE); + } + + /** + * Set this transform to a shear (points are shifted in the x direction based + * on a factor of their y coordinate, and in the y direction as a factor of + * their x coordinate): + *
+   * [  1  shx 0 ]
+   * [ shy  1  0 ]
+   * [  0   0  1 ]
+   * 
+ * + * @param shx the x shearing factor + * @param shy the y shearing factor + */ + public void setToShear(double shx, double shy) + { + m00 = m11 = 1; + m01 = shx; + m10 = shy; + m02 = m12 = 0; + updateType(); + } + + /** + * Set this transform to a copy of the given one. + * + * @param tx the transform to copy + * @throws NullPointerException if tx is null + */ + public void setTransform(AffineTransform tx) + { + m00 = tx.m00; + m01 = tx.m01; + m02 = tx.m02; + m10 = tx.m10; + m11 = tx.m11; + m12 = tx.m12; + type = tx.type; + } + + /** + * Set this transform to the given values: + *
+   * [ m00 m01 m02 ]
+   * [ m10 m11 m12 ]
+   * [  0   0   1  ]
+   * 
+ * + * @param m00 the x scaling component + * @param m10 the y shearing component + * @param m01 the x shearing component + * @param m11 the y scaling component + * @param m02 the x translation component + * @param m12 the y translation component + */ + public void setTransform(double m00, double m10, double m01, + double m11, double m02, double m12) + { + this.m00 = m00; + this.m10 = m10; + this.m01 = m01; + this.m11 = m11; + this.m02 = m02; + this.m12 = m12; + updateType(); + } + + /** + * Set this transform to the result of performing the original version of + * this followed by tx. This is commonly used when chaining transformations + * from one space to another. In matrix form: + *
+   * [ this ] = [ this ] x [ tx ]
+   * 
+ * + * @param tx the transform to concatenate + * @throws NullPointerException if tx is null + * @see #preConcatenate(AffineTransform) + */ + public void concatenate(AffineTransform tx) + { + double n00 = m00 * tx.m00 + m01 * tx.m10; + double n01 = m00 * tx.m01 + m01 * tx.m11; + double n02 = m00 * tx.m02 + m01 * tx.m12 + m02; + double n10 = m10 * tx.m00 + m11 * tx.m10; + double n11 = m10 * tx.m01 + m11 * tx.m11; + double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; + m00 = n00; + m01 = n01; + m02 = n02; + m10 = n10; + m11 = n11; + m12 = n12; + updateType(); + } + + /** + * Set this transform to the result of performing tx followed by the + * original version of this. This is less common than normal concatenation, + * but can still be used to chain transformations from one space to another. + * In matrix form: + *
+   * [ this ] = [ tx ] x [ this ]
+   * 
+ * + * @param tx the transform to concatenate + * @throws NullPointerException if tx is null + * @see #concatenate(AffineTransform) + */ + public void preConcatenate(AffineTransform tx) + { + double n00 = tx.m00 * m00 + tx.m01 * m10; + double n01 = tx.m00 * m01 + tx.m01 * m11; + double n02 = tx.m00 * m02 + tx.m01 * m12 + tx.m02; + double n10 = tx.m10 * m00 + tx.m11 * m10; + double n11 = tx.m10 * m01 + tx.m11 * m11; + double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; + m00 = n00; + m01 = n01; + m02 = n02; + m10 = n10; + m11 = n11; + m12 = n12; + updateType(); + } + + /** + * Returns a transform, which if concatenated to this one, will result in + * the identity transform. This is useful for undoing transformations, but + * is only possible if the original transform has an inverse (ie. does not + * map multiple points to the same line or point). A transform exists only + * if getDeterminant() has a non-zero value. + * + * The inverse is calculated as: + * + *
+   *
+   * Let A be the matrix for which we want to find the inverse:
+   *
+   * A = [ m00 m01 m02 ]
+   *     [ m10 m11 m12 ]
+   *     [ 0   0   1   ]
+   *
+   *
+   *                 1
+   * inverse (A) =  ---   x  adjoint(A)
+   *                det
+   *
+   *
+   *
+   *             =   1       [  m11  -m01   m01*m12-m02*m11  ]
+   *                ---   x  [ -m10   m00  -m00*m12+m10*m02  ]
+   *                det      [  0     0     m00*m11-m10*m01  ]
+   *
+   *
+   *
+   *             = [  m11/det  -m01/det   m01*m12-m02*m11/det ]
+   *               [ -m10/det   m00/det  -m00*m12+m10*m02/det ]
+   *               [   0           0          1               ]
+   *
+   *
+   * 
+ * + * + * + * @return a new inverse transform + * @throws NoninvertibleTransformException if inversion is not possible + * @see #getDeterminant() + */ + public AffineTransform createInverse() + throws NoninvertibleTransformException + { + double det = getDeterminant(); + if (det == 0) + throw new NoninvertibleTransformException("can't invert transform"); + + double im00 = m11 / det; + double im10 = -m10 / det; + double im01 = -m01 / det; + double im11 = m00 / det; + double im02 = (m01 * m12 - m02 * m11) / det; + double im12 = (-m00 * m12 + m10 * m02) / det; + + return new AffineTransform (im00, im10, im01, im11, im02, im12); + } + + /** + * Perform this transformation on the given source point, and store the + * result in the destination (creating it if necessary). It is safe for + * src and dst to be the same. + * + * @param src the source point + * @param dst the destination, or null + * @return the transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + */ + public Point2D transform(Point2D src, Point2D dst) + { + if (dst == null) + dst = new Point2D.Double(); + double x = src.getX(); + double y = src.getY(); + double nx = m00 * x + m01 * y + m02; + double ny = m10 * x + m11 * y + m12; + dst.setLocation(nx, ny); + return dst; + } + + /** + * Perform this transformation on an array of points, storing the results + * in another (possibly same) array. This will not create a destination + * array, but will create points for the null entries of the destination. + * The transformation is done sequentially. While having a single source + * and destination point be the same is safe, you should be aware that + * duplicate references to the same point in the source, and having the + * source overlap the destination, may result in your source points changing + * from a previous transform before it is their turn to be evaluated. + * + * @param src the array of source points + * @param srcOff the starting offset into src + * @param dst the array of destination points (may have null entries) + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null, or src has null + * entries + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + * @throws ArrayStoreException if new points are incompatible with dst + */ + public void transform(Point2D[] src, int srcOff, + Point2D[] dst, int dstOff, int num) + { + while (--num >= 0) + dst[dstOff] = transform(src[srcOff++], dst[dstOff++]); + } + + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another (possibly same) array. This will not + * create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(float[] srcPts, int srcOff, + float[] dstPts, int dstOff, int num) + { + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) + { + float[] f = new float[2 * num]; + System.arraycopy(srcPts, srcOff, f, 0, 2 * num); + srcPts = f; + } + while (--num >= 0) + { + float x = srcPts[srcOff++]; + float y = srcPts[srcOff++]; + dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); + dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); + } + } + + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another (possibly same) array. This will not + * create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) + { + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) + { + double[] d = new double[2 * num]; + System.arraycopy(srcPts, srcOff, d, 0, 2 * num); + srcPts = d; + } + while (--num >= 0) + { + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y + m02; + dstPts[dstOff++] = m10 * x + m11 * y + m12; + } + } + + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another array. This will not create a destination + * array. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(float[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) + { + while (--num >= 0) + { + float x = srcPts[srcOff++]; + float y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y + m02; + dstPts[dstOff++] = m10 * x + m11 * y + m12; + } + } + + /** + * Perform this transformation on an array of points, in (x,y) pairs, + * storing the results in another array. This will not create a destination + * array. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void transform(double[] srcPts, int srcOff, + float[] dstPts, int dstOff, int num) + { + while (--num >= 0) + { + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); + dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); + } + } + + /** + * Perform the inverse of this transformation on the given source point, + * and store the result in the destination (creating it if necessary). It + * is safe for src and dst to be the same. + * + * @param src the source point + * @param dst the destination, or null + * @return the inverse transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + * @throws NoninvertibleTransformException if the inverse does not exist + * @see #getDeterminant() + */ + public Point2D inverseTransform(Point2D src, Point2D dst) + throws NoninvertibleTransformException + { + return createInverse().transform(src, dst); + } + + /** + * Perform the inverse of this transformation on an array of points, in + * (x,y) pairs, storing the results in another (possibly same) array. This + * will not create a destination array. All sources are copied before the + * transformation, so that no result will overwrite a point that has not yet + * been evaluated. + * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + * @throws NoninvertibleTransformException if the inverse does not exist + * @see #getDeterminant() + */ + public void inverseTransform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, int num) + throws NoninvertibleTransformException + { + createInverse().transform(srcPts, srcOff, dstPts, dstOff, num); + } + + /** + * Perform this transformation, less any translation, on the given source + * point, and store the result in the destination (creating it if + * necessary). It is safe for src and dst to be the same. The reduced + * transform is equivalent to: + *
+   * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ]
+   * [ y' ]   [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ]
+   * 
+ * + * @param src the source point + * @param dst the destination, or null + * @return the delta transformation of src, in dst if it was non-null + * @throws NullPointerException if src is null + */ + public Point2D deltaTransform(Point2D src, Point2D dst) + { + if (dst == null) + dst = new Point2D.Double(); + double x = src.getX(); + double y = src.getY(); + double nx = m00 * x + m01 * y; + double ny = m10 * x + m11 * y; + dst.setLocation(nx, ny); + return dst; + } + + /** + * Perform this transformation, less any translation, on an array of points, + * in (x,y) pairs, storing the results in another (possibly same) array. + * This will not create a destination array. All sources are copied before + * the transformation, so that no result will overwrite a point that has + * not yet been evaluated. The reduced transform is equivalent to: + *
+   * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ]
+   * [ y' ]   [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ]
+   * 
+ * + * @param srcPts the array of source points + * @param srcOff the starting offset into src + * @param dstPts the array of destination points + * @param dstOff the starting offset into dst + * @param num the number of points to transform + * @throws NullPointerException if src or dst is null + * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded + */ + public void deltaTransform(double[] srcPts, int srcOff, + double[] dstPts, int dstOff, + int num) + { + if (srcPts == dstPts && dstOff > srcOff + && num > 1 && srcOff + 2 * num > dstOff) + { + double[] d = new double[2 * num]; + System.arraycopy(srcPts, srcOff, d, 0, 2 * num); + srcPts = d; + } + while (--num >= 0) + { + double x = srcPts[srcOff++]; + double y = srcPts[srcOff++]; + dstPts[dstOff++] = m00 * x + m01 * y; + dstPts[dstOff++] = m10 * x + m11 * y; + } + } + + /** + * Return a new Shape, based on the given one, where the path of the shape + * has been transformed by this transform. Notice that this uses GeneralPath, + * which only stores points in float precision. + * + * @param src the shape source to transform + * @return the shape, transformed by this, null if src is + * null. + * @see GeneralPath#transform(AffineTransform) + */ + public Shape createTransformedShape(Shape src) + { + if(src == null) + return null; + GeneralPath p = new GeneralPath(src); + p.transform(this); + return p; + } + + /** + * Returns a string representation of the transform, in the format: + * "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" + * + m10 + ", " + m11 + ", " + m12 + "]]". + * + * @return the string representation + */ + public String toString() + { + return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" + + m10 + ", " + m11 + ", " + m12 + "]]"; + } + + /** + * Tests if this transformation is the identity: + *
+   * [ 1 0 0 ]
+   * [ 0 1 0 ]
+   * [ 0 0 1 ]
+   * 
+ * + * @return true if this is the identity transform + */ + public boolean isIdentity() + { + // Rather than rely on type, check explicitly. + return (m00 == 1 && m01 == 0 && m02 == 0 + && m10 == 0 && m11 == 1 && m12 == 0); + } + + /** + * Create a new transform of the same run-time type, with the same + * transforming properties as this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Return the hashcode for this transformation. The formula is not + * documented, but appears to be the same as: + *
+   * long l = Double.doubleToLongBits(getScaleX());
+   * l = l * 31 + Double.doubleToLongBits(getShearX());
+   * l = l * 31 + Double.doubleToLongBits(getTranslateX());
+   * l = l * 31 + Double.doubleToLongBits(getShearY());
+   * l = l * 31 + Double.doubleToLongBits(getScaleY());
+   * l = l * 31 + Double.doubleToLongBits(getTranslateY());
+   * return (int) ((l >> 32) ^ l);
+   * 
+ * + * @return the hashcode + */ + public int hashCode() + { + long l = Double.doubleToLongBits(m00); + l = l * 31 + Double.doubleToLongBits(m01); + l = l * 31 + Double.doubleToLongBits(m02); + l = l * 31 + Double.doubleToLongBits(m10); + l = l * 31 + Double.doubleToLongBits(m11); + l = l * 31 + Double.doubleToLongBits(m12); + return (int) ((l >> 32) ^ l); + } + + /** + * Compares two transforms for equality. This returns true if they have the + * same matrix values. + * + * @param obj the transform to compare + * @return true if it is equal + */ + public boolean equals(Object obj) + { + if (! (obj instanceof AffineTransform)) + return false; + AffineTransform t = (AffineTransform) obj; + return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02 + && m10 == t.m10 && m11 == t.m11 && m12 == t.m12); + } + + /** + * Helper to decode the type from the matrix. This is not guaranteed + * to find the optimal type, but at least it will be valid. + */ + private void updateType() + { + double det = getDeterminant(); + if (det == 0) + { + type = TYPE_GENERAL_TRANSFORM; + return; + } + // Scale (includes rotation by PI) or translation. + if (m01 == 0 && m10 == 0) + { + if (m00 == m11) + type = m00 == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE; + else + type = TYPE_GENERAL_SCALE; + if (m02 != 0 || m12 != 0) + type |= TYPE_TRANSLATION; + } + // Rotation. + else if (m00 == m11 && m01 == -m10) + { + type = m00 == 0 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION; + if (det != 1) + type |= TYPE_UNIFORM_SCALE; + if (m02 != 0 || m12 != 0) + type |= TYPE_TRANSLATION; + } + else + type = TYPE_GENERAL_TRANSFORM; + } + + /** + * Reads a transform from an object stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if there is a problem deserializing + * @throws IOException if there is a problem deserializing + */ + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + updateType(); + } +} // class AffineTransform diff --git a/libjava/classpath/java/awt/geom/Arc2D.java b/libjava/classpath/java/awt/geom/Arc2D.java new file mode 100644 index 000000000..928c5cfc8 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Arc2D.java @@ -0,0 +1,1413 @@ +/* Arc2D.java -- represents an arc in 2-D space + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + + +/** + * This class represents all arcs (segments of an ellipse in 2-D space). The + * arcs are defined by starting angle and extent (arc length) in degrees, as + * opposed to radians (like the rest of Java), and can be open, chorded, or + * wedge shaped. The angles are skewed according to the ellipse, so that 45 + * degrees always points to the upper right corner (positive x, negative y) + * of the bounding rectangle. A positive extent draws a counterclockwise arc, + * and while the angle can be any value, the path iterator only traverses the + * first 360 degrees. Storage is up to the subclasses. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sven de Marothy (sven@physto.se) + * @since 1.2 + */ +public abstract class Arc2D extends RectangularShape +{ + /** + * An open arc, with no segment connecting the endpoints. This type of + * arc still contains the same points as a chorded version. + */ + public static final int OPEN = 0; + + /** + * A closed arc with a single segment connecting the endpoints (a chord). + */ + public static final int CHORD = 1; + + /** + * A closed arc with two segments, one from each endpoint, meeting at the + * center of the ellipse. + */ + public static final int PIE = 2; + + /** The closure type of this arc. This is package-private to avoid an + * accessor method. */ + int type; + + /** + * Create a new arc, with the specified closure type. + * + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. + * @throws IllegalArgumentException if type is invalid + */ + protected Arc2D(int type) + { + if (type < OPEN || type > PIE) + throw new IllegalArgumentException(); + this.type = type; + } + + /** + * Get the starting angle of the arc in degrees. + * + * @return the starting angle + * @see #setAngleStart(double) + */ + public abstract double getAngleStart(); + + /** + * Get the extent angle of the arc in degrees. + * + * @return the extent angle + * @see #setAngleExtent(double) + */ + public abstract double getAngleExtent(); + + /** + * Return the closure type of the arc. + * + * @return the closure type + * @see #OPEN + * @see #CHORD + * @see #PIE + * @see #setArcType(int) + */ + public int getArcType() + { + return type; + } + + /** + * Returns the starting point of the arc. + * + * @return the start point + */ + public Point2D getStartPoint() + { + double angle = Math.toRadians(getAngleStart()); + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double x = getX() + rx + rx * Math.cos(angle); + double y = getY() + ry - ry * Math.sin(angle); + return new Point2D.Double(x, y); + } + + /** + * Returns the ending point of the arc. + * + * @return the end point + */ + public Point2D getEndPoint() + { + double angle = Math.toRadians(getAngleStart() + getAngleExtent()); + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double x = getX() + rx + rx * Math.cos(angle); + double y = getY() + ry - ry * Math.sin(angle); + return new Point2D.Double(x, y); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param x the new x coordinate of the upper left of the bounding box + * @param y the new y coordinate of the upper left of the bounding box + * @param w the new width of the bounding box + * @param h the new height of the bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public abstract void setArc(double x, double y, double w, double h, + double start, double extent, int type); + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param p the upper left point of the bounding box + * @param d the dimensions of the bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if p or d is null + */ + public void setArc(Point2D p, Dimension2D d, double start, double extent, + int type) + { + setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(), start, extent, type); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param r the new bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public void setArc(Rectangle2D r, double start, double extent, int type) + { + setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(), start, extent, type); + } + + /** + * Set the parameters of the arc from the given one. + * + * @param a the arc to copy + * @throws NullPointerException if a is null + */ + public void setArc(Arc2D a) + { + setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), a.getAngleStart(), + a.getAngleExtent(), a.getArcType()); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). This controls the center point and radius, so the arc will be + * circular. + * + * @param x the x coordinate of the center of the circle + * @param y the y coordinate of the center of the circle + * @param r the radius of the circle + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArcByCenter(double x, double y, double r, double start, + double extent, int type) + { + setArc(x - r, y - r, r + r, r + r, start, extent, type); + } + + /** + * Sets the parameters of the arc by finding the tangents of two lines, and + * using the specified radius. The arc will be circular, will begin on the + * tangent point of the line extending from p1 to p2, and will end on the + * tangent point of the line extending from p2 to p3. + * + * XXX What happens if the points are colinear, or the radius negative? + * + * @param p1 the first point + * @param p2 the tangent line intersection point + * @param p3 the third point + * @param r the radius of the arc + * @throws NullPointerException if any point is null + */ + public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double r) + { + if ((p2.getX() - p1.getX()) * (p3.getY() - p1.getY()) + - (p3.getX() - p1.getX()) * (p2.getY() - p1.getY()) > 0) + { + Point2D p = p3; + p3 = p1; + p1 = p; + } + + // normalized tangent vectors + double dx1 = (p1.getX() - p2.getX()) / p1.distance(p2); + double dy1 = (p1.getY() - p2.getY()) / p1.distance(p2); + double dx2 = (p2.getX() - p3.getX()) / p3.distance(p2); + double dy2 = (p2.getY() - p3.getY()) / p3.distance(p2); + double theta1 = Math.atan2(dx1, dy1); + double theta2 = Math.atan2(dx2, dy2); + + double dx = r * Math.cos(theta2) - r * Math.cos(theta1); + double dy = -r * Math.sin(theta2) + r * Math.sin(theta1); + + if (theta1 < 0) + theta1 += 2 * Math.PI; + if (theta2 < 0) + theta2 += 2 * Math.PI; + if (theta2 < theta1) + theta2 += 2 * Math.PI; + + // Vectors of the lines, not normalized, note we change + // the direction of line 2. + dx1 = p1.getX() - p2.getX(); + dy1 = p1.getY() - p2.getY(); + dx2 = p3.getX() - p2.getX(); + dy2 = p3.getY() - p2.getY(); + + // Calculate the tangent point to the second line + double t2 = -(dx1 * dy - dy1 * dx) / (dx2 * dy1 - dx1 * dy2); + double x2 = t2 * (p3.getX() - p2.getX()) + p2.getX(); + double y2 = t2 * (p3.getY() - p2.getY()) + p2.getY(); + + // calculate the center point + double x = x2 - r * Math.cos(theta2); + double y = y2 + r * Math.sin(theta2); + + setArc(x - r, y - r, 2 * r, 2 * r, Math.toDegrees(theta1), + Math.toDegrees(theta2 - theta1), getArcType()); + } + + /** + * Set the start, in degrees. + * + * @param start the new start angle + * @see #getAngleStart() + */ + public abstract void setAngleStart(double start); + + /** + * Set the extent, in degrees. + * + * @param extent the new extent angle + * @see #getAngleExtent() + */ + public abstract void setAngleExtent(double extent); + + /** + * Sets the starting angle to the angle of the given point relative to + * the center of the arc. The extent remains constant; in other words, + * this rotates the arc. + * + * @param p the new start point + * @throws NullPointerException if p is null + * @see #getStartPoint() + * @see #getAngleStart() + */ + public void setAngleStart(Point2D p) + { + // Normalize. + double x = p.getX() - (getX() + getWidth() / 2); + double y = p.getY() - (getY() + getHeight() / 2); + setAngleStart(Math.toDegrees(Math.atan2(-y, x))); + } + + /** + * Sets the starting and extent angles to those of the given points + * relative to the center of the arc. The arc will be non-empty, and will + * extend counterclockwise. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + * @see #setAngleStart(Point2D) + */ + public void setAngles(double x1, double y1, double x2, double y2) + { + // Normalize the points. + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + x1 = x1 - (mx + mw / 2); + y1 = y1 - (my + mh / 2); + x2 = x2 - (mx + mw / 2); + y2 = y2 - (my + mh / 2); + double start = Math.toDegrees(Math.atan2(-y1, x1)); + double extent = Math.toDegrees(Math.atan2(-y2, x2)) - start; + if (extent < 0) + extent += 360; + setAngleStart(start); + setAngleExtent(extent); + } + + /** + * Sets the starting and extent angles to those of the given points + * relative to the center of the arc. The arc will be non-empty, and will + * extend counterclockwise. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + * @see #setAngleStart(Point2D) + */ + public void setAngles(Point2D p1, Point2D p2) + { + setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Set the closure type of this arc. + * + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @see #getArcType() + */ + public void setArcType(int type) + { + if (type < OPEN || type > PIE) + throw new IllegalArgumentException(); + this.type = type; + } + + /** + * Sets the location and bounds of the ellipse of which this arc is a part. + * + * @param x the new x coordinate + * @param y the new y coordinate + * @param w the new width + * @param h the new height + * @see #getFrame() + */ + public void setFrame(double x, double y, double w, double h) + { + setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type); + } + + /** + * Gets the bounds of the arc. This is much tighter than + * getBounds, as it takes into consideration the start and + * end angles, and the center point of a pie wedge, rather than just the + * overall ellipse. + * + * @return the bounds of the arc + * @see #getBounds() + */ + public Rectangle2D getBounds2D() + { + double extent = getAngleExtent(); + if (Math.abs(extent) >= 360) + return makeBounds(getX(), getY(), getWidth(), getHeight()); + + // Find the minimal bounding box. This determined by its extrema, + // which are the center, the endpoints of the arc, and any local + // maximum contained by the arc. + double rX = getWidth() / 2; + double rY = getHeight() / 2; + double centerX = getX() + rX; + double centerY = getY() + rY; + + Point2D p1 = getStartPoint(); + Rectangle2D result = makeBounds(p1.getX(), p1.getY(), 0, 0); + result.add(getEndPoint()); + + if (type == PIE) + result.add(centerX, centerY); + if (containsAngle(0)) + result.add(centerX + rX, centerY); + if (containsAngle(90)) + result.add(centerX, centerY - rY); + if (containsAngle(180)) + result.add(centerX - rX, centerY); + if (containsAngle(270)) + result.add(centerX, centerY + rY); + + return result; + } + + /** + * Construct a bounding box in a precision appropriate for the subclass. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @return the rectangle for use in getBounds2D + */ + protected abstract Rectangle2D makeBounds(double x, double y, double w, + double h); + + /** + * Tests if the given angle, in degrees, is included in the arc. + * All angles are normalized to be between 0 and 360 degrees. + * + * @param a the angle to test + * @return true if it is contained + */ + public boolean containsAngle(double a) + { + double start = getAngleStart(); + double extent = getAngleExtent(); + double end = start + extent; + + if (extent == 0) + return false; + + if (extent >= 360 || extent <= -360) + return true; + + if (extent < 0) + { + end = start; + start += extent; + } + + start %= 360; + while (start < 0) + start += 360; + + end %= 360; + while (end < start) + end += 360; + + a %= 360; + while (a < start) + a += 360; + + return a >= start && a < end; // starting angle included, ending angle not + } + + /** + * Determines if the arc contains the given point. If the bounding box + * is empty, then this will return false. + * + * The area considered 'inside' an arc of type OPEN is the same as the + * area inside an equivalent filled CHORD-type arc. The area considered + * 'inside' a CHORD-type arc is the same as the filled area. + * + * @param x the x coordinate to test + * @param y the y coordinate to test + * @return true if the point is inside the arc + */ + public boolean contains(double x, double y) + { + double w = getWidth(); + double h = getHeight(); + double extent = getAngleExtent(); + if (w <= 0 || h <= 0 || extent == 0) + return false; + + double mx = getX() + w / 2; + double my = getY() + h / 2; + double dx = (x - mx) * 2 / w; + double dy = (y - my) * 2 / h; + if ((dx * dx + dy * dy) >= 1.0) + return false; + + double angle = Math.toDegrees(Math.atan2(-dy, dx)); + if (getArcType() == PIE) + return containsAngle(angle); + + double a1 = Math.toRadians(getAngleStart()); + double a2 = Math.toRadians(getAngleStart() + extent); + double x1 = mx + getWidth() * Math.cos(a1) / 2; + double y1 = my - getHeight() * Math.sin(a1) / 2; + double x2 = mx + getWidth() * Math.cos(a2) / 2; + double y2 = my - getHeight() * Math.sin(a2) / 2; + double sgn = ((x2 - x1) * (my - y1) - (mx - x1) * (y2 - y1)) * ((x2 - x1) * (y + - y1) - (x - x1) * (y2 - y1)); + + if (Math.abs(extent) > 180) + { + if (containsAngle(angle)) + return true; + return sgn > 0; + } + else + { + if (! containsAngle(angle)) + return false; + return sgn < 0; + } + } + + /** + * Tests if a given rectangle intersects the area of the arc. + * + * For a definition of the 'inside' area, see the contains() method. + * @see #contains(double, double) + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the two shapes share common points + */ + public boolean intersects(double x, double y, double w, double h) + { + double extent = getAngleExtent(); + if (extent == 0) + return false; + + if (contains(x, y) || contains(x, y + h) || contains(x + w, y) + || contains(x + w, y + h)) + return true; + + Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); + + double a = getWidth() / 2.0; + double b = getHeight() / 2.0; + + double mx = getX() + a; + double my = getY() + b; + double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart())); + double y1 = my - b * Math.sin(Math.toRadians(getAngleStart())); + double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent)); + double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent)); + + if (getArcType() != CHORD) + { + // check intersections against the pie radii + if (rect.intersectsLine(mx, my, x1, y1)) + return true; + if (rect.intersectsLine(mx, my, x2, y2)) + return true; + } + else// check the chord + if (rect.intersectsLine(x1, y1, x2, y2)) + return true; + + // Check the Arc segment against the four edges + double dx; + + // Check the Arc segment against the four edges + double dy; + dy = y - my; + dx = a * Math.sqrt(1 - ((dy * dy) / (b * b))); + if (! java.lang.Double.isNaN(dx)) + { + if (mx + dx >= x && mx + dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (mx - dx >= x && mx - dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx)))) + return true; + } + dy = (y + h) - my; + dx = a * Math.sqrt(1 - ((dy * dy) / (b * b))); + if (! java.lang.Double.isNaN(dx)) + { + if (mx + dx >= x && mx + dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (mx - dx >= x && mx - dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx)))) + return true; + } + dx = x - mx; + dy = b * Math.sqrt(1 - ((dx * dx) / (a * a))); + if (! java.lang.Double.isNaN(dy)) + { + if (my + dy >= y && my + dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (my - dy >= y && my - dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(dy, dx)))) + return true; + } + + dx = (x + w) - mx; + dy = b * Math.sqrt(1 - ((dx * dx) / (a * a))); + if (! java.lang.Double.isNaN(dy)) + { + if (my + dy >= y && my + dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (my - dy >= y && my - dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(dy, dx)))) + return true; + } + + // Check whether the arc is contained within the box + if (rect.contains(mx, my)) + return true; + + return false; + } + + /** + * Tests if a given rectangle is contained in the area of the arc. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the arc contains the rectangle + */ + public boolean contains(double x, double y, double w, double h) + { + double extent = getAngleExtent(); + if (extent == 0) + return false; + + if (! (contains(x, y) && contains(x, y + h) && contains(x + w, y) + && contains(x + w, y + h))) + return false; + + Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); + + double a = getWidth() / 2.0; + double b = getHeight() / 2.0; + + double mx = getX() + a; + double my = getY() + b; + double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart())); + double y1 = my - b * Math.sin(Math.toRadians(getAngleStart())); + double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent)); + double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent)); + if (getArcType() != CHORD) + { + // check intersections against the pie radii + if (rect.intersectsLine(mx, my, x1, y1)) + return false; + + if (rect.intersectsLine(mx, my, x2, y2)) + return false; + } + else if (rect.intersectsLine(x1, y1, x2, y2)) + return false; + return true; + } + + /** + * Tests if a given rectangle is contained in the area of the arc. + * + * @param r the rectangle + * @return true if the arc contains the rectangle + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns an iterator over this arc, with an optional transformation. + * This iterator is threadsafe, so future modifications to the arc do not + * affect the iteration. + * + * @param at the transformation, or null + * @return a path iterator + */ + public PathIterator getPathIterator(AffineTransform at) + { + return new ArcIterator(this, at); + } + + /** + * This class is used to iterate over an arc. Since ellipses are a subclass + * of arcs, this is used by Ellipse2D as well. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static final class ArcIterator implements PathIterator + { + /** The current iteration. */ + private int current; + + /** The last iteration. */ + private final int limit; + + /** The optional transformation. */ + private final AffineTransform xform; + + /** The x coordinate of the bounding box. */ + private final double x; + + /** The y coordinate of the bounding box. */ + private final double y; + + /** The width of the bounding box. */ + private final double w; + + /** The height of the bounding box. */ + private final double h; + + /** The start angle, in radians (not degrees). */ + private final double start; + + /** The extent angle, in radians (not degrees). */ + private final double extent; + + /** The arc closure type. */ + private final int type; + + /** + * Construct a new iterator over an arc. + * + * @param a the arc + * @param xform the transform + */ + public ArcIterator(Arc2D a, AffineTransform xform) + { + this.xform = xform; + x = a.getX(); + y = a.getY(); + w = a.getWidth(); + h = a.getHeight(); + double start = Math.toRadians(a.getAngleStart()); + double extent = Math.toRadians(a.getAngleExtent()); + + this.start = start; + this.extent = extent; + + type = a.type; + if (w < 0 || h < 0) + limit = -1; + else if (extent == 0) + limit = type; + else if (Math.abs(extent) <= Math.PI / 2.0) + limit = type + 1; + else if (Math.abs(extent) <= Math.PI) + limit = type + 2; + else if (Math.abs(extent) <= 3.0 * (Math.PI / 2.0)) + limit = type + 3; + else + limit = type + 4; + } + + /** + * Construct a new iterator over an ellipse. + * + * @param e the ellipse + * @param xform the transform + */ + public ArcIterator(Ellipse2D e, AffineTransform xform) + { + this.xform = xform; + x = e.getX(); + y = e.getY(); + w = e.getWidth(); + h = e.getHeight(); + start = 0; + extent = 2 * Math.PI; + type = CHORD; + limit = (w < 0 || h < 0) ? -1 : 5; + } + + /** + * Return the winding rule. + * + * @return {@link PathIterator#WIND_NON_ZERO} + */ + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + /** + * Test if the iteration is complete. + * + * @return true if more segments exist + */ + public boolean isDone() + { + return current > limit; + } + + /** + * Advance the iterator. + */ + public void next() + { + current++; + } + + /** + * Put the current segment into the array, and return the segment type. + * + * @param coords an array of 6 elements + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + */ + public int currentSegment(float[] coords) + { + double[] double_coords = new double[6]; + int code = currentSegment(double_coords); + for (int i = 0; i < 6; ++i) + coords[i] = (float) double_coords[i]; + return code; + } + + /** + * Put the current segment into the array, and return the segment type. + * + * @param coords an array of 6 elements + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + */ + public int currentSegment(double[] coords) + { + double rx = w / 2; + double ry = h / 2; + double xmid = x + rx; + double ymid = y + ry; + + if (current > limit) + throw new NoSuchElementException("arc iterator out of bounds"); + + if (current == 0) + { + coords[0] = xmid + rx * Math.cos(start); + coords[1] = ymid - ry * Math.sin(start); + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + + if (type != OPEN && current == limit) + return SEG_CLOSE; + + if ((current == limit - 1) && (type == PIE)) + { + coords[0] = xmid; + coords[1] = ymid; + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_LINETO; + } + + // note that this produces a cubic approximation of the arc segment, + // not a true ellipsoid. there's no ellipsoid path segment code, + // unfortunately. the cubic approximation looks about right, though. + double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0); + double quad = (Math.PI / 2.0); + + double curr_begin; + double curr_extent; + if (extent > 0) + { + curr_begin = start + (current - 1) * quad; + curr_extent = Math.min((start + extent) - curr_begin, quad); + } + else + { + curr_begin = start - (current - 1) * quad; + curr_extent = Math.max((start + extent) - curr_begin, -quad); + } + + double portion_of_a_quadrant = Math.abs(curr_extent / quad); + + double x0 = xmid + rx * Math.cos(curr_begin); + double y0 = ymid - ry * Math.sin(curr_begin); + + double x1 = xmid + rx * Math.cos(curr_begin + curr_extent); + double y1 = ymid - ry * Math.sin(curr_begin + curr_extent); + + AffineTransform trans = new AffineTransform(); + double[] cvec = new double[2]; + double len = kappa * portion_of_a_quadrant; + double angle = curr_begin; + + // in a hypothetical "first quadrant" setting, our first control + // vector would be sticking up, from [1,0] to [1,kappa]. + // + // let us recall however that in java2d, y coords are upside down + // from what one would consider "normal" first quadrant rules, so we + // will *subtract* the y value of this control vector from our first + // point. + cvec[0] = 0; + if (extent > 0) + cvec[1] = len; + else + cvec[1] = -len; + + trans.scale(rx, ry); + trans.rotate(angle); + trans.transform(cvec, 0, cvec, 0, 1); + coords[0] = x0 + cvec[0]; + coords[1] = y0 - cvec[1]; + + // control vector #2 would, ideally, be sticking out and to the + // right, in a first quadrant arc segment. again, subtraction of y. + cvec[0] = 0; + if (extent > 0) + cvec[1] = -len; + else + cvec[1] = len; + + trans.rotate(curr_extent); + trans.transform(cvec, 0, cvec, 0, 1); + coords[2] = x1 + cvec[0]; + coords[3] = y1 - cvec[1]; + + // end point + coords[4] = x1; + coords[5] = y1; + + if (xform != null) + xform.transform(coords, 0, coords, 0, 3); + + return SEG_CUBICTO; + } + } // class ArcIterator + + /** + * This class implements an arc in double precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + */ + public static class Double extends Arc2D + { + /** The x coordinate of the box bounding the ellipse of this arc. */ + public double x; + + /** The y coordinate of the box bounding the ellipse of this arc. */ + public double y; + + /** The width of the box bounding the ellipse of this arc. */ + public double width; + + /** The height of the box bounding the ellipse of this arc. */ + public double height; + + /** The start angle of this arc, in degrees. */ + public double start; + + /** The extent angle of this arc, in degrees. */ + public double extent; + + /** + * Create a new, open arc at (0,0) with 0 extent. + */ + public Double() + { + super(OPEN); + } + + /** + * Create a new arc of the given type at (0,0) with 0 extent. + * + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Double(int type) + { + super(type); + } + + /** + * Create a new arc with the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Double(double x, double y, double w, double h, double start, + double extent, int type) + { + super(type); + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + } + + /** + * Create a new arc with the given dimensions. + * + * @param r the bounding box + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public Double(Rectangle2D r, double start, double extent, int type) + { + super(type); + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); + this.start = start; + this.extent = extent; + } + + /** + * Return the x coordinate of the bounding box. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate of the bounding box. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width of the bounding box. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height of the bounding box. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Return the start angle of the arc, in degrees. + * + * @return the value of start + */ + public double getAngleStart() + { + return start; + } + + /** + * Return the extent of the arc, in degrees. + * + * @return the value of extent + */ + public double getAngleExtent() + { + return extent; + } + + /** + * Tests if the arc contains points. + * + * @return true if the arc has no interior + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the arc to the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArc(double x, double y, double w, double h, double start, + double extent, int type) + { + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + setArcType(type); + } + + /** + * Sets the start angle of the arc. + * + * @param start the new start angle + */ + public void setAngleStart(double start) + { + this.start = start; + } + + /** + * Sets the extent angle of the arc. + * + * @param extent the new extent angle + */ + public void setAngleExtent(double extent) + { + this.extent = extent; + } + + /** + * Creates a tight bounding box given dimensions that more precise than + * the bounding box of the ellipse. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + protected Rectangle2D makeBounds(double x, double y, double w, double h) + { + return new Rectangle2D.Double(x, y, w, h); + } + } // class Double + + /** + * This class implements an arc in float precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + */ + public static class Float extends Arc2D + { + /** The x coordinate of the box bounding the ellipse of this arc. */ + public float x; + + /** The y coordinate of the box bounding the ellipse of this arc. */ + public float y; + + /** The width of the box bounding the ellipse of this arc. */ + public float width; + + /** The height of the box bounding the ellipse of this arc. */ + public float height; + + /** The start angle of this arc, in degrees. */ + public float start; + + /** The extent angle of this arc, in degrees. */ + public float extent; + + /** + * Create a new, open arc at (0,0) with 0 extent. + */ + public Float() + { + super(OPEN); + } + + /** + * Create a new arc of the given type at (0,0) with 0 extent. + * + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Float(int type) + { + super(type); + } + + /** + * Create a new arc with the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Float(float x, float y, float w, float h, float start, + float extent, int type) + { + super(type); + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + } + + /** + * Create a new arc with the given dimensions. + * + * @param r the bounding box + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public Float(Rectangle2D r, float start, float extent, int type) + { + super(type); + x = (float) r.getX(); + y = (float) r.getY(); + width = (float) r.getWidth(); + height = (float) r.getHeight(); + this.start = start; + this.extent = extent; + } + + /** + * Return the x coordinate of the bounding box. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate of the bounding box. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width of the bounding box. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height of the bounding box. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Return the start angle of the arc, in degrees. + * + * @return the value of start + */ + public double getAngleStart() + { + return start; + } + + /** + * Return the extent of the arc, in degrees. + * + * @return the value of extent + */ + public double getAngleExtent() + { + return extent; + } + + /** + * Tests if the arc contains points. + * + * @return true if the arc has no interior + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the arc to the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArc(double x, double y, double w, double h, double start, + double extent, int type) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + this.start = (float) start; + this.extent = (float) extent; + setArcType(type); + } + + /** + * Sets the start angle of the arc. + * + * @param start the new start angle + */ + public void setAngleStart(double start) + { + this.start = (float) start; + } + + /** + * Sets the extent angle of the arc. + * + * @param extent the new extent angle + */ + public void setAngleExtent(double extent) + { + this.extent = (float) extent; + } + + /** + * Creates a tight bounding box given dimensions that more precise than + * the bounding box of the ellipse. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + protected Rectangle2D makeBounds(double x, double y, double w, double h) + { + return new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h); + } + } // class Float +} // class Arc2D diff --git a/libjava/classpath/java/awt/geom/Area.java b/libjava/classpath/java/awt/geom/Area.java new file mode 100644 index 000000000..a1eaf63f3 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Area.java @@ -0,0 +1,3312 @@ +/* Area.java -- represents a shape built by constructive area geometry + Copyright (C) 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.Vector; + + +/** + * The Area class represents any area for the purpose of + * Constructive Area Geometry (CAG) manipulations. CAG manipulations + * work as an area-wise form of boolean logic, where the basic operations are: + *

  • Add (in boolean algebra: A or B)
    + *
  • Subtract (in boolean algebra: A and (not B) )
    + *
  • Intersect (in boolean algebra: A and B)
    + *
  • Exclusive Or
    + * Illustration of CAG operations
    + * Above is an illustration of the CAG operations on two ring shapes.

    + * + * The contains and intersects() methods are also more accurate than the + * specification of #Shape requires.

    + * + * Please note that constructing an Area can be slow + * (Self-intersection resolving is proportional to the square of + * the number of segments).

    + * @see #add(Area) + * @see #subtract(Area) + * @see #intersect(Area) + * @see #exclusiveOr(Area) + * + * @author Sven de Marothy (sven@physto.se) + * + * @since 1.2 + * @status Works, but could be faster and more reliable. + */ +public class Area implements Shape, Cloneable +{ + /** + * General numerical precision + */ + private static final double EPSILON = 1E-11; + + /** + * recursive subdivision epsilon - (see getRecursionDepth) + */ + private static final double RS_EPSILON = 1E-13; + + /** + * Snap distance - points within this distance are considered equal + */ + private static final double PE_EPSILON = 1E-11; + + /** + * Segment vectors containing solid areas and holes + * This is package-private to avoid an accessor method. + */ + Vector solids; + + /** + * Segment vectors containing solid areas and holes + * This is package-private to avoid an accessor method. + */ + Vector holes; + + /** + * Vector (temporary) storing curve-curve intersections + */ + private Vector cc_intersections; + + /** + * Winding rule WIND_NON_ZERO used, after construction, + * this is irrelevant. + */ + private int windingRule; + + /** + * Constructs an empty Area + */ + public Area() + { + solids = new Vector(); + holes = new Vector(); + } + + /** + * Constructs an Area from any given Shape.

    + * + * If the Shape is self-intersecting, the created Area will consist + * of non-self-intersecting subpaths, and any inner paths which + * are found redundant in accordance with the Shape's winding rule + * will not be included. + * + * @param s the shape (null not permitted). + * + * @throws NullPointerException if s is null. + */ + public Area(Shape s) + { + this(); + + Vector p = makeSegment(s); + + // empty path + if (p == null) + return; + + // delete empty paths + for (int i = 0; i < p.size(); i++) + if (((Segment) p.elementAt(i)).getSignedArea() == 0.0) + p.remove(i--); + + /* + * Resolve self intersecting paths into non-intersecting + * solids and holes. + * Algorithm is as follows: + * 1: Create nodes at all self intersections + * 2: Put all segments into a list + * 3: Grab a segment, follow it, change direction at each node, + * removing segments from the list in the process + * 4: Repeat (3) until no segments remain in the list + * 5: Remove redundant paths and sort into solids and holes + */ + Vector paths = new Vector(); + Segment v; + + for (int i = 0; i < p.size(); i++) + { + Segment path = (Segment) p.elementAt(i); + createNodesSelf(path); + } + + if (p.size() > 1) + { + for (int i = 0; i < p.size() - 1; i++) + for (int j = i + 1; j < p.size(); j++) + { + Segment path1 = (Segment) p.elementAt(i); + Segment path2 = (Segment) p.elementAt(j); + createNodes(path1, path2); + } + } + + // we have intersecting points. + Vector segments = new Vector(); + + for (int i = 0; i < p.size(); i++) + { + Segment path = v = (Segment) p.elementAt(i); + do + { + segments.add(v); + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Performs an add (union) operation on this area with another Area.
    + * @param area - the area to be unioned with this one + */ + public void add(Area area) + { + if (equals(area)) + return; + if (area.isEmpty()) + return; + + Area B = (Area) area.clone(); + + Vector pathA = new Vector(); + Vector pathB = new Vector(); + pathA.addAll(solids); + pathA.addAll(holes); + pathB.addAll(B.solids); + pathB.addAll(B.holes); + + int nNodes = 0; + + for (int i = 0; i < pathA.size(); i++) + { + Segment a = (Segment) pathA.elementAt(i); + for (int j = 0; j < pathB.size(); j++) + { + Segment b = (Segment) pathB.elementAt(j); + nNodes += createNodes(a, b); + } + } + + Vector paths = new Vector(); + Segment v; + + // we have intersecting points. + Vector segments = new Vector(); + + // In a union operation, we keep all + // segments of A oustide B and all B outside A + for (int i = 0; i < pathA.size(); i++) + { + v = (Segment) pathA.elementAt(i); + Segment path = v; + do + { + if (v.isSegmentOutside(area)) + segments.add(v); + v = v.next; + } + while (v != path); + } + + for (int i = 0; i < pathB.size(); i++) + { + v = (Segment) pathB.elementAt(i); + Segment path = v; + do + { + if (v.isSegmentOutside(this)) + segments.add(v); + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Performs a subtraction operation on this Area.
    + * @param area the area to be subtracted from this area. + * @throws NullPointerException if area is null. + */ + public void subtract(Area area) + { + if (isEmpty() || area.isEmpty()) + return; + + if (equals(area)) + { + reset(); + return; + } + + Vector pathA = new Vector(); + Area B = (Area) area.clone(); + pathA.addAll(solids); + pathA.addAll(holes); + + // reverse the directions of B paths. + setDirection(B.holes, true); + setDirection(B.solids, false); + + Vector pathB = new Vector(); + pathB.addAll(B.solids); + pathB.addAll(B.holes); + + int nNodes = 0; + + // create nodes + for (int i = 0; i < pathA.size(); i++) + { + Segment a = (Segment) pathA.elementAt(i); + for (int j = 0; j < pathB.size(); j++) + { + Segment b = (Segment) pathB.elementAt(j); + nNodes += createNodes(a, b); + } + } + + Vector paths = new Vector(); + + // we have intersecting points. + Vector segments = new Vector(); + + // In a subtraction operation, we keep all + // segments of A oustide B and all B within A + // We outsideness-test only one segment in each path + // and the segments before and after any node + for (int i = 0; i < pathA.size(); i++) + { + Segment v = (Segment) pathA.elementAt(i); + Segment path = v; + if (v.isSegmentOutside(area) && v.node == null) + segments.add(v); + boolean node = false; + do + { + if ((v.node != null || node)) + { + node = (v.node != null); + if (v.isSegmentOutside(area)) + segments.add(v); + } + v = v.next; + } + while (v != path); + } + + for (int i = 0; i < pathB.size(); i++) + { + Segment v = (Segment) pathB.elementAt(i); + Segment path = v; + if (! v.isSegmentOutside(this) && v.node == null) + segments.add(v); + v = v.next; + boolean node = false; + do + { + if ((v.node != null || node)) + { + node = (v.node != null); + if (! v.isSegmentOutside(this)) + segments.add(v); + } + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Performs an intersection operation on this Area.
    + * @param area - the area to be intersected with this area. + * @throws NullPointerException if area is null. + */ + public void intersect(Area area) + { + if (isEmpty() || area.isEmpty()) + { + reset(); + return; + } + if (equals(area)) + return; + + Vector pathA = new Vector(); + Area B = (Area) area.clone(); + pathA.addAll(solids); + pathA.addAll(holes); + + Vector pathB = new Vector(); + pathB.addAll(B.solids); + pathB.addAll(B.holes); + + int nNodes = 0; + + // create nodes + for (int i = 0; i < pathA.size(); i++) + { + Segment a = (Segment) pathA.elementAt(i); + for (int j = 0; j < pathB.size(); j++) + { + Segment b = (Segment) pathB.elementAt(j); + nNodes += createNodes(a, b); + } + } + + Vector paths = new Vector(); + + // we have intersecting points. + Vector segments = new Vector(); + + // In an intersection operation, we keep all + // segments of A within B and all B within A + // (The rest must be redundant) + // We outsideness-test only one segment in each path + // and the segments before and after any node + for (int i = 0; i < pathA.size(); i++) + { + Segment v = (Segment) pathA.elementAt(i); + Segment path = v; + if (! v.isSegmentOutside(area) && v.node == null) + segments.add(v); + boolean node = false; + do + { + if ((v.node != null || node)) + { + node = (v.node != null); + if (! v.isSegmentOutside(area)) + segments.add(v); + } + v = v.next; + } + while (v != path); + } + + for (int i = 0; i < pathB.size(); i++) + { + Segment v = (Segment) pathB.elementAt(i); + Segment path = v; + if (! v.isSegmentOutside(this) && v.node == null) + segments.add(v); + v = v.next; + boolean node = false; + do + { + if ((v.node != null || node)) + { + node = (v.node != null); + if (! v.isSegmentOutside(this)) + segments.add(v); + } + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Performs an exclusive-or operation on this Area.
    + * @param area - the area to be XORed with this area. + * @throws NullPointerException if area is null. + */ + public void exclusiveOr(Area area) + { + if (area.isEmpty()) + return; + + if (isEmpty()) + { + Area B = (Area) area.clone(); + solids = B.solids; + holes = B.holes; + return; + } + if (equals(area)) + { + reset(); + return; + } + + Vector pathA = new Vector(); + + Area B = (Area) area.clone(); + Vector pathB = new Vector(); + pathA.addAll(solids); + pathA.addAll(holes); + + // reverse the directions of B paths. + setDirection(B.holes, true); + setDirection(B.solids, false); + pathB.addAll(B.solids); + pathB.addAll(B.holes); + + int nNodes = 0; + + for (int i = 0; i < pathA.size(); i++) + { + Segment a = (Segment) pathA.elementAt(i); + for (int j = 0; j < pathB.size(); j++) + { + Segment b = (Segment) pathB.elementAt(j); + nNodes += createNodes(a, b); + } + } + + Vector paths = new Vector(); + Segment v; + + // we have intersecting points. + Vector segments = new Vector(); + + // In an XOR operation, we operate on all segments + for (int i = 0; i < pathA.size(); i++) + { + v = (Segment) pathA.elementAt(i); + Segment path = v; + do + { + segments.add(v); + v = v.next; + } + while (v != path); + } + + for (int i = 0; i < pathB.size(); i++) + { + v = (Segment) pathB.elementAt(i); + Segment path = v; + do + { + segments.add(v); + v = v.next; + } + while (v != path); + } + + paths = weilerAtherton(segments); + deleteRedundantPaths(paths); + } + + /** + * Clears the Area object, creating an empty area. + */ + public void reset() + { + solids = new Vector(); + holes = new Vector(); + } + + /** + * Returns whether this area encloses any area. + * @return true if the object encloses any area. + */ + public boolean isEmpty() + { + if (solids.size() == 0) + return true; + + double totalArea = 0; + for (int i = 0; i < solids.size(); i++) + totalArea += Math.abs(((Segment) solids.elementAt(i)).getSignedArea()); + for (int i = 0; i < holes.size(); i++) + totalArea -= Math.abs(((Segment) holes.elementAt(i)).getSignedArea()); + if (totalArea <= EPSILON) + return true; + + return false; + } + + /** + * Determines whether the Area consists entirely of line segments + * @return true if the Area lines-only, false otherwise + */ + public boolean isPolygonal() + { + for (int i = 0; i < holes.size(); i++) + if (! ((Segment) holes.elementAt(i)).isPolygonal()) + return false; + for (int i = 0; i < solids.size(); i++) + if (! ((Segment) solids.elementAt(i)).isPolygonal()) + return false; + return true; + } + + /** + * Determines if the Area is rectangular.

    + * + * This is strictly qualified. An area is considered rectangular if:
    + *

  • It consists of a single polygonal path.
    + *
  • It is oriented parallel/perpendicular to the xy axis
    + *
  • It must be exactly rectangular, i.e. small errors induced by + * transformations may cause a false result, although the area is + * visibly rectangular.

    + * @return true if the above criteria are met, false otherwise + */ + public boolean isRectangular() + { + if (isEmpty()) + return true; + + if (holes.size() != 0 || solids.size() != 1) + return false; + + Segment path = (Segment) solids.elementAt(0); + if (! path.isPolygonal()) + return false; + + int nCorners = 0; + Segment s = path; + do + { + Segment s2 = s.next; + double d1 = (s.P2.getX() - s.P1.getX())*(s2.P2.getX() - s2.P1.getX())/ + ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2))); + double d2 = (s.P2.getY() - s.P1.getY())*(s2.P2.getY() - s2.P1.getY())/ + ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2))); + double dotproduct = d1 + d2; + + // For some reason, only rectangles on the XY axis count. + if (d1 != 0 && d2 != 0) + return false; + + if (Math.abs(dotproduct) == 0) // 90 degree angle + nCorners++; + else if ((Math.abs(1.0 - dotproduct) > 0)) // 0 degree angle? + return false; // if not, return false + + s = s.next; + } + while (s != path); + + return nCorners == 4; + } + + /** + * Returns whether the Area consists of more than one simple + * (non self-intersecting) subpath. + * + * @return true if the Area consists of none or one simple subpath, + * false otherwise. + */ + public boolean isSingular() + { + return (holes.size() == 0 && solids.size() <= 1); + } + + /** + * Returns the bounding box of the Area.

    Unlike the CubicCurve2D and + * QuadraticCurve2D classes, this method will return the tightest possible + * bounding box, evaluating the extreme points of each curved segment.

    + * @return the bounding box + */ + public Rectangle2D getBounds2D() + { + if (solids.size() == 0) + return new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0); + + double xmin; + double xmax; + double ymin; + double ymax; + xmin = xmax = ((Segment) solids.elementAt(0)).P1.getX(); + ymin = ymax = ((Segment) solids.elementAt(0)).P1.getY(); + + for (int path = 0; path < solids.size(); path++) + { + Rectangle2D r = ((Segment) solids.elementAt(path)).getPathBounds(); + xmin = Math.min(r.getMinX(), xmin); + ymin = Math.min(r.getMinY(), ymin); + xmax = Math.max(r.getMaxX(), xmax); + ymax = Math.max(r.getMaxY(), ymax); + } + + return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin))); + } + + /** + * Returns the bounds of this object in Rectangle format. + * Please note that this may lead to loss of precision. + * + * @return The bounds. + * @see #getBounds2D() + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + /** + * Create a new area of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + Area clone = new Area(); + for (int i = 0; i < solids.size(); i++) + clone.solids.add(((Segment) solids.elementAt(i)).cloneSegmentList()); + for (int i = 0; i < holes.size(); i++) + clone.holes.add(((Segment) holes.elementAt(i)).cloneSegmentList()); + return clone; + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Compares two Areas. + * + * @param area the area to compare against this area (null + * permitted). + * @return true if the areas are equal, and false + * otherwise. + */ + public boolean equals(Area area) + { + if (area == null) + return false; + + if (! getBounds2D().equals(area.getBounds2D())) + return false; + + if (solids.size() != area.solids.size() + || holes.size() != area.holes.size()) + return false; + + Vector pathA = new Vector(); + pathA.addAll(solids); + pathA.addAll(holes); + Vector pathB = new Vector(); + pathB.addAll(area.solids); + pathB.addAll(area.holes); + + int nPaths = pathA.size(); + boolean[][] match = new boolean[2][nPaths]; + + for (int i = 0; i < nPaths; i++) + { + for (int j = 0; j < nPaths; j++) + { + Segment p1 = (Segment) pathA.elementAt(i); + Segment p2 = (Segment) pathB.elementAt(j); + if (! match[0][i] && ! match[1][j]) + if (p1.pathEquals(p2)) + match[0][i] = match[1][j] = true; + } + } + + boolean result = true; + for (int i = 0; i < nPaths; i++) + result = result && match[0][i] && match[1][i]; + return result; + } + + /** + * Transforms this area by the AffineTransform at. + * + * @param at the transform. + */ + public void transform(AffineTransform at) + { + for (int i = 0; i < solids.size(); i++) + ((Segment) solids.elementAt(i)).transformSegmentList(at); + for (int i = 0; i < holes.size(); i++) + ((Segment) holes.elementAt(i)).transformSegmentList(at); + + // Note that the orientation is not invariant under inversion + if ((at.getType() & AffineTransform.TYPE_FLIP) != 0) + { + setDirection(holes, false); + setDirection(solids, true); + } + } + + /** + * Returns a new Area equal to this one, transformed + * by the AffineTransform at. + * @param at the transform. + * @return the transformed area + * @throws NullPointerException if at is null. + */ + public Area createTransformedArea(AffineTransform at) + { + Area a = (Area) clone(); + a.transform(at); + return a; + } + + /** + * Determines if the point (x,y) is contained within this Area. + * + * @param x the x-coordinate of the point. + * @param y the y-coordinate of the point. + * @return true if the point is contained, false otherwise. + */ + public boolean contains(double x, double y) + { + int n = 0; + for (int i = 0; i < solids.size(); i++) + if (((Segment) solids.elementAt(i)).contains(x, y)) + n++; + + for (int i = 0; i < holes.size(); i++) + if (((Segment) holes.elementAt(i)).contains(x, y)) + n--; + + return (n != 0); + } + + /** + * Determines if the Point2D p is contained within this Area. + * + * @param p the point. + * @return true if the point is contained, false + * otherwise. + * @throws NullPointerException if p is null. + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Determines if the rectangle specified by (x,y) as the upper-left + * and with width w and height h is completely contained within this Area, + * returns false otherwise.

    + * + * This method should always produce the correct results, unlike for other + * classes in geom. + * + * @param x the x-coordinate of the rectangle. + * @param y the y-coordinate of the rectangle. + * @param w the width of the the rectangle. + * @param h the height of the rectangle. + * @return true if the rectangle is considered contained + */ + public boolean contains(double x, double y, double w, double h) + { + LineSegment[] l = new LineSegment[4]; + l[0] = new LineSegment(x, y, x + w, y); + l[1] = new LineSegment(x, y + h, x + w, y + h); + l[2] = new LineSegment(x, y, x, y + h); + l[3] = new LineSegment(x + w, y, x + w, y + h); + + // Since every segment in the area must a contour + // between inside/outside segments, ANY intersection + // will mean the rectangle is not entirely contained. + for (int i = 0; i < 4; i++) + { + for (int path = 0; path < solids.size(); path++) + { + Segment v; + Segment start; + start = v = (Segment) solids.elementAt(path); + do + { + if (l[i].hasIntersections(v)) + return false; + v = v.next; + } + while (v != start); + } + for (int path = 0; path < holes.size(); path++) + { + Segment v; + Segment start; + start = v = (Segment) holes.elementAt(path); + do + { + if (l[i].hasIntersections(v)) + return false; + v = v.next; + } + while (v != start); + } + } + + // Is any point inside? + if (! contains(x, y)) + return false; + + // Final hoop: Is the rectangle non-intersecting and inside, + // but encloses a hole? + Rectangle2D r = new Rectangle2D.Double(x, y, w, h); + for (int path = 0; path < holes.size(); path++) + if (! ((Segment) holes.elementAt(path)).isSegmentOutside(r)) + return false; + + return true; + } + + /** + * Determines if the Rectangle2D specified by r is completely contained + * within this Area, returns false otherwise.

    + * + * This method should always produce the correct results, unlike for other + * classes in geom. + * + * @param r the rectangle. + * @return true if the rectangle is considered contained + * + * @throws NullPointerException if r is null. + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determines if the rectangle specified by (x,y) as the upper-left + * and with width w and height h intersects any part of this Area. + * + * @param x the x-coordinate for the rectangle. + * @param y the y-coordinate for the rectangle. + * @param w the width of the rectangle. + * @param h the height of the rectangle. + * @return true if the rectangle intersects the area, + * false otherwise. + */ + public boolean intersects(double x, double y, double w, double h) + { + if (solids.size() == 0) + return false; + + LineSegment[] l = new LineSegment[4]; + l[0] = new LineSegment(x, y, x + w, y); + l[1] = new LineSegment(x, y + h, x + w, y + h); + l[2] = new LineSegment(x, y, x, y + h); + l[3] = new LineSegment(x + w, y, x + w, y + h); + + // Return true on any intersection + for (int i = 0; i < 4; i++) + { + for (int path = 0; path < solids.size(); path++) + { + Segment v; + Segment start; + start = v = (Segment) solids.elementAt(path); + do + { + if (l[i].hasIntersections(v)) + return true; + v = v.next; + } + while (v != start); + } + for (int path = 0; path < holes.size(); path++) + { + Segment v; + Segment start; + start = v = (Segment) holes.elementAt(path); + do + { + if (l[i].hasIntersections(v)) + return true; + v = v.next; + } + while (v != start); + } + } + + // Non-intersecting, Is any point inside? + if (contains(x + w * 0.5, y + h * 0.5)) + return true; + + // What if the rectangle encloses the whole shape? + Point2D p = ((Segment) solids.elementAt(0)).getMidPoint(); + if ((new Rectangle2D.Double(x, y, w, h)).contains(p)) + return true; + return false; + } + + /** + * Determines if the Rectangle2D specified by r intersects any + * part of this Area. + * @param r the rectangle to test intersection with (null + * not permitted). + * @return true if the rectangle intersects the area, + * false otherwise. + * @throws NullPointerException if r is null. + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns a PathIterator object defining the contour of this Area, + * transformed by at. + * + * @param at the transform. + * @return A path iterator. + */ + public PathIterator getPathIterator(AffineTransform at) + { + return (new AreaIterator(at)); + } + + /** + * Returns a flattened PathIterator object defining the contour of this + * Area, transformed by at and with a defined flatness. + * + * @param at the transform. + * @param flatness the flatness. + * @return A path iterator. + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + //--------------------------------------------------------------------- + // Non-public methods and classes + + /** + * Private pathiterator object. + */ + private class AreaIterator implements PathIterator + { + private Vector segments; + private int index; + private AffineTransform at; + + // Simple compound type for segments + class IteratorSegment + { + int type; + double[] coords; + + IteratorSegment() + { + coords = new double[6]; + } + } + + /** + * The contructor here does most of the work, + * creates a vector of IteratorSegments, which can + * readily be returned + */ + public AreaIterator(AffineTransform at) + { + this.at = at; + index = 0; + segments = new Vector(); + Vector allpaths = new Vector(); + allpaths.addAll(solids); + allpaths.addAll(holes); + + for (int i = 0; i < allpaths.size(); i++) + { + Segment v = (Segment) allpaths.elementAt(i); + Segment start = v; + + IteratorSegment is = new IteratorSegment(); + is.type = SEG_MOVETO; + is.coords[0] = start.P1.getX(); + is.coords[1] = start.P1.getY(); + segments.add(is); + + do + { + is = new IteratorSegment(); + is.type = v.pathIteratorFormat(is.coords); + segments.add(is); + v = v.next; + } + while (v != start); + + is = new IteratorSegment(); + is.type = SEG_CLOSE; + segments.add(is); + } + } + + public int currentSegment(double[] coords) + { + IteratorSegment s = (IteratorSegment) segments.elementAt(index); + if (at != null) + at.transform(s.coords, 0, coords, 0, 3); + else + for (int i = 0; i < 6; i++) + coords[i] = s.coords[i]; + return (s.type); + } + + public int currentSegment(float[] coords) + { + IteratorSegment s = (IteratorSegment) segments.elementAt(index); + double[] d = new double[6]; + if (at != null) + { + at.transform(s.coords, 0, d, 0, 3); + for (int i = 0; i < 6; i++) + coords[i] = (float) d[i]; + } + else + for (int i = 0; i < 6; i++) + coords[i] = (float) s.coords[i]; + return (s.type); + } + + // Note that the winding rule should not matter here, + // EVEN_ODD is chosen because it renders faster. + public int getWindingRule() + { + return (PathIterator.WIND_EVEN_ODD); + } + + public boolean isDone() + { + return (index >= segments.size()); + } + + public void next() + { + index++; + } + } + + /** + * Performs the fundamental task of the Weiler-Atherton algorithm, + * traverse a list of segments, for each segment: + * Follow it, removing segments from the list and switching paths + * at each node. Do so until the starting segment is reached. + * + * Returns a Vector of the resulting paths. + */ + private Vector weilerAtherton(Vector segments) + { + Vector paths = new Vector(); + while (segments.size() > 0) + { + // Iterate over the path + Segment start = (Segment) segments.elementAt(0); + Segment s = start; + do + { + segments.remove(s); + if (s.node != null) + { // switch over + s.next = s.node; + s.node = null; + } + s = s.next; // continue + } + while (s != start); + + paths.add(start); + } + return paths; + } + + /** + * A small wrapper class to store intersection points + */ + private class Intersection + { + Point2D p; // the 2D point of intersection + double ta; // the parametric value on a + double tb; // the parametric value on b + Segment seg; // segment placeholder for node setting + + public Intersection(Point2D p, double ta, double tb) + { + this.p = p; + this.ta = ta; + this.tb = tb; + } + } + + /** + * Returns the recursion depth necessary to approximate the + * curve by line segments within the error RS_EPSILON. + * + * This is done with Wang's formula: + * L0 = max{0<=i<=N-2}(|xi - 2xi+1 + xi+2|,|yi - 2yi+1 + yi+2|) + * r0 = log4(sqrt(2)*N*(N-1)*L0/8e) + * Where e is the maximum distance error (RS_EPSILON) + */ + private int getRecursionDepth(CubicSegment curve) + { + double x0 = curve.P1.getX(); + double y0 = curve.P1.getY(); + + double x1 = curve.cp1.getX(); + double y1 = curve.cp1.getY(); + + double x2 = curve.cp2.getX(); + double y2 = curve.cp2.getY(); + + double x3 = curve.P2.getX(); + double y3 = curve.P2.getY(); + + double L0 = Math.max(Math.max(Math.abs(x0 - 2 * x1 + x2), + Math.abs(x1 - 2 * x2 + x3)), + Math.max(Math.abs(y0 - 2 * y1 + y2), + Math.abs(y1 - 2 * y2 + y3))); + + double f = Math.sqrt(2) * 6.0 * L0 / (8.0 * RS_EPSILON); + + int r0 = (int) Math.ceil(Math.log(f) / Math.log(4.0)); + return (r0); + } + + /** + * Performs recursive subdivision: + * @param c1 - curve 1 + * @param c2 - curve 2 + * @param depth1 - recursion depth of curve 1 + * @param depth2 - recursion depth of curve 2 + * @param t1 - global parametric value of the first curve's starting point + * @param t2 - global parametric value of the second curve's starting point + * @param w1 - global parametric length of curve 1 + * @param w2 - global parametric length of curve 2 + * + * The final four parameters are for keeping track of the parametric + * value of the curve. For a full curve t = 0, w = 1, w is halved with + * each subdivision. + */ + private void recursiveSubdivide(CubicCurve2D c1, CubicCurve2D c2, + int depth1, int depth2, double t1, + double t2, double w1, double w2) + { + boolean flat1 = depth1 <= 0; + boolean flat2 = depth2 <= 0; + + if (flat1 && flat2) + { + double xlk = c1.getP2().getX() - c1.getP1().getX(); + double ylk = c1.getP2().getY() - c1.getP1().getY(); + + double xnm = c2.getP2().getX() - c2.getP1().getX(); + double ynm = c2.getP2().getY() - c2.getP1().getY(); + + double xmk = c2.getP1().getX() - c1.getP1().getX(); + double ymk = c2.getP1().getY() - c1.getP1().getY(); + double det = xnm * ylk - ynm * xlk; + + if (det + 1.0 == 1.0) + return; + + double detinv = 1.0 / det; + double s = (xnm * ymk - ynm * xmk) * detinv; + double t = (xlk * ymk - ylk * xmk) * detinv; + if ((s < 0.0) || (s > 1.0) || (t < 0.0) || (t > 1.0)) + return; + + double[] temp = new double[2]; + temp[0] = t1 + s * w1; + temp[1] = t2 + t * w1; + cc_intersections.add(temp); + return; + } + + CubicCurve2D.Double c11 = new CubicCurve2D.Double(); + CubicCurve2D.Double c12 = new CubicCurve2D.Double(); + CubicCurve2D.Double c21 = new CubicCurve2D.Double(); + CubicCurve2D.Double c22 = new CubicCurve2D.Double(); + + if (! flat1 && ! flat2) + { + depth1--; + depth2--; + w1 = w1 * 0.5; + w2 = w2 * 0.5; + c1.subdivide(c11, c12); + c2.subdivide(c21, c22); + if (c11.getBounds2D().intersects(c21.getBounds2D())) + recursiveSubdivide(c11, c21, depth1, depth2, t1, t2, w1, w2); + if (c11.getBounds2D().intersects(c22.getBounds2D())) + recursiveSubdivide(c11, c22, depth1, depth2, t1, t2 + w2, w1, w2); + if (c12.getBounds2D().intersects(c21.getBounds2D())) + recursiveSubdivide(c12, c21, depth1, depth2, t1 + w1, t2, w1, w2); + if (c12.getBounds2D().intersects(c22.getBounds2D())) + recursiveSubdivide(c12, c22, depth1, depth2, t1 + w1, t2 + w2, w1, w2); + return; + } + + if (! flat1) + { + depth1--; + c1.subdivide(c11, c12); + w1 = w1 * 0.5; + if (c11.getBounds2D().intersects(c2.getBounds2D())) + recursiveSubdivide(c11, c2, depth1, depth2, t1, t2, w1, w2); + if (c12.getBounds2D().intersects(c2.getBounds2D())) + recursiveSubdivide(c12, c2, depth1, depth2, t1 + w1, t2, w1, w2); + return; + } + + depth2--; + c2.subdivide(c21, c22); + w2 = w2 * 0.5; + if (c1.getBounds2D().intersects(c21.getBounds2D())) + recursiveSubdivide(c1, c21, depth1, depth2, t1, t2, w1, w2); + if (c1.getBounds2D().intersects(c22.getBounds2D())) + recursiveSubdivide(c1, c22, depth1, depth2, t1, t2 + w2, w1, w2); + } + + /** + * Returns a set of interesections between two Cubic segments + * Or null if no intersections were found. + * + * The method used to find the intersection is recursive midpoint + * subdivision. Outline description: + * + * 1) Check if the bounding boxes of the curves intersect, + * 2) If so, divide the curves in the middle and test the bounding + * boxes again, + * 3) Repeat until a maximum recursion depth has been reached, where + * the intersecting curves can be approximated by line segments. + * + * This is a reasonably accurate method, although the recursion depth + * is typically around 20, the bounding-box tests allow for significant + * pruning of the subdivision tree. + * + * This is package-private to avoid an accessor method. + */ + Intersection[] cubicCubicIntersect(CubicSegment curve1, CubicSegment curve2) + { + Rectangle2D r1 = curve1.getBounds(); + Rectangle2D r2 = curve2.getBounds(); + + if (! r1.intersects(r2)) + return null; + + cc_intersections = new Vector(); + recursiveSubdivide(curve1.getCubicCurve2D(), curve2.getCubicCurve2D(), + getRecursionDepth(curve1), getRecursionDepth(curve2), + 0.0, 0.0, 1.0, 1.0); + + if (cc_intersections.size() == 0) + return null; + + Intersection[] results = new Intersection[cc_intersections.size()]; + for (int i = 0; i < cc_intersections.size(); i++) + { + double[] temp = (double[]) cc_intersections.elementAt(i); + results[i] = new Intersection(curve1.evaluatePoint(temp[0]), temp[0], + temp[1]); + } + cc_intersections = null; + return (results); + } + + /** + * Returns the intersections between a line and a quadratic bezier + * Or null if no intersections are found1 + * This is done through combining the line's equation with the + * parametric form of the Bezier and solving the resulting quadratic. + * This is package-private to avoid an accessor method. + */ + Intersection[] lineQuadIntersect(LineSegment l, QuadSegment c) + { + double[] y = new double[3]; + double[] x = new double[3]; + double[] r = new double[3]; + int nRoots; + double x0 = c.P1.getX(); + double y0 = c.P1.getY(); + double x1 = c.cp.getX(); + double y1 = c.cp.getY(); + double x2 = c.P2.getX(); + double y2 = c.P2.getY(); + + double lx0 = l.P1.getX(); + double ly0 = l.P1.getY(); + double lx1 = l.P2.getX(); + double ly1 = l.P2.getY(); + double dx = lx1 - lx0; + double dy = ly1 - ly0; + + // form r(t) = y(t) - x(t) for the bezier + y[0] = y0; + y[1] = 2 * (y1 - y0); + y[2] = (y2 - 2 * y1 + y0); + + x[0] = x0; + x[1] = 2 * (x1 - x0); + x[2] = (x2 - 2 * x1 + x0); + + // a point, not a line + if (dy == 0 && dx == 0) + return null; + + // line on y axis + if (dx == 0 || (dy / dx) > 1.0) + { + double k = dx / dy; + x[0] -= lx0; + y[0] -= ly0; + y[0] *= k; + y[1] *= k; + y[2] *= k; + } + else + { + double k = dy / dx; + x[0] -= lx0; + y[0] -= ly0; + x[0] *= k; + x[1] *= k; + x[2] *= k; + } + + for (int i = 0; i < 3; i++) + r[i] = y[i] - x[i]; + + if ((nRoots = QuadCurve2D.solveQuadratic(r)) > 0) + { + Intersection[] temp = new Intersection[nRoots]; + int intersections = 0; + for (int i = 0; i < nRoots; i++) + { + double t = r[i]; + if (t >= 0.0 && t <= 1.0) + { + Point2D p = c.evaluatePoint(t); + + // if the line is on an axis, snap the point to that axis. + if (dx == 0) + p.setLocation(lx0, p.getY()); + if (dy == 0) + p.setLocation(p.getX(), ly0); + + if (p.getX() <= Math.max(lx0, lx1) + && p.getX() >= Math.min(lx0, lx1) + && p.getY() <= Math.max(ly0, ly1) + && p.getY() >= Math.min(ly0, ly1)) + { + double lineparameter = p.distance(l.P1) / l.P2.distance(l.P1); + temp[i] = new Intersection(p, lineparameter, t); + intersections++; + } + } + else + temp[i] = null; + } + if (intersections == 0) + return null; + + Intersection[] rValues = new Intersection[intersections]; + + for (int i = 0; i < nRoots; i++) + if (temp[i] != null) + rValues[--intersections] = temp[i]; + return (rValues); + } + return null; + } + + /** + * Returns the intersections between a line and a cubic segment + * This is done through combining the line's equation with the + * parametric form of the Bezier and solving the resulting quadratic. + * This is package-private to avoid an accessor method. + */ + Intersection[] lineCubicIntersect(LineSegment l, CubicSegment c) + { + double[] y = new double[4]; + double[] x = new double[4]; + double[] r = new double[4]; + int nRoots; + double x0 = c.P1.getX(); + double y0 = c.P1.getY(); + double x1 = c.cp1.getX(); + double y1 = c.cp1.getY(); + double x2 = c.cp2.getX(); + double y2 = c.cp2.getY(); + double x3 = c.P2.getX(); + double y3 = c.P2.getY(); + + double lx0 = l.P1.getX(); + double ly0 = l.P1.getY(); + double lx1 = l.P2.getX(); + double ly1 = l.P2.getY(); + double dx = lx1 - lx0; + double dy = ly1 - ly0; + + // form r(t) = y(t) - x(t) for the bezier + y[0] = y0; + y[1] = 3 * (y1 - y0); + y[2] = 3 * (y2 + y0 - 2 * y1); + y[3] = y3 - 3 * y2 + 3 * y1 - y0; + + x[0] = x0; + x[1] = 3 * (x1 - x0); + x[2] = 3 * (x2 + x0 - 2 * x1); + x[3] = x3 - 3 * x2 + 3 * x1 - x0; + + // a point, not a line + if (dy == 0 && dx == 0) + return null; + + // line on y axis + if (dx == 0 || (dy / dx) > 1.0) + { + double k = dx / dy; + x[0] -= lx0; + y[0] -= ly0; + y[0] *= k; + y[1] *= k; + y[2] *= k; + y[3] *= k; + } + else + { + double k = dy / dx; + x[0] -= lx0; + y[0] -= ly0; + x[0] *= k; + x[1] *= k; + x[2] *= k; + x[3] *= k; + } + for (int i = 0; i < 4; i++) + r[i] = y[i] - x[i]; + + if ((nRoots = CubicCurve2D.solveCubic(r)) > 0) + { + Intersection[] temp = new Intersection[nRoots]; + int intersections = 0; + for (int i = 0; i < nRoots; i++) + { + double t = r[i]; + if (t >= 0.0 && t <= 1.0) + { + // if the line is on an axis, snap the point to that axis. + Point2D p = c.evaluatePoint(t); + if (dx == 0) + p.setLocation(lx0, p.getY()); + if (dy == 0) + p.setLocation(p.getX(), ly0); + + if (p.getX() <= Math.max(lx0, lx1) + && p.getX() >= Math.min(lx0, lx1) + && p.getY() <= Math.max(ly0, ly1) + && p.getY() >= Math.min(ly0, ly1)) + { + double lineparameter = p.distance(l.P1) / l.P2.distance(l.P1); + temp[i] = new Intersection(p, lineparameter, t); + intersections++; + } + } + else + temp[i] = null; + } + + if (intersections == 0) + return null; + + Intersection[] rValues = new Intersection[intersections]; + for (int i = 0; i < nRoots; i++) + if (temp[i] != null) + rValues[--intersections] = temp[i]; + return (rValues); + } + return null; + } + + /** + * Returns the intersection between two lines, or null if there is no + * intersection. + * This is package-private to avoid an accessor method. + */ + Intersection linesIntersect(LineSegment a, LineSegment b) + { + Point2D P1 = a.P1; + Point2D P2 = a.P2; + Point2D P3 = b.P1; + Point2D P4 = b.P2; + + if (! Line2D.linesIntersect(P1.getX(), P1.getY(), P2.getX(), P2.getY(), + P3.getX(), P3.getY(), P4.getX(), P4.getY())) + return null; + + double x1 = P1.getX(); + double y1 = P1.getY(); + double rx = P2.getX() - x1; + double ry = P2.getY() - y1; + + double x2 = P3.getX(); + double y2 = P3.getY(); + double sx = P4.getX() - x2; + double sy = P4.getY() - y2; + + double determinant = sx * ry - sy * rx; + double nom = (sx * (y2 - y1) + sy * (x1 - x2)); + + // Parallel lines don't intersect. At least we pretend they don't. + if (Math.abs(determinant) < EPSILON) + return null; + + nom = nom / determinant; + + if (nom == 0.0) + return null; + if (nom == 1.0) + return null; + + Point2D p = new Point2D.Double(x1 + nom * rx, y1 + nom * ry); + + return new Intersection(p, p.distance(P1) / P1.distance(P2), + p.distance(P3) / P3.distance(P4)); + } + + /** + * Determines if two points are equal, within an error margin + * 'snap distance' + * This is package-private to avoid an accessor method. + */ + boolean pointEquals(Point2D a, Point2D b) + { + return (a.equals(b) || a.distance(b) < PE_EPSILON); + } + + /** + * Helper method + * Turns a shape into a Vector of Segments + */ + private Vector makeSegment(Shape s) + { + Vector paths = new Vector(); + PathIterator pi = s.getPathIterator(null); + double[] coords = new double[6]; + Segment subpath = null; + Segment current = null; + double cx; + double cy; + double subpathx; + double subpathy; + cx = cy = subpathx = subpathy = 0.0; + + this.windingRule = pi.getWindingRule(); + + while (! pi.isDone()) + { + Segment v; + switch (pi.currentSegment(coords)) + { + case PathIterator.SEG_MOVETO: + if (subpath != null) + { // close existing open path + current.next = new LineSegment(cx, cy, subpathx, subpathy); + current = current.next; + current.next = subpath; + } + subpath = null; + subpathx = cx = coords[0]; + subpathy = cy = coords[1]; + break; + + // replace 'close' with a line-to. + case PathIterator.SEG_CLOSE: + if (subpath != null && (subpathx != cx || subpathy != cy)) + { + current.next = new LineSegment(cx, cy, subpathx, subpathy); + current = current.next; + current.next = subpath; + cx = subpathx; + cy = subpathy; + subpath = null; + } + else if (subpath != null) + { + current.next = subpath; + subpath = null; + } + break; + case PathIterator.SEG_LINETO: + if (cx != coords[0] || cy != coords[1]) + { + v = new LineSegment(cx, cy, coords[0], coords[1]); + if (subpath == null) + { + subpath = current = v; + paths.add(subpath); + } + else + { + current.next = v; + current = current.next; + } + cx = coords[0]; + cy = coords[1]; + } + break; + case PathIterator.SEG_QUADTO: + v = new QuadSegment(cx, cy, coords[0], coords[1], coords[2], + coords[3]); + if (subpath == null) + { + subpath = current = v; + paths.add(subpath); + } + else + { + current.next = v; + current = current.next; + } + cx = coords[2]; + cy = coords[3]; + break; + case PathIterator.SEG_CUBICTO: + v = new CubicSegment(cx, cy, coords[0], coords[1], coords[2], + coords[3], coords[4], coords[5]); + if (subpath == null) + { + subpath = current = v; + paths.add(subpath); + } + else + { + current.next = v; + current = current.next; + } + + // check if the cubic is self-intersecting + double[] lpts = ((CubicSegment) v).getLoop(); + if (lpts != null) + { + // if it is, break off the loop into its own path. + v.subdivideInsert(lpts[0]); + v.next.subdivideInsert((lpts[1] - lpts[0]) / (1.0 - lpts[0])); + + CubicSegment loop = (CubicSegment) v.next; + v.next = loop.next; + loop.next = loop; + + v.P2 = v.next.P1 = loop.P2 = loop.P1; // snap points + paths.add(loop); + current = v.next; + } + + cx = coords[4]; + cy = coords[5]; + break; + } + pi.next(); + } + + if (subpath != null) + { // close any open path + if (subpathx != cx || subpathy != cy) + { + current.next = new LineSegment(cx, cy, subpathx, subpathy); + current = current.next; + current.next = subpath; + } + else + current.next = subpath; + } + + if (paths.size() == 0) + return (null); + + return (paths); + } + + /** + * Find the intersections of two separate closed paths, + * A and B, split the segments at the intersection points, + * and create nodes pointing from one to the other + */ + private int createNodes(Segment A, Segment B) + { + int nNodes = 0; + + Segment a = A; + Segment b = B; + + do + { + do + { + nNodes += a.splitIntersections(b); + b = b.next; + } + while (b != B); + + a = a.next; // move to the next segment + } + while (a != A); // until one wrap. + + return (nNodes); + } + + /** + * Find the intersections of a path with itself. + * Splits the segments at the intersection points, + * and create nodes pointing from one to the other. + */ + private int createNodesSelf(Segment A) + { + int nNodes = 0; + Segment a = A; + + if (A.next == A) + return 0; + + do + { + Segment b = a.next; + do + { + if (b != a) // necessary + nNodes += a.splitIntersections(b); + b = b.next; + } + while (b != A); + a = a.next; // move to the next segment + } + while (a != A); // until one wrap. + + return (nNodes); + } + + /** + * Deletes paths which are redundant from a list, (i.e. solid areas within + * solid areas) Clears any nodes. Sorts the remaining paths into solids + * and holes, sets their orientation and sets the solids and holes lists. + */ + private void deleteRedundantPaths(Vector paths) + { + int npaths = paths.size(); + + int[][] contains = new int[npaths][npaths]; + int[][] windingNumbers = new int[npaths][2]; + int neg; + Rectangle2D[] bb = new Rectangle2D[npaths]; // path bounding boxes + + neg = ((windingRule == PathIterator.WIND_NON_ZERO) ? -1 : 1); + + for (int i = 0; i < npaths; i++) + bb[i] = ((Segment) paths.elementAt(i)).getPathBounds(); + + // Find which path contains which, assign winding numbers + for (int i = 0; i < npaths; i++) + { + Segment pathA = (Segment) paths.elementAt(i); + pathA.nullNodes(); // remove any now-redundant nodes, in case. + int windingA = pathA.hasClockwiseOrientation() ? 1 : neg; + + for (int j = 0; j < npaths; j++) + if (i != j) + { + Segment pathB = (Segment) paths.elementAt(j); + + // A contains B + if (bb[i].intersects(bb[j])) + { + Segment s = pathB.next; + while (s.P1.getY() == s.P2.getY() && s != pathB) + s = s.next; + Point2D p = s.getMidPoint(); + if (pathA.contains(p.getX(), p.getY())) + contains[i][j] = windingA; + } + else + // A does not contain B + contains[i][j] = 0; + } + else + contains[i][j] = windingA; // i == j + } + + for (int i = 0; i < npaths; i++) + { + windingNumbers[i][0] = 0; + for (int j = 0; j < npaths; j++) + windingNumbers[i][0] += contains[j][i]; + windingNumbers[i][1] = contains[i][i]; + } + + Vector solids = new Vector(); + Vector holes = new Vector(); + + if (windingRule == PathIterator.WIND_NON_ZERO) + { + for (int i = 0; i < npaths; i++) + { + if (windingNumbers[i][0] == 0) + holes.add(paths.elementAt(i)); + else if (windingNumbers[i][0] - windingNumbers[i][1] == 0 + && Math.abs(windingNumbers[i][0]) == 1) + solids.add(paths.elementAt(i)); + } + } + else + { + windingRule = PathIterator.WIND_NON_ZERO; + for (int i = 0; i < npaths; i++) + { + if ((windingNumbers[i][0] & 1) == 0) + holes.add(paths.elementAt(i)); + else if ((windingNumbers[i][0] & 1) == 1) + solids.add(paths.elementAt(i)); + } + } + + setDirection(holes, false); + setDirection(solids, true); + this.holes = holes; + this.solids = solids; + } + + /** + * Sets the winding direction of a Vector of paths + * @param clockwise gives the direction, + * true = clockwise, false = counter-clockwise + */ + private void setDirection(Vector paths, boolean clockwise) + { + Segment v; + for (int i = 0; i < paths.size(); i++) + { + v = (Segment) paths.elementAt(i); + if (clockwise != v.hasClockwiseOrientation()) + v.reverseAll(); + } + } + + /** + * Class representing a linked-list of vertices forming a closed polygon, + * convex or concave, without holes. + */ + private abstract class Segment implements Cloneable + { + // segment type, PathIterator segment types are used. + Point2D P1; + Point2D P2; + Segment next; + Segment node; + + Segment() + { + P1 = P2 = null; + node = next = null; + } + + /** + * Reverses the direction of a single segment + */ + abstract void reverseCoords(); + + /** + * Returns the segment's midpoint + */ + abstract Point2D getMidPoint(); + + /** + * Returns the bounding box of this segment + */ + abstract Rectangle2D getBounds(); + + /** + * Transforms a single segment + */ + abstract void transform(AffineTransform at); + + /** + * Returns the PathIterator type of a segment + */ + abstract int getType(); + + /** + */ + abstract int splitIntersections(Segment b); + + /** + * Returns the PathIterator coords of a segment + */ + abstract int pathIteratorFormat(double[] coords); + + /** + * Returns the number of intersections on the positive X axis, + * with the origin at (x,y), used for contains()-testing + * + * (Although that could be done by the line-intersect methods, + * a dedicated method is better to guarantee consitent handling + * of endpoint-special-cases) + */ + abstract int rayCrossing(double x, double y); + + /** + * Subdivides the segment at parametric value t, inserting + * the new segment into the linked list after this, + * such that this becomes [0,t] and this.next becomes [t,1] + */ + abstract void subdivideInsert(double t); + + /** + * Returns twice the area of a curve, relative the P1-P2 line + * Used for area calculations. + */ + abstract double curveArea(); + + /** + * Compare two segments. + */ + abstract boolean equals(Segment b); + + /** + * Determines if this path of segments contains the point (x,y) + */ + boolean contains(double x, double y) + { + Segment v = this; + int crossings = 0; + do + { + int n = v.rayCrossing(x, y); + crossings += n; + v = v.next; + } + while (v != this); + return ((crossings & 1) == 1); + } + + /** + * Nulls all nodes of the path. Clean up any 'hairs'. + */ + void nullNodes() + { + Segment v = this; + do + { + v.node = null; + v = v.next; + } + while (v != this); + } + + /** + * Transforms each segment in the closed path + */ + void transformSegmentList(AffineTransform at) + { + Segment v = this; + do + { + v.transform(at); + v = v.next; + } + while (v != this); + } + + /** + * Determines the winding direction of the path + * By the sign of the area. + */ + boolean hasClockwiseOrientation() + { + return (getSignedArea() > 0.0); + } + + /** + * Returns the bounds of this path + */ + public Rectangle2D getPathBounds() + { + double xmin; + double xmax; + double ymin; + double ymax; + xmin = xmax = P1.getX(); + ymin = ymax = P1.getY(); + + Segment v = this; + do + { + Rectangle2D r = v.getBounds(); + xmin = Math.min(r.getMinX(), xmin); + ymin = Math.min(r.getMinY(), ymin); + xmax = Math.max(r.getMaxX(), xmax); + ymax = Math.max(r.getMaxY(), ymax); + v = v.next; + } + while (v != this); + + return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin))); + } + + /** + * Calculates twice the signed area of the path; + */ + double getSignedArea() + { + Segment s; + double area = 0.0; + + s = this; + do + { + area += s.curveArea(); + + area += s.P1.getX() * s.next.P1.getY() + - s.P1.getY() * s.next.P1.getX(); + s = s.next; + } + while (s != this); + + return area; + } + + /** + * Reverses the orientation of the whole polygon + */ + void reverseAll() + { + reverseCoords(); + Segment v = next; + Segment former = this; + while (v != this) + { + v.reverseCoords(); + Segment vnext = v.next; + v.next = former; + former = v; + v = vnext; + } + next = former; + } + + /** + * Inserts a Segment after this one + */ + void insert(Segment v) + { + Segment n = next; + next = v; + v.next = n; + } + + /** + * Returns if this segment path is polygonal + */ + boolean isPolygonal() + { + Segment v = this; + do + { + if (! (v instanceof LineSegment)) + return false; + v = v.next; + } + while (v != this); + return true; + } + + /** + * Clones this path + */ + Segment cloneSegmentList() throws CloneNotSupportedException + { + Vector list = new Vector(); + Segment v = next; + + while (v != this) + { + list.add(v); + v = v.next; + } + + Segment clone = (Segment) this.clone(); + v = clone; + for (int i = 0; i < list.size(); i++) + { + clone.next = (Segment) ((Segment) list.elementAt(i)).clone(); + clone = clone.next; + } + clone.next = v; + return v; + } + + /** + * Creates a node between this segment and segment b + * at the given intersection + * @return the number of nodes created (0 or 1) + */ + int createNode(Segment b, Intersection i) + { + Point2D p = i.p; + if ((pointEquals(P1, p) || pointEquals(P2, p)) + && (pointEquals(b.P1, p) || pointEquals(b.P2, p))) + return 0; + + subdivideInsert(i.ta); + b.subdivideInsert(i.tb); + + // snap points + b.P2 = b.next.P1 = P2 = next.P1 = i.p; + + node = b.next; + b.node = next; + return 1; + } + + /** + * Creates multiple nodes from a list of intersections, + * This must be done in the order of ascending parameters, + * and the parameters must be recalculated in accordance + * with each split. + * @return the number of nodes created + */ + protected int createNodes(Segment b, Intersection[] x) + { + Vector v = new Vector(); + for (int i = 0; i < x.length; i++) + { + Point2D p = x[i].p; + if (! ((pointEquals(P1, p) || pointEquals(P2, p)) + && (pointEquals(b.P1, p) || pointEquals(b.P2, p)))) + v.add(x[i]); + } + + int nNodes = v.size(); + Intersection[] A = new Intersection[nNodes]; + Intersection[] B = new Intersection[nNodes]; + for (int i = 0; i < nNodes; i++) + A[i] = B[i] = (Intersection) v.elementAt(i); + + // Create two lists sorted by the parameter + // Bubble sort, OK I suppose, since the number of intersections + // cannot be larger than 9 (cubic-cubic worst case) anyway + for (int i = 0; i < nNodes - 1; i++) + { + for (int j = i + 1; j < nNodes; j++) + { + if (A[i].ta > A[j].ta) + { + Intersection swap = A[i]; + A[i] = A[j]; + A[j] = swap; + } + if (B[i].tb > B[j].tb) + { + Intersection swap = B[i]; + B[i] = B[j]; + B[j] = swap; + } + } + } + // subdivide a + Segment s = this; + for (int i = 0; i < nNodes; i++) + { + s.subdivideInsert(A[i].ta); + + // renormalize the parameters + for (int j = i + 1; j < nNodes; j++) + A[j].ta = (A[j].ta - A[i].ta) / (1.0 - A[i].ta); + + A[i].seg = s; + s = s.next; + } + + // subdivide b, set nodes + s = b; + for (int i = 0; i < nNodes; i++) + { + s.subdivideInsert(B[i].tb); + + for (int j = i + 1; j < nNodes; j++) + B[j].tb = (B[j].tb - B[i].tb) / (1.0 - B[i].tb); + + // set nodes + B[i].seg.node = s.next; // node a -> b + s.node = B[i].seg.next; // node b -> a + + // snap points + B[i].seg.P2 = B[i].seg.next.P1 = s.P2 = s.next.P1 = B[i].p; + s = s.next; + } + return nNodes; + } + + /** + * Determines if two paths are equal. + * Colinear line segments are ignored in the comparison. + */ + boolean pathEquals(Segment B) + { + if (! getPathBounds().equals(B.getPathBounds())) + return false; + + Segment startA = getTopLeft(); + Segment startB = B.getTopLeft(); + Segment a = startA; + Segment b = startB; + do + { + if (! a.equals(b)) + return false; + + if (a instanceof LineSegment) + a = ((LineSegment) a).lastCoLinear(); + if (b instanceof LineSegment) + b = ((LineSegment) b).lastCoLinear(); + + a = a.next; + b = b.next; + } + while (a != startA && b != startB); + return true; + } + + /** + * Return the segment with the top-leftmost first point + */ + Segment getTopLeft() + { + Segment v = this; + Segment tl = this; + do + { + if (v.P1.getY() < tl.P1.getY()) + tl = v; + else if (v.P1.getY() == tl.P1.getY()) + { + if (v.P1.getX() < tl.P1.getX()) + tl = v; + } + v = v.next; + } + while (v != this); + return tl; + } + + /** + * Returns if the path has a segment outside a shape + */ + boolean isSegmentOutside(Shape shape) + { + return ! shape.contains(getMidPoint()); + } + } // class Segment + + private class LineSegment extends Segment + { + public LineSegment(double x1, double y1, double x2, double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + } + + public LineSegment(Point2D p1, Point2D p2) + { + super(); + P1 = (Point2D) p1.clone(); + P2 = (Point2D) p2.clone(); + } + + /** + * Clones this segment + */ + public Object clone() + { + return new LineSegment(P1, P2); + } + + /** + * Transforms the segment + */ + void transform(AffineTransform at) + { + P1 = at.transform(P1, null); + P2 = at.transform(P2, null); + } + + /** + * Swap start and end points + */ + void reverseCoords() + { + Point2D p = P1; + P1 = P2; + P2 = p; + } + + /** + * Returns the segment's midpoint + */ + Point2D getMidPoint() + { + return (new Point2D.Double(0.5 * (P1.getX() + P2.getX()), + 0.5 * (P1.getY() + P2.getY()))); + } + + /** + * Returns twice the area of a curve, relative the P1-P2 line + * Obviously, a line does not enclose any area besides the line + */ + double curveArea() + { + return 0; + } + + /** + * Returns the PathIterator type of a segment + */ + int getType() + { + return (PathIterator.SEG_LINETO); + } + + /** + * Subdivides the segment at parametric value t, inserting + * the new segment into the linked list after this, + * such that this becomes [0,t] and this.next becomes [t,1] + */ + void subdivideInsert(double t) + { + Point2D p = new Point2D.Double((P2.getX() - P1.getX()) * t + P1.getX(), + (P2.getY() - P1.getY()) * t + P1.getY()); + insert(new LineSegment(p, P2)); + P2 = p; + next.node = node; + node = null; + } + + /** + * Determines if two line segments are strictly colinear + */ + boolean isCoLinear(LineSegment b) + { + double x1 = P1.getX(); + double y1 = P1.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + double x3 = b.P1.getX(); + double y3 = b.P1.getY(); + double x4 = b.P2.getX(); + double y4 = b.P2.getY(); + + if ((y1 - y3) * (x4 - x3) - (x1 - x3) * (y4 - y3) != 0.0) + return false; + + return ((x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3) == 0.0); + } + + /** + * Return the last segment colinear with this one. + * Used in comparing paths. + */ + Segment lastCoLinear() + { + Segment prev = this; + Segment v = next; + + while (v instanceof LineSegment) + { + if (isCoLinear((LineSegment) v)) + { + prev = v; + v = v.next; + } + else + return prev; + } + return prev; + } + + /** + * Compare two segments. + * We must take into account that the lines may be broken into colinear + * subsegments and ignore them. + */ + boolean equals(Segment b) + { + if (! (b instanceof LineSegment)) + return false; + Point2D p1 = P1; + Point2D p3 = b.P1; + + if (! p1.equals(p3)) + return false; + + Point2D p2 = lastCoLinear().P2; + Point2D p4 = ((LineSegment) b).lastCoLinear().P2; + return (p2.equals(p4)); + } + + /** + * Returns a line segment + */ + int pathIteratorFormat(double[] coords) + { + coords[0] = P2.getX(); + coords[1] = P2.getY(); + return (PathIterator.SEG_LINETO); + } + + /** + * Returns if the line has intersections. + */ + boolean hasIntersections(Segment b) + { + if (b instanceof LineSegment) + return (linesIntersect(this, (LineSegment) b) != null); + + if (b instanceof QuadSegment) + return (lineQuadIntersect(this, (QuadSegment) b) != null); + + if (b instanceof CubicSegment) + return (lineCubicIntersect(this, (CubicSegment) b) != null); + + return false; + } + + /** + * Splits intersections into nodes, + * This one handles line-line, line-quadratic, line-cubic + */ + int splitIntersections(Segment b) + { + if (b instanceof LineSegment) + { + Intersection i = linesIntersect(this, (LineSegment) b); + + if (i == null) + return 0; + + return createNode(b, i); + } + + Intersection[] x = null; + + if (b instanceof QuadSegment) + x = lineQuadIntersect(this, (QuadSegment) b); + + if (b instanceof CubicSegment) + x = lineCubicIntersect(this, (CubicSegment) b); + + if (x == null) + return 0; + + if (x.length == 1) + return createNode(b, (Intersection) x[0]); + + return createNodes(b, x); + } + + /** + * Returns the bounding box of this segment + */ + Rectangle2D getBounds() + { + return (new Rectangle2D.Double(Math.min(P1.getX(), P2.getX()), + Math.min(P1.getY(), P2.getY()), + Math.abs(P1.getX() - P2.getX()), + Math.abs(P1.getY() - P2.getY()))); + } + + /** + * Returns the number of intersections on the positive X axis, + * with the origin at (x,y), used for contains()-testing + */ + int rayCrossing(double x, double y) + { + double x0 = P1.getX() - x; + double y0 = P1.getY() - y; + double x1 = P2.getX() - x; + double y1 = P2.getY() - y; + + if (y0 * y1 > 0) + return 0; + + if (x0 < 0 && x1 < 0) + return 0; + + if (y0 == 0.0) + y0 -= EPSILON; + + if (y1 == 0.0) + y1 -= EPSILON; + + if (Line2D.linesIntersect(x0, y0, x1, y1, + EPSILON, 0.0, Double.MAX_VALUE, 0.0)) + return 1; + return 0; + } + } // class LineSegment + + /** + * Quadratic Bezier curve segment + * + * Note: Most peers don't support quadratics directly, so it might make + * sense to represent them as cubics internally and just be done with it. + * I think we should be peer-agnostic, however, and stay faithful to the + * input geometry types as far as possible. + */ + private class QuadSegment extends Segment + { + Point2D cp; // control point + + /** + * Constructor, takes the coordinates of the start, control, + * and end point, respectively. + */ + QuadSegment(double x1, double y1, double cx, double cy, double x2, + double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + cp = new Point2D.Double(cx, cy); + } + + /** + * Clones this segment + */ + public Object clone() + { + return new QuadSegment(P1.getX(), P1.getY(), cp.getX(), cp.getY(), + P2.getX(), P2.getY()); + } + + /** + * Returns twice the area of a curve, relative the P1-P2 line + * + * The area formula can be derived by using Green's formula in the + * plane on the parametric form of the bezier. + */ + double curveArea() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + + double P = (y2 - 2 * y1 + y0); + double Q = 2 * (y1 - y0); + + double A = (x2 - 2 * x1 + x0); + double B = 2 * (x1 - x0); + + double area = (B * P - A * Q) / 3.0; + return (area); + } + + /** + * Compare two segments. + */ + boolean equals(Segment b) + { + if (! (b instanceof QuadSegment)) + return false; + + return (P1.equals(b.P1) && cp.equals(((QuadSegment) b).cp) + && P2.equals(b.P2)); + } + + /** + * Returns a Point2D corresponding to the parametric value t + * of the curve + */ + Point2D evaluatePoint(double t) + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + + return new Point2D.Double(t * t * (x2 - 2 * x1 + x0) + 2 * t * (x1 - x0) + + x0, + t * t * (y2 - 2 * y1 + y0) + 2 * t * (y1 - y0) + + y0); + } + + /** + * Returns the bounding box of this segment + */ + Rectangle2D getBounds() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + double r0; + double r1; + + double xmax = Math.max(x0, x2); + double ymax = Math.max(y0, y2); + double xmin = Math.min(x0, x2); + double ymin = Math.min(y0, y2); + + r0 = 2 * (y1 - y0); + r1 = 2 * (y2 - 2 * y1 + y0); + if (r1 != 0.0) + { + double t = -r0 / r1; + if (t > 0.0 && t < 1.0) + { + double y = evaluatePoint(t).getY(); + ymax = Math.max(y, ymax); + ymin = Math.min(y, ymin); + } + } + r0 = 2 * (x1 - x0); + r1 = 2 * (x2 - 2 * x1 + x0); + if (r1 != 0.0) + { + double t = -r0 / r1; + if (t > 0.0 && t < 1.0) + { + double x = evaluatePoint(t).getY(); + xmax = Math.max(x, xmax); + xmin = Math.min(x, xmin); + } + } + + return (new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin)); + } + + /** + * Returns a cubic segment corresponding to this curve + */ + CubicSegment getCubicSegment() + { + double x1 = P1.getX() + 2.0 * (cp.getX() - P1.getX()) / 3.0; + double y1 = P1.getY() + 2.0 * (cp.getY() - P1.getY()) / 3.0; + double x2 = cp.getX() + (P2.getX() - cp.getX()) / 3.0; + double y2 = cp.getY() + (P2.getY() - cp.getY()) / 3.0; + + return new CubicSegment(P1.getX(), P1.getY(), x1, y1, x2, y2, P2.getX(), + P2.getY()); + } + + /** + * Returns the segment's midpoint + */ + Point2D getMidPoint() + { + return evaluatePoint(0.5); + } + + /** + * Returns the PathIterator type of a segment + */ + int getType() + { + return (PathIterator.SEG_QUADTO); + } + + /** + * Returns the PathIterator coords of a segment + */ + int pathIteratorFormat(double[] coords) + { + coords[0] = cp.getX(); + coords[1] = cp.getY(); + coords[2] = P2.getX(); + coords[3] = P2.getY(); + return (PathIterator.SEG_QUADTO); + } + + /** + * Returns the number of intersections on the positive X axis, + * with the origin at (x,y), used for contains()-testing + */ + int rayCrossing(double x, double y) + { + double x0 = P1.getX() - x; + double y0 = P1.getY() - y; + double x1 = cp.getX() - x; + double y1 = cp.getY() - y; + double x2 = P2.getX() - x; + double y2 = P2.getY() - y; + double[] r = new double[3]; + int nRoots; + int nCrossings = 0; + + /* check if curve may intersect X+ axis. */ + if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0) && (y0 * y1 <= 0 || y1 * y2 <= 0)) + { + if (y0 == 0.0) + y0 -= EPSILON; + if (y2 == 0.0) + y2 -= EPSILON; + + r[0] = y0; + r[1] = 2 * (y1 - y0); + r[2] = (y2 - 2 * y1 + y0); + + nRoots = QuadCurve2D.solveQuadratic(r); + for (int i = 0; i < nRoots; i++) + if (r[i] > 0.0f && r[i] < 1.0f) + { + double t = r[i]; + if (t * t * (x2 - 2 * x1 + x0) + 2 * t * (x1 - x0) + x0 > 0.0) + nCrossings++; + } + } + return nCrossings; + } + + /** + * Swap start and end points + */ + void reverseCoords() + { + Point2D temp = P1; + P1 = P2; + P2 = temp; + } + + /** + * Splits intersections into nodes, + * This one handles quadratic-quadratic only, + * Quadratic-line is passed on to the LineSegment class, + * Quadratic-cubic is passed on to the CubicSegment class + */ + int splitIntersections(Segment b) + { + if (b instanceof LineSegment) + return (b.splitIntersections(this)); + + if (b instanceof CubicSegment) + return (b.splitIntersections(this)); + + if (b instanceof QuadSegment) + { + // Use the cubic-cubic intersection routine for quads as well, + // Since a quadratic can be exactly described as a cubic, this + // should not be a problem; + // The recursion depth will be the same in any case. + Intersection[] x = cubicCubicIntersect(getCubicSegment(), + ((QuadSegment) b) + .getCubicSegment()); + if (x == null) + return 0; + + if (x.length == 1) + return createNode(b, (Intersection) x[0]); + + return createNodes(b, x); + } + return 0; + } + + /** + * Subdivides the segment at parametric value t, inserting + * the new segment into the linked list after this, + * such that this becomes [0,t] and this.next becomes [t,1] + */ + void subdivideInsert(double t) + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp.getX(); + double y1 = cp.getY(); + double x2 = P2.getX(); + double y2 = P2.getY(); + + double p10x = x0 + t * (x1 - x0); + double p10y = y0 + t * (y1 - y0); + double p11x = x1 + t * (x2 - x1); + double p11y = y1 + t * (y2 - y1); + double p20x = p10x + t * (p11x - p10x); + double p20y = p10y + t * (p11y - p10y); + + insert(new QuadSegment(p20x, p20y, p11x, p11y, x2, y2)); + P2 = next.P1; + cp.setLocation(p10x, p10y); + + next.node = node; + node = null; + } + + /** + * Transforms the segment + */ + void transform(AffineTransform at) + { + P1 = at.transform(P1, null); + P2 = at.transform(P2, null); + cp = at.transform(cp, null); + } + } // class QuadSegment + + /** + * Cubic Bezier curve segment + */ + private class CubicSegment extends Segment + { + Point2D cp1; // control points + Point2D cp2; // control points + + /** + * Constructor - takes coordinates of the starting point, + * first control point, second control point and end point, + * respecively. + */ + public CubicSegment(double x1, double y1, double c1x, double c1y, + double c2x, double c2y, double x2, double y2) + { + super(); + P1 = new Point2D.Double(x1, y1); + P2 = new Point2D.Double(x2, y2); + cp1 = new Point2D.Double(c1x, c1y); + cp2 = new Point2D.Double(c2x, c2y); + } + + /** + * Clones this segment + */ + public Object clone() + { + return new CubicSegment(P1.getX(), P1.getY(), cp1.getX(), cp1.getY(), + cp2.getX(), cp2.getY(), P2.getX(), P2.getY()); + } + + /** + * Returns twice the area of a curve, relative the P1-P2 line + * + * The area formula can be derived by using Green's formula in the + * plane on the parametric form of the bezier. + */ + double curveArea() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + + double P = y3 - 3 * y2 + 3 * y1 - y0; + double Q = 3 * (y2 + y0 - 2 * y1); + double R = 3 * (y1 - y0); + + double A = x3 - 3 * x2 + 3 * x1 - x0; + double B = 3 * (x2 + x0 - 2 * x1); + double C = 3 * (x1 - x0); + + double area = (B * P - A * Q) / 5.0 + (C * P - A * R) / 2.0 + + (C * Q - B * R) / 3.0; + + return (area); + } + + /** + * Compare two segments. + */ + boolean equals(Segment b) + { + if (! (b instanceof CubicSegment)) + return false; + + return (P1.equals(b.P1) && cp1.equals(((CubicSegment) b).cp1) + && cp2.equals(((CubicSegment) b).cp2) && P2.equals(b.P2)); + } + + /** + * Returns a Point2D corresponding to the parametric value t + * of the curve + */ + Point2D evaluatePoint(double t) + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + + return new Point2D.Double(-(t * t * t) * (x0 - 3 * x1 + 3 * x2 - x3) + + 3 * t * t * (x0 - 2 * x1 + x2) + + 3 * t * (x1 - x0) + x0, + -(t * t * t) * (y0 - 3 * y1 + 3 * y2 - y3) + + 3 * t * t * (y0 - 2 * y1 + y2) + + 3 * t * (y1 - y0) + y0); + } + + /** + * Returns the bounding box of this segment + */ + Rectangle2D getBounds() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + double[] r = new double[3]; + + double xmax = Math.max(x0, x3); + double ymax = Math.max(y0, y3); + double xmin = Math.min(x0, x3); + double ymin = Math.min(y0, y3); + + r[0] = 3 * (y1 - y0); + r[1] = 6.0 * (y2 + y0 - 2 * y1); + r[2] = 3.0 * (y3 - 3 * y2 + 3 * y1 - y0); + + int n = QuadCurve2D.solveQuadratic(r); + for (int i = 0; i < n; i++) + { + double t = r[i]; + if (t > 0 && t < 1.0) + { + double y = evaluatePoint(t).getY(); + ymax = Math.max(y, ymax); + ymin = Math.min(y, ymin); + } + } + + r[0] = 3 * (x1 - x0); + r[1] = 6.0 * (x2 + x0 - 2 * x1); + r[2] = 3.0 * (x3 - 3 * x2 + 3 * x1 - x0); + n = QuadCurve2D.solveQuadratic(r); + for (int i = 0; i < n; i++) + { + double t = r[i]; + if (t > 0 && t < 1.0) + { + double x = evaluatePoint(t).getX(); + xmax = Math.max(x, xmax); + xmin = Math.min(x, xmin); + } + } + return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin))); + } + + /** + * Returns a CubicCurve2D object corresponding to this segment. + */ + CubicCurve2D getCubicCurve2D() + { + return new CubicCurve2D.Double(P1.getX(), P1.getY(), cp1.getX(), + cp1.getY(), cp2.getX(), cp2.getY(), + P2.getX(), P2.getY()); + } + + /** + * Returns the parametric points of self-intersection if the cubic + * is self-intersecting, null otherwise. + */ + double[] getLoop() + { + double x0 = P1.getX(); + double y0 = P1.getY(); + double x1 = cp1.getX(); + double y1 = cp1.getY(); + double x2 = cp2.getX(); + double y2 = cp2.getY(); + double x3 = P2.getX(); + double y3 = P2.getY(); + double[] r = new double[4]; + double k; + double R; + double T; + double A; + double B; + double[] results = new double[2]; + + R = x3 - 3 * x2 + 3 * x1 - x0; + T = y3 - 3 * y2 + 3 * y1 - y0; + + // A qudratic + if (R == 0.0 && T == 0.0) + return null; + + // true cubic + if (R != 0.0 && T != 0.0) + { + A = 3 * (x2 + x0 - 2 * x1) / R; + B = 3 * (x1 - x0) / R; + + double P = 3 * (y2 + y0 - 2 * y1) / T; + double Q = 3 * (y1 - y0) / T; + + if (A == P || Q == B) + return null; + + k = (Q - B) / (A - P); + } + else + { + if (R == 0.0) + { + // quadratic in x + k = -(3 * (x1 - x0)) / (3 * (x2 + x0 - 2 * x1)); + A = 3 * (y2 + y0 - 2 * y1) / T; + B = 3 * (y1 - y0) / T; + } + else + { + // quadratic in y + k = -(3 * (y1 - y0)) / (3 * (y2 + y0 - 2 * y1)); + A = 3 * (x2 + x0 - 2 * x1) / R; + B = 3 * (x1 - x0) / R; + } + } + + r[0] = -k * k * k - A * k * k - B * k; + r[1] = 3 * k * k + 2 * k * A + 2 * B; + r[2] = -3 * k; + r[3] = 2; + + int n = CubicCurve2D.solveCubic(r); + if (n != 3) + return null; + + // sort r + double t; + for (int i = 0; i < 2; i++) + for (int j = i + 1; j < 3; j++) + if (r[j] < r[i]) + { + t = r[i]; + r[i] = r[j]; + r[j] = t; + } + + if (Math.abs(r[0] + r[2] - k) < 1E-13) + if (r[0] >= 0.0 && r[0] <= 1.0 && r[2] >= 0.0 && r[2] <= 1.0) + if (evaluatePoint(r[0]).distance(evaluatePoint(r[2])) < PE_EPSILON * 10) + { // we snap the points anyway + results[0] = r[0]; + results[1] = r[2]; + return (results); + } + return null; + } + + /** + * Returns the segment's midpoint + */ + Point2D getMidPoint() + { + return evaluatePoint(0.5); + } + + /** + * Returns the PathIterator type of a segment + */ + int getType() + { + return (PathIterator.SEG_CUBICTO); + } + + /** + * Returns the PathIterator coords of a segment + */ + int pathIteratorFormat(double[] coords) + { + coords[0] = cp1.getX(); + coords[1] = cp1.getY(); + coords[2] = cp2.getX(); + coords[3] = cp2.getY(); + coords[4] = P2.getX(); + coords[5] = P2.getY(); + return (PathIterator.SEG_CUBICTO); + } + + /** + * Returns the number of intersections on the positive X axis, + * with the origin at (x,y), used for contains()-testing + */ + int rayCrossing(double x, double y) + { + double x0 = P1.getX() - x; + double y0 = P1.getY() - y; + double x1 = cp1.getX() - x; + double y1 = cp1.getY() - y; + double x2 = cp2.getX() - x; + double y2 = cp2.getY() - y; + double x3 = P2.getX() - x; + double y3 = P2.getY() - y; + double[] r = new double[4]; + int nRoots; + int nCrossings = 0; + + /* check if curve may intersect X+ axis. */ + if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0) + && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0)) + { + if (y0 == 0.0) + y0 -= EPSILON; + if (y3 == 0.0) + y3 -= EPSILON; + + r[0] = y0; + r[1] = 3 * (y1 - y0); + r[2] = 3 * (y2 + y0 - 2 * y1); + r[3] = y3 - 3 * y2 + 3 * y1 - y0; + + if ((nRoots = CubicCurve2D.solveCubic(r)) > 0) + for (int i = 0; i < nRoots; i++) + { + if (r[i] > 0.0 && r[i] < 1.0) + { + double t = r[i]; + if (-(t * t * t) * (x0 - 3 * x1 + 3 * x2 - x3) + + 3 * t * t * (x0 - 2 * x1 + x2) + 3 * t * (x1 - x0) + + x0 > 0.0) + nCrossings++; + } + } + } + return nCrossings; + } + + /** + * Swap start and end points + */ + void reverseCoords() + { + Point2D p = P1; + P1 = P2; + P2 = p; + p = cp1; // swap control points + cp1 = cp2; + cp2 = p; + } + + /** + * Splits intersections into nodes, + * This one handles cubic-cubic and cubic-quadratic intersections + */ + int splitIntersections(Segment b) + { + if (b instanceof LineSegment) + return (b.splitIntersections(this)); + + Intersection[] x = null; + + if (b instanceof QuadSegment) + x = cubicCubicIntersect(this, ((QuadSegment) b).getCubicSegment()); + + if (b instanceof CubicSegment) + x = cubicCubicIntersect(this, (CubicSegment) b); + + if (x == null) + return 0; + + if (x.length == 1) + return createNode(b, x[0]); + + return createNodes(b, x); + } + + /** + * Subdivides the segment at parametric value t, inserting + * the new segment into the linked list after this, + * such that this becomes [0,t] and this.next becomes [t,1] + */ + void subdivideInsert(double t) + { + CubicSegment s = (CubicSegment) clone(); + double p1x = (s.cp1.getX() - s.P1.getX()) * t + s.P1.getX(); + double p1y = (s.cp1.getY() - s.P1.getY()) * t + s.P1.getY(); + + double px = (s.cp2.getX() - s.cp1.getX()) * t + s.cp1.getX(); + double py = (s.cp2.getY() - s.cp1.getY()) * t + s.cp1.getY(); + + s.cp2.setLocation((s.P2.getX() - s.cp2.getX()) * t + s.cp2.getX(), + (s.P2.getY() - s.cp2.getY()) * t + s.cp2.getY()); + + s.cp1.setLocation((s.cp2.getX() - px) * t + px, + (s.cp2.getY() - py) * t + py); + + double p2x = (px - p1x) * t + p1x; + double p2y = (py - p1y) * t + p1y; + + double p3x = (s.cp1.getX() - p2x) * t + p2x; + double p3y = (s.cp1.getY() - p2y) * t + p2y; + s.P1.setLocation(p3x, p3y); + + // insert new curve + insert(s); + + // set this curve + cp1.setLocation(p1x, p1y); + cp2.setLocation(p2x, p2y); + P2 = s.P1; + next.node = node; + node = null; + } + + /** + * Transforms the segment + */ + void transform(AffineTransform at) + { + P1 = at.transform(P1, null); + P2 = at.transform(P2, null); + cp1 = at.transform(cp1, null); + cp2 = at.transform(cp2, null); + } + } // class CubicSegment +} // class Area diff --git a/libjava/classpath/java/awt/geom/CubicCurve2D.java b/libjava/classpath/java/awt/geom/CubicCurve2D.java new file mode 100644 index 000000000..5cb11fe77 --- /dev/null +++ b/libjava/classpath/java/awt/geom/CubicCurve2D.java @@ -0,0 +1,1724 @@ +/* CubicCurve2D.java -- represents a parameterized cubic curve in 2-D space + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + + +/** + * A two-dimensional curve that is parameterized with a cubic + * function. + * + *

    A drawing of a CubicCurve2D + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Graydon Hoare (graydon@redhat.com) + * @author Sascha Brawer (brawer@dandelis.ch) + * @author Sven de Marothy (sven@physto.se) + * + * @since 1.2 + */ +public abstract class CubicCurve2D implements Shape, Cloneable +{ + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + private static final double EPSILON = 1E-10; + + /** + * Constructs a new CubicCurve2D. Typical users will want to + * construct instances of a subclass, such as {@link + * CubicCurve2D.Float} or {@link CubicCurve2D.Double}. + */ + protected CubicCurve2D() + { + } + + /** + * Returns the x coordinate of the curve’s start + * point. + */ + public abstract double getX1(); + + /** + * Returns the y coordinate of the curve’s start + * point. + */ + public abstract double getY1(); + + /** + * Returns the curve’s start point. + */ + public abstract Point2D getP1(); + + /** + * Returns the x coordinate of the curve’s first + * control point. + */ + public abstract double getCtrlX1(); + + /** + * Returns the y coordinate of the curve’s first + * control point. + */ + public abstract double getCtrlY1(); + + /** + * Returns the curve’s first control point. + */ + public abstract Point2D getCtrlP1(); + + /** + * Returns the x coordinate of the curve’s second + * control point. + */ + public abstract double getCtrlX2(); + + /** + * Returns the y coordinate of the curve’s second + * control point. + */ + public abstract double getCtrlY2(); + + /** + * Returns the curve’s second control point. + */ + public abstract Point2D getCtrlP2(); + + /** + * Returns the x coordinate of the curve’s end + * point. + */ + public abstract double getX2(); + + /** + * Returns the y coordinate of the curve’s end + * point. + */ + public abstract double getY2(); + + /** + * Returns the curve’s end point. + */ + public abstract Point2D getP2(); + + /** + * Changes the curve geometry, separately specifying each coordinate + * value. + * + *

    A drawing of a CubicCurve2D + * + * @param x1 the x coordinate of the curve’s new start + * point. + * + * @param y1 the y coordinate of the curve’s new start + * point. + * + * @param cx1 the x coordinate of the curve’s new + * first control point. + * + * @param cy1 the y coordinate of the curve’s new + * first control point. + * + * @param cx2 the x coordinate of the curve’s new + * second control point. + * + * @param cy2 the y coordinate of the curve’s new + * second control point. + * + * @param x2 the x coordinate of the curve’s new end + * point. + * + * @param y2 the y coordinate of the curve’s new end + * point. + */ + public abstract void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2); + + /** + * Changes the curve geometry, specifying coordinate values in an + * array. + * + * @param coords an array containing the new coordinate values. The + * x coordinate of the new start point is located at + * coords[offset], its y coordinate at + * coords[offset + 1]. The x coordinate of the + * new first control point is located at coords[offset + + * 2], its y coordinate at coords[offset + + * 3]. The x coordinate of the new second control + * point is located at coords[offset + 4], its y + * coordinate at coords[offset + 5]. The x + * coordinate of the new end point is located at coords[offset + * + 6], its y coordinate at coords[offset + + * 7]. + * + * @param offset the offset of the first coordinate value in + * coords. + */ + public void setCurve(double[] coords, int offset) + { + setCurve(coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++]); + } + + /** + * Changes the curve geometry, specifying coordinate values in + * separate Point objects. + * + *

    A drawing of a CubicCurve2D + * + *

    The curve does not keep any reference to the passed point + * objects. Therefore, a later change to p1, + * c1, c2 or p2 will not + * affect the curve geometry. + * + * @param p1 the new start point. + * @param c1 the new first control point. + * @param c2 the new second control point. + * @param p2 the new end point. + */ + public void setCurve(Point2D p1, Point2D c1, Point2D c2, Point2D p2) + { + setCurve(p1.getX(), p1.getY(), c1.getX(), c1.getY(), c2.getX(), c2.getY(), + p2.getX(), p2.getY()); + } + + /** + * Changes the curve geometry, specifying coordinate values in an + * array of Point objects. + * + *

    A drawing of a CubicCurve2D + * + *

    The curve does not keep references to the passed point + * objects. Therefore, a later change to the pts array + * or any of its elements will not affect the curve geometry. + * + * @param pts an array containing the points. The new start point + * is located at pts[offset], the new first control + * point at pts[offset + 1], the new second control + * point at pts[offset + 2], and the new end point + * at pts[offset + 3]. + * + * @param offset the offset of the start point in pts. + */ + public void setCurve(Point2D[] pts, int offset) + { + setCurve(pts[offset].getX(), pts[offset++].getY(), pts[offset].getX(), + pts[offset++].getY(), pts[offset].getX(), pts[offset++].getY(), + pts[offset].getX(), pts[offset++].getY()); + } + + /** + * Changes the curve geometry to that of another curve. + * + * @param c the curve whose coordinates will be copied. + */ + public void setCurve(CubicCurve2D c) + { + setCurve(c.getX1(), c.getY1(), c.getCtrlX1(), c.getCtrlY1(), + c.getCtrlX2(), c.getCtrlY2(), c.getX2(), c.getY2()); + } + + /** + * Calculates the squared flatness of a cubic curve, directly + * specifying each coordinate value. The flatness is the maximal + * distance of a control point to the line between start and end + * point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the square of the distance between C2 and the + * gray line, i.e. the squared length of the red line. + * + * @param x1 the x coordinate of the start point P1. + * @param y1 the y coordinate of the start point P1. + * @param cx1 the x coordinate of the first control point C1. + * @param cy1 the y coordinate of the first control point C1. + * @param cx2 the x coordinate of the second control point C2. + * @param cy2 the y coordinate of the second control point C2. + * @param x2 the x coordinate of the end point P2. + * @param y2 the y coordinate of the end point P2. + */ + public static double getFlatnessSq(double x1, double y1, double cx1, + double cy1, double cx2, double cy2, + double x2, double y2) + { + return Math.max(Line2D.ptSegDistSq(x1, y1, x2, y2, cx1, cy1), + Line2D.ptSegDistSq(x1, y1, x2, y2, cx2, cy2)); + } + + /** + * Calculates the flatness of a cubic curve, directly specifying + * each coordinate value. The flatness is the maximal distance of a + * control point to the line between start and end point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the distance between C2 and the gray line, + * i.e. the length of the red line. + * + * @param x1 the x coordinate of the start point P1. + * @param y1 the y coordinate of the start point P1. + * @param cx1 the x coordinate of the first control point C1. + * @param cy1 the y coordinate of the first control point C1. + * @param cx2 the x coordinate of the second control point C2. + * @param cy2 the y coordinate of the second control point C2. + * @param x2 the x coordinate of the end point P2. + * @param y2 the y coordinate of the end point P2. + */ + public static double getFlatness(double x1, double y1, double cx1, + double cy1, double cx2, double cy2, + double x2, double y2) + { + return Math.sqrt(getFlatnessSq(x1, y1, cx1, cy1, cx2, cy2, x2, y2)); + } + + /** + * Calculates the squared flatness of a cubic curve, specifying the + * coordinate values in an array. The flatness is the maximal + * distance of a control point to the line between start and end + * point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the square of the distance between C2 and the + * gray line, i.e. the squared length of the red line. + * + * @param coords an array containing the coordinate values. The + * x coordinate of the start point P1 is located at + * coords[offset], its y coordinate at + * coords[offset + 1]. The x coordinate of the + * first control point C1 is located at coords[offset + + * 2], its y coordinate at coords[offset + + * 3]. The x coordinate of the second control point C2 + * is located at coords[offset + 4], its y + * coordinate at coords[offset + 5]. The x + * coordinate of the end point P2 is located at coords[offset + * + 6], its y coordinate at coords[offset + + * 7]. + * + * @param offset the offset of the first coordinate value in + * coords. + */ + public static double getFlatnessSq(double[] coords, int offset) + { + return getFlatnessSq(coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++]); + } + + /** + * Calculates the flatness of a cubic curve, specifying the + * coordinate values in an array. The flatness is the maximal + * distance of a control point to the line between start and end + * point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the distance between C2 and the gray line, + * i.e. the length of the red line. + * + * @param coords an array containing the coordinate values. The + * x coordinate of the start point P1 is located at + * coords[offset], its y coordinate at + * coords[offset + 1]. The x coordinate of the + * first control point C1 is located at coords[offset + + * 2], its y coordinate at coords[offset + + * 3]. The x coordinate of the second control point C2 + * is located at coords[offset + 4], its y + * coordinate at coords[offset + 5]. The x + * coordinate of the end point P2 is located at coords[offset + * + 6], its y coordinate at coords[offset + + * 7]. + * + * @param offset the offset of the first coordinate value in + * coords. + */ + public static double getFlatness(double[] coords, int offset) + { + return Math.sqrt(getFlatnessSq(coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++], + coords[offset++], coords[offset++])); + } + + /** + * Calculates the squared flatness of this curve. The flatness is + * the maximal distance of a control point to the line between start + * and end point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the square of the distance between C2 and the + * gray line, i.e. the squared length of the red line. + */ + public double getFlatnessSq() + { + return getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), + getCtrlX2(), getCtrlY2(), getX2(), getY2()); + } + + /** + * Calculates the flatness of this curve. The flatness is the + * maximal distance of a control point to the line between start and + * end point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. In comparison to C1, + * control point C2 is father away from the gray line. Therefore, + * the result will be the distance between C2 and the gray line, + * i.e. the length of the red line. + */ + public double getFlatness() + { + return Math.sqrt(getFlatnessSq(getX1(), getY1(), getCtrlX1(), getCtrlY1(), + getCtrlX2(), getCtrlY2(), getX2(), getY2())); + } + + /** + * Subdivides this curve into two halves. + * + *

    A drawing that illustrates the effects of
+   * subdividing a CubicCurve2D + * + * @param left a curve whose geometry will be set to the left half + * of this curve, or null if the caller is not + * interested in the left half. + * + * @param right a curve whose geometry will be set to the right half + * of this curve, or null if the caller is not + * interested in the right half. + */ + public void subdivide(CubicCurve2D left, CubicCurve2D right) + { + // Use empty slots at end to share single array. + double[] d = new double[] + { + getX1(), getY1(), getCtrlX1(), getCtrlY1(), getCtrlX2(), + getCtrlY2(), getX2(), getY2(), 0, 0, 0, 0, 0, 0 + }; + subdivide(d, 0, d, 0, d, 6); + if (left != null) + left.setCurve(d, 0); + if (right != null) + right.setCurve(d, 6); + } + + /** + * Subdivides a cubic curve into two halves. + * + *

    A drawing that illustrates the effects of
+   * subdividing a CubicCurve2D + * + * @param src the curve to be subdivided. + * + * @param left a curve whose geometry will be set to the left half + * of src, or null if the caller is not + * interested in the left half. + * + * @param right a curve whose geometry will be set to the right half + * of src, or null if the caller is not + * interested in the right half. + */ + public static void subdivide(CubicCurve2D src, CubicCurve2D left, + CubicCurve2D right) + { + src.subdivide(left, right); + } + + /** + * Subdivides a cubic curve into two halves, passing all coordinates + * in an array. + * + *

    A drawing that illustrates the effects of
+   * subdividing a CubicCurve2D + * + *

    The left end point and the right start point will always be + * identical. Memory-concious programmers thus may want to pass the + * same array for both left and right, and + * set rightOff to leftOff + 6. + * + * @param src an array containing the coordinates of the curve to be + * subdivided. The x coordinate of the start point P1 is + * located at src[srcOff], its y at + * src[srcOff + 1]. The x coordinate of the + * first control point C1 is located at src[srcOff + + * 2], its y at src[srcOff + 3]. The + * x coordinate of the second control point C2 is located at + * src[srcOff + 4], its y at src[srcOff + + * 5]. The x coordinate of the end point is located at + * src[srcOff + 6], its y at src[srcOff + + * 7]. + * + * @param srcOff an offset into src, specifying + * the index of the start point’s x coordinate. + * + * @param left an array that will receive the coordinates of the + * left half of src. It is acceptable to pass + * src. A caller who is not interested in the left half + * can pass null. + * + * @param leftOff an offset into left, specifying the + * index where the start point’s x coordinate will be + * stored. + * + * @param right an array that will receive the coordinates of the + * right half of src. It is acceptable to pass + * src or left. A caller who is not + * interested in the right half can pass null. + * + * @param rightOff an offset into right, specifying the + * index where the start point’s x coordinate will be + * stored. + */ + public static void subdivide(double[] src, int srcOff, double[] left, + int leftOff, double[] right, int rightOff) + { + // To understand this code, please have a look at the image + // "CubicCurve2D-3.png" in the sub-directory "doc-files". + double src_C1_x; + double src_C1_y; + double src_C2_x; + double src_C2_y; + double left_P1_x; + double left_P1_y; + double left_C1_x; + double left_C1_y; + double left_C2_x; + double left_C2_y; + double right_C1_x; + double right_C1_y; + double right_C2_x; + double right_C2_y; + double right_P2_x; + double right_P2_y; + double Mid_x; // Mid = left.P2 = right.P1 + double Mid_y; // Mid = left.P2 = right.P1 + + left_P1_x = src[srcOff]; + left_P1_y = src[srcOff + 1]; + src_C1_x = src[srcOff + 2]; + src_C1_y = src[srcOff + 3]; + src_C2_x = src[srcOff + 4]; + src_C2_y = src[srcOff + 5]; + right_P2_x = src[srcOff + 6]; + right_P2_y = src[srcOff + 7]; + + left_C1_x = (left_P1_x + src_C1_x) / 2; + left_C1_y = (left_P1_y + src_C1_y) / 2; + right_C2_x = (right_P2_x + src_C2_x) / 2; + right_C2_y = (right_P2_y + src_C2_y) / 2; + Mid_x = (src_C1_x + src_C2_x) / 2; + Mid_y = (src_C1_y + src_C2_y) / 2; + left_C2_x = (left_C1_x + Mid_x) / 2; + left_C2_y = (left_C1_y + Mid_y) / 2; + right_C1_x = (Mid_x + right_C2_x) / 2; + right_C1_y = (Mid_y + right_C2_y) / 2; + Mid_x = (left_C2_x + right_C1_x) / 2; + Mid_y = (left_C2_y + right_C1_y) / 2; + + if (left != null) + { + left[leftOff] = left_P1_x; + left[leftOff + 1] = left_P1_y; + left[leftOff + 2] = left_C1_x; + left[leftOff + 3] = left_C1_y; + left[leftOff + 4] = left_C2_x; + left[leftOff + 5] = left_C2_y; + left[leftOff + 6] = Mid_x; + left[leftOff + 7] = Mid_y; + } + + if (right != null) + { + right[rightOff] = Mid_x; + right[rightOff + 1] = Mid_y; + right[rightOff + 2] = right_C1_x; + right[rightOff + 3] = right_C1_y; + right[rightOff + 4] = right_C2_x; + right[rightOff + 5] = right_C2_y; + right[rightOff + 6] = right_P2_x; + right[rightOff + 7] = right_P2_y; + } + } + + /** + * Finds the non-complex roots of a cubic equation, placing the + * results into the same array as the equation coefficients. The + * following equation is being solved: + * + *

    eqn[3] · x3 + * + eqn[2] · x2 + * + eqn[1] · x + * + eqn[0] + * = 0 + *
    + * + *

    For some background about solving cubic equations, see the + * article “Cubic Formula” in PlanetMath. For an extensive + * library of numerical algorithms written in the C programming + * language, see the GNU + * Scientific Library, from which this implementation was + * adapted. + * + * @param eqn an array with the coefficients of the equation. When + * this procedure has returned, eqn will contain the + * non-complex solutions of the equation, in no particular order. + * + * @return the number of non-complex solutions. A result of 0 + * indicates that the equation has no non-complex solutions. A + * result of -1 indicates that the equation is constant (i.e., + * always or never zero). + * + * @see #solveCubic(double[], double[]) + * @see QuadCurve2D#solveQuadratic(double[],double[]) + * + * @author Brian Gough (bjg@network-theory.com) + * (original C implementation in the GNU Scientific Library) + * + * @author Sascha Brawer (brawer@dandelis.ch) + * (adaptation to Java) + */ + public static int solveCubic(double[] eqn) + { + return solveCubic(eqn, eqn); + } + + /** + * Finds the non-complex roots of a cubic equation. The following + * equation is being solved: + * + *

    eqn[3] · x3 + * + eqn[2] · x2 + * + eqn[1] · x + * + eqn[0] + * = 0 + *
    + * + *

    For some background about solving cubic equations, see the + * article “Cubic Formula” in PlanetMath. For an extensive + * library of numerical algorithms written in the C programming + * language, see the GNU + * Scientific Library, from which this implementation was + * adapted. + * + * @see QuadCurve2D#solveQuadratic(double[],double[]) + * + * @param eqn an array with the coefficients of the equation. + * + * @param res an array into which the non-complex roots will be + * stored. The results may be in an arbitrary order. It is safe to + * pass the same array object reference for both eqn + * and res. + * + * @return the number of non-complex solutions. A result of 0 + * indicates that the equation has no non-complex solutions. A + * result of -1 indicates that the equation is constant (i.e., + * always or never zero). + * + * @author Brian Gough (bjg@network-theory.com) + * (original C implementation in the GNU Scientific Library) + * + * @author Sascha Brawer (brawer@dandelis.ch) + * (adaptation to Java) + */ + public static int solveCubic(double[] eqn, double[] res) + { + // Adapted from poly/solve_cubic.c in the GNU Scientific Library + // (GSL), revision 1.7 of 2003-07-26. For the original source, see + // http://www.gnu.org/software/gsl/ + // + // Brian Gough, the author of that code, has granted the + // permission to use it in GNU Classpath under the GNU Classpath + // license, and has assigned the copyright to the Free Software + // Foundation. + // + // The Java implementation is very similar to the GSL code, but + // not a strict one-to-one copy. For example, GSL would sort the + // result. + + double a; + double b; + double c; + double q; + double r; + double Q; + double R; + double c3; + double Q3; + double R2; + double CR2; + double CQ3; + + // If the cubic coefficient is zero, we have a quadratic equation. + c3 = eqn[3]; + if (c3 == 0) + return QuadCurve2D.solveQuadratic(eqn, res); + + // Divide the equation by the cubic coefficient. + c = eqn[0] / c3; + b = eqn[1] / c3; + a = eqn[2] / c3; + + // We now need to solve x^3 + ax^2 + bx + c = 0. + q = a * a - 3 * b; + r = 2 * a * a * a - 9 * a * b + 27 * c; + + Q = q / 9; + R = r / 54; + + Q3 = Q * Q * Q; + R2 = R * R; + + CR2 = 729 * r * r; + CQ3 = 2916 * q * q * q; + + if (R == 0 && Q == 0) + { + // The GNU Scientific Library would return three identical + // solutions in this case. + res[0] = -a / 3; + return 1; + } + + if (CR2 == CQ3) + { + /* this test is actually R2 == Q3, written in a form suitable + for exact computation with integers */ + /* Due to finite precision some double roots may be missed, and + considered to be a pair of complex roots z = x +/- epsilon i + close to the real axis. */ + double sqrtQ = Math.sqrt(Q); + + if (R > 0) + { + res[0] = -2 * sqrtQ - a / 3; + res[1] = sqrtQ - a / 3; + } + else + { + res[0] = -sqrtQ - a / 3; + res[1] = 2 * sqrtQ - a / 3; + } + return 2; + } + + if (CR2 < CQ3) /* equivalent to R2 < Q3 */ + { + double sqrtQ = Math.sqrt(Q); + double sqrtQ3 = sqrtQ * sqrtQ * sqrtQ; + double theta = Math.acos(R / sqrtQ3); + double norm = -2 * sqrtQ; + res[0] = norm * Math.cos(theta / 3) - a / 3; + res[1] = norm * Math.cos((theta + 2.0 * Math.PI) / 3) - a / 3; + res[2] = norm * Math.cos((theta - 2.0 * Math.PI) / 3) - a / 3; + + // The GNU Scientific Library sorts the results. We don't. + return 3; + } + + double sgnR = (R >= 0 ? 1 : -1); + double A = -sgnR * Math.pow(Math.abs(R) + Math.sqrt(R2 - Q3), 1.0 / 3.0); + double B = Q / A; + res[0] = A + B - a / 3; + return 1; + } + + /** + * Determines whether a position lies inside the area bounded + * by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” a CubicCurve2D. + */ + public boolean contains(double x, double y) + { + if (! getBounds2D().contains(x, y)) + return false; + + return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0); + } + + /** + * Determines whether a point lies inside the area bounded + * by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” a CubicCurve2D. + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Determines whether any part of a rectangle is inside the area bounded + * by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” in a CubicCurve2D. + * @see #contains(double, double) + */ + public boolean intersects(double x, double y, double w, double h) + { + if (! getBounds2D().contains(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, true, w) != 0 /* top */ + || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, false, h) != 0 /* right */ + || getAxisIntersections(x, y, false, h) != 0) /* left */ + return true; + + /* No intersections, is any point inside? */ + if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Determines whether any part of a Rectangle2D is inside the area bounded + * by the curve and the straight line connecting its end points. + * @see #intersects(double, double, double, double) + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determine whether a rectangle is entirely inside the area that is bounded + * by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” a CubicCurve2D. + * @see #contains(double, double) + */ + public boolean contains(double x, double y, double w, double h) + { + if (! getBounds2D().intersects(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, true, w) != 0 /* top */ + || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, false, h) != 0 /* right */ + || getAxisIntersections(x, y, false, h) != 0) /* left */ + return false; + + /* No intersections, is any point inside? */ + if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Determine whether a Rectangle2D is entirely inside the area that is + * bounded by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” a CubicCurve2D. + * @see #contains(double, double) + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control points. + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + /** Current coordinate. */ + private int current = 0; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current >= 2; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = (float) getCtrlX1(); + coords[1] = (float) getCtrlY1(); + coords[2] = (float) getCtrlX2(); + coords[3] = (float) getCtrlY2(); + coords[4] = (float) getX2(); + coords[5] = (float) getY2(); + result = SEG_CUBICTO; + break; + default: + throw new NoSuchElementException("cubic iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 3); + return result; + } + + public int currentSegment(double[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = getX1(); + coords[1] = getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = getCtrlX1(); + coords[1] = getCtrlY1(); + coords[2] = getCtrlX2(); + coords[3] = getCtrlY2(); + coords[4] = getX2(); + coords[5] = getY2(); + result = SEG_CUBICTO; + break; + default: + throw new NoSuchElementException("cubic iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 3); + return result; + } + }; + } + + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Create a new curve with the same contents as this one. + * + * @return the clone. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Helper method used by contains() and intersects() methods, that + * returns the number of curve/line intersections on a given axis + * extending from a certain point. + * + * @param x x coordinate of the origin point + * @param y y coordinate of the origin point + * @param useYaxis axis used, if true the positive Y axis is used, + * false uses the positive X axis. + * + * This is an implementation of the line-crossings algorithm, + * Detailed in an article on Eric Haines' page: + * http://www.acm.org/tog/editors/erich/ptinpoly/ + * + * A special-case not adressed in this code is self-intersections + * of the curve, e.g. if the axis intersects the self-itersection, + * the degenerate roots of the polynomial will erroneously count as + * a single intersection of the curve, and not two. + */ + private int getAxisIntersections(double x, double y, boolean useYaxis, + double distance) + { + int nCrossings = 0; + double a0; + double a1; + double a2; + double a3; + double b0; + double b1; + double b2; + double b3; + double[] r = new double[4]; + int nRoots; + + a0 = a3 = 0.0; + + if (useYaxis) + { + a0 = getY1() - y; + a1 = getCtrlY1() - y; + a2 = getCtrlY2() - y; + a3 = getY2() - y; + b0 = getX1() - x; + b1 = getCtrlX1() - x; + b2 = getCtrlX2() - x; + b3 = getX2() - x; + } + else + { + a0 = getX1() - x; + a1 = getCtrlX1() - x; + a2 = getCtrlX2() - x; + a3 = getX2() - x; + b0 = getY1() - y; + b1 = getCtrlY1() - y; + b2 = getCtrlY2() - y; + b3 = getY2() - y; + } + + /* If the axis intersects a start/endpoint, shift it up by some small + amount to guarantee the line is 'inside' + If this is not done, bad behaviour may result for points on that axis.*/ + if (a0 == 0.0 || a3 == 0.0) + { + double small = getFlatness() * EPSILON; + if (a0 == 0.0) + a0 -= small; + if (a3 == 0.0) + a3 -= small; + } + + if (useYaxis) + { + if (Line2D.linesIntersect(b0, a0, b3, a3, EPSILON, 0.0, distance, 0.0)) + nCrossings++; + } + else + { + if (Line2D.linesIntersect(a0, b0, a3, b3, 0.0, EPSILON, 0.0, distance)) + nCrossings++; + } + + r[0] = a0; + r[1] = 3 * (a1 - a0); + r[2] = 3 * (a2 + a0 - 2 * a1); + r[3] = a3 - 3 * a2 + 3 * a1 - a0; + + if ((nRoots = solveCubic(r)) != 0) + for (int i = 0; i < nRoots; i++) + { + double t = r[i]; + if (t >= 0.0 && t <= 1.0) + { + double crossing = -(t * t * t) * (b0 - 3 * b1 + 3 * b2 - b3) + + 3 * t * t * (b0 - 2 * b1 + b2) + + 3 * t * (b1 - b0) + b0; + if (crossing > 0.0 && crossing <= distance) + nCrossings++; + } + } + + return (nCrossings); + } + + /** + * A two-dimensional curve that is parameterized with a cubic + * function and stores coordinate values in double-precision + * floating-point format. + * + * @see CubicCurve2D.Float + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sascha Brawer (brawer@dandelis.ch) + */ + public static class Double extends CubicCurve2D + { + /** + * The x coordinate of the curve’s start point. + */ + public double x1; + + /** + * The y coordinate of the curve’s start point. + */ + public double y1; + + /** + * The x coordinate of the curve’s first control point. + */ + public double ctrlx1; + + /** + * The y coordinate of the curve’s first control point. + */ + public double ctrly1; + + /** + * The x coordinate of the curve’s second control point. + */ + public double ctrlx2; + + /** + * The y coordinate of the curve’s second control point. + */ + public double ctrly2; + + /** + * The x coordinate of the curve’s end point. + */ + public double x2; + + /** + * The y coordinate of the curve’s end point. + */ + public double y2; + + /** + * Constructs a new CubicCurve2D that stores its coordinate values + * in double-precision floating-point format. All points are + * initially at position (0, 0). + */ + public Double() + { + } + + /** + * Constructs a new CubicCurve2D that stores its coordinate values + * in double-precision floating-point format, specifying the + * initial position of each point. + * + *

    A drawing of a CubicCurve2D + * + * @param x1 the x coordinate of the curve’s start + * point. + * + * @param y1 the y coordinate of the curve’s start + * point. + * + * @param cx1 the x coordinate of the curve’s first + * control point. + * + * @param cy1 the y coordinate of the curve’s first + * control point. + * + * @param cx2 the x coordinate of the curve’s second + * control point. + * + * @param cy2 the y coordinate of the curve’s second + * control point. + * + * @param x2 the x coordinate of the curve’s end + * point. + * + * @param y2 the y coordinate of the curve’s end + * point. + */ + public Double(double x1, double y1, double cx1, double cy1, double cx2, + double cy2, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Returns the x coordinate of the curve’s start + * point. + */ + public double getX1() + { + return x1; + } + + /** + * Returns the y coordinate of the curve’s start + * point. + */ + public double getY1() + { + return y1; + } + + /** + * Returns the curve’s start point. + */ + public Point2D getP1() + { + return new Point2D.Double(x1, y1); + } + + /** + * Returns the x coordinate of the curve’s first + * control point. + */ + public double getCtrlX1() + { + return ctrlx1; + } + + /** + * Returns the y coordinate of the curve’s first + * control point. + */ + public double getCtrlY1() + { + return ctrly1; + } + + /** + * Returns the curve’s first control point. + */ + public Point2D getCtrlP1() + { + return new Point2D.Double(ctrlx1, ctrly1); + } + + /** + * Returns the x coordinate of the curve’s second + * control point. + */ + public double getCtrlX2() + { + return ctrlx2; + } + + /** + * Returns the y coordinate of the curve’s second + * control point. + */ + public double getCtrlY2() + { + return ctrly2; + } + + /** + * Returns the curve’s second control point. + */ + public Point2D getCtrlP2() + { + return new Point2D.Double(ctrlx2, ctrly2); + } + + /** + * Returns the x coordinate of the curve’s end + * point. + */ + public double getX2() + { + return x2; + } + + /** + * Returns the y coordinate of the curve’s end + * point. + */ + public double getY2() + { + return y2; + } + + /** + * Returns the curve’s end point. + */ + public Point2D getP2() + { + return new Point2D.Double(x2, y2); + } + + /** + * Changes the curve geometry, separately specifying each coordinate + * value. + * + *

    A drawing of a CubicCurve2D + * + * @param x1 the x coordinate of the curve’s new start + * point. + * + * @param y1 the y coordinate of the curve’s new start + * point. + * + * @param cx1 the x coordinate of the curve’s new + * first control point. + * + * @param cy1 the y coordinate of the curve’s new + * first control point. + * + * @param cx2 the x coordinate of the curve’s new + * second control point. + * + * @param cy2 the y coordinate of the curve’s new + * second control point. + * + * @param x2 the x coordinate of the curve’s new end + * point. + * + * @param y2 the y coordinate of the curve’s new end + * point. + */ + public void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control points. As the + * illustration below shows, the invisible control points may cause + * the bounds to be much larger than the area that is actually + * covered by the curve. + * + *

    An illustration of the bounds of a CubicCurve2D + */ + public Rectangle2D getBounds2D() + { + double nx1 = Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2)); + double ny1 = Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2)); + double nx2 = Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2)); + double ny2 = Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2)); + return new Rectangle2D.Double(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } + + /** + * A two-dimensional curve that is parameterized with a cubic + * function and stores coordinate values in single-precision + * floating-point format. + * + * @see CubicCurve2D.Float + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sascha Brawer (brawer@dandelis.ch) + */ + public static class Float extends CubicCurve2D + { + /** + * The x coordinate of the curve’s start point. + */ + public float x1; + + /** + * The y coordinate of the curve’s start point. + */ + public float y1; + + /** + * The x coordinate of the curve’s first control point. + */ + public float ctrlx1; + + /** + * The y coordinate of the curve’s first control point. + */ + public float ctrly1; + + /** + * The x coordinate of the curve’s second control point. + */ + public float ctrlx2; + + /** + * The y coordinate of the curve’s second control point. + */ + public float ctrly2; + + /** + * The x coordinate of the curve’s end point. + */ + public float x2; + + /** + * The y coordinate of the curve’s end point. + */ + public float y2; + + /** + * Constructs a new CubicCurve2D that stores its coordinate values + * in single-precision floating-point format. All points are + * initially at position (0, 0). + */ + public Float() + { + } + + /** + * Constructs a new CubicCurve2D that stores its coordinate values + * in single-precision floating-point format, specifying the + * initial position of each point. + * + *

    A drawing of a CubicCurve2D + * + * @param x1 the x coordinate of the curve’s start + * point. + * + * @param y1 the y coordinate of the curve’s start + * point. + * + * @param cx1 the x coordinate of the curve’s first + * control point. + * + * @param cy1 the y coordinate of the curve’s first + * control point. + * + * @param cx2 the x coordinate of the curve’s second + * control point. + * + * @param cy2 the y coordinate of the curve’s second + * control point. + * + * @param x2 the x coordinate of the curve’s end + * point. + * + * @param y2 the y coordinate of the curve’s end + * point. + */ + public Float(float x1, float y1, float cx1, float cy1, float cx2, + float cy2, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Returns the x coordinate of the curve’s start + * point. + */ + public double getX1() + { + return x1; + } + + /** + * Returns the y coordinate of the curve’s start + * point. + */ + public double getY1() + { + return y1; + } + + /** + * Returns the curve’s start point. + */ + public Point2D getP1() + { + return new Point2D.Float(x1, y1); + } + + /** + * Returns the x coordinate of the curve’s first + * control point. + */ + public double getCtrlX1() + { + return ctrlx1; + } + + /** + * Returns the y coordinate of the curve’s first + * control point. + */ + public double getCtrlY1() + { + return ctrly1; + } + + /** + * Returns the curve’s first control point. + */ + public Point2D getCtrlP1() + { + return new Point2D.Float(ctrlx1, ctrly1); + } + + /** + * Returns the s coordinate of the curve’s second + * control point. + */ + public double getCtrlX2() + { + return ctrlx2; + } + + /** + * Returns the y coordinate of the curve’s second + * control point. + */ + public double getCtrlY2() + { + return ctrly2; + } + + /** + * Returns the curve’s second control point. + */ + public Point2D getCtrlP2() + { + return new Point2D.Float(ctrlx2, ctrly2); + } + + /** + * Returns the x coordinate of the curve’s end + * point. + */ + public double getX2() + { + return x2; + } + + /** + * Returns the y coordinate of the curve’s end + * point. + */ + public double getY2() + { + return y2; + } + + /** + * Returns the curve’s end point. + */ + public Point2D getP2() + { + return new Point2D.Float(x2, y2); + } + + /** + * Changes the curve geometry, separately specifying each coordinate + * value as a double-precision floating-point number. + * + *

    A drawing of a CubicCurve2D + * + * @param x1 the x coordinate of the curve’s new start + * point. + * + * @param y1 the y coordinate of the curve’s new start + * point. + * + * @param cx1 the x coordinate of the curve’s new + * first control point. + * + * @param cy1 the y coordinate of the curve’s new + * first control point. + * + * @param cx2 the x coordinate of the curve’s new + * second control point. + * + * @param cy2 the y coordinate of the curve’s new + * second control point. + * + * @param x2 the x coordinate of the curve’s new end + * point. + * + * @param y2 the y coordinate of the curve’s new end + * point. + */ + public void setCurve(double x1, double y1, double cx1, double cy1, + double cx2, double cy2, double x2, double y2) + { + this.x1 = (float) x1; + this.y1 = (float) y1; + ctrlx1 = (float) cx1; + ctrly1 = (float) cy1; + ctrlx2 = (float) cx2; + ctrly2 = (float) cy2; + this.x2 = (float) x2; + this.y2 = (float) y2; + } + + /** + * Changes the curve geometry, separately specifying each coordinate + * value as a single-precision floating-point number. + * + *

    A drawing of a CubicCurve2D + * + * @param x1 the x coordinate of the curve’s new start + * point. + * + * @param y1 the y coordinate of the curve’s new start + * point. + * + * @param cx1 the x coordinate of the curve’s new + * first control point. + * + * @param cy1 the y coordinate of the curve’s new + * first control point. + * + * @param cx2 the x coordinate of the curve’s new + * second control point. + * + * @param cy2 the y coordinate of the curve’s new + * second control point. + * + * @param x2 the x coordinate of the curve’s new end + * point. + * + * @param y2 the y coordinate of the curve’s new end + * point. + */ + public void setCurve(float x1, float y1, float cx1, float cy1, float cx2, + float cy2, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx1 = cx1; + ctrly1 = cy1; + ctrlx2 = cx2; + ctrly2 = cy2; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control points. As the + * illustration below shows, the invisible control points may cause + * the bounds to be much larger than the area that is actually + * covered by the curve. + * + *

    An illustration of the bounds of a CubicCurve2D + */ + public Rectangle2D getBounds2D() + { + float nx1 = Math.min(Math.min(x1, ctrlx1), Math.min(ctrlx2, x2)); + float ny1 = Math.min(Math.min(y1, ctrly1), Math.min(ctrly2, y2)); + float nx2 = Math.max(Math.max(x1, ctrlx1), Math.max(ctrlx2, x2)); + float ny2 = Math.max(Math.max(y1, ctrly1), Math.max(ctrly2, y2)); + return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } +} diff --git a/libjava/classpath/java/awt/geom/Dimension2D.java b/libjava/classpath/java/awt/geom/Dimension2D.java new file mode 100644 index 000000000..6b5ce8830 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Dimension2D.java @@ -0,0 +1,118 @@ +/* Dimension2D.java -- abstraction of a dimension + Copyright (C) 1999, 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +/** + * This stores a dimension in 2-dimensional space - a width (along the x-axis) + * and height (along the y-axis). The storage is left to subclasses. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class Dimension2D implements Cloneable +{ + /** + * The default constructor. + */ + protected Dimension2D() + { + } + + /** + * Get the width of this dimension. A negative result, while legal, is + * undefined in meaning. + * + * @return the width + */ + public abstract double getWidth(); + + /** + * Get the height of this dimension. A negative result, while legal, is + * undefined in meaning. + * + * @return the height + */ + public abstract double getHeight(); + + /** + * Set the size of this dimension to the requested values. Loss of precision + * may occur. + * + * @param w the new width + * @param h the new height + */ + public abstract void setSize(double w, double h); + + /** + * Set the size of this dimension to the requested value. Loss of precision + * may occur. + * + * @param d the dimension containing the new values + * + * @throws NullPointerException if d is null + */ + public void setSize(Dimension2D d) + { + setSize(d.getWidth(), d.getHeight()); + } + + /** + * Create a new dimension of the same run-time type with the same contents + * as this one. + * + * @return the clone + * + * @exception OutOfMemoryError If there is not enough memory available. + * + * @since 1.2 + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } +} // class Dimension2D diff --git a/libjava/classpath/java/awt/geom/Ellipse2D.java b/libjava/classpath/java/awt/geom/Ellipse2D.java new file mode 100644 index 000000000..3bbf2f010 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Ellipse2D.java @@ -0,0 +1,413 @@ +/* Ellipse2D.java -- represents an ellipse in 2-D space + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + + +/** + * Ellipse2D is the shape of an ellipse. + *
    + * A drawing of an ellipse
    + * The ellipse is defined by it's bounding box (shown in red), + * and is defined by the implicit curve:
    + *

    (x/a)2 + + * (y/b)2 = 1

    + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * + * @since 1.2 + */ +public abstract class Ellipse2D extends RectangularShape +{ + /** + * Ellipse2D is defined as abstract. + * Implementing classes are Ellipse2D.Float and Ellipse2D.Double. + */ + protected Ellipse2D() + { + } + + /** + * Determines if a point is contained within the ellipse.

    + * @param x - x coordinate of the point. + * @param y - y coordinate of the point. + * @return true if the point is within the ellipse, false otherwise. + */ + public boolean contains(double x, double y) + { + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double tx = (x - (getX() + rx)) / rx; + double ty = (y - (getY() + ry)) / ry; + return tx * tx + ty * ty < 1.0; + } + + /** + * Determines if a rectangle is completely contained within the + * ellipse.

    + * @param x - x coordinate of the upper-left corner of the rectangle + * @param y - y coordinate of the upper-left corner of the rectangle + * @param w - width of the rectangle + * @param h - height of the rectangle + * @return true if the rectangle is completely contained, false otherwise. + */ + public boolean contains(double x, double y, double w, double h) + { + double x2 = x + w; + double y2 = y + h; + return (contains(x, y) && contains(x, y2) && contains(x2, y) + && contains(x2, y2)); + } + + /** + * Returns a PathIterator object corresponding to the ellipse.

    + * + * Note: An ellipse cannot be represented exactly in PathIterator + * segments, the outline is thefore approximated with cubic + * Bezier segments. + * + * @param at an optional transform. + * @return A path iterator. + */ + public PathIterator getPathIterator(AffineTransform at) + { + // An ellipse is just a complete arc. + return new Arc2D.ArcIterator(this, at); + } + + /** + * Determines if a rectangle intersects any part of the ellipse.

    + * @param x - x coordinate of the upper-left corner of the rectangle + * @param y - y coordinate of the upper-left corner of the rectangle + * @param w - width of the rectangle + * @param h - height of the rectangle + * @return true if the rectangle intersects the ellipse, false otherwise. + */ + public boolean intersects(double x, double y, double w, double h) + { + Rectangle2D r = new Rectangle2D.Double(x, y, w, h); + if (! r.intersects(getX(), getY(), getWidth(), getHeight())) + return false; + + if (contains(x, y) || contains(x, y + h) || contains(x + w, y) + || contains(x + w, y + h)) + return true; + + Line2D l1 = new Line2D.Double(getX(), getY() + (getHeight() / 2), + getX() + getWidth(), + getY() + (getHeight() / 2)); + Line2D l2 = new Line2D.Double(getX() + (getWidth() / 2), getY(), + getX() + (getWidth() / 2), + getY() + getHeight()); + + if (l1.intersects(r) || l2.intersects(r)) + return true; + + return false; + } + + /** + * An {@link Ellipse2D} that stores its coordinates using double + * primitives. + */ + public static class Double extends Ellipse2D + { + /** + * The height of the ellipse. + */ + public double height; + + /** + * The width of the ellipse. + */ + public double width; + + /** + * The upper-left x coordinate of the bounding-box + */ + public double x; + + /** + * The upper-left y coordinate of the bounding-box + */ + public double y; + + /** + * Creates a new Ellipse2D with an upper-left coordinate of (0,0) + * and a zero size. + */ + public Double() + { + } + + /** + * Creates a new Ellipse2D within a given rectangle + * using double-precision coordinates.

    + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ + public Double(double x, double y, double w, double h) + { + this.x = x; + this.y = y; + height = h; + width = w; + } + + /** + * Returns the bounding-box of the ellipse. + * @return The bounding box. + */ + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Double(x, y, width, height); + } + + /** + * Returns the height of the ellipse. + * @return The height of the ellipse. + */ + public double getHeight() + { + return height; + } + + /** + * Returns the width of the ellipse. + * @return The width of the ellipse. + */ + public double getWidth() + { + return width; + } + + /** + * Returns x coordinate of the upper-left corner of + * the ellipse's bounding-box. + * @return The x coordinate. + */ + public double getX() + { + return x; + } + + /** + * Returns y coordinate of the upper-left corner of + * the ellipse's bounding-box. + * @return The y coordinate. + */ + public double getY() + { + return y; + } + + /** + * Returns true if the ellipse encloses no area, and + * false otherwise. + * + * @return A boolean. + */ + public boolean isEmpty() + { + return height <= 0 || width <= 0; + } + + /** + * Sets the geometry of the ellipse's bounding box.

    + * + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ + public void setFrame(double x, double y, double w, double h) + { + this.x = x; + this.y = y; + height = h; + width = w; + } + } // class Double + + /** + * An {@link Ellipse2D} that stores its coordinates using float + * primitives. + */ + public static class Float extends Ellipse2D + { + /** + * The height of the ellipse. + */ + public float height; + + /** + * The width of the ellipse. + */ + public float width; + + /** + * The upper-left x coordinate of the bounding-box + */ + public float x; + + /** + * The upper-left y coordinate of the bounding-box + */ + public float y; + + /** + * Creates a new Ellipse2D with an upper-left coordinate of (0,0) + * and a zero size. + */ + public Float() + { + } + + /** + * Creates a new Ellipse2D within a given rectangle + * using floating-point precision.

    + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + * + */ + public Float(float x, float y, float w, float h) + { + this.x = x; + this.y = y; + this.height = h; + this.width = w; + } + + /** + * Returns the bounding-box of the ellipse. + * @return The bounding box. + */ + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Float(x, y, width, height); + } + + /** + * Returns the height of the ellipse. + * @return The height of the ellipse. + */ + public double getHeight() + { + return height; + } + + /** + * Returns the width of the ellipse. + * @return The width of the ellipse. + */ + public double getWidth() + { + return width; + } + + /** + * Returns x coordinate of the upper-left corner of + * the ellipse's bounding-box. + * @return The x coordinate. + */ + public double getX() + { + return x; + } + + /** + * Returns y coordinate of the upper-left corner of + * the ellipse's bounding-box. + * @return The y coordinate. + */ + public double getY() + { + return y; + } + + /** + * Returns true if the ellipse encloses no area, and + * false otherwise. + * + * @return A boolean. + */ + public boolean isEmpty() + { + return height <= 0 || width <= 0; + } + + /** + * Sets the geometry of the ellipse's bounding box.

    + * + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ + public void setFrame(float x, float y, float w, float h) + { + this.x = x; + this.y = y; + height = h; + width = w; + } + + /** + * Sets the geometry of the ellipse's bounding box. + * + * Note: This leads to a loss of precision.

    + * + * @param x - x coordinate of the upper-left of the bounding rectangle + * @param y - y coordinate of the upper-left of the bounding rectangle + * @param w - width of the ellipse + * @param h - height of the ellipse + */ + public void setFrame(double x, double y, double w, double h) + { + this.x = (float) x; + this.y = (float) y; + height = (float) h; + width = (float) w; + } + } // class Float +} // class Ellipse2D diff --git a/libjava/classpath/java/awt/geom/FlatteningPathIterator.java b/libjava/classpath/java/awt/geom/FlatteningPathIterator.java new file mode 100644 index 000000000..629936bf7 --- /dev/null +++ b/libjava/classpath/java/awt/geom/FlatteningPathIterator.java @@ -0,0 +1,579 @@ +/* FlatteningPathIterator.java -- Approximates curves by straight lines + Copyright (C) 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.util.NoSuchElementException; + + +/** + * A PathIterator for approximating curved path segments by sequences + * of straight lines. Instances of this class will only return + * segments of type {@link PathIterator#SEG_MOVETO}, {@link + * PathIterator#SEG_LINETO}, and {@link PathIterator#SEG_CLOSE}. + * + *

    The accuracy of the approximation is determined by two + * parameters: + * + *

    • The flatness is a threshold value for deciding when + * a curved segment is consided flat enough for being approximated by + * a single straight line. Flatness is defined as the maximal distance + * of a curve control point to the straight line that connects the + * curve start and end. A lower flatness threshold means a closer + * approximation. See {@link QuadCurve2D#getFlatness()} and {@link + * CubicCurve2D#getFlatness()} for drawings which illustrate the + * meaning of flatness.
    • + * + *
    • The recursion limit imposes an upper bound for how often + * a curved segment gets subdivided. A limit of n means that + * for each individual quadratic and cubic Bézier spline + * segment, at most 2n {@link + * PathIterator#SEG_LINETO} segments will be created.
    + * + *

    Memory Efficiency: The memory consumption grows linearly + * with the recursion limit. Neither the flatness parameter nor + * the number of segments in the flattened path will affect the memory + * consumption. + * + *

    Thread Safety: Multiple threads can safely work on + * separate instances of this class. However, multiple threads should + * not concurrently access the same instance, as no synchronization is + * performed. + * + * @see Implementation Note + * + * @author Sascha Brawer (brawer@dandelis.ch) + * + * @since 1.2 + */ +public class FlatteningPathIterator + implements PathIterator +{ + /** + * The PathIterator whose curved segments are being approximated. + */ + private final PathIterator srcIter; + + + /** + * The square of the flatness threshold value, which determines when + * a curve segment is considered flat enough that no further + * subdivision is needed. + * + *

    Calculating flatness actually produces the squared flatness + * value. To avoid the relatively expensive calculation of a square + * root for each curve segment, we perform all flatness comparisons + * on squared values. + * + * @see QuadCurve2D#getFlatnessSq() + * @see CubicCurve2D#getFlatnessSq() + */ + private final double flatnessSq; + + + /** + * The maximal number of subdivions that are performed to + * approximate a quadratic or cubic curve segment. + */ + private final int recursionLimit; + + + /** + * A stack for holding the coordinates of subdivided segments. + * + * @see Implementation Note + */ + private double[] stack; + + + /** + * The current stack size. + * + * @see Implementation Note + */ + private int stackSize; + + + /** + * The number of recursions that were performed to arrive at + * a segment on the stack. + * + * @see Implementation Note + */ + private int[] recLevel; + + + + private final double[] scratch = new double[6]; + + + /** + * The segment type of the last segment that was returned by + * the source iterator. + */ + private int srcSegType; + + + /** + * The current x position of the source iterator. + */ + private double srcPosX; + + + /** + * The current y position of the source iterator. + */ + private double srcPosY; + + + /** + * A flag that indicates when this path iterator has finished its + * iteration over path segments. + */ + private boolean done; + + + /** + * Constructs a new PathIterator for approximating an input + * PathIterator with straight lines. The approximation works by + * recursive subdivisons, until the specified flatness threshold is + * not exceeded. + * + *

    There will not be more than 10 nested recursion steps, which + * means that a single SEG_QUADTO or + * SEG_CUBICTO segment is approximated by at most + * 210 = 1024 straight lines. + */ + public FlatteningPathIterator(PathIterator src, double flatness) + { + this(src, flatness, 10); + } + + + /** + * Constructs a new PathIterator for approximating an input + * PathIterator with straight lines. The approximation works by + * recursive subdivisons, until the specified flatness threshold is + * not exceeded. Additionally, the number of recursions is also + * bound by the specified recursion limit. + */ + public FlatteningPathIterator(PathIterator src, double flatness, + int limit) + { + if (flatness < 0 || limit < 0) + throw new IllegalArgumentException(); + + srcIter = src; + flatnessSq = flatness * flatness; + recursionLimit = limit; + fetchSegment(); + } + + + /** + * Returns the maximally acceptable flatness. + * + * @see QuadCurve2D#getFlatness() + * @see CubicCurve2D#getFlatness() + */ + public double getFlatness() + { + return Math.sqrt(flatnessSq); + } + + + /** + * Returns the maximum number of recursive curve subdivisions. + */ + public int getRecursionLimit() + { + return recursionLimit; + } + + + // Documentation will be copied from PathIterator. + public int getWindingRule() + { + return srcIter.getWindingRule(); + } + + + // Documentation will be copied from PathIterator. + public boolean isDone() + { + return done; + } + + + // Documentation will be copied from PathIterator. + public void next() + { + if (stackSize > 0) + { + --stackSize; + if (stackSize > 0) + { + switch (srcSegType) + { + case PathIterator.SEG_QUADTO: + subdivideQuadratic(); + return; + + case PathIterator.SEG_CUBICTO: + subdivideCubic(); + return; + + default: + throw new IllegalStateException(); + } + } + } + + srcIter.next(); + fetchSegment(); + } + + + // Documentation will be copied from PathIterator. + public int currentSegment(double[] coords) + { + if (done) + throw new NoSuchElementException(); + + switch (srcSegType) + { + case PathIterator.SEG_CLOSE: + return srcSegType; + + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + coords[0] = srcPosX; + coords[1] = srcPosY; + return srcSegType; + + case PathIterator.SEG_QUADTO: + if (stackSize == 0) + { + coords[0] = srcPosX; + coords[1] = srcPosY; + } + else + { + int sp = stack.length - 4 * stackSize; + coords[0] = stack[sp + 2]; + coords[1] = stack[sp + 3]; + } + return PathIterator.SEG_LINETO; + + case PathIterator.SEG_CUBICTO: + if (stackSize == 0) + { + coords[0] = srcPosX; + coords[1] = srcPosY; + } + else + { + int sp = stack.length - 6 * stackSize; + coords[0] = stack[sp + 4]; + coords[1] = stack[sp + 5]; + } + return PathIterator.SEG_LINETO; + } + + throw new IllegalStateException(); + } + + + // Documentation will be copied from PathIterator. + public int currentSegment(float[] coords) + { + if (done) + throw new NoSuchElementException(); + + switch (srcSegType) + { + case PathIterator.SEG_CLOSE: + return srcSegType; + + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + coords[0] = (float) srcPosX; + coords[1] = (float) srcPosY; + return srcSegType; + + case PathIterator.SEG_QUADTO: + if (stackSize == 0) + { + coords[0] = (float) srcPosX; + coords[1] = (float) srcPosY; + } + else + { + int sp = stack.length - 4 * stackSize; + coords[0] = (float) stack[sp + 2]; + coords[1] = (float) stack[sp + 3]; + } + return PathIterator.SEG_LINETO; + + case PathIterator.SEG_CUBICTO: + if (stackSize == 0) + { + coords[0] = (float) srcPosX; + coords[1] = (float) srcPosY; + } + else + { + int sp = stack.length - 6 * stackSize; + coords[0] = (float) stack[sp + 4]; + coords[1] = (float) stack[sp + 5]; + } + return PathIterator.SEG_LINETO; + } + + throw new IllegalStateException(); + } + + + /** + * Fetches the next segment from the source iterator. + */ + private void fetchSegment() + { + int sp; + + if (srcIter.isDone()) + { + done = true; + return; + } + + srcSegType = srcIter.currentSegment(scratch); + + switch (srcSegType) + { + case PathIterator.SEG_CLOSE: + return; + + case PathIterator.SEG_MOVETO: + case PathIterator.SEG_LINETO: + srcPosX = scratch[0]; + srcPosY = scratch[1]; + return; + + case PathIterator.SEG_QUADTO: + if (recursionLimit == 0) + { + srcPosX = scratch[2]; + srcPosY = scratch[3]; + stackSize = 0; + return; + } + sp = 4 * recursionLimit; + stackSize = 1; + if (stack == null) + { + stack = new double[sp + /* 4 + 2 */ 6]; + recLevel = new int[recursionLimit + 1]; + } + recLevel[0] = 0; + stack[sp] = srcPosX; // P1.x + stack[sp + 1] = srcPosY; // P1.y + stack[sp + 2] = scratch[0]; // C.x + stack[sp + 3] = scratch[1]; // C.y + srcPosX = stack[sp + 4] = scratch[2]; // P2.x + srcPosY = stack[sp + 5] = scratch[3]; // P2.y + subdivideQuadratic(); + break; + + case PathIterator.SEG_CUBICTO: + if (recursionLimit == 0) + { + srcPosX = scratch[4]; + srcPosY = scratch[5]; + stackSize = 0; + return; + } + sp = 6 * recursionLimit; + stackSize = 1; + if ((stack == null) || (stack.length < sp + 8)) + { + stack = new double[sp + /* 6 + 2 */ 8]; + recLevel = new int[recursionLimit + 1]; + } + recLevel[0] = 0; + stack[sp] = srcPosX; // P1.x + stack[sp + 1] = srcPosY; // P1.y + stack[sp + 2] = scratch[0]; // C1.x + stack[sp + 3] = scratch[1]; // C1.y + stack[sp + 4] = scratch[2]; // C2.x + stack[sp + 5] = scratch[3]; // C2.y + srcPosX = stack[sp + 6] = scratch[4]; // P2.x + srcPosY = stack[sp + 7] = scratch[5]; // P2.y + subdivideCubic(); + return; + } + } + + + /** + * Repeatedly subdivides the quadratic curve segment that is on top + * of the stack. The iteration terminates when the recursion limit + * has been reached, or when the resulting segment is flat enough. + */ + private void subdivideQuadratic() + { + int sp; + int level; + + sp = stack.length - 4 * stackSize - 2; + level = recLevel[stackSize - 1]; + while ((level < recursionLimit) + && (QuadCurve2D.getFlatnessSq(stack, sp) >= flatnessSq)) + { + recLevel[stackSize] = recLevel[stackSize - 1] = ++level; + QuadCurve2D.subdivide(stack, sp, stack, sp - 4, stack, sp); + ++stackSize; + sp -= 4; + } + } + + + /** + * Repeatedly subdivides the cubic curve segment that is on top + * of the stack. The iteration terminates when the recursion limit + * has been reached, or when the resulting segment is flat enough. + */ + private void subdivideCubic() + { + int sp; + int level; + + sp = stack.length - 6 * stackSize - 2; + level = recLevel[stackSize - 1]; + while ((level < recursionLimit) + && (CubicCurve2D.getFlatnessSq(stack, sp) >= flatnessSq)) + { + recLevel[stackSize] = recLevel[stackSize - 1] = ++level; + + CubicCurve2D.subdivide(stack, sp, stack, sp - 6, stack, sp); + ++stackSize; + sp -= 6; + } + } + + + /* These routines were useful for debugging. Since they would + * just bloat the implementation, they are commented out. + * + * + + private static String segToString(int segType, double[] d, int offset) + { + String s; + + switch (segType) + { + case PathIterator.SEG_CLOSE: + return "SEG_CLOSE"; + + case PathIterator.SEG_MOVETO: + return "SEG_MOVETO (" + d[offset] + ", " + d[offset + 1] + ")"; + + case PathIterator.SEG_LINETO: + return "SEG_LINETO (" + d[offset] + ", " + d[offset + 1] + ")"; + + case PathIterator.SEG_QUADTO: + return "SEG_QUADTO (" + d[offset] + ", " + d[offset + 1] + + ") (" + d[offset + 2] + ", " + d[offset + 3] + ")"; + + case PathIterator.SEG_CUBICTO: + return "SEG_CUBICTO (" + d[offset] + ", " + d[offset + 1] + + ") (" + d[offset + 2] + ", " + d[offset + 3] + + ") (" + d[offset + 4] + ", " + d[offset + 5] + ")"; + } + + throw new IllegalStateException(); + } + + + private void dumpQuadraticStack(String msg) + { + int sp = stack.length - 4 * stackSize - 2; + int i = 0; + System.err.print(" " + msg + ":"); + while (sp < stack.length) + { + System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")"); + if (i < recLevel.length) + System.out.print("/" + recLevel[i++]); + if (sp + 3 < stack.length) + System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]"); + sp += 4; + } + System.err.println(); + } + + + private void dumpCubicStack(String msg) + { + int sp = stack.length - 6 * stackSize - 2; + int i = 0; + System.err.print(" " + msg + ":"); + while (sp < stack.length) + { + System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")"); + if (i < recLevel.length) + System.out.print("/" + recLevel[i++]); + if (sp + 3 < stack.length) + { + System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]"); + System.err.print(" [" + stack[sp+4] + ", " + stack[sp+5] + "]"); + } + sp += 6; + } + System.err.println(); + } + + * + * + */ +} diff --git a/libjava/classpath/java/awt/geom/GeneralPath.java b/libjava/classpath/java/awt/geom/GeneralPath.java new file mode 100644 index 000000000..99f1905e2 --- /dev/null +++ b/libjava/classpath/java/awt/geom/GeneralPath.java @@ -0,0 +1,992 @@ +/* GeneralPath.java -- represents a shape built from subpaths + Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; + + +/** + * A general geometric path, consisting of any number of subpaths + * constructed out of straight lines and cubic or quadratic Bezier + * curves. + * + *

    The inside of the curve is defined for drawing purposes by a winding + * rule. Either the WIND_EVEN_ODD or WIND_NON_ZERO winding rule can be chosen. + * + *

    A drawing of a GeneralPath + *

    The EVEN_ODD winding rule defines a point as inside a path if: + * A ray from the point towards infinity in an arbitrary direction + * intersects the path an odd number of times. Points A and + * C in the image are considered to be outside the path. + * (both intersect twice) + * Point B intersects once, and is inside. + * + *

    The NON_ZERO winding rule defines a point as inside a path if: + * The path intersects the ray in an equal number of opposite directions. + * Point A in the image is outside (one intersection in the + * ’up’ + * direction, one in the ’down’ direction) Point B in + * the image is inside (one intersection ’down’) + * Point C in the image is inside (two intersections in the + * ’down’ direction) + * + * @see Line2D + * @see CubicCurve2D + * @see QuadCurve2D + * + * @author Sascha Brawer (brawer@dandelis.ch) + * @author Sven de Marothy (sven@physto.se) + * + * @since 1.2 + */ +public final class GeneralPath implements Shape, Cloneable +{ + /** Same constant as {@link PathIterator#WIND_EVEN_ODD}. */ + public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; + + /** Same constant as {@link PathIterator#WIND_NON_ZERO}. */ + public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; + + /** Initial size if not specified. */ + private static final int INIT_SIZE = 10; + + /** A big number, but not so big it can't survive a few float operations */ + private static final double BIG_VALUE = Double.MAX_VALUE / 10.0; + + /** The winding rule. + * This is package-private to avoid an accessor method. + */ + int rule; + + /** + * The path type in points. Note that xpoints[index] and ypoints[index] maps + * to types[index]; the control points of quad and cubic paths map as + * well but are ignored. + * This is package-private to avoid an accessor method. + */ + byte[] types; + + /** + * The list of all points seen. Since you can only append floats, it makes + * sense for these to be float[]. I have no idea why Sun didn't choose to + * allow a general path of double precision points. + * Note: Storing x and y coords seperately makes for a slower transforms, + * But it speeds up and simplifies box-intersection checking a lot. + * These are package-private to avoid accessor methods. + */ + float[] xpoints; + float[] ypoints; + + /** The index of the most recent moveto point, or null. */ + private int subpath = -1; + + /** The next available index into points. + * This is package-private to avoid an accessor method. + */ + int index; + + /** + * Constructs a GeneralPath with the default (NON_ZERO) + * winding rule and initial capacity (20). + */ + public GeneralPath() + { + this(WIND_NON_ZERO, INIT_SIZE); + } + + /** + * Constructs a GeneralPath with a specific winding rule + * and the default initial capacity (20). + * @param rule the winding rule ({@link #WIND_NON_ZERO} or + * {@link #WIND_EVEN_ODD}) + * + * @throws IllegalArgumentException if rule is not one of the + * listed values. + */ + public GeneralPath(int rule) + { + this(rule, INIT_SIZE); + } + + /** + * Constructs a GeneralPath with a specific winding rule + * and the initial capacity. The initial capacity should be + * the approximate number of path segments to be used. + * @param rule the winding rule ({@link #WIND_NON_ZERO} or + * {@link #WIND_EVEN_ODD}) + * @param capacity the inital capacity, in path segments + * + * @throws IllegalArgumentException if rule is not one of the + * listed values. + */ + public GeneralPath(int rule, int capacity) + { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) + throw new IllegalArgumentException(); + this.rule = rule; + if (capacity < INIT_SIZE) + capacity = INIT_SIZE; + types = new byte[capacity]; + xpoints = new float[capacity]; + ypoints = new float[capacity]; + } + + /** + * Constructs a GeneralPath from an arbitrary shape object. + * The Shapes PathIterator path and winding rule will be used. + * + * @param s the shape (null not permitted). + * + * @throws NullPointerException if shape is null. + */ + public GeneralPath(Shape s) + { + types = new byte[INIT_SIZE]; + xpoints = new float[INIT_SIZE]; + ypoints = new float[INIT_SIZE]; + PathIterator pi = s.getPathIterator(null); + setWindingRule(pi.getWindingRule()); + append(pi, false); + } + + /** + * Adds a new point to a path. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + */ + public void moveTo(float x, float y) + { + subpath = index; + ensureSize(index + 1); + types[index] = PathIterator.SEG_MOVETO; + xpoints[index] = x; + ypoints[index++] = y; + } + + /** + * Appends a straight line to the current path. + * @param x x coordinate of the line endpoint. + * @param y y coordinate of the line endpoint. + */ + public void lineTo(float x, float y) + { + ensureSize(index + 1); + types[index] = PathIterator.SEG_LINETO; + xpoints[index] = x; + ypoints[index++] = y; + } + + /** + * Appends a quadratic Bezier curve to the current path. + * @param x1 x coordinate of the control point + * @param y1 y coordinate of the control point + * @param x2 x coordinate of the curve endpoint. + * @param y2 y coordinate of the curve endpoint. + */ + public void quadTo(float x1, float y1, float x2, float y2) + { + ensureSize(index + 2); + types[index] = PathIterator.SEG_QUADTO; + xpoints[index] = x1; + ypoints[index++] = y1; + xpoints[index] = x2; + ypoints[index++] = y2; + } + + /** + * Appends a cubic Bezier curve to the current path. + * @param x1 x coordinate of the first control point + * @param y1 y coordinate of the first control point + * @param x2 x coordinate of the second control point + * @param y2 y coordinate of the second control point + * @param x3 x coordinate of the curve endpoint. + * @param y3 y coordinate of the curve endpoint. + */ + public void curveTo(float x1, float y1, float x2, float y2, float x3, + float y3) + { + ensureSize(index + 3); + types[index] = PathIterator.SEG_CUBICTO; + xpoints[index] = x1; + ypoints[index++] = y1; + xpoints[index] = x2; + ypoints[index++] = y2; + xpoints[index] = x3; + ypoints[index++] = y3; + } + + /** + * Closes the current subpath by drawing a line + * back to the point of the last moveTo, unless the path is already closed. + */ + public void closePath() + { + if (index >= 1 && types[index - 1] == PathIterator.SEG_CLOSE) + return; + ensureSize(index + 1); + types[index] = PathIterator.SEG_CLOSE; + xpoints[index] = xpoints[subpath]; + ypoints[index++] = ypoints[subpath]; + } + + /** + * Appends the segments of a Shape to the path. If connect is + * true, the new path segments are connected to the existing one with a line. + * The winding rule of the Shape is ignored. + * + * @param s the shape (null not permitted). + * @param connect whether to connect the new shape to the existing path. + * + * @throws NullPointerException if s is null. + */ + public void append(Shape s, boolean connect) + { + append(s.getPathIterator(null), connect); + } + + /** + * Appends the segments of a PathIterator to this GeneralPath. + * Optionally, the initial {@link PathIterator#SEG_MOVETO} segment + * of the appended path is changed into a {@link + * PathIterator#SEG_LINETO} segment. + * + * @param iter the PathIterator specifying which segments shall be + * appended (null not permitted). + * + * @param connect true for substituting the initial + * {@link PathIterator#SEG_MOVETO} segment by a {@link + * PathIterator#SEG_LINETO}, or false for not + * performing any substitution. If this GeneralPath is currently + * empty, connect is assumed to be false, + * thus leaving the initial {@link PathIterator#SEG_MOVETO} + * unchanged. + */ + public void append(PathIterator iter, boolean connect) + { + // A bad implementation of this method had caused Classpath bug #6076. + float[] f = new float[6]; + while (! iter.isDone()) + { + switch (iter.currentSegment(f)) + { + case PathIterator.SEG_MOVETO: + if (! connect || (index == 0)) + { + moveTo(f[0], f[1]); + break; + } + if ((index >= 1) && (types[index - 1] == PathIterator.SEG_CLOSE) + && (f[0] == xpoints[index - 1]) + && (f[1] == ypoints[index - 1])) + break; + + // Fall through. + case PathIterator.SEG_LINETO: + lineTo(f[0], f[1]); + break; + case PathIterator.SEG_QUADTO: + quadTo(f[0], f[1], f[2], f[3]); + break; + case PathIterator.SEG_CUBICTO: + curveTo(f[0], f[1], f[2], f[3], f[4], f[5]); + break; + case PathIterator.SEG_CLOSE: + closePath(); + break; + } + + connect = false; + iter.next(); + } + } + + /** + * Returns the path’s current winding rule. + * + * @return {@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO}. + */ + public int getWindingRule() + { + return rule; + } + + /** + * Sets the path’s winding rule, which controls which areas are + * considered ’inside’ or ’outside’ the path + * on drawing. Valid rules are WIND_EVEN_ODD for an even-odd winding rule, + * or WIND_NON_ZERO for a non-zero winding rule. + * + * @param rule the rule ({@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO}). + */ + public void setWindingRule(int rule) + { + if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) + throw new IllegalArgumentException(); + this.rule = rule; + } + + /** + * Returns the current appending point of the path. + * + * @return The point. + */ + public Point2D getCurrentPoint() + { + if (subpath < 0) + return null; + return new Point2D.Float(xpoints[index - 1], ypoints[index - 1]); + } + + /** + * Resets the path. All points and segments are destroyed. + */ + public void reset() + { + subpath = -1; + index = 0; + } + + /** + * Applies a transform to the path. + * + * @param xform the transform (null not permitted). + */ + public void transform(AffineTransform xform) + { + double nx; + double ny; + double[] m = new double[6]; + xform.getMatrix(m); + for (int i = 0; i < index; i++) + { + nx = m[0] * xpoints[i] + m[2] * ypoints[i] + m[4]; + ny = m[1] * xpoints[i] + m[3] * ypoints[i] + m[5]; + xpoints[i] = (float) nx; + ypoints[i] = (float) ny; + } + } + + /** + * Creates a transformed version of the path. + * @param xform the transform to apply + * @return a new transformed GeneralPath + */ + public Shape createTransformedShape(AffineTransform xform) + { + GeneralPath p = new GeneralPath(this); + p.transform(xform); + return p; + } + + /** + * Returns the path’s bounding box. + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + /** + * Returns the path’s bounding box, in float precision + */ + public Rectangle2D getBounds2D() + { + float x1; + float y1; + float x2; + float y2; + + if (index > 0) + { + x1 = x2 = xpoints[0]; + y1 = y2 = ypoints[0]; + } + else + x1 = x2 = y1 = y2 = 0.0f; + + for (int i = 0; i < index; i++) + { + x1 = Math.min(xpoints[i], x1); + y1 = Math.min(ypoints[i], y1); + x2 = Math.max(xpoints[i], x2); + y2 = Math.max(ypoints[i], y2); + } + return (new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1)); + } + + /** + * Evaluates if a point is within the GeneralPath, + * The NON_ZERO winding rule is used, regardless of the + * set winding rule. + * @param x x coordinate of the point to evaluate + * @param y y coordinate of the point to evaluate + * @return true if the point is within the path, false otherwise + */ + public boolean contains(double x, double y) + { + return (getWindingNumber(x, y) != 0); + } + + /** + * Evaluates if a Point2D is within the GeneralPath, + * The NON_ZERO winding rule is used, regardless of the + * set winding rule. + * @param p The Point2D to evaluate + * @return true if the point is within the path, false otherwise + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Evaluates if a rectangle is completely contained within the path. + * This method will return false in the cases when the box + * intersects an inner segment of the path. + * (i.e.: The method is accurate for the EVEN_ODD winding rule) + */ + public boolean contains(double x, double y, double w, double h) + { + if (! getBounds2D().intersects(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, false, w) != 0 /* top */ + || getAxisIntersections(x, y + h, false, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, true, h) != 0 /* right */ + || getAxisIntersections(x, y, true, h) != 0) /* left */ + return false; + + /* No intersections, is any point inside? */ + if (getWindingNumber(x, y) != 0) + return true; + + return false; + } + + /** + * Evaluates if a rectangle is completely contained within the path. + * This method will return false in the cases when the box + * intersects an inner segment of the path. + * (i.e.: The method is accurate for the EVEN_ODD winding rule) + * @param r the rectangle + * @return true if the rectangle is completely contained + * within the path, false otherwise + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Evaluates if a rectangle intersects the path. + * @param x x coordinate of the rectangle + * @param y y coordinate of the rectangle + * @param w width of the rectangle + * @param h height of the rectangle + * @return true if the rectangle intersects the path, + * false otherwise + */ + public boolean intersects(double x, double y, double w, double h) + { + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, false, w) != 0 /* top */ + || getAxisIntersections(x, y + h, false, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, true, h) != 0 /* right */ + || getAxisIntersections(x, y, true, h) != 0) /* left */ + return true; + + /* No intersections, is any point inside? */ + if (getWindingNumber(x, y) != 0) + return true; + + return false; + } + + /** + * Evaluates if a Rectangle2D intersects the path. + * @param r The rectangle + * @return true if the rectangle intersects the path, + * false otherwise + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * A PathIterator that iterates over the segments of a GeneralPath. + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ + private static class GeneralPathIterator implements PathIterator + { + /** + * The number of coordinate values for each segment type. + */ + private static final int[] NUM_COORDS = { + /* 0: SEG_MOVETO */ 1, + /* 1: SEG_LINETO */ 1, + /* 2: SEG_QUADTO */ 2, + /* 3: SEG_CUBICTO */ 3, + /* 4: SEG_CLOSE */ 0}; + + /** + * The GeneralPath whose segments are being iterated. + * This is package-private to avoid an accessor method. + */ + final GeneralPath path; + + /** + * The affine transformation used to transform coordinates. + */ + private final AffineTransform transform; + + /** + * The current position of the iterator. + */ + private int pos; + + /** + * Constructs a new iterator for enumerating the segments of a + * GeneralPath. + * + * @param path the path to enumerate + * @param transform an affine transformation for projecting the returned + * points, or null to return the original points + * without any mapping. + */ + GeneralPathIterator(GeneralPath path, AffineTransform transform) + { + this.path = path; + this.transform = transform; + } + + /** + * Returns the current winding rule of the GeneralPath. + */ + public int getWindingRule() + { + return path.rule; + } + + /** + * Determines whether the iterator has reached the last segment in + * the path. + */ + public boolean isDone() + { + return pos >= path.index; + } + + /** + * Advances the iterator position by one segment. + */ + public void next() + { + int seg; + + /* + * Increment pos by the number of coordinate pairs. + */ + seg = path.types[pos]; + if (seg == SEG_CLOSE) + pos++; + else + pos += NUM_COORDS[seg]; + } + + /** + * Returns the current segment in float coordinates. + */ + public int currentSegment(float[] coords) + { + int seg; + int numCoords; + + seg = path.types[pos]; + numCoords = NUM_COORDS[seg]; + if (numCoords > 0) + { + for (int i = 0; i < numCoords; i++) + { + coords[i << 1] = path.xpoints[pos + i]; + coords[(i << 1) + 1] = path.ypoints[pos + i]; + } + + if (transform != null) + transform.transform( /* src */ + coords, /* srcOffset */ + 0, /* dest */ coords, /* destOffset */ + 0, /* numPoints */ numCoords); + } + return seg; + } + + /** + * Returns the current segment in double coordinates. + */ + public int currentSegment(double[] coords) + { + int seg; + int numCoords; + + seg = path.types[pos]; + numCoords = NUM_COORDS[seg]; + if (numCoords > 0) + { + for (int i = 0; i < numCoords; i++) + { + coords[i << 1] = (double) path.xpoints[pos + i]; + coords[(i << 1) + 1] = (double) path.ypoints[pos + i]; + } + if (transform != null) + transform.transform( /* src */ + coords, /* srcOffset */ + 0, /* dest */ coords, /* destOffset */ + 0, /* numPoints */ numCoords); + } + return seg; + } + } + + /** + * Creates a PathIterator for iterating along the segments of the path. + * + * @param at an affine transformation for projecting the returned + * points, or null to let the created iterator return + * the original points without any mapping. + */ + public PathIterator getPathIterator(AffineTransform at) + { + return new GeneralPathIterator(this, at); + } + + /** + * Creates a new FlatteningPathIterator for the path + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Creates a new shape of the same run-time type with the same contents + * as this one. + * + * @return the clone + * + * @exception OutOfMemoryError If there is not enough memory available. + * + * @since 1.2 + */ + public Object clone() + { + // This class is final; no need to use super.clone(). + return new GeneralPath(this); + } + + /** + * Helper method - ensure the size of the data arrays, + * otherwise, reallocate new ones twice the size + * + * @param size the minimum array size. + */ + private void ensureSize(int size) + { + if (subpath < 0) + throw new IllegalPathStateException("need initial moveto"); + if (size <= xpoints.length) + return; + byte[] b = new byte[types.length << 1]; + System.arraycopy(types, 0, b, 0, index); + types = b; + float[] f = new float[xpoints.length << 1]; + System.arraycopy(xpoints, 0, f, 0, index); + xpoints = f; + f = new float[ypoints.length << 1]; + System.arraycopy(ypoints, 0, f, 0, index); + ypoints = f; + } + + /** + * Helper method - Get the total number of intersections from (x,y) along + * a given axis, within a given distance. + */ + private int getAxisIntersections(double x, double y, boolean useYaxis, + double distance) + { + return (evaluateCrossings(x, y, false, useYaxis, distance)); + } + + /** + * Helper method - returns the winding number of a point. + */ + private int getWindingNumber(double x, double y) + { + /* Evaluate the crossings from x,y to infinity on the y axis (arbitrary + choice). Note that we don't actually use Double.INFINITY, since that's + slower, and may cause problems. */ + return (evaluateCrossings(x, y, true, true, BIG_VALUE)); + } + + /** + * Helper method - evaluates the number of intersections on an axis from + * the point (x,y) to the point (x,y+distance) or (x+distance,y). + * @param x x coordinate. + * @param y y coordinate. + * @param neg True if opposite-directed intersections should cancel, + * false to sum all intersections. + * @param useYaxis Use the Y axis, false uses the X axis. + * @param distance Interval from (x,y) on the selected axis to find + * intersections. + */ + private int evaluateCrossings(double x, double y, boolean neg, + boolean useYaxis, double distance) + { + float cx = 0.0f; + float cy = 0.0f; + float firstx = 0.0f; + float firsty = 0.0f; + + int negative = (neg) ? -1 : 1; + double x0; + double x1; + double x2; + double x3; + double y0; + double y1; + double y2; + double y3; + double[] r = new double[4]; + int nRoots; + double epsilon = 0.0; + int pos = 0; + int windingNumber = 0; + boolean pathStarted = false; + + if (index == 0) + return (0); + if (useYaxis) + { + float[] swap1; + swap1 = ypoints; + ypoints = xpoints; + xpoints = swap1; + double swap2; + swap2 = y; + y = x; + x = swap2; + } + + /* Get a value which is hopefully small but not insignificant relative + the path. */ + epsilon = ypoints[0] * 1E-7; + + if(epsilon == 0) + epsilon = 1E-7; + + pos = 0; + while (pos < index) + { + switch (types[pos]) + { + case PathIterator.SEG_MOVETO: + if (pathStarted) // close old path + { + x0 = cx; + y0 = cy; + x1 = firstx; + y1 = firsty; + + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (Line2D.linesIntersect(x0, y0, x1, y1, + epsilon, 0.0, distance, 0.0)) + windingNumber += (y1 < y0) ? 1 : negative; + + cx = firstx; + cy = firsty; + } + cx = firstx = xpoints[pos] - (float) x; + cy = firsty = ypoints[pos++] - (float) y; + pathStarted = true; + break; + case PathIterator.SEG_CLOSE: + x0 = cx; + y0 = cy; + x1 = firstx; + y1 = firsty; + + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (Line2D.linesIntersect(x0, y0, x1, y1, + epsilon, 0.0, distance, 0.0)) + windingNumber += (y1 < y0) ? 1 : negative; + + cx = firstx; + cy = firsty; + pos++; + pathStarted = false; + break; + case PathIterator.SEG_LINETO: + x0 = cx; + y0 = cy; + x1 = xpoints[pos] - (float) x; + y1 = ypoints[pos++] - (float) y; + + if (y0 == 0.0) + y0 -= epsilon; + if (y1 == 0.0) + y1 -= epsilon; + if (Line2D.linesIntersect(x0, y0, x1, y1, + epsilon, 0.0, distance, 0.0)) + windingNumber += (y1 < y0) ? 1 : negative; + + cx = xpoints[pos - 1] - (float) x; + cy = ypoints[pos - 1] - (float) y; + break; + case PathIterator.SEG_QUADTO: + x0 = cx; + y0 = cy; + x1 = xpoints[pos] - x; + y1 = ypoints[pos++] - y; + x2 = xpoints[pos] - x; + y2 = ypoints[pos++] - y; + + /* check if curve may intersect X+ axis. */ + if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0) + && (y0 * y1 <= 0 || y1 * y2 <= 0)) + { + if (y0 == 0.0) + y0 -= epsilon; + if (y2 == 0.0) + y2 -= epsilon; + + r[0] = y0; + r[1] = 2 * (y1 - y0); + r[2] = (y2 - 2 * y1 + y0); + + /* degenerate roots (=tangent points) do not + contribute to the winding number. */ + if ((nRoots = QuadCurve2D.solveQuadratic(r)) == 2) + for (int i = 0; i < nRoots; i++) + { + float t = (float) r[i]; + if (t > 0.0f && t < 1.0f) + { + double crossing = t * t * (x2 - 2 * x1 + x0) + + 2 * t * (x1 - x0) + x0; + if (crossing >= 0.0 && crossing <= distance) + windingNumber += (2 * t * (y2 - 2 * y1 + y0) + + 2 * (y1 - y0) < 0) ? 1 : negative; + } + } + } + + cx = xpoints[pos - 1] - (float) x; + cy = ypoints[pos - 1] - (float) y; + break; + case PathIterator.SEG_CUBICTO: + x0 = cx; + y0 = cy; + x1 = xpoints[pos] - x; + y1 = ypoints[pos++] - y; + x2 = xpoints[pos] - x; + y2 = ypoints[pos++] - y; + x3 = xpoints[pos] - x; + y3 = ypoints[pos++] - y; + + /* check if curve may intersect X+ axis. */ + if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0) + && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0)) + { + if (y0 == 0.0) + y0 -= epsilon; + if (y3 == 0.0) + y3 -= epsilon; + + r[0] = y0; + r[1] = 3 * (y1 - y0); + r[2] = 3 * (y2 + y0 - 2 * y1); + r[3] = y3 - 3 * y2 + 3 * y1 - y0; + + if ((nRoots = CubicCurve2D.solveCubic(r)) != 0) + for (int i = 0; i < nRoots; i++) + { + float t = (float) r[i]; + if (t > 0.0 && t < 1.0) + { + double crossing = -(t * t * t) * (x0 - 3 * x1 + + 3 * x2 - x3) + + 3 * t * t * (x0 - 2 * x1 + x2) + + 3 * t * (x1 - x0) + x0; + if (crossing >= 0 && crossing <= distance) + windingNumber += (3 * t * t * (y3 + 3 * y1 + - 3 * y2 - y0) + + 6 * t * (y0 - 2 * y1 + y2) + + 3 * (y1 - y0) < 0) ? 1 : negative; + } + } + } + + cx = xpoints[pos - 1] - (float) x; + cy = ypoints[pos - 1] - (float) y; + break; + } + } + + // swap coordinates back + if (useYaxis) + { + float[] swap; + swap = ypoints; + ypoints = xpoints; + xpoints = swap; + } + return (windingNumber); + } +} // class GeneralPath diff --git a/libjava/classpath/java/awt/geom/IllegalPathStateException.java b/libjava/classpath/java/awt/geom/IllegalPathStateException.java new file mode 100644 index 000000000..4d190c748 --- /dev/null +++ b/libjava/classpath/java/awt/geom/IllegalPathStateException.java @@ -0,0 +1,71 @@ +/* IllegalPathStateException.java -- an operation was in an illegal path state + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +/** + * Thrown when an operation on a path is in an illegal state, such as appending + * a segment to a GeneralPath without an initial moveto. + * + * @author Tom Tromey (tromey@cygnus.com) + * @see GeneralPath + * @status updated to 1.4 + */ +public class IllegalPathStateException extends RuntimeException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -5158084205220481094L; + + /** + * Create an exception with no message. + */ + public IllegalPathStateException() + { + } + + /** + * Create an exception with a message. + * + * @param msg the message + */ + public IllegalPathStateException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/awt/geom/Line2D.java b/libjava/classpath/java/awt/geom/Line2D.java new file mode 100644 index 000000000..c92aab004 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Line2D.java @@ -0,0 +1,1182 @@ +/* Line2D.java -- represents a line in 2-D space, plus operations on a line + Copyright (C) 2000, 2001, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +/** + * Represents a directed line bewteen two points in (x,y) Cartesian space. + * Remember, on-screen graphics have increasing x from left-to-right, and + * increasing y from top-to-bottom. The storage is left to subclasses. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @author David Gilbert + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class Line2D implements Shape, Cloneable +{ + /** + * The default constructor. + */ + protected Line2D() + { + } + + /** + * Return the x coordinate of the first point. + * + * @return the starting x coordinate + */ + public abstract double getX1(); + + /** + * Return the y coordinate of the first point. + * + * @return the starting y coordinate + */ + public abstract double getY1(); + + /** + * Return the first point. + * + * @return the starting point + */ + public abstract Point2D getP1(); + + /** + * Return the x coordinate of the second point. + * + * @return the ending x coordinate + */ + public abstract double getX2(); + + /** + * Return the y coordinate of the second point. + * + * @return the ending y coordinate + */ + public abstract double getY2(); + + /** + * Return the second point. + * + * @return the ending point + */ + public abstract Point2D getP2(); + + /** + * Set the coordinates of the line to the given coordinates. Loss of + * precision may occur due to rounding issues. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + */ + public abstract void setLine(double x1, double y1, double x2, double y2); + + /** + * Set the coordinates to the given points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public void setLine(Point2D p1, Point2D p2) + { + setLine(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Set the coordinates to those of the given line. + * + * @param l the line to copy + * @throws NullPointerException if l is null + */ + public void setLine(Line2D l) + { + setLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); + } + + /** + * Computes the relative rotation direction needed to pivot the line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + * @param px the reference x coordinate + * @param py the reference y coordinate + * @return the relative rotation direction + */ + public static int relativeCCW(double x1, double y1, double x2, double y2, + double px, double py) + { + if ((x1 == x2 && y1 == y2) + || (x1 == px && y1 == py)) + return 0; // Coincident points. + // Translate to the origin. + x2 -= x1; + y2 -= y1; + px -= x1; + py -= y1; + double slope2 = y2 / x2; + double slopep = py / px; + if (slope2 == slopep || (x2 == 0 && px == 0)) + return y2 > 0 // Colinear. + ? (py < 0 ? -1 : py > y2 ? 1 : 0) + : (py > 0 ? -1 : py < y2 ? 1 : 0); + if (x2 >= 0 && slope2 >= 0) + return px >= 0 // Quadrant 1. + ? (slope2 > slopep ? 1 : -1) + : (slope2 < slopep ? 1 : -1); + if (y2 > 0) + return px < 0 // Quadrant 2. + ? (slope2 > slopep ? 1 : -1) + : (slope2 < slopep ? 1 : -1); + if (slope2 >= 0.0) + return px >= 0 // Quadrant 3. + ? (slope2 < slopep ? 1 : -1) + : (slope2 > slopep ? 1 : -1); + return px < 0 // Quadrant 4. + ? (slope2 < slopep ? 1 : -1) + : (slope2 > slopep ? 1 : -1); + } + + /** + * Computes the relative rotation direction needed to pivot this line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param px the reference x coordinate + * @param py the reference y coordinate + * @return the relative rotation direction + * @see #relativeCCW(double, double, double, double, double, double) + */ + public int relativeCCW(double px, double py) + { + return relativeCCW(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Computes the relative rotation direction needed to pivot this line about + * the first point in order to have the second point colinear with point p. + * Because of floating point rounding, don't expect this to be a perfect + * measure of colinearity. The answer is 1 if the line has a shorter rotation + * in the direction of the positive X axis to the negative Y axis + * (counter-clockwise in the default Java coordinate system), or -1 if the + * shortest rotation is in the opposite direction (clockwise). If p + * is already colinear, the return value is -1 if it lies beyond the first + * point, 0 if it lies in the segment, or 1 if it lies beyond the second + * point. If the first and second point are coincident, this returns 0. + * + * @param p the reference point + * @return the relative rotation direction + * @throws NullPointerException if p is null + * @see #relativeCCW(double, double, double, double, double, double) + */ + public int relativeCCW(Point2D p) + { + return relativeCCW(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Computes twice the (signed) area of the triangle defined by the three + * points. This method is used for intersection testing. + * + * @param x1 the x-coordinate of the first point. + * @param y1 the y-coordinate of the first point. + * @param x2 the x-coordinate of the second point. + * @param y2 the y-coordinate of the second point. + * @param x3 the x-coordinate of the third point. + * @param y3 the y-coordinate of the third point. + * + * @return Twice the area. + */ + private static double area2(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + return (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1); + } + + /** + * Returns true if (x3, y3) lies between (x1, y1) and (x2, y2), + * and false otherwise, This test assumes that the three points are + * collinear, and is used for intersection testing. + * + * @param x1 the x-coordinate of the first point. + * @param y1 the y-coordinate of the first point. + * @param x2 the x-coordinate of the second point. + * @param y2 the y-coordinate of the second point. + * @param x3 the x-coordinate of the third point. + * @param y3 the y-coordinate of the third point. + * + * @return A boolean. + */ + private static boolean between(double x1, double y1, + double x2, double y2, + double x3, double y3) + { + if (x1 != x2) { + return (x1 <= x3 && x3 <= x2) || (x1 >= x3 && x3 >= x2); + } + else { + return (y1 <= y3 && y3 <= y2) || (y1 >= y3 && y3 >= y2); + } + } + + /** + * Test if the line segment (x1,y1)->(x2,y2) intersects the line segment + * (x3,y3)->(x4,y4). + * + * @param x1 the first x coordinate of the first segment + * @param y1 the first y coordinate of the first segment + * @param x2 the second x coordinate of the first segment + * @param y2 the second y coordinate of the first segment + * @param x3 the first x coordinate of the second segment + * @param y3 the first y coordinate of the second segment + * @param x4 the second x coordinate of the second segment + * @param y4 the second y coordinate of the second segment + * @return true if the segments intersect + */ + public static boolean linesIntersect(double x1, double y1, + double x2, double y2, + double x3, double y3, + double x4, double y4) + { + double a1, a2, a3, a4; + + // deal with special cases + if ((a1 = area2(x1, y1, x2, y2, x3, y3)) == 0.0) + { + // check if p3 is between p1 and p2 OR + // p4 is collinear also AND either between p1 and p2 OR at opposite ends + if (between(x1, y1, x2, y2, x3, y3)) + { + return true; + } + else + { + if (area2(x1, y1, x2, y2, x4, y4) == 0.0) + { + return between(x3, y3, x4, y4, x1, y1) + || between (x3, y3, x4, y4, x2, y2); + } + else { + return false; + } + } + } + else if ((a2 = area2(x1, y1, x2, y2, x4, y4)) == 0.0) + { + // check if p4 is between p1 and p2 (we already know p3 is not + // collinear) + return between(x1, y1, x2, y2, x4, y4); + } + + if ((a3 = area2(x3, y3, x4, y4, x1, y1)) == 0.0) { + // check if p1 is between p3 and p4 OR + // p2 is collinear also AND either between p1 and p2 OR at opposite ends + if (between(x3, y3, x4, y4, x1, y1)) { + return true; + } + else { + if (area2(x3, y3, x4, y4, x2, y2) == 0.0) { + return between(x1, y1, x2, y2, x3, y3) + || between (x1, y1, x2, y2, x4, y4); + } + else { + return false; + } + } + } + else if ((a4 = area2(x3, y3, x4, y4, x2, y2)) == 0.0) { + // check if p2 is between p3 and p4 (we already know p1 is not + // collinear) + return between(x3, y3, x4, y4, x2, y2); + } + else { // test for regular intersection + return ((a1 > 0.0) ^ (a2 > 0.0)) && ((a3 > 0.0) ^ (a4 > 0.0)); + } + } + + /** + * Test if this line intersects the line given by (x1,y1)->(x2,y2). + * + * @param x1 the first x coordinate of the other segment + * @param y1 the first y coordinate of the other segment + * @param x2 the second x coordinate of the other segment + * @param y2 the second y coordinate of the other segment + * @return true if the segments intersect + * @see #linesIntersect(double, double, double, double, + * double, double, double, double) + */ + public boolean intersectsLine(double x1, double y1, double x2, double y2) + { + return linesIntersect(getX1(), getY1(), getX2(), getY2(), + x1, y1, x2, y2); + } + + /** + * Test if this line intersects the given line. + * + * @param l the other segment + * @return true if the segments intersect + * @throws NullPointerException if l is null + * @see #linesIntersect(double, double, double, double, + * double, double, double, double) + */ + public boolean intersectsLine(Line2D l) + { + return linesIntersect(getX1(), getY1(), getX2(), getY2(), + l.getX1(), l.getY1(), l.getX2(), l.getY2()); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on the line segment. If the point is on the segment, the + * result will be 0. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the segment + * @see #ptSegDist(double, double, double, double, double, double) + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public static double ptSegDistSq(double x1, double y1, double x2, double y2, + double px, double py) + { + double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + + double x, y; + if (pd2 == 0) + { + // Points are coincident. + x = x1; + y = y2; + } + else + { + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; + + if (u < 0) + { + // "Off the end" + x = x1; + y = y1; + } + else if (u > 1.0) + { + x = x2; + y = y2; + } + else + { + x = x1 + u * (x2 - x1); + y = y1 + u * (y2 - y1); + } + } + + return (x - px) * (x - px) + (y - py) * (y - py); + } + + /** + * Measures the shortest distance from the reference point to a point on + * the line segment. If the point is on the segment, the result will be 0. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the segment + * @see #ptSegDistSq(double, double, double, double, double, double) + * @see #ptLineDist(double, double, double, double, double, double) + */ + public static double ptSegDist(double x1, double y1, double x2, double y2, + double px, double py) + { + return Math.sqrt(ptSegDistSq(x1, y1, x2, y2, px, py)); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on this line segment. If the point is on the segment, the + * result will be 0. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the segment + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public double ptSegDistSq(double px, double py) + { + return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on this line segment. If the point is on the segment, the + * result will be 0. + * + * @param p the point + * @return the square of the distance from the point to the segment + * @throws NullPointerException if p is null + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public double ptSegDistSq(Point2D p) + { + return ptSegDistSq(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Measures the shortest distance from the reference point to a point on + * this line segment. If the point is on the segment, the result will be 0. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the segment + * @see #ptSegDist(double, double, double, double, double, double) + */ + public double ptSegDist(double px, double py) + { + return ptSegDist(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Measures the shortest distance from the reference point to a point on + * this line segment. If the point is on the segment, the result will be 0. + * + * @param p the point + * @return the distance from the point to the segment + * @throws NullPointerException if p is null + * @see #ptSegDist(double, double, double, double, double, double) + */ + public double ptSegDist(Point2D p) + { + return ptSegDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from the segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the extended line + * @see #ptLineDist(double, double, double, double, double, double) + * @see #ptSegDistSq(double, double, double, double, double, double) + */ + public static double ptLineDistSq(double x1, double y1, double x2, double y2, + double px, double py) + { + double pd2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + + double x, y; + if (pd2 == 0) + { + // Points are coincident. + x = x1; + y = y2; + } + else + { + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / pd2; + x = x1 + u * (x2 - x1); + y = y1 + u * (y2 - y1); + } + + return (x - px) * (x - px) + (y - py) * (y - py); + } + + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from the segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param x1 the first x coordinate of the segment + * @param y1 the first y coordinate of the segment + * @param x2 the second x coordinate of the segment + * @param y2 the second y coordinate of the segment + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the extended line + * @see #ptLineDistSq(double, double, double, double, double, double) + * @see #ptSegDist(double, double, double, double, double, double) + */ + public static double ptLineDist(double x1, double y1, + double x2, double y2, + double px, double py) + { + return Math.sqrt(ptLineDistSq(x1, y1, x2, y2, px, py)); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from this segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the square of the distance from the point to the extended line + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public double ptLineDistSq(double px, double py) + { + return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Measures the square of the shortest distance from the reference point + * to a point on the infinite line extended from this segment. If the point + * is on the segment, the result will be 0. If the segment is length 0, + * the distance is to the common endpoint. + * + * @param p the point + * @return the square of the distance from the point to the extended line + * @throws NullPointerException if p is null + * @see #ptLineDistSq(double, double, double, double, double, double) + */ + public double ptLineDistSq(Point2D p) + { + return ptLineDistSq(getX1(), getY1(), getX2(), getY2(), + p.getX(), p.getY()); + } + + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from this segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param px the x coordinate of the point + * @param py the y coordinate of the point + * @return the distance from the point to the extended line + * @see #ptLineDist(double, double, double, double, double, double) + */ + public double ptLineDist(double px, double py) + { + return ptLineDist(getX1(), getY1(), getX2(), getY2(), px, py); + } + + /** + * Measures the shortest distance from the reference point to a point on + * the infinite line extended from this segment. If the point is on the + * segment, the result will be 0. If the segment is length 0, the distance + * is to the common endpoint. + * + * @param p the point + * @return the distance from the point to the extended line + * @throws NullPointerException if p is null + * @see #ptLineDist(double, double, double, double, double, double) + */ + public double ptLineDist(Point2D p) + { + return ptLineDist(getX1(), getY1(), getX2(), getY2(), p.getX(), p.getY()); + } + + /** + * Test if a point is contained inside the line. Since a line has no area, + * this returns false. + * + * @param x the x coordinate + * @param y the y coordinate + * @return false; the line does not contain points + */ + public boolean contains(double x, double y) + { + return false; + } + + /** + * Test if a point is contained inside the line. Since a line has no area, + * this returns false. + * + * @param p the point + * @return false; the line does not contain points + */ + public boolean contains(Point2D p) + { + return false; + } + + /** + * Tests if this line intersects the interior of the specified rectangle. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the line intersects the rectangle + */ + public boolean intersects(double x, double y, double w, double h) + { + if (w <= 0 || h <= 0) + return false; + double x1 = getX1(); + double y1 = getY1(); + double x2 = getX2(); + double y2 = getY2(); + + if (x1 >= x && x1 <= x + w && y1 >= y && y1 <= y + h) + return true; + if (x2 >= x && x2 <= x + w && y2 >= y && y2 <= y + h) + return true; + + double x3 = x + w; + double y3 = y + h; + + return (linesIntersect(x1, y1, x2, y2, x, y, x, y3) + || linesIntersect(x1, y1, x2, y2, x, y3, x3, y3) + || linesIntersect(x1, y1, x2, y2, x3, y3, x3, y) + || linesIntersect(x1, y1, x2, y2, x3, y, x, y)); + } + + /** + * Tests if this line intersects the interior of the specified rectangle. + * + * @param r the rectangle + * @return true if the line intersects the rectangle + * @throws NullPointerException if r is null + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Tests if the line contains a rectangle. Since lines have no area, this + * always returns false. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return false; the line does not contain points + */ + public boolean contains(double x, double y, double w, double h) + { + return false; + } + + /** + * Tests if the line contains a rectangle. Since lines have no area, this + * always returns false. + * + * @param r the rectangle + * @return false; the line does not contain points + */ + public boolean contains(Rectangle2D r) + { + return false; + } + + /** + * Gets a bounding box (not necessarily minimal) for this line. + * + * @return the integer bounding box + * @see #getBounds2D() + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + /** + * Return a path iterator, possibly applying a transform on the result. This + * iterator is not threadsafe. + * + * @param at the transform, or null + * @return a new path iterator + */ + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + /** Current coordinate. */ + private int current = 0; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current >= 2; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = (float) getX2(); + coords[1] = (float) getY2(); + result = SEG_LINETO; + break; + default: + throw new NoSuchElementException("line iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return result; + } + + public int currentSegment(double[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = getX1(); + coords[1] = getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = getX2(); + coords[1] = getY2(); + result = SEG_LINETO; + break; + default: + throw new NoSuchElementException("line iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return result; + } + }; + } + + /** + * Return a flat path iterator, possibly applying a transform on the result. + * This iterator is not threadsafe. + * + * @param at the transform, or null + * @param flatness ignored, since lines are already flat + * @return a new path iterator + * @see #getPathIterator(AffineTransform) + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return getPathIterator(at); + } + + /** + * Create a new line of the same run-time type with the same contents as + * this one. + * + * @return the clone + * + * @exception OutOfMemoryError If there is not enough memory available. + * + * @since 1.2 + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * This class defines a point in double precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Double extends Line2D + { + /** The x coordinate of the first point. */ + public double x1; + + /** The y coordinate of the first point. */ + public double y1; + + /** The x coordinate of the second point. */ + public double x2; + + /** The y coordinate of the second point. */ + public double y2; + + /** + * Construct the line segment (0,0)->(0,0). + */ + public Double() + { + } + + /** + * Construct the line segment with the specified points. + * + * @param x1 the x coordinate of the first point + * @param y1 the y coordinate of the first point + * @param x2 the x coordinate of the second point + * @param y2 the y coordinate of the second point + */ + public Double(double x1, double y1, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Construct the line segment with the specified points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public Double(Point2D p1, Point2D p2) + { + x1 = p1.getX(); + y1 = p1.getY(); + x2 = p2.getX(); + y2 = p2.getY(); + } + + /** + * Return the x coordinate of the first point. + * + * @return the value of x1 + */ + public double getX1() + { + return x1; + } + + /** + * Return the y coordinate of the first point. + * + * @return the value of y1 + */ + public double getY1() + { + return y1; + } + + /** + * Return the first point. + * + * @return the point (x1,y1) + */ + public Point2D getP1() + { + return new Point2D.Double(x1, y1); + } + + /** + * Return the x coordinate of the second point. + * + * @return the value of x2 + */ + public double getX2() + { + return x2; + } + + /** + * Return the y coordinate of the second point. + * + * @return the value of y2 + */ + public double getY2() + { + return y2; + } + + /** + * Return the second point. + * + * @return the point (x2,y2) + */ + public Point2D getP2() + { + return new Point2D.Double(x2, y2); + } + + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(double x1, double y1, double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Return the exact bounds of this line segment. + * + * @return the bounding box + */ + public Rectangle2D getBounds2D() + { + double x = Math.min(x1, x2); + double y = Math.min(y1, y2); + double w = Math.abs(x1 - x2); + double h = Math.abs(y1 - y2); + return new Rectangle2D.Double(x, y, w, h); + } + } // class Double + + /** + * This class defines a point in float precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Float extends Line2D + { + /** The x coordinate of the first point. */ + public float x1; + + /** The y coordinate of the first point. */ + public float y1; + + /** The x coordinate of the second point. */ + public float x2; + + /** The y coordinate of the second point. */ + public float y2; + + /** + * Construct the line segment (0,0)->(0,0). + */ + public Float() + { + } + + /** + * Construct the line segment with the specified points. + * + * @param x1 the x coordinate of the first point + * @param y1 the y coordinate of the first point + * @param x2 the x coordinate of the second point + * @param y2 the y coordinate of the second point + */ + public Float(float x1, float y1, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Construct the line segment with the specified points. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public Float(Point2D p1, Point2D p2) + { + x1 = (float) p1.getX(); + y1 = (float) p1.getY(); + x2 = (float) p2.getX(); + y2 = (float) p2.getY(); + } + + /** + * Return the x coordinate of the first point. + * + * @return the value of x1 + */ + public double getX1() + { + return x1; + } + + /** + * Return the y coordinate of the first point. + * + * @return the value of y1 + */ + public double getY1() + { + return y1; + } + + /** + * Return the first point. + * + * @return the point (x1,y1) + */ + public Point2D getP1() + { + return new Point2D.Float(x1, y1); + } + + /** + * Return the x coordinate of the second point. + * + * @return the value of x2 + */ + public double getX2() + { + return x2; + } + + /** + * Return the y coordinate of the second point. + * + * @return the value of y2 + */ + public double getY2() + { + return y2; + } + + /** + * Return the second point. + * + * @return the point (x2,y2) + */ + public Point2D getP2() + { + return new Point2D.Float(x2, y2); + } + + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(double x1, double y1, double x2, double y2) + { + this.x1 = (float) x1; + this.y1 = (float) y1; + this.x2 = (float) x2; + this.y2 = (float) y2; + } + + /** + * Set this line to the given points. + * + * @param x1 the new x coordinate of the first point + * @param y1 the new y coordinate of the first point + * @param x2 the new x coordinate of the second point + * @param y2 the new y coordinate of the second point + */ + public void setLine(float x1, float y1, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Return the exact bounds of this line segment. + * + * @return the bounding box + */ + public Rectangle2D getBounds2D() + { + float x = Math.min(x1, x2); + float y = Math.min(y1, y2); + float w = Math.abs(x1 - x2); + float h = Math.abs(y1 - y2); + return new Rectangle2D.Float(x, y, w, h); + } + } // class Float +} // class Line2D diff --git a/libjava/classpath/java/awt/geom/NoninvertibleTransformException.java b/libjava/classpath/java/awt/geom/NoninvertibleTransformException.java new file mode 100644 index 000000000..7995a52eb --- /dev/null +++ b/libjava/classpath/java/awt/geom/NoninvertibleTransformException.java @@ -0,0 +1,65 @@ +/* NoninvertibleTransformException.java -- a transform can't be inverted + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +/** + * Thrown if an operation requires an inverse of an + * AffineTransform, but the transform is in a non-invertible + * state. + * + * @author Tom Tromey (tromey@cygnus.com) + * @see AffineTransform + * @status updated to 1.4 + */ +public class NoninvertibleTransformException extends Exception +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 6137225240503990466L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoninvertibleTransformException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/awt/geom/PathIterator.java b/libjava/classpath/java/awt/geom/PathIterator.java new file mode 100644 index 000000000..2cd08b9b4 --- /dev/null +++ b/libjava/classpath/java/awt/geom/PathIterator.java @@ -0,0 +1,189 @@ +/* PathIterator.java -- describes a shape by iterating over its vertices + Copyright (C) 2000, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +/** + * This interface provides a directed path over the boundary of a shape. The + * path can contain 1st through 3rd order Bezier curves (lines, and quadratic + * and cubic splines). A shape can have multiple disjoint paths via the + * MOVETO directive, and can close a circular path back to the previos + * MOVETO via the CLOSE directive. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see java.awt.Shape + * @see java.awt.Stroke + * @see FlatteningPathIterator + * @since 1.2 + * @status updated to 1.4 + */ +public interface PathIterator +{ + /** + * The even-odd winding mode: a point is internal to the shape if a ray + * from the point to infinity (in any direction) crosses an odd number of + * segments. + */ + int WIND_EVEN_ODD = 0; + + /** + * The non-zero winding mode: a point is internal to the shape if a ray + * from the point to infinity (in any direction) crosses a different number + * of segments headed clockwise than those headed counterclockwise. + */ + int WIND_NON_ZERO = 1; + + /** + * Starts a new subpath. There is no segment from the previous vertex. + */ + int SEG_MOVETO = 0; + + /** + * The current segment is a line. + */ + int SEG_LINETO = 1; + + /** + * The current segment is a quadratic parametric curve. It is interpolated + * as t varies from 0 to 1 over the current point (CP), first control point + * (P1), and final interpolated control point (P2): + *

    +   *  P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2
    +   *    0 <= t <= 1
    +   *  B(n,m) = mth coefficient of nth degree Bernstein polynomial
    +   *         = C(n,m) * t^(m) * (1 - t)^(n-m)
    +   *  C(n,m) = Combinations of n things, taken m at a time
    +   *         = n! / (m! * (n-m)!)
    +   * 
    + */ + int SEG_QUADTO = 2; + + /** + * The current segment is a cubic parametric curve (more commonly known as + * a Bezier curve). It is interpolated as t varies from 0 to 1 over the + * current point (CP), first control point (P1), the second control point + * (P2), and final interpolated control point (P3): + *
    +   *  P(t) = B(3,0)*CP + B(3,1)*P1 + B(3,2)*P2 + B(3,3)*P3
    +   *    0 <= t <= 1
    +   *  B(n,m) = mth coefficient of nth degree Bernstein polynomial
    +   *         = C(n,m) * t^(m) * (1 - t)^(n-m)
    +   *  C(n,m) = Combinations of n things, taken m at a time
    +   *         = n! / (m! * (n-m)!)
    +   * 
    + */ + int SEG_CUBICTO = 3; + + /** + * The current segment closes a loop by an implicit line to the previous + * SEG_MOVETO coordinate. + */ + int SEG_CLOSE = 4; + + /** + * Returns the winding rule to determine which points are inside this path. + * + * @return the winding rule + * @see #WIND_EVEN_ODD + * @see #WIND_NON_ZERO + */ + int getWindingRule(); + + /** + * Tests if the iterator is exhausted. If this returns true, currentSegment + * and next may throw a NoSuchElementException (although this is not + * required). + * + * @return true if the iteration is complete + */ + boolean isDone(); + + /** + * Advance to the next segment in the iteration. It is not specified what + * this does if called when isDone() returns true. + * + * @throws java.util.NoSuchElementException optional when isDone() is true + */ + void next(); + + /** + * Returns the coordinates of the next point(s), as well as the type of + * line segment. The input array must be at least a float[6], to accomodate + * up to three (x,y) point pairs (although if you know the iterator is + * flat, you can probably get by with a float[2]). If the returned type is + * SEG_MOVETO or SEG_LINETO, the first point in the array is modified; if + * the returned type is SEG_QUADTO, the first two points are modified; if + * the returned type is SEG_CUBICTO, all three points are modified; and if + * the returned type is SEG_CLOSE, the array is untouched. + * + * @param coords the array to place the point coordinates in + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + * @throws java.util.NoSuchElementException optional when isDone() is true + * @see #SEG_MOVETO + * @see #SEG_LINETO + * @see #SEG_QUADTO + * @see #SEG_CUBICTO + * @see #SEG_CLOSE + */ + int currentSegment(float[] coords); + + /** + * Returns the coordinates of the next point(s), as well as the type of + * line segment. The input array must be at least a double[6], to accomodate + * up to three (x,y) point pairs (although if you know the iterator is + * flat, you can probably get by with a double[2]). If the returned type is + * SEG_MOVETO or SEG_LINETO, the first point in the array is modified; if + * the returned type is SEG_QUADTO, the first two points are modified; if + * the returned type is SEG_CUBICTO, all three points are modified; and if + * the returned type is SEG_CLOSE, the array is untouched. + * + * @param coords the array to place the point coordinates in + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + * @throws java.util.NoSuchElementException optional when isDone() is true + * @see #SEG_MOVETO + * @see #SEG_LINETO + * @see #SEG_QUADTO + * @see #SEG_CUBICTO + * @see #SEG_CLOSE + */ + int currentSegment(double[] coords); +} // interface PathIterator diff --git a/libjava/classpath/java/awt/geom/Point2D.java b/libjava/classpath/java/awt/geom/Point2D.java new file mode 100644 index 000000000..a2689abf8 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Point2D.java @@ -0,0 +1,396 @@ +/* Point2D.java -- generic point in 2-D space + Copyright (C) 1999, 2000, 2002, 2004, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +/** + * This class implements a generic point in 2D Cartesian space. The storage + * representation is left up to the subclass. Point includes two useful + * nested classes, for float and double storage respectively. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class Point2D implements Cloneable +{ + /** + * The default constructor. + * + * @see java.awt.Point + * @see Point2D.Float + * @see Point2D.Double + */ + protected Point2D() + { + } + + /** + * Get the X coordinate, in double precision. + * + * @return the x coordinate + */ + public abstract double getX(); + + /** + * Get the Y coordinate, in double precision. + * + * @return the y coordinate + */ + public abstract double getY(); + + /** + * Set the location of this point to the new coordinates. There may be a + * loss of precision. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public abstract void setLocation(double x, double y); + + /** + * Set the location of this point to the new coordinates. There may be a + * loss of precision. + * + * @param p the point to copy + * @throws NullPointerException if p is null + */ + public void setLocation(Point2D p) + { + setLocation(p.getX(), p.getY()); + } + + /** + * Return the square of the distance between two points. + * + * @param x1 the x coordinate of point 1 + * @param y1 the y coordinate of point 1 + * @param x2 the x coordinate of point 2 + * @param y2 the y coordinate of point 2 + * @return (x2 - x1)^2 + (y2 - y1)^2 + */ + public static double distanceSq(double x1, double y1, double x2, double y2) + { + x2 -= x1; + y2 -= y1; + return x2 * x2 + y2 * y2; + } + + /** + * Return the distance between two points. + * + * @param x1 the x coordinate of point 1 + * @param y1 the y coordinate of point 1 + * @param x2 the x coordinate of point 2 + * @param y2 the y coordinate of point 2 + * @return the distance from (x1,y1) to (x2,y2) + */ + public static double distance(double x1, double y1, double x2, double y2) + { + return Math.sqrt(distanceSq(x1, y1, x2, y2)); + } + + /** + * Return the square of the distance from this point to the given one. + * + * @param x the x coordinate of the other point + * @param y the y coordinate of the other point + * @return the square of the distance + */ + public double distanceSq(double x, double y) + { + return distanceSq(getX(), getY(), x, y); + } + + /** + * Return the square of the distance from this point to the given one. + * + * @param p the other point + * @return the square of the distance + * @throws NullPointerException if p is null + */ + public double distanceSq(Point2D p) + { + return distanceSq(getX(), getY(), p.getX(), p.getY()); + } + + /** + * Return the distance from this point to the given one. + * + * @param x the x coordinate of the other point + * @param y the y coordinate of the other point + * @return the distance + */ + public double distance(double x, double y) + { + return distance(getX(), getY(), x, y); + } + + /** + * Return the distance from this point to the given one. + * + * @param p the other point + * @return the distance + * @throws NullPointerException if p is null + */ + public double distance(Point2D p) + { + return distance(getX(), getY(), p.getX(), p.getY()); + } + + /** + * Create a new point of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Return the hashcode for this point. The formula is not documented, but + * appears to be the same as: + *
    +   * long l = Double.doubleToLongBits(getY());
    +   * l = l * 31 ^ Double.doubleToLongBits(getX());
    +   * return (int) ((l >> 32) ^ l);
    +   * 
    + * + * @return the hashcode + */ + public int hashCode() + { + // Talk about a fun time reverse engineering this one! + long l = java.lang.Double.doubleToLongBits(getY()); + l = l * 31 ^ java.lang.Double.doubleToLongBits(getX()); + return (int) ((l >> 32) ^ l); + } + + /** + * Compares two points for equality. This returns true if they have the + * same coordinates. + * + * @param o the point to compare + * @return true if it is equal + */ + public boolean equals(Object o) + { + if (! (o instanceof Point2D)) + return false; + Point2D p = (Point2D) o; + return getX() == p.getX() && getY() == p.getY(); + } + + /** + * This class defines a point in double precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Double extends Point2D + { + /** The X coordinate. */ + public double x; + + /** The Y coordinate. */ + public double y; + + /** + * Create a new point at (0,0). + */ + public Double() + { + } + + /** + * Create a new point at (x,y). + * + * @param x the x coordinate + * @param y the y coordinate + */ + public Double(double x, double y) + { + this.x = x; + this.y = y; + } + + /** + * Return the x coordinate. + * + * @return the x coordinate + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate. + * + * @return the y coordinate + */ + public double getY() + { + return y; + } + + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(double x, double y) + { + this.x = x; + this.y = y; + } + + /** + * Returns a string representation of this object. The format is: + * "Point2D.Double[" + x + ", " + y + ']'. + * + * @return a string representation of this object + */ + public String toString() + { + return "Point2D.Double[" + x + ", " + y + ']'; + } + } // class Double + + /** + * This class defines a point in float precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Float extends Point2D + { + /** The X coordinate. */ + public float x; + + /** The Y coordinate. */ + public float y; + + /** + * Create a new point at (0,0). + */ + public Float() + { + } + + /** + * Create a new point at (x,y). + * + * @param x the x coordinate + * @param y the y coordinate + */ + public Float(float x, float y) + { + this.x = x; + this.y = y; + } + + /** + * Return the x coordinate. + * + * @return the x coordinate + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate. + * + * @return the y coordinate + */ + public double getY() + { + return y; + } + + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(double x, double y) + { + this.x = (float) x; + this.y = (float) y; + } + + /** + * Sets the location of this point. + * + * @param x the new x coordinate + * @param y the new y coordinate + */ + public void setLocation(float x, float y) + { + this.x = x; + this.y = y; + } + + /** + * Returns a string representation of this object. The format is: + * "Point2D.Float[" + x + ", " + y + ']'. + * + * @return a string representation of this object + */ + public String toString() + { + return "Point2D.Float[" + x + ", " + y + ']'; + } + } // class Float +} // class Point2D diff --git a/libjava/classpath/java/awt/geom/QuadCurve2D.java b/libjava/classpath/java/awt/geom/QuadCurve2D.java new file mode 100644 index 000000000..62c829d30 --- /dev/null +++ b/libjava/classpath/java/awt/geom/QuadCurve2D.java @@ -0,0 +1,1467 @@ +/* QuadCurve2D.java -- represents a parameterized quadratic curve in 2-D space + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.NoSuchElementException; + +/** + * A two-dimensional curve that is parameterized with a quadratic + * function. + * + *

    A drawing of a QuadCurve2D + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Graydon Hoare (graydon@redhat.com) + * @author Sascha Brawer (brawer@dandelis.ch) + * @author Sven de Marothy (sven@physto.se) + * + * @since 1.2 + */ +public abstract class QuadCurve2D implements Shape, Cloneable +{ + private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0; + private static final double EPSILON = 1E-10; + + /** + * Constructs a new QuadCurve2D. Typical users will want to + * construct instances of a subclass, such as {@link + * QuadCurve2D.Float} or {@link QuadCurve2D.Double}. + */ + protected QuadCurve2D() + { + } + + /** + * Returns the x coordinate of the curve’s start + * point. + */ + public abstract double getX1(); + + /** + * Returns the y coordinate of the curve’s start + * point. + */ + public abstract double getY1(); + + /** + * Returns the curve’s start point. + */ + public abstract Point2D getP1(); + + /** + * Returns the x coordinate of the curve’s control + * point. + */ + public abstract double getCtrlX(); + + /** + * Returns the y coordinate of the curve’s control + * point. + */ + public abstract double getCtrlY(); + + /** + * Returns the curve’s control point. + */ + public abstract Point2D getCtrlPt(); + + /** + * Returns the x coordinate of the curve’s end + * point. + */ + public abstract double getX2(); + + /** + * Returns the y coordinate of the curve’s end + * point. + */ + public abstract double getY2(); + + /** + * Returns the curve’s end point. + */ + public abstract Point2D getP2(); + + /** + * Changes the curve geometry, separately specifying each coordinate + * value. + * + * @param x1 the x coordinate of the curve’s new start + * point. + * + * @param y1 the y coordinate of the curve’s new start + * point. + * + * @param cx the x coordinate of the curve’s new + * control point. + * + * @param cy the y coordinate of the curve’s new + * control point. + * + * @param x2 the x coordinate of the curve’s new end + * point. + * + * @param y2 the y coordinate of the curve’s new end + * point. + */ + public abstract void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2); + + /** + * Changes the curve geometry, passing coordinate values in an + * array. + * + * @param coords an array containing the new coordinate values. The + * x coordinate of the new start point is located at + * coords[offset], its y coordinate at + * coords[offset + 1]. The x coordinate of the + * new control point is located at coords[offset + 2], + * its y coordinate at coords[offset + 3]. The + * x coordinate of the new end point is located at + * coords[offset + 4], its y coordinate at + * coords[offset + 5]. + * + * @param offset the offset of the first coordinate value in + * coords. + */ + public void setCurve(double[] coords, int offset) + { + setCurve(coords[offset++], coords[offset++], coords[offset++], + coords[offset++], coords[offset++], coords[offset++]); + } + + /** + * Changes the curve geometry, specifying coordinate values in + * separate Point objects. + * + *

    A drawing of a QuadCurve2D + * + *

    The curve does not keep any reference to the passed point + * objects. Therefore, a later change to p1, + * c p2 will not affect the curve + * geometry. + * + * @param p1 the new start point. + * @param c the new control point. + * @param p2 the new end point. + */ + public void setCurve(Point2D p1, Point2D c, Point2D p2) + { + setCurve(p1.getX(), p1.getY(), c.getX(), c.getY(), p2.getX(), p2.getY()); + } + + /** + * Changes the curve geometry, specifying coordinate values in an + * array of Point objects. + * + *

    A drawing of a QuadCurve2D + * + *

    The curve does not keep references to the passed point + * objects. Therefore, a later change to the pts array + * or any of its elements will not affect the curve geometry. + * + * @param pts an array containing the points. The new start point + * is located at pts[offset], the new control + * point at pts[offset + 1], and the new end point + * at pts[offset + 2]. + * + * @param offset the offset of the start point in pts. + */ + public void setCurve(Point2D[] pts, int offset) + { + setCurve(pts[offset].getX(), pts[offset].getY(), pts[offset + 1].getX(), + pts[offset + 1].getY(), pts[offset + 2].getX(), + pts[offset + 2].getY()); + } + + /** + * Changes the geometry of the curve to that of another curve. + * + * @param c the curve whose coordinates will be copied. + */ + public void setCurve(QuadCurve2D c) + { + setCurve(c.getX1(), c.getY1(), c.getCtrlX(), c.getCtrlY(), c.getX2(), + c.getY2()); + } + + /** + * Calculates the squared flatness of a quadratic curve, directly + * specifying each coordinate value. The flatness is the distance of + * the control point to the line between start and end point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the square of the distance between C and the gray line, i.e. + * the squared length of the red line. + * + * @param x1 the x coordinate of the start point P1. + * @param y1 the y coordinate of the start point P1. + * @param cx the x coordinate of the control point C. + * @param cy the y coordinate of the control point C. + * @param x2 the x coordinate of the end point P2. + * @param y2 the y coordinate of the end point P2. + */ + public static double getFlatnessSq(double x1, double y1, double cx, + double cy, double x2, double y2) + { + return Line2D.ptSegDistSq(x1, y1, x2, y2, cx, cy); + } + + /** + * Calculates the flatness of a quadratic curve, directly specifying + * each coordinate value. The flatness is the distance of the + * control point to the line between start and end point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the distance between C and the gray line, i.e. the length of + * the red line. + * + * @param x1 the x coordinate of the start point P1. + * @param y1 the y coordinate of the start point P1. + * @param cx the x coordinate of the control point C. + * @param cy the y coordinate of the control point C. + * @param x2 the x coordinate of the end point P2. + * @param y2 the y coordinate of the end point P2. + */ + public static double getFlatness(double x1, double y1, double cx, double cy, + double x2, double y2) + { + return Line2D.ptSegDist(x1, y1, x2, y2, cx, cy); + } + + /** + * Calculates the squared flatness of a quadratic curve, specifying + * the coordinate values in an array. The flatness is the distance + * of the control point to the line between start and end point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the square of the distance between C and the gray line, i.e. + * the squared length of the red line. + * + * @param coords an array containing the coordinate values. The + * x coordinate of the start point P1 is located at + * coords[offset], its y coordinate at + * coords[offset + 1]. The x coordinate of the + * control point C is located at coords[offset + 2], + * its y coordinate at coords[offset + 3]. The + * x coordinate of the end point P2 is located at + * coords[offset + 4], its y coordinate at + * coords[offset + 5]. + * + * @param offset the offset of the first coordinate value in + * coords. + */ + public static double getFlatnessSq(double[] coords, int offset) + { + return Line2D.ptSegDistSq(coords[offset], coords[offset + 1], + coords[offset + 4], coords[offset + 5], + coords[offset + 2], coords[offset + 3]); + } + + /** + * Calculates the flatness of a quadratic curve, specifying the + * coordinate values in an array. The flatness is the distance of + * the control point to the line between start and end point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the the distance between C and the gray line, i.e. the length of + * the red line. + * + * @param coords an array containing the coordinate values. The + * x coordinate of the start point P1 is located at + * coords[offset], its y coordinate at + * coords[offset + 1]. The x coordinate of the + * control point C is located at coords[offset + 2], + * its y coordinate at coords[offset + 3]. The + * x coordinate of the end point P2 is located at + * coords[offset + 4], its y coordinate at + * coords[offset + 5]. + * + * @param offset the offset of the first coordinate value in + * coords. + */ + public static double getFlatness(double[] coords, int offset) + { + return Line2D.ptSegDist(coords[offset], coords[offset + 1], + coords[offset + 4], coords[offset + 5], + coords[offset + 2], coords[offset + 3]); + } + + /** + * Calculates the squared flatness of this curve. The flatness is + * the distance of the control point to the line between start and + * end point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the square of the distance between C and the gray line, i.e. the + * squared length of the red line. + */ + public double getFlatnessSq() + { + return Line2D.ptSegDistSq(getX1(), getY1(), getX2(), getY2(), getCtrlX(), + getCtrlY()); + } + + /** + * Calculates the flatness of this curve. The flatness is the + * distance of the control point to the line between start and end + * point. + * + *

    A drawing that illustrates the flatness + * + *

    In the above drawing, the straight line connecting start point + * P1 and end point P2 is depicted in gray. The result will be the + * the distance between C and the gray line, i.e. the length of the + * red line. + */ + public double getFlatness() + { + return Line2D.ptSegDist(getX1(), getY1(), getX2(), getY2(), getCtrlX(), + getCtrlY()); + } + + /** + * Subdivides this curve into two halves. + * + *

    A drawing that illustrates the effects of
+   * subdividing a QuadCurve2D + * + * @param left a curve whose geometry will be set to the left half + * of this curve, or null if the caller is not + * interested in the left half. + * + * @param right a curve whose geometry will be set to the right half + * of this curve, or null if the caller is not + * interested in the right half. + */ + public void subdivide(QuadCurve2D left, QuadCurve2D right) + { + // Use empty slots at end to share single array. + double[] d = new double[] + { + getX1(), getY1(), getCtrlX(), getCtrlY(), getX2(), getY2(), + 0, 0, 0, 0 + }; + subdivide(d, 0, d, 0, d, 4); + if (left != null) + left.setCurve(d, 0); + if (right != null) + right.setCurve(d, 4); + } + + /** + * Subdivides a quadratic curve into two halves. + * + *

    A drawing that illustrates the effects of
+   * subdividing a QuadCurve2D + * + * @param src the curve to be subdivided. + * + * @param left a curve whose geometry will be set to the left half + * of src, or null if the caller is not + * interested in the left half. + * + * @param right a curve whose geometry will be set to the right half + * of src, or null if the caller is not + * interested in the right half. + */ + public static void subdivide(QuadCurve2D src, QuadCurve2D left, + QuadCurve2D right) + { + src.subdivide(left, right); + } + + /** + * Subdivides a quadratic curve into two halves, passing all + * coordinates in an array. + * + *

    A drawing that illustrates the effects of
+   * subdividing a QuadCurve2D + * + *

    The left end point and the right start point will always be + * identical. Memory-concious programmers thus may want to pass the + * same array for both left and right, and + * set rightOff to leftOff + 4. + * + * @param src an array containing the coordinates of the curve to be + * subdivided. The x coordinate of the start point is + * located at src[srcOff], its y at + * src[srcOff + 1]. The x coordinate of the + * control point is located at src[srcOff + 2], its + * y at src[srcOff + 3]. The x + * coordinate of the end point is located at src[srcOff + + * 4], its y at src[srcOff + 5]. + * + * @param srcOff an offset into src, specifying + * the index of the start point’s x coordinate. + * + * @param left an array that will receive the coordinates of the + * left half of src. It is acceptable to pass + * src. A caller who is not interested in the left half + * can pass null. + * + * @param leftOff an offset into left, specifying the + * index where the start point’s x coordinate will be + * stored. + * + * @param right an array that will receive the coordinates of the + * right half of src. It is acceptable to pass + * src or left. A caller who is not + * interested in the right half can pass null. + * + * @param rightOff an offset into right, specifying the + * index where the start point’s x coordinate will be + * stored. + */ + public static void subdivide(double[] src, int srcOff, double[] left, + int leftOff, double[] right, int rightOff) + { + double x1; + double y1; + double xc; + double yc; + double x2; + double y2; + + x1 = src[srcOff]; + y1 = src[srcOff + 1]; + xc = src[srcOff + 2]; + yc = src[srcOff + 3]; + x2 = src[srcOff + 4]; + y2 = src[srcOff + 5]; + + if (left != null) + { + left[leftOff] = x1; + left[leftOff + 1] = y1; + } + + if (right != null) + { + right[rightOff + 4] = x2; + right[rightOff + 5] = y2; + } + + x1 = (x1 + xc) / 2; + x2 = (xc + x2) / 2; + xc = (x1 + x2) / 2; + y1 = (y1 + yc) / 2; + y2 = (y2 + yc) / 2; + yc = (y1 + y2) / 2; + + if (left != null) + { + left[leftOff + 2] = x1; + left[leftOff + 3] = y1; + left[leftOff + 4] = xc; + left[leftOff + 5] = yc; + } + + if (right != null) + { + right[rightOff] = xc; + right[rightOff + 1] = yc; + right[rightOff + 2] = x2; + right[rightOff + 3] = y2; + } + } + + /** + * Finds the non-complex roots of a quadratic equation, placing the + * results into the same array as the equation coefficients. The + * following equation is being solved: + * + *

    eqn[2] · x2 + * + eqn[1] · x + * + eqn[0] + * = 0 + *
    + * + *

    For some background about solving quadratic equations, see the + * article “Quadratic Formula” in PlanetMath. For an extensive library + * of numerical algorithms written in the C programming language, + * see the GNU Scientific + * Library. + * + * @see #solveQuadratic(double[], double[]) + * @see CubicCurve2D#solveCubic(double[], double[]) + * + * @param eqn an array with the coefficients of the equation. When + * this procedure has returned, eqn will contain the + * non-complex solutions of the equation, in no particular order. + * + * @return the number of non-complex solutions. A result of 0 + * indicates that the equation has no non-complex solutions. A + * result of -1 indicates that the equation is constant (i.e., + * always or never zero). + * + * @author Brian Gough (bjg@network-theory.com) + * (original C implementation in the GNU Scientific Library) + * + * @author Sascha Brawer (brawer@dandelis.ch) + * (adaptation to Java) + */ + public static int solveQuadratic(double[] eqn) + { + return solveQuadratic(eqn, eqn); + } + + /** + * Finds the non-complex roots of a quadratic equation. The + * following equation is being solved: + * + *

    eqn[2] · x2 + * + eqn[1] · x + * + eqn[0] + * = 0 + *
    + * + *

    For some background about solving quadratic equations, see the + * article “Quadratic Formula” in PlanetMath. For an extensive library + * of numerical algorithms written in the C programming language, + * see the GNU Scientific + * Library. + * + * @see CubicCurve2D#solveCubic(double[],double[]) + * + * @param eqn an array with the coefficients of the equation. + * + * @param res an array into which the non-complex roots will be + * stored. The results may be in an arbitrary order. It is safe to + * pass the same array object reference for both eqn + * and res. + * + * @return the number of non-complex solutions. A result of 0 + * indicates that the equation has no non-complex solutions. A + * result of -1 indicates that the equation is constant (i.e., + * always or never zero). + * + * @author Brian Gough (bjg@network-theory.com) + * (original C implementation in the GNU Scientific Library) + * + * @author Sascha Brawer (brawer@dandelis.ch) + * (adaptation to Java) + */ + public static int solveQuadratic(double[] eqn, double[] res) + { + // Taken from poly/solve_quadratic.c in the GNU Scientific Library + // (GSL), cvs revision 1.7 of 2003-07-26. For the original source, + // see http://www.gnu.org/software/gsl/ + // + // Brian Gough, the author of that code, has granted the + // permission to use it in GNU Classpath under the GNU Classpath + // license, and has assigned the copyright to the Free Software + // Foundation. + // + // The Java implementation is very similar to the GSL code, but + // not a strict one-to-one copy. For example, GSL would sort the + // result. + double a; + double b; + double c; + double disc; + + c = eqn[0]; + b = eqn[1]; + a = eqn[2]; + + // Check for linear or constant functions. This is not done by the + // GNU Scientific Library. Without this special check, we + // wouldn't return -1 for constant functions, and 2 instead of 1 + // for linear functions. + if (a == 0) + { + if (b == 0) + return -1; + + res[0] = -c / b; + return 1; + } + + disc = b * b - 4 * a * c; + + if (disc < 0) + return 0; + + if (disc == 0) + { + // The GNU Scientific Library returns two identical results here. + // We just return one. + res[0] = -0.5 * b / a; + return 1; + } + + // disc > 0 + if (b == 0) + { + double r; + + r = Math.abs(0.5 * Math.sqrt(disc) / a); + res[0] = -r; + res[1] = r; + } + else + { + double sgnb; + double temp; + + sgnb = (b > 0 ? 1 : -1); + temp = -0.5 * (b + sgnb * Math.sqrt(disc)); + + // The GNU Scientific Library sorts the result here. We don't. + res[0] = temp / a; + res[1] = c / temp; + } + return 2; + } + + /** + * Determines whether a point is inside the area bounded + * by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” a QuadCurve2D. + */ + public boolean contains(double x, double y) + { + if (! getBounds2D().contains(x, y)) + return false; + + return ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0); + } + + /** + * Determines whether a point is inside the area bounded + * by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” a QuadCurve2D. + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Determines whether any part of a rectangle is inside the area bounded + * by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” in a CubicCurve2D. + */ + public boolean intersects(double x, double y, double w, double h) + { + if (! getBounds2D().contains(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, true, w) != 0 /* top */ + || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, false, h) != 0 /* right */ + || getAxisIntersections(x, y, false, h) != 0) /* left */ + return true; + + /* No intersections, is any point inside? */ + if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Determines whether any part of a Rectangle2D is inside the area bounded + * by the curve and the straight line connecting its end points. + * @see #intersects(double, double, double, double) + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determines whether a rectangle is entirely inside the area bounded + * by the curve and the straight line connecting its end points. + * + *

    A drawing of the area spanned by the curve + * + *

    The above drawing illustrates in which area points are + * considered “inside” a QuadCurve2D. + * @see #contains(double, double) + */ + public boolean contains(double x, double y, double w, double h) + { + if (! getBounds2D().intersects(x, y, w, h)) + return false; + + /* Does any edge intersect? */ + if (getAxisIntersections(x, y, true, w) != 0 /* top */ + || getAxisIntersections(x, y + h, true, w) != 0 /* bottom */ + || getAxisIntersections(x + w, y, false, h) != 0 /* right */ + || getAxisIntersections(x, y, false, h) != 0) /* left */ + return false; + + /* No intersections, is any point inside? */ + if ((getAxisIntersections(x, y, true, BIG_VALUE) & 1) != 0) + return true; + + return false; + } + + /** + * Determines whether a Rectangle2D is entirely inside the area that is + * bounded by the curve and the straight line connecting its end points. + * @see #contains(double, double, double, double) + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control point. As the illustration + * below shows, the invisible control point may cause the bounds to + * be much larger than the area that is actually covered by the + * curve. + * + *

    An illustration of the bounds of a QuadCurve2D + */ + public Rectangle getBounds() + { + return getBounds2D().getBounds(); + } + + public PathIterator getPathIterator(final AffineTransform at) + { + return new PathIterator() + { + /** Current coordinate. */ + private int current = 0; + + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current >= 2; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = (float) getX1(); + coords[1] = (float) getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = (float) getCtrlX(); + coords[1] = (float) getCtrlY(); + coords[2] = (float) getX2(); + coords[3] = (float) getY2(); + result = SEG_QUADTO; + break; + default: + throw new NoSuchElementException("quad iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 2); + return result; + } + + public int currentSegment(double[] coords) + { + int result; + switch (current) + { + case 0: + coords[0] = getX1(); + coords[1] = getY1(); + result = SEG_MOVETO; + break; + case 1: + coords[0] = getCtrlX(); + coords[1] = getCtrlY(); + coords[2] = getX2(); + coords[3] = getY2(); + result = SEG_QUADTO; + break; + default: + throw new NoSuchElementException("quad iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 2); + return result; + } + }; + } + + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Creates a new curve with the same contents as this one. + * + * @return the clone. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } + + /** + * Helper method used by contains() and intersects() methods + * Return the number of curve/line intersections on a given axis + * extending from a certain point. useYaxis is true for using the Y axis, + * @param x x coordinate of the origin point + * @param y y coordinate of the origin point + * @param useYaxis axis to follow, if true the positive Y axis is used, + * false uses the positive X axis. + * + * This is an implementation of the line-crossings algorithm, + * Detailed in an article on Eric Haines' page: + * http://www.acm.org/tog/editors/erich/ptinpoly/ + */ + private int getAxisIntersections(double x, double y, boolean useYaxis, + double distance) + { + int nCrossings = 0; + double a0; + double a1; + double a2; + double b0; + double b1; + double b2; + double[] r = new double[3]; + int nRoots; + + a0 = a2 = 0.0; + + if (useYaxis) + { + a0 = getY1() - y; + a1 = getCtrlY() - y; + a2 = getY2() - y; + b0 = getX1() - x; + b1 = getCtrlX() - x; + b2 = getX2() - x; + } + else + { + a0 = getX1() - x; + a1 = getCtrlX() - x; + a2 = getX2() - x; + b0 = getY1() - y; + b1 = getCtrlY() - y; + b2 = getY2() - y; + } + + /* If the axis intersects a start/endpoint, shift it up by some small + amount to guarantee the line is 'inside' + If this is not done,bad behaviour may result for points on that axis. */ + if (a0 == 0.0 || a2 == 0.0) + { + double small = getFlatness() * EPSILON; + if (a0 == 0.0) + a0 -= small; + + if (a2 == 0.0) + a2 -= small; + } + + r[0] = a0; + r[1] = 2 * (a1 - a0); + r[2] = (a2 - 2 * a1 + a0); + + nRoots = solveQuadratic(r); + for (int i = 0; i < nRoots; i++) + { + double t = r[i]; + if (t >= 0.0 && t <= 1.0) + { + double crossing = t * t * (b2 - 2 * b1 + b0) + 2 * t * (b1 - b0) + + b0; + /* single root is always doubly degenerate in quads */ + if (crossing > 0 && crossing < distance) + nCrossings += (nRoots == 1) ? 2 : 1; + } + } + + if (useYaxis) + { + if (Line2D.linesIntersect(b0, a0, b2, a2, EPSILON, 0.0, distance, 0.0)) + nCrossings++; + } + else + { + if (Line2D.linesIntersect(a0, b0, a2, b2, 0.0, EPSILON, 0.0, distance)) + nCrossings++; + } + + return (nCrossings); + } + + /** + * A two-dimensional curve that is parameterized with a quadratic + * function and stores coordinate values in double-precision + * floating-point format. + * + * @see QuadCurve2D.Float + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sascha Brawer (brawer@dandelis.ch) + */ + public static class Double extends QuadCurve2D + { + /** + * The x coordinate of the curve’s start point. + */ + public double x1; + + /** + * The y coordinate of the curve’s start point. + */ + public double y1; + + /** + * The x coordinate of the curve’s control point. + */ + public double ctrlx; + + /** + * The y coordinate of the curve’s control point. + */ + public double ctrly; + + /** + * The x coordinate of the curve’s end point. + */ + public double x2; + + /** + * The y coordinate of the curve’s end point. + */ + public double y2; + + /** + * Constructs a new QuadCurve2D that stores its coordinate values + * in double-precision floating-point format. All points are + * initially at position (0, 0). + */ + public Double() + { + } + + /** + * Constructs a new QuadCurve2D that stores its coordinate values + * in double-precision floating-point format, specifying the + * initial position of each point. + * + * @param x1 the x coordinate of the curve’s start + * point. + * + * @param y1 the y coordinate of the curve’s start + * point. + * + * @param cx the x coordinate of the curve’s control + * point. + * + * @param cy the y coordinate of the curve’s control + * point. + * + * @param x2 the x coordinate of the curve’s end + * point. + * + * @param y2 the y coordinate of the curve’s end + * point. + */ + public Double(double x1, double y1, double cx, double cy, double x2, + double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Returns the x coordinate of the curve’s start + * point. + */ + public double getX1() + { + return x1; + } + + /** + * Returns the y coordinate of the curve’s start + * point. + */ + public double getY1() + { + return y1; + } + + /** + * Returns the curve’s start point. + */ + public Point2D getP1() + { + return new Point2D.Double(x1, y1); + } + + /** + * Returns the x coordinate of the curve’s control + * point. + */ + public double getCtrlX() + { + return ctrlx; + } + + /** + * Returns the y coordinate of the curve’s control + * point. + */ + public double getCtrlY() + { + return ctrly; + } + + /** + * Returns the curve’s control point. + */ + public Point2D getCtrlPt() + { + return new Point2D.Double(ctrlx, ctrly); + } + + /** + * Returns the x coordinate of the curve’s end + * point. + */ + public double getX2() + { + return x2; + } + + /** + * Returns the y coordinate of the curve’s end + * point. + */ + public double getY2() + { + return y2; + } + + /** + * Returns the curve’s end point. + */ + public Point2D getP2() + { + return new Point2D.Double(x2, y2); + } + + /** + * Changes the geometry of the curve. + * + * @param x1 the x coordinate of the curve’s new + * start point. + * + * @param y1 the y coordinate of the curve’s new + * start point. + * + * @param cx the x coordinate of the curve’s new + * control point. + * + * @param cy the y coordinate of the curve’s new + * control point. + * + * @param x2 the x coordinate of the curve’s new + * end point. + * + * @param y2 the y coordinate of the curve’s new + * end point. + */ + public void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control point. As the + * illustration below shows, the invisible control point may cause + * the bounds to be much larger than the area that is actually + * covered by the curve. + * + *

    An illustration of the bounds of a QuadCurve2D + */ + public Rectangle2D getBounds2D() + { + double nx1 = Math.min(Math.min(x1, ctrlx), x2); + double ny1 = Math.min(Math.min(y1, ctrly), y2); + double nx2 = Math.max(Math.max(x1, ctrlx), x2); + double ny2 = Math.max(Math.max(y1, ctrly), y2); + return new Rectangle2D.Double(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } + + /** + * A two-dimensional curve that is parameterized with a quadratic + * function and stores coordinate values in single-precision + * floating-point format. + * + * @see QuadCurve2D.Double + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sascha Brawer (brawer@dandelis.ch) + */ + public static class Float extends QuadCurve2D + { + /** + * The x coordinate of the curve’s start point. + */ + public float x1; + + /** + * The y coordinate of the curve’s start point. + */ + public float y1; + + /** + * The x coordinate of the curve’s control point. + */ + public float ctrlx; + + /** + * The y coordinate of the curve’s control point. + */ + public float ctrly; + + /** + * The x coordinate of the curve’s end point. + */ + public float x2; + + /** + * The y coordinate of the curve’s end point. + */ + public float y2; + + /** + * Constructs a new QuadCurve2D that stores its coordinate values + * in single-precision floating-point format. All points are + * initially at position (0, 0). + */ + public Float() + { + } + + /** + * Constructs a new QuadCurve2D that stores its coordinate values + * in single-precision floating-point format, specifying the + * initial position of each point. + * + * @param x1 the x coordinate of the curve’s start + * point. + * + * @param y1 the y coordinate of the curve’s start + * point. + * + * @param cx the x coordinate of the curve’s control + * point. + * + * @param cy the y coordinate of the curve’s control + * point. + * + * @param x2 the x coordinate of the curve’s end + * point. + * + * @param y2 the y coordinate of the curve’s end + * point. + */ + public Float(float x1, float y1, float cx, float cy, float x2, float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Returns the x coordinate of the curve’s start + * point. + */ + public double getX1() + { + return x1; + } + + /** + * Returns the y coordinate of the curve’s start + * point. + */ + public double getY1() + { + return y1; + } + + /** + * Returns the curve’s start point. + */ + public Point2D getP1() + { + return new Point2D.Float(x1, y1); + } + + /** + * Returns the x coordinate of the curve’s control + * point. + */ + public double getCtrlX() + { + return ctrlx; + } + + /** + * Returns the y coordinate of the curve’s control + * point. + */ + public double getCtrlY() + { + return ctrly; + } + + /** + * Returns the curve’s control point. + */ + public Point2D getCtrlPt() + { + return new Point2D.Float(ctrlx, ctrly); + } + + /** + * Returns the x coordinate of the curve’s end + * point. + */ + public double getX2() + { + return x2; + } + + /** + * Returns the y coordinate of the curve’s end + * point. + */ + public double getY2() + { + return y2; + } + + /** + * Returns the curve’s end point. + */ + public Point2D getP2() + { + return new Point2D.Float(x2, y2); + } + + /** + * Changes the geometry of the curve, specifying coordinate values + * as double-precision floating-point numbers. + * + * @param x1 the x coordinate of the curve’s new + * start point. + * + * @param y1 the y coordinate of the curve’s new + * start point. + * + * @param cx the x coordinate of the curve’s new + * control point. + * + * @param cy the y coordinate of the curve’s new + * control point. + * + * @param x2 the x coordinate of the curve’s new + * end point. + * + * @param y2 the y coordinate of the curve’s new + * end point. + */ + public void setCurve(double x1, double y1, double cx, double cy, + double x2, double y2) + { + this.x1 = (float) x1; + this.y1 = (float) y1; + ctrlx = (float) cx; + ctrly = (float) cy; + this.x2 = (float) x2; + this.y2 = (float) y2; + } + + /** + * Changes the geometry of the curve, specifying coordinate values + * as single-precision floating-point numbers. + * + * @param x1 the x coordinate of the curve’s new + * start point. + * + * @param y1 the y coordinate of the curve’s new + * start point. + * + * @param cx the x coordinate of the curve’s new + * control point. + * + * @param cy the y coordinate of the curve’s new + * control point. + * + * @param x2 the x coordinate of the curve’s new + * end point. + * + * @param y2 the y coordinate of the curve’s new + * end point. + */ + public void setCurve(float x1, float y1, float cx, float cy, float x2, + float y2) + { + this.x1 = x1; + this.y1 = y1; + ctrlx = cx; + ctrly = cy; + this.x2 = x2; + this.y2 = y2; + } + + /** + * Determines the smallest rectangle that encloses the + * curve’s start, end and control point. As the + * illustration below shows, the invisible control point may cause + * the bounds to be much larger than the area that is actually + * covered by the curve. + * + *

    An illustration of the bounds of a QuadCurve2D + */ + public Rectangle2D getBounds2D() + { + float nx1 = Math.min(Math.min(x1, ctrlx), x2); + float ny1 = Math.min(Math.min(y1, ctrly), y2); + float nx2 = Math.max(Math.max(x1, ctrlx), x2); + float ny2 = Math.max(Math.max(y1, ctrly), y2); + return new Rectangle2D.Float(nx1, ny1, nx2 - nx1, ny2 - ny1); + } + } +} diff --git a/libjava/classpath/java/awt/geom/Rectangle2D.java b/libjava/classpath/java/awt/geom/Rectangle2D.java new file mode 100644 index 000000000..6a255f953 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Rectangle2D.java @@ -0,0 +1,992 @@ +/* Rectangle2D.java -- generic rectangles in 2-D space + Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.util.NoSuchElementException; + +/** + * This class describes a rectangle by a point (x,y) and dimension (w x h). + * The actual storage is left up to subclasses. + * + *

    It is valid for a rectangle to have negative width or height; but it + * is considered to have no area or internal points. Therefore, the behavior + * in methods like contains or intersects is + * undefined unless the rectangle has positive width and height. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class Rectangle2D extends RectangularShape +{ + /** + * The point lies left of the rectangle (p.x < r.x). + * + * @see #outcode(double, double) + */ + public static final int OUT_LEFT = 1; + + /** + * The point lies above the rectangle (p.y < r.y). + * + * @see #outcode(double, double) + */ + public static final int OUT_TOP = 2; + + /** + * The point lies right of the rectangle (p.x > r.maxX). + * + * @see #outcode(double, double) + */ + public static final int OUT_RIGHT = 4; + + /** + * The point lies below of the rectangle (p.y > r.maxY). + * + * @see #outcode(double, double) + */ + public static final int OUT_BOTTOM = 8; + + /** + * Default constructor. + */ + protected Rectangle2D() + { + } + + /** + * Set the bounding box of this rectangle. + * + * @param x the new X coordinate + * @param y the new Y coordinate + * @param w the new width + * @param h the new height + */ + public abstract void setRect(double x, double y, double w, double h); + + /** + * Set the bounding box of this rectangle from the given one. + * + * @param r rectangle to copy + * @throws NullPointerException if r is null + */ + public void setRect(Rectangle2D r) + { + setRect(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Tests if the specified line intersects the interior of this rectangle. + * + * @param x1 the first x coordinate of line segment + * @param y1 the first y coordinate of line segment + * @param x2 the second x coordinate of line segment + * @param y2 the second y coordinate of line segment + * @return true if the line intersects the rectangle + */ + public boolean intersectsLine(double x1, double y1, double x2, double y2) + { + double x = getX(); + double y = getY(); + double w = getWidth(); + double h = getHeight(); + if (w <= 0 || h <= 0) + return false; + + if (x1 >= x && x1 <= x + w && y1 >= y && y1 <= y + h) + return true; + if (x2 >= x && x2 <= x + w && y2 >= y && y2 <= y + h) + return true; + + double x3 = x + w; + double y3 = y + h; + + return (Line2D.linesIntersect(x1, y1, x2, y2, x, y, x, y3) + || Line2D.linesIntersect(x1, y1, x2, y2, x, y3, x3, y3) + || Line2D.linesIntersect(x1, y1, x2, y2, x3, y3, x3, y) + || Line2D.linesIntersect(x1, y1, x2, y2, x3, y, x, y)); + } + + /** + * Tests if the specified line intersects the interior of this rectangle. + * + * @param l the line segment + * @return true if the line intersects the rectangle + * @throws NullPointerException if l is null + */ + public boolean intersectsLine(Line2D l) + { + return intersectsLine(l.getX1(), l.getY1(), l.getX2(), l.getY2()); + } + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + */ + public abstract int outcode(double x, double y); + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param p the point to check + * @return the binary OR of the result + * @throws NullPointerException if p is null + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + */ + public int outcode(Point2D p) + { + return outcode(p.getX(), p.getY()); + } + + /** + * Set the bounding box of this rectangle. + * + * @param x the new X coordinate + * @param y the new Y coordinate + * @param w the new width + * @param h the new height + */ + public void setFrame(double x, double y, double w, double h) + { + setRect(x, y, w, h); + } + + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle + */ + public Rectangle2D getBounds2D() + { + return (Rectangle2D) clone(); + } + + /** + * Test if the given point is contained in the rectangle. + * + * @param x the x coordinate of the point + * @param y the y coordinate of the point + * @return true if (x,y) is in the rectangle + */ + public boolean contains(double x, double y) + { + double mx = getX(); + double my = getY(); + double w = getWidth(); + double h = getHeight(); + return w > 0 && h > 0 && x >= mx && x < mx + w && y >= my && y < my + h; + } + + /** + * Tests if the given rectangle intersects this one. In other words, test if + * the two rectangles share at least one internal point. + * + * @param x the x coordinate of the other rectangle + * @param y the y coordinate of the other rectangle + * @param w the width of the other rectangle + * @param h the height of the other rectangle + * @return true if the rectangles intersect + */ + public boolean intersects(double x, double y, double w, double h) + { + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + return w > 0 && h > 0 && mw > 0 && mh > 0 + && x < mx + mw && x + w > mx && y < my + mh && y + h > my; + } + + /** + * Tests if this rectangle contains the given one. In other words, test if + * this rectangle contains all points in the given one. + * + * @param x the x coordinate of the other rectangle + * @param y the y coordinate of the other rectangle + * @param w the width of the other rectangle + * @param h the height of the other rectangle + * @return true if this rectangle contains the other + */ + public boolean contains(double x, double y, double w, double h) + { + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + return w > 0 && h > 0 && mw > 0 && mh > 0 + && x >= mx && x + w <= mx + mw && y >= my && y + h <= my + mh; + } + + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public abstract Rectangle2D createIntersection(Rectangle2D r); + + /** + * Intersects a pair of rectangles, and places the result in the + * destination; this can be used to avoid object creation. This method + * even works when the destination is also a source, although you stand + * to lose the original data. + * + * @param src1 the first source + * @param src2 the second source + * @param dest the destination for the intersection + * @throws NullPointerException if any rectangle is null + */ + public static void intersect(Rectangle2D src1, Rectangle2D src2, + Rectangle2D dest) + { + double x = Math.max(src1.getX(), src2.getX()); + double y = Math.max(src1.getY(), src2.getY()); + double maxx = Math.min(src1.getMaxX(), src2.getMaxX()); + double maxy = Math.min(src1.getMaxY(), src2.getMaxY()); + dest.setRect(x, y, maxx - x, maxy - y); + } + + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public abstract Rectangle2D createUnion(Rectangle2D r); + + /** + * Joins a pair of rectangles, and places the result in the destination; + * this can be used to avoid object creation. This method even works when + * the destination is also a source, although you stand to lose the + * original data. + * + * @param src1 the first source + * @param src2 the second source + * @param dest the destination for the union + * @throws NullPointerException if any rectangle is null + */ + public static void union(Rectangle2D src1, Rectangle2D src2, + Rectangle2D dest) + { + double x = Math.min(src1.getX(), src2.getX()); + double y = Math.min(src1.getY(), src2.getY()); + double maxx = Math.max(src1.getMaxX(), src2.getMaxX()); + double maxy = Math.max(src1.getMaxY(), src2.getMaxY()); + dest.setRect(x, y, maxx - x, maxy - y); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to contains may + * return false. + * + * @param newx the X coordinate of the point to add to this rectangle + * @param newy the Y coordinate of the point to add to this rectangle + */ + public void add(double newx, double newy) + { + double minx = Math.min(getX(), newx); + double maxx = Math.max(getMaxX(), newx); + double miny = Math.min(getY(), newy); + double maxy = Math.max(getMaxY(), newy); + setRect(minx, miny, maxx - minx, maxy - miny); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified point. + * However, if the point falls on one of the two borders which are not + * inside the rectangle, a subsequent call to contains may + * return false. + * + * @param p the point to add to this rectangle + * @throws NullPointerException if p is null + */ + public void add(Point2D p) + { + add(p.getX(), p.getY()); + } + + /** + * Modifies this rectangle so that it represents the smallest rectangle + * that contains both the existing rectangle and the specified rectangle. + * + * @param r the rectangle to add to this rectangle + * @throws NullPointerException if r is null + * @see #union(Rectangle2D, Rectangle2D, Rectangle2D) + */ + public void add(Rectangle2D r) + { + union(this, r, this); + } + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. This iterator is thread + * safe; modifications to the rectangle do not affect the results of this + * path instance. + * + * @param at an optional transform to apply to the iterator + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(final AffineTransform at) + { + final double minx = getX(); + final double miny = getY(); + final double maxx = minx + getWidth(); + final double maxy = miny + getHeight(); + return new PathIterator() + { + /** Current coordinate. */ + private int current = (maxx <= minx && maxy <= miny) ? 6 : 0; + + public int getWindingRule() + { + // A test program showed that Sun J2SE 1.3.1 and 1.4.1_01 + // return WIND_NON_ZERO paths. While this does not really + // make any difference for rectangles (because they are not + // self-intersecting), it seems appropriate to behave + // identically. + + return WIND_NON_ZERO; + } + + public boolean isDone() + { + return current > 5; + } + + public void next() + { + current++; + } + + public int currentSegment(float[] coords) + { + switch (current) + { + case 1: + coords[0] = (float) maxx; + coords[1] = (float) miny; + break; + case 2: + coords[0] = (float) maxx; + coords[1] = (float) maxy; + break; + case 3: + coords[0] = (float) minx; + coords[1] = (float) maxy; + break; + case 0: + case 4: + coords[0] = (float) minx; + coords[1] = (float) miny; + break; + case 5: + return SEG_CLOSE; + default: + throw new NoSuchElementException("rect iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return current == 0 ? SEG_MOVETO : SEG_LINETO; + } + + public int currentSegment(double[] coords) + { + switch (current) + { + case 1: + coords[0] = maxx; + coords[1] = miny; + break; + case 2: + coords[0] = maxx; + coords[1] = maxy; + break; + case 3: + coords[0] = minx; + coords[1] = maxy; + break; + case 0: + case 4: + coords[0] = minx; + coords[1] = miny; + break; + case 5: + return SEG_CLOSE; + default: + throw new NoSuchElementException("rect iterator out of bounds"); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return current == 0 ? SEG_MOVETO : SEG_LINETO; + } + }; + } + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. Each call returns + * a new object, independent from others in use. This iterator is thread + * safe; modifications to the rectangle do not affect the results of this + * path instance. As the rectangle is already flat, the flatness parameter + * is ignored. + * + * @param at an optional transform to apply to the iterator + * @param flatness the maximum distance for deviation from the real boundary + * @return a new iterator over the boundary + * @since 1.2 + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return getPathIterator(at); + } + + /** + * Return the hashcode for this rectangle. The formula is not documented, but + * appears to be the same as: + *

    +   * long l = Double.doubleToLongBits(getX())
    +   *   + 37 * Double.doubleToLongBits(getY())
    +   *   + 43 * Double.doubleToLongBits(getWidth())
    +   *   + 47 * Double.doubleToLongBits(getHeight());
    +   * return (int) ((l >> 32) ^ l);
    +   * 
    + * + * @return the hashcode + */ + public int hashCode() + { + // Talk about a fun time reverse engineering this one! + long l = java.lang.Double.doubleToLongBits(getX()) + + 37 * java.lang.Double.doubleToLongBits(getY()) + + 43 * java.lang.Double.doubleToLongBits(getWidth()) + + 47 * java.lang.Double.doubleToLongBits(getHeight()); + return (int) ((l >> 32) ^ l); + } + + /** + * Tests this rectangle for equality against the specified object. This + * will be true if an only if the specified object is an instance of + * Rectangle2D with the same coordinates and dimensions. + * + * @param obj the object to test against for equality + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Rectangle2D)) + return false; + Rectangle2D r = (Rectangle2D) obj; + return r.getX() == getX() && r.getY() == getY() + && r.getWidth() == getWidth() && r.getHeight() == getHeight(); + } + + /** + * This class defines a rectangle in double precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Double extends Rectangle2D + { + /** The x coordinate of the lower left corner. */ + public double x; + + /** The y coordinate of the lower left corner. */ + public double y; + + /** The width of the rectangle. */ + public double width; + + /** The height of the rectangle. */ + public double height; + + /** + * Create a rectangle at (0,0) with width 0 and height 0. + */ + public Double() + { + } + + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public Double(double x, double y, double w, double h) + { + this.x = x; + this.y = y; + width = w; + height = h; + } + + /** + * Return the X coordinate. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the Y coordinate. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Test if the rectangle is empty. + * + * @return true if width or height is not positive + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(double x, double y, double w, double h) + { + this.x = x; + this.y = y; + width = w; + height = h; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param r the rectangle to copy + * @throws NullPointerException if r is null + */ + public void setRect(Rectangle2D r) + { + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); + } + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + * @since 1.2 + */ + public int outcode(double x, double y) + { + int result = 0; + if (width <= 0) + result |= OUT_LEFT | OUT_RIGHT; + else if (x < this.x) + result |= OUT_LEFT; + else if (x > this.x + width) + result |= OUT_RIGHT; + if (height <= 0) + result |= OUT_BOTTOM | OUT_TOP; + else if (y < this.y) // Remember that +y heads top-to-bottom. + result |= OUT_TOP; + else if (y > this.y + height) + result |= OUT_BOTTOM; + return result; + } + + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle + */ + public Rectangle2D getBounds2D() + { + return new Double(x, y, width, height); + } + + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public Rectangle2D createIntersection(Rectangle2D r) + { + Double res = new Double(); + intersect(this, r, res); + return res; + } + + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public Rectangle2D createUnion(Rectangle2D r) + { + Double res = new Double(); + union(this, r, res); + return res; + } + + /** + * Returns a string representation of this rectangle. This is in the form + * getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + * + ",h=" + height + ']'. + * + * @return a string representation of this rectangle + */ + public String toString() + { + return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + + ",h=" + height + ']'; + } + } + + /** + * This class defines a rectangle in float precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ + public static class Float extends Rectangle2D + { + /** The x coordinate of the lower left corner. */ + public float x; + + /** The y coordinate of the lower left corner. */ + public float y; + + /** The width of the rectangle. */ + public float width; + + /** The height of the rectangle. */ + public float height; + + /** + * Create a rectangle at (0,0) with width 0 and height 0. + */ + public Float() + { + } + + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public Float(float x, float y, float w, float h) + { + this.x = x; + this.y = y; + width = w; + height = h; + } + + /** + * Create a rectangle with the given values. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + Float(double x, double y, double w, double h) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + } + + /** + * Return the X coordinate. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the Y coordinate. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Test if the rectangle is empty. + * + * @return true if width or height is not positive + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(float x, float y, float w, float h) + { + this.x = x; + this.y = y; + width = w; + height = h; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + public void setRect(double x, double y, double w, double h) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + } + + /** + * Set the contents of this rectangle to those specified. + * + * @param r the rectangle to copy + * @throws NullPointerException if r is null + */ + public void setRect(Rectangle2D r) + { + x = (float) r.getX(); + y = (float) r.getY(); + width = (float) r.getWidth(); + height = (float) r.getHeight(); + } + + /** + * Determine where the point lies with respect to this rectangle. The + * result will be the binary OR of the appropriate bit masks. + * + * @param x the x coordinate to check + * @param y the y coordinate to check + * @return the binary OR of the result + * @see #OUT_LEFT + * @see #OUT_TOP + * @see #OUT_RIGHT + * @see #OUT_BOTTOM + * @since 1.2 + */ + public int outcode(double x, double y) + { + int result = 0; + if (width <= 0) + result |= OUT_LEFT | OUT_RIGHT; + else if (x < this.x) + result |= OUT_LEFT; + else if (x > this.x + width) + result |= OUT_RIGHT; + if (height <= 0) + result |= OUT_BOTTOM | OUT_TOP; + else if (y < this.y) // Remember that +y heads top-to-bottom. + result |= OUT_TOP; + else if (y > this.y + height) + result |= OUT_BOTTOM; + return result; + } + + /** + * Returns the bounds of this rectangle. A pretty useless method, as this + * is already a rectangle. + * + * @return a copy of this rectangle + */ + public Rectangle2D getBounds2D() + { + return new Float(x, y, width, height); + } + + /** + * Return a new rectangle which is the intersection of this and the given + * one. The result will be empty if there is no intersection. + * + * @param r the rectangle to be intersected + * @return the intersection + * @throws NullPointerException if r is null + */ + public Rectangle2D createIntersection(Rectangle2D r) + { + Float res = new Float(); + intersect(this, r, res); + return res; + } + + /** + * Return a new rectangle which is the union of this and the given one. + * + * @param r the rectangle to be merged + * @return the union + * @throws NullPointerException if r is null + */ + public Rectangle2D createUnion(Rectangle2D r) + { + Float res = new Float(); + union(this, r, res); + return res; + } + + /** + * Returns a string representation of this rectangle. This is in the form + * getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + * + ",h=" + height + ']'. + * + * @return a string representation of this rectangle + */ + public String toString() + { + return getClass().getName() + "[x=" + x + ",y=" + y + ",w=" + width + + ",h=" + height + ']'; + } + } +} diff --git a/libjava/classpath/java/awt/geom/RectangularShape.java b/libjava/classpath/java/awt/geom/RectangularShape.java new file mode 100644 index 000000000..68bc451cc --- /dev/null +++ b/libjava/classpath/java/awt/geom/RectangularShape.java @@ -0,0 +1,382 @@ +/* RectangularShape.java -- a rectangular frame for several generic shapes + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.geom; + +import java.awt.Rectangle; +import java.awt.Shape; + +/** + * This class provides a generic framework, and several helper methods, for + * subclasses which represent geometric objects inside a rectangular frame. + * This does not specify any geometry except for the bounding box. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @see Arc2D + * @see Ellipse2D + * @see Rectangle2D + * @see RoundRectangle2D + * @status updated to 1.4 + */ +public abstract class RectangularShape implements Shape, Cloneable +{ + /** + * Default constructor. + */ + protected RectangularShape() + { + } + + /** + * Get the x coordinate of the upper-left corner of the framing rectangle. + * + * @return the x coordinate + */ + public abstract double getX(); + + /** + * Get the y coordinate of the upper-left corner of the framing rectangle. + * + * @return the y coordinate + */ + public abstract double getY(); + + /** + * Get the width of the framing rectangle. + * + * @return the width + */ + public abstract double getWidth(); + + /** + * Get the height of the framing rectangle. + * + * @return the height + */ + public abstract double getHeight(); + + /** + * Get the minimum x coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getX() even when + * getWidth() is negative. + * + * @return the minimum x coordinate + */ + public double getMinX() + { + return getX(); + } + + /** + * Get the minimum y coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getY() even when + * getHeight() is negative. + * + * @return the minimum y coordinate + */ + public double getMinY() + { + return getY(); + } + + /** + * Get the maximum x coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getX()+getWidth() even + * when getWidth() is negative. + * + * @return the maximum x coordinate + */ + public double getMaxX() + { + return getX() + getWidth(); + } + + /** + * Get the maximum y coordinate in the frame. This is misnamed, or else + * Sun has a bug, because the implementation returns getY()+getHeight() even + * when getHeight() is negative. + * + * @return the maximum y coordinate + */ + public double getMaxY() + { + return getY() + getHeight(); + } + + /** + * Return the x coordinate of the center point of the framing rectangle. + * + * @return the central x coordinate + */ + public double getCenterX() + { + return getX() + getWidth() / 2; + } + + /** + * Return the y coordinate of the center point of the framing rectangle. + * + * @return the central y coordinate + */ + public double getCenterY() + { + return getY() + getHeight() / 2; + } + + /** + * Return the frame around this object. Note that this may be a looser + * bounding box than getBounds2D. + * + * @return the frame, in double precision + * @see #setFrame(double, double, double, double) + */ + public Rectangle2D getFrame() + { + return new Rectangle2D.Double(getX(), getY(), getWidth(), getHeight()); + } + + /** + * Test if the shape is empty, meaning that no points are inside it. + * + * @return true if the shape is empty + */ + public abstract boolean isEmpty(); + + /** + * Set the framing rectangle of this shape to the given coordinate and size. + * + * @param x the new x coordinate + * @param y the new y coordinate + * @param w the new width + * @param h the new height + * @see #getFrame() + */ + public abstract void setFrame(double x, double y, double w, double h); + + /** + * Set the framing rectangle of this shape to the given coordinate and size. + * + * @param p the new point + * @param d the new dimension + * @throws NullPointerException if p or d is null + * @see #getFrame() + */ + public void setFrame(Point2D p, Dimension2D d) + { + setFrame(p.getX(), p.getY(), d.getWidth(), d.getHeight()); + } + + /** + * Set the framing rectangle of this shape to the given rectangle. + * + * @param r the new framing rectangle + * @throws NullPointerException if r is null + * @see #getFrame() + */ + public void setFrame(Rectangle2D r) + { + setFrame(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Set the framing rectangle of this shape using two points on a diagonal. + * The area will be positive. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + */ + public void setFrameFromDiagonal(double x1, double y1, double x2, double y2) + { + if (x1 > x2) + { + double t = x2; + x2 = x1; + x1 = t; + } + if (y1 > y2) + { + double t = y2; + y2 = y1; + y1 = t; + } + setFrame(x1, y1, x2 - x1, y2 - y1); + } + + /** + * Set the framing rectangle of this shape using two points on a diagonal. + * The area will be positive. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + */ + public void setFrameFromDiagonal(Point2D p1, Point2D p2) + { + setFrameFromDiagonal(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Set the framing rectangle of this shape using the center of the frame, + * and one of the four corners. The area will be positive. + * + * @param centerX the x coordinate at the center + * @param centerY the y coordinate at the center + * @param cornerX the x coordinate at a corner + * @param cornerY the y coordinate at a corner + */ + public void setFrameFromCenter(double centerX, double centerY, + double cornerX, double cornerY) + { + double halfw = Math.abs(cornerX - centerX); + double halfh = Math.abs(cornerY - centerY); + setFrame(centerX - halfw, centerY - halfh, halfw + halfw, halfh + halfh); + } + + /** + * Set the framing rectangle of this shape using the center of the frame, + * and one of the four corners. The area will be positive. + * + * @param center the center point + * @param corner a corner point + * @throws NullPointerException if either point is null + */ + public void setFrameFromCenter(Point2D center, Point2D corner) + { + setFrameFromCenter(center.getX(), center.getY(), + corner.getX(), corner.getY()); + } + + /** + * Tests if a point is inside the boundary of the shape. + * + * @param p the point to test + * @return true if the point is inside the shape + * @throws NullPointerException if p is null + * @see #contains(double, double) + */ + public boolean contains(Point2D p) + { + return contains(p.getX(), p.getY()); + } + + /** + * Tests if a rectangle and this shape share common internal points. + * + * @param r the rectangle to test + * @return true if the rectangle intersects this shpae + * @throws NullPointerException if r is null + * @see #intersects(double, double, double, double) + */ + public boolean intersects(Rectangle2D r) + { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Tests if the shape completely contains the given rectangle. + * + * @param r the rectangle to test + * @return true if r is contained in this shape + * @throws NullPointerException if r is null + * @see #contains(double, double, double, double) + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns a bounding box for this shape, in integer format. Notice that you + * may get a tighter bound with getBounds2D. + * + * @return a bounding box + */ + public Rectangle getBounds() + { + double x = getX(); + double y = getY(); + double maxx = Math.ceil(x + getWidth()); + double maxy = Math.ceil(y + getHeight()); + x = Math.floor(x); + y = Math.floor(y); + return new Rectangle((int) x, (int) y, (int) (maxx - x), (int) (maxy - y)); + } + + /** + * Return an iterator along the shape boundary. If the optional transform + * is provided, the iterator is transformed accordingly. The path is + * flattened until all segments differ from the curve by at most the value + * of the flatness parameter, within the limits of the default interpolation + * recursion limit of 1024 segments between actual points. Each call + * returns a new object, independent from others in use. The result is + * threadsafe if and only if the iterator returned by + * {@link #getPathIterator(AffineTransform)} is as well. + * + * @param at an optional transform to apply to the iterator + * @param flatness the desired flatness + * @return a new iterator over the boundary + * @throws IllegalArgumentException if flatness is invalid + * @since 1.2 + */ + public PathIterator getPathIterator(AffineTransform at, double flatness) + { + return new FlatteningPathIterator(getPathIterator(at), flatness); + } + + /** + * Create a new shape of the same run-time type with the same contents as + * this one. + * + * @return the clone + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } +} // class RectangularShape diff --git a/libjava/classpath/java/awt/geom/RoundRectangle2D.java b/libjava/classpath/java/awt/geom/RoundRectangle2D.java new file mode 100644 index 000000000..19a7b4237 --- /dev/null +++ b/libjava/classpath/java/awt/geom/RoundRectangle2D.java @@ -0,0 +1,584 @@ +/* RoundRectangle2D.java -- represents a rectangle with rounded corners + Copyright (C) 2000, 2002, 2003, 2004, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + + + +/** This class implements a rectangle with rounded corners. + * @author Tom Tromey (tromey@cygnus.com) + * @date December 3, 2000 + */ +public abstract class RoundRectangle2D extends RectangularShape +{ + /** + * Return the arc height of this round rectangle. The arc height and width + * control the roundness of the corners of the rectangle. + * + * @return The arc height. + * + * @see #getArcWidth() + */ + public abstract double getArcHeight(); + + /** + * Return the arc width of this round rectangle. The arc width and height + * control the roundness of the corners of the rectangle. + * + * @return The arc width. + * + * @see #getArcHeight() + */ + public abstract double getArcWidth(); + + /** + * Set the values of this round rectangle. + * + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + * @param arcWidth The arc width + * @param arcHeight The arc height + */ + public abstract void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight); + + /** + * Create a RoundRectangle2D. This is protected because this class + * is abstract and cannot be instantiated. + */ + protected RoundRectangle2D() + { + } + + /** + * Return true if this object contains the specified point. + * @param x The x coordinate + * @param y The y coordinate + */ + public boolean contains(double x, double y) + { + double mx = getX(); + double mw = getWidth(); + if (x < mx || x >= mx + mw) + return false; + double my = getY(); + double mh = getHeight(); + if (y < my || y >= my + mh) + return false; + + // Now check to see if the point is in range of an arc. + double dy = Math.min(Math.abs(my - y), Math.abs(my + mh - y)); + double dx = Math.min(Math.abs(mx - x), Math.abs(mx + mw - x)); + + // The arc dimensions are that of the corresponding ellipse + // thus a 90 degree segment is half of that. + double aw = getArcWidth() / 2.0; + double ah = getArcHeight() / 2.0; + if (dx > aw || dy > ah) + return true; + + // At this point DX represents the distance from the nearest edge + // of the rectangle. But we want to transform it to represent the + // scaled distance from the center of the ellipse that forms the + // arc. Hence this code: + dy = (ah - dy) / ah; + dx = (aw - dx) / aw; + + return dx * dx + dy * dy <= 1.0; + } + + /** + * Return true if this object contains the specified rectangle + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + */ + public boolean contains(double x, double y, double w, double h) + { + // We have to check all four points here (for ordinary rectangles + // we can just check opposing corners). + return (contains(x, y) && contains(x, y + h) && contains(x + w, y + h) + && contains(x + w, y)); + } + + /** + * Return a new path iterator which iterates over this rectangle. + * + * @param at An affine transform to apply to the object + */ + public PathIterator getPathIterator(final AffineTransform at) + { + double arcW = Math.min(getArcWidth(), getWidth()); + double arcH = Math.min(getArcHeight(), getHeight()); + + // check for special cases... + if (arcW <= 0 || arcH <= 0) + { + Rectangle2D r = new Rectangle2D.Double(getX(), getY(), getWidth(), + getHeight()); + return r.getPathIterator(at); + } + else if (arcW >= getWidth() && arcH >= getHeight()) + { + Ellipse2D e = new Ellipse2D.Double(getX(), getY(), getWidth(), + getHeight()); + return e.getPathIterator(at); + } + + // otherwise return the standard case... + return new PathIterator() + { + double x = getX(); + double y = getY(); + double w = getWidth(); + double h = getHeight(); + double arcW = Math.min(getArcWidth(), w); + double arcH = Math.min(getArcHeight(), h); + Arc2D.Double arc = new Arc2D.Double(); + PathIterator corner; + int step = -1; + + public int currentSegment(double[] coords) + { + if (corner != null) // steps 1, 3, 5 and 7 + { + int r = corner.currentSegment(coords); + if (r == SEG_MOVETO) + r = SEG_LINETO; + return r; + } + if (step == -1) + { + // move to the start position + coords[0] = x + w - arcW / 2; + coords[1] = y; + } + else if (step == 0) + { + // top line + coords[0] = x + arcW / 2; + coords[1] = y; + } + else if (step == 2) + { + // left line + coords[0] = x; + coords[1] = y + h - arcH / 2; + } + else if (step == 4) + { + // bottom line + coords[0] = x + w - arcW / 2; + coords[1] = y + h; + } + else if (step == 6) + { + // right line + coords[0] = x + w; + coords[1] = y + arcH / 2; + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return step == -1 ? SEG_MOVETO : SEG_LINETO; + } + + public int currentSegment(float[] coords) { + if (corner != null) // steps 1, 3, 5 and 7 + { + int r = corner.currentSegment(coords); + if (r == SEG_MOVETO) + r = SEG_LINETO; + return r; + } + if (step == -1) + { + // move to the start position + coords[0] = (float) (x + w - arcW / 2); + coords[1] = (float) y; + } + else if (step == 0) + { + // top line + coords[0] = (float) (x + arcW / 2); + coords[1] = (float) y; + } + else if (step == 2) + { + // left line + coords[0] = (float) x; + coords[1] = (float) (y + h - arcH / 2); + } + else if (step == 4) + { + // bottom line + coords[0] = (float) (x + w - arcW / 2); + coords[1] = (float) (y + h); + } + else if (step == 6) + { + // right line + coords[0] = (float) (x + w); + coords[1] = (float) (y + arcH / 2); + } + if (at != null) + at.transform(coords, 0, coords, 0, 1); + return step == -1 ? SEG_MOVETO : SEG_LINETO; + } + + public int getWindingRule() { + return WIND_NON_ZERO; + } + + public boolean isDone() { + return step >= 8; + } + + public void next() + { + if (corner != null) + { + corner.next(); + if (corner.isDone()) + { + corner = null; + step++; + } + } + else + { + step++; + if (step == 1) + { + // create top left corner + arc.setArc(x, y, arcW, arcH, 90, 90, Arc2D.OPEN); + corner = arc.getPathIterator(at); + } + else if (step == 3) + { + // create bottom left corner + arc.setArc(x, y + h - arcH, arcW, arcH, 180, 90, + Arc2D.OPEN); + corner = arc.getPathIterator(at); + } + else if (step == 5) + { + // create bottom right corner + arc.setArc(x + w - arcW, y + h - arcH, arcW, arcH, 270, 90, + Arc2D.OPEN); + corner = arc.getPathIterator(at); + } + else if (step == 7) + { + // create top right corner + arc.setArc(x + w - arcW, y, arcW, arcH, 0, 90, Arc2D.OPEN); + corner = arc.getPathIterator(at); + } + } + } + }; + } + + /** + * Return true if the given rectangle intersects this shape. + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + */ + public boolean intersects(double x, double y, double w, double h) + { + // Check if any corner is within the rectangle + return (contains(x, y) || contains(x, y + h) || contains(x + w, y + h) + || contains(x + w, y)); + } + + /** + * Set the boundary of this round rectangle. + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + */ + public void setFrame(double x, double y, double w, double h) + { + // This is a bit lame. + setRoundRect(x, y, w, h, getArcWidth(), getArcHeight()); + } + + /** + * Set the values of this round rectangle to be the same as those + * of the argument. + * @param rr The round rectangle to copy + */ + public void setRoundRect(RoundRectangle2D rr) + { + setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr.getHeight(), + rr.getArcWidth(), rr.getArcHeight()); + } + + /** + * A subclass of RoundRectangle which keeps its parameters as + * doubles. + */ + public static class Double extends RoundRectangle2D + { + /** The height of the corner arc. */ + public double archeight; + + /** The width of the corner arc. */ + public double arcwidth; + + /** The x coordinate of this object. */ + public double x; + + /** The y coordinate of this object. */ + public double y; + + /** The width of this object. */ + public double width; + + /** The height of this object. */ + public double height; + + /** + * Construct a new instance, with all parameters set to 0. + */ + public Double() + { + } + + /** + * Construct a new instance with the given arguments. + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + * @param arcWidth The arc width + * @param arcHeight The arc height + */ + public Double(double x, double y, double w, double h, double arcWidth, + double arcHeight) + { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.arcwidth = arcWidth; + this.archeight = arcHeight; + } + + public double getArcHeight() + { + return archeight; + } + + public double getArcWidth() + { + return arcwidth; + } + + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Double(x, y, width, height); + } + + public double getX() + { + return x; + } + + public double getY() + { + return y; + } + + public double getWidth() + { + return width; + } + + public double getHeight() + { + return height; + } + + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + public void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight) + { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.arcwidth = arcWidth; + this.archeight = arcHeight; + } + } // class Double + + /** + * A subclass of RoundRectangle which keeps its parameters as + * floats. + */ + public static class Float extends RoundRectangle2D + { + /** The height of the corner arc. */ + public float archeight; + + /** The width of the corner arc. */ + public float arcwidth; + + /** The x coordinate of this object. */ + public float x; + + /** The y coordinate of this object. */ + public float y; + + /** The width of this object. */ + public float width; + + /** The height of this object. */ + public float height; + + /** + * Construct a new instance, with all parameters set to 0. + */ + public Float() + { + } + + /** + * Construct a new instance with the given arguments. + * @param x The x coordinate + * @param y The y coordinate + * @param w The width + * @param h The height + * @param arcWidth The arc width + * @param arcHeight The arc height + */ + public Float(float x, float y, float w, float h, float arcWidth, + float arcHeight) + { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.arcwidth = arcWidth; + this.archeight = arcHeight; + } + + public double getArcHeight() + { + return archeight; + } + + public double getArcWidth() + { + return arcwidth; + } + + public Rectangle2D getBounds2D() + { + return new Rectangle2D.Float(x, y, width, height); + } + + public double getX() + { + return x; + } + + public double getY() + { + return y; + } + + public double getWidth() + { + return width; + } + + public double getHeight() + { + return height; + } + + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the dimensions for this rounded rectangle. + * + * @param x the x-coordinate of the top left corner. + * @param y the y-coordinate of the top left corner. + * @param w the width of the rectangle. + * @param h the height of the rectangle. + * @param arcWidth the arc width. + * @param arcHeight the arc height. + * + * @see #setRoundRect(double, double, double, double, double, double) + */ + public void setRoundRect(float x, float y, float w, float h, + float arcWidth, float arcHeight) + { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.arcwidth = arcWidth; + this.archeight = arcHeight; + } + + public void setRoundRect(double x, double y, double w, double h, + double arcWidth, double arcHeight) + { + this.x = (float) x; + this.y = (float) y; + this.width = (float) w; + this.height = (float) h; + this.arcwidth = (float) arcWidth; + this.archeight = (float) arcHeight; + } + } // class Float +} // class RoundRectangle2D diff --git a/libjava/classpath/java/awt/geom/doc-files/Area-1.png b/libjava/classpath/java/awt/geom/doc-files/Area-1.png new file mode 100644 index 000000000..44650f2d8 Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/Area-1.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-1.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-1.png new file mode 100644 index 000000000..1784509be Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-1.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-2.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-2.png new file mode 100644 index 000000000..1ddae9fc8 Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-2.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-3.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-3.png new file mode 100644 index 000000000..b200dad37 Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-3.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-4.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-4.png new file mode 100644 index 000000000..e57ffdc5c Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-4.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-5.png b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-5.png new file mode 100644 index 000000000..701ab138f Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/CubicCurve2D-5.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/Ellipse-1.png b/libjava/classpath/java/awt/geom/doc-files/Ellipse-1.png new file mode 100644 index 000000000..8317db661 Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/Ellipse-1.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/FlatteningPathIterator-1.html b/libjava/classpath/java/awt/geom/doc-files/FlatteningPathIterator-1.html new file mode 100644 index 000000000..5a52d693e --- /dev/null +++ b/libjava/classpath/java/awt/geom/doc-files/FlatteningPathIterator-1.html @@ -0,0 +1,481 @@ + + + + + The GNU Implementation of java.awt.geom.FlatteningPathIterator + + + + + +

    The GNU Implementation of FlatteningPathIterator

    + +

    Sascha +Brawer, November 2003

    + +

    This document describes the GNU implementation of the class +java.awt.geom.FlatteningPathIterator. It does +not describe how a programmer should use this class; please +refer to the generated API documentation for this purpose. Instead, it +is intended for maintenance programmers who want to understand the +implementation, for example because they want to extend the class or +fix a bug.

    + + +

    Data Structures

    + +

    The algorithm uses a stack. Its allocation is delayed to the time +when the source path iterator actually returns the first curved +segment (either SEG_QUADTO or SEG_CUBICTO). +If the input path does not contain any curved segments, the value of +the stack variable stays null. In this quite +common case, the memory consumption is minimal.

    + +
    stack
    The variable stack is +a double array that holds the start, control and end +points of individual sub-segments.
    + +
    recLevel
    The variable recLevel +holds how many recursive sub-divisions were needed to calculate a +segment. The original curve has recursion level 0. For each +sub-division, the corresponding recursion level is increased by +one.
    + +
    stackSize
    Finally, the variable +stackSize indicates how many sub-segments are stored on +the stack.
    + +

    Algorithm

    + +

    The implementation separately processes each segment that the +base iterator returns.

    + +

    In the case of SEG_CLOSE, +SEG_MOVETO and SEG_LINETO segments, the +implementation simply hands the segment to the consumer, without actually +doing anything.

    + +

    Any SEG_QUADTO and SEG_CUBICTO segments +need to be flattened. Flattening is performed with a fixed-sized +stack, holding the coordinates of subdivided segments. When the base +iterator returns a SEG_QUADTO and +SEG_CUBICTO segments, it is recursively flattened as +follows:

    + +
    1. Intialization: Allocate memory for the stack (unless a +sufficiently large stack has been allocated previously). Push the +original quadratic or cubic curve onto the stack. Mark that segment as +having a recLevel of zero.
    2. + +
    3. If the stack is empty, flattening the segment is complete, +and the next segment is fetched from the base iterator.
    4. + +
    5. If the stack is not empty, pop a curve segment from the +stack. + +
      • If its recLevel exceeds the recursion limit, + hand the current segment to the consumer.
      • + +
      • Calculate the squared flatness of the segment. If it smaller + than flatnessSq, hand the current segment to the + consumer.
      • + +
      • Otherwise, split the segment in two halves. Push the right + half onto the stack. Then, push the left half onto the stack. + Continue with step two.
    6. +
    + +

    The implementation is slightly complicated by the fact that +consumers pull the flattened segments from the +FlatteningPathIterator. This means that we actually +cannot “hand the curent segment over to the consumer.” +But the algorithm is easier to understand if one assumes a +push paradigm.

    + + +

    Example

    + +

    The following example shows how a +FlatteningPathIterator processes a +SEG_QUADTO segment. It is (arbitrarily) assumed that the +recursion limit was set to 2.

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ABCDEFGH
    stack[0]Sll.x
    stack[1]Sll.y
    stack[2]Cll.x
    stack[3]Cll.y
    stack[4]Sl.xEll.x + = Slr.xSlr.xSrl.x
    stack[5]Sl.yEll.x + = Slr.ySlr.ySrl.y
    stack[6]Cl.xClr.xClr.xCrl.x
    stack[7]Cl.yClr.yClr.yCrl.y
    stack[8]S.xEl.x + = Sr.xElr.x + = Sr.xElr.x + = Sr.xSr.xErl.x + = Srr.xSrr.x
    stack[9]S.yEl.y + = Sr.yElr.y + = Sr.yElr.y + = Sr.ySr.yErl.y + = Srr.ySrr.y
    stack[10]C.xCr.xCr.xCr.xCr.xCrr.xCrr.x
    stack[11]C.yCr.yCr.yCr.yCr.yCrr.yCrr.y
    stack[12]E.xEr.xEr.xEr.xEr.xErr.xErr.x
    stack[13]E.yEr.yEr.yEr.yEr.yErr.yErr.x
    stackSize12321210
    recLevel[2]2
    recLevel[1]1222
    recLevel[0]0111122
    +
    + +
      + +
    1. The data structures are initialized as follows. + +
      • The segment’s end point E, control point +C, and start point S are pushed onto the stack.
      • + +
      • Currently, the curve in the stack would be approximated by one + single straight line segment (SE). + Therefore, stackSize is set to 1.
      • + +
      • This single straight line segment is approximating the original + curve, which can be seen as the result of zero recursive + splits. Therefore, recLevel[0] is set to + zero.
      + +Column A shows the state after the initialization step.
    2. + +
    3. The algorithm proceeds by taking the topmost curve segment +(SCE) from the stack. + +
      • The recursion level of this segment (stored in + recLevel[0]) is zero, which is smaller than + the limit 2.
      • + +
      • The method java.awt.geom.QuadCurve2D.getFlatnessSq + is called to calculate the squared flatness.
      • + +
      • For the sake of argument, we assume that the squared flatness is + exceeding the threshold stored in flatnessSq. Thus, the + curve segment SCE gets + subdivided into a left and a right half, namely + SlCl – + El and Sr – + CrEr. Both halves are + pushed onto the stack, so the left half is now on top. + +
         
        The left half starts at the same point + as the original curve, so Sl has the same + coordinates as S. Similarly, the end point of the right + half and of the original curve are identical + (Er = E). More interestingly, the left + half ends where the right half starts. Because + El = Sr, their coordinates need + to be stored only once, which amounts to saving 16 bytes (two + double values) for each iteration.
      + +Column B shows the state after the first iteration.
    4. + +
    5. Again, the topmost curve segment (Sl +– ClEl) is +taken from the stack. + +
      • The recursion level of this segment (stored in + recLevel[1]) is 1, which is smaller than + the limit 2.
      • + +
      • The method java.awt.geom.QuadCurve2D.getFlatnessSq + is called to calculate the squared flatness.
      • + +
      • Assuming that the segment is still not considered + flat enough, it gets subdivided into a left + (SllCll – + Ell) and a right (Slr + – ClrElr) + half.
      + +Column C shows the state after the second iteration.
    6. + +
    7. The topmost curve segment (Sll – +CllEll) is popped from +the stack. + +
      • The recursion level of this segment (stored in + recLevel[2]) is 2, which is not smaller than + the limit 2. Therefore, a SEG_LINETO (from + Sll to Ell) is passed to the + consumer.
      + + The new state is shown in column D.
    8. + + +
    9. The topmost curve segment (Slr – +ClrElr) is popped from +the stack. + +
      • The recursion level of this segment (stored in + recLevel[1]) is 2, which is not smaller than + the limit 2. Therefore, a SEG_LINETO (from + Slr to Elr) is passed to the + consumer.
      + + The new state is shown in column E.
    10. + +
    11. The algorithm proceeds by taking the topmost curve segment +(SrCr – +Er) from the stack. + +
      • The recursion level of this segment (stored in + recLevel[0]) is 1, which is smaller than + the limit 2.
      • + +
      • The method java.awt.geom.QuadCurve2D.getFlatnessSq + is called to calculate the squared flatness.
      • + +
      • For the sake of argument, we again assume that the squared + flatness is exceeding the threshold stored in + flatnessSq. Thus, the curve segment + (SrCr – + Er) is subdivided into a left and a right half, + namely + SrlCrl – + Erl and Srr – + CrrErr. Both halves + are pushed onto the stack.
      + + The new state is shown in column F.
    12. + +
    13. The topmost curve segment (Srl – +CrlErl) is popped from +the stack. + +
      • The recursion level of this segment (stored in + recLevel[2]) is 2, which is not smaller than + the limit 2. Therefore, a SEG_LINETO (from + Srl to Erl) is passed to the + consumer.
      + + The new state is shown in column G.
    14. + +
    15. The topmost curve segment (Srr – +CrrErr) is popped from +the stack. + +
      • The recursion level of this segment (stored in + recLevel[2]) is 2, which is not smaller than + the limit 2. Therefore, a SEG_LINETO (from + Srr to Err) is passed to the + consumer.
      + + The new state is shown in column H.
    16. + +
    17. The stack is now empty. The FlatteningPathIterator will fetch the +next segment from the base iterator, and process it.
    18. + +
    + +

    In order to split the most recently pushed segment, the +subdivideQuadratic() method passes stack +directly to +QuadCurve2D.subdivide(double[],int,double[],int,double[],int). +Because the stack grows towards the beginning of the array, no data +needs to be copied around: subdivide will directly store +the result into the stack, which will have the contents shown to the +right.

    + + + diff --git a/libjava/classpath/java/awt/geom/doc-files/GeneralPath-1.png b/libjava/classpath/java/awt/geom/doc-files/GeneralPath-1.png new file mode 100644 index 000000000..d1d75d575 Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/GeneralPath-1.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-1.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-1.png new file mode 100644 index 000000000..7c2ec0ea9 Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-1.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-2.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-2.png new file mode 100644 index 000000000..496180c44 Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-2.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-3.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-3.png new file mode 100644 index 000000000..a7557ba7b Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-3.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-4.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-4.png new file mode 100644 index 000000000..835c0643b Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-4.png differ diff --git a/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-5.png b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-5.png new file mode 100644 index 000000000..72110cd5a Binary files /dev/null and b/libjava/classpath/java/awt/geom/doc-files/QuadCurve2D-5.png differ diff --git a/libjava/classpath/java/awt/geom/package.html b/libjava/classpath/java/awt/geom/package.html new file mode 100644 index 000000000..c8ee8272f --- /dev/null +++ b/libjava/classpath/java/awt/geom/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.geom + + +

    Classes to represent 2D objects and different path transformations.

    + + + diff --git a/libjava/classpath/java/awt/im/InputContext.java b/libjava/classpath/java/awt/im/InputContext.java new file mode 100644 index 000000000..8667272b8 --- /dev/null +++ b/libjava/classpath/java/awt/im/InputContext.java @@ -0,0 +1,438 @@ +/* InputContext.java -- provides the context for text input + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.im; + +import gnu.java.util.EmptyEnumeration; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.Component; +import java.awt.im.spi.InputMethod; +import java.awt.im.spi.InputMethodDescriptor; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.text.AttributedCharacterIterator.Attribute; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Locale; + +/** + * Provides a context for controlling input methods and keyboard layouts. + * This class provides the communication layer between the client component, + * and the various locale-dependent text entry input methods that can be used + * for the client. By default, there is one instance per Window, shared among + * all components, but this limits text entry to one component at a time. + * Thus, text components can create their own instance to allow text entry + * in multiple components at a time. + * + *

    By using the interfaces of {@link java.awt.im.spi}, you can install + * extensions which allow additional input methods. Some of these may use + * platform native input methods, or keyboard layouts provided by the platform. + * Input methods are unavailable if none have been installed and the platform + * has no underlying native input methods. Extensions are installed as jar + * files, usually accessed in the default extension location or specified by + * the -extdir VM flag. The jar must contain a file named + * "META_INF/services/java.awt.im.spi.InputMethodDescriptor" which lists, + * one entry per line in UTF-8 encoding, each class in the jar that implements + * java.awt.im.spi.InputMethodDescriptor. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Component#getInputContext() + * @see Component#enableInputMethods(boolean) + * @since 1.2 + * @status updated to 1.4, but unverified + */ +public class InputContext +{ + /** + * The list of installed input method descriptors. + */ + private static final ArrayList descriptors + = new ArrayList(); + + static + { + Enumeration e; + try + { + e = ClassLoader.getSystemResources + ("META_INF/services/java.awt.im.spi.InputMethodDescriptor"); + } + catch (IOException ex) + { + // XXX Should we do something else? + e = EmptyEnumeration.getInstance(); + } + while (e.hasMoreElements()) + { + URL url = (URL) e.nextElement(); + BufferedReader in; + String line; + try + { + in = new BufferedReader + (new InputStreamReader(url.openConnection().getInputStream(), + "UTF-8")); + line = in.readLine().trim(); + } + catch (IOException ignored) + { + continue; + } + outer: + while (line != null) + { + try + { + if (line.charAt(0) != '#') + { + Class c = Class.forName(line); + descriptors.add((InputMethodDescriptor) c.newInstance()); + } + line = in.readLine().trim(); + } + catch (IOException ex) + { + continue outer; + } + catch (Exception ignored) + { + } + } + } + } + + /** The current input method; null if no input methods are installed. */ + private InputMethod im; + + /** Map of locales to the most recently selected input method. */ + private final HashMap recent + = new HashMap(); + + /** The list of acceptable character subsets. */ + private Character.Subset[] subsets; + + /** + * Construct an InputContext. This is protected, so clients must use + * {@link #getInstance()} instead. + */ + protected InputContext() + { + } + + /** + * Returns a new InputContext. + * + * @return a new instance, initialized to the default locale if available + */ + public static InputContext getInstance() + { + InputContext ic = new InputContext(); + ic.selectInputMethod(Locale.getDefault()); + return ic; + } + + /** + * Attempts to select an input method or keyboard layout which supports the + * given locale. This returns true if a locale is available and was selected. + * The following steps are taken in choosing an input method:

      + *
    • If the currently selected input method or keyboard layout supports + * the requested locale, it remains selected.
    • + *
    • If there is no input method or keyboard layout available that + * supports the requested locale, the current input method or keyboard + * layout remains selected.
    • + *
    • If the user has previously selected an input method or keyboard + * layout for the requested locale from the user interface, then the most + * recently selected such input method or keyboard layout is reselected.
    • + *
    • Otherwise, an input method or keyboard layout that supports the + * requested locale is selected in an implementation dependent way. This + * implementation chooses the first input method which supports the requested + * locale based on the InputMethodDescriptors loaded from the extensions + * installed on the CLASSPATH.
    • + *
    + * + *

    Before switching away from an input method, any currently uncommitted + * text is committed. Not all host operating systems provide API to + * determine the locale of the currently selected native input method or + * keyboard layout, and to select a native input method or keyboard layout + * by locale. For host operating systems that don't provide such API, + * selectInputMethod assumes that native input methods or keyboard layouts + * provided by the host operating system support only the system's default + * locale. + * + *

    An example of where this may be called is in a multi-language document, + * when moving the insertion point between sections of different locale, so + * that the user may use the input method appropriate to that section of the + * document. + * + * @param locale the desired new locale + * @return true if the new locale is active + * @throws NullPointerException if locale is null + */ + public boolean selectInputMethod(Locale locale) + { + if (im != null && im.setLocale(locale)) + { + recent.put(locale, im); + return true; + } + InputMethod next = recent.get(locale); + if (next != null) + for (int i = 0, limit = descriptors.size(); i < limit; i++) + { + InputMethodDescriptor d = descriptors.get(i); + Locale[] list; + try + { + list = d.getAvailableLocales(); + } + catch (AWTException ignored) + { + continue; + } + for (int j = list.length; --j >= 0; ) + if (locale.equals(list[j])) + { + try + { + next = d.createInputMethod(); + recent.put(locale, next); + } + catch (Exception ignored) + { + continue; + } + } + } + if (next == null) + return false; + // XXX I'm not sure if this does all the necessary steps in the switch. + if (im != null) + { + try + { + next.setCompositionEnabled(im.isCompositionEnabled()); + } + catch (UnsupportedOperationException ignored) + { + } + im.endComposition(); + im.deactivate(false); + im.hideWindows(); + } + im = next; + im.setLocale(locale); + im.setCharacterSubsets(subsets); + return true; + } + + /** + * Returns the current locale of the current input method or keyboard + * layout. Returns null if the input context does not have a current input + * method or keyboard layout or if the current input method's + * {@link InputMethod#getLocale()} method returns null. Not all host + * operating systems provide API to determine the locale of the currently + * selected native input method or keyboard layout. For host operating + * systems that don't provide such API, getLocale assumes that the current + * locale of all native input methods or keyboard layouts provided by the + * host operating system is the system's default locale. + * + * @return the locale of the current input method, or null + * @since 1.3 + */ + public Locale getLocale() + { + return im == null ? null : im.getLocale(); + } + + /** + * Sets the subsets of Unicode characters allowed to be input by the current + * input method, as well as subsequent input methods. The value of null + * implies all characters are legal. Applications should not rely on this + * behavior, since native host input methods may not allow restrictions. + * If no current input method is available, this has no immediate effect. + * + * @param subsets the set of Unicode subsets to accept, or null + */ + public void setCharacterSubsets(Character.Subset[] subsets) + { + this.subsets = subsets; + if (im != null) + im.setCharacterSubsets(subsets); + } + + /** + * Changes the enabled status of the current input method. An input method + * that is enabled for composition interprets incoming events for both + * composition and control purposes, while a disabled input method only + * interprets control commands (including commands to enable itself). + * + * @param enable whether to enable the input method + * @throws UnsupportedOperationException if there is no current input method, + * or the input method does not support enabling + * @see #isCompositionEnabled() + * @since 1.3 + */ + public void setCompositionEnabled(boolean enable) + { + if (im == null) + throw new UnsupportedOperationException(); + im.setCompositionEnabled(enable); + } + + /** + * Find out if the current input method is enabled. + * + * @return true if the current input method is enabled + * @throws UnsupportedOperationException if there is no current input method, + * or the input method does not support enabling + * @see #setCompositionEnabled(boolean) + * @since 1.3 + */ + public boolean isCompositionEnabled() + { + if (im == null) + throw new UnsupportedOperationException(); + return im.isCompositionEnabled(); + } + + /** + * Starts a reconversion operation in the current input method. The input + * method gets the text to reconvert from the client component, using + * {@link InputMethodRequests#getSelectedText(Attribute[])}. Then the + * composed and committed text produced by the operation is sent back to + * the client using a sequence of InputMethodRequests. + * + * @throws UnsupportedOperationException if there is no current input method, + * or the input method does not support reconversion + * @since 1.3 + */ + public void reconvert() + { + if (im == null) + throw new UnsupportedOperationException(); + im.reconvert(); + } + + /** + * Dispatches an event to the current input method. This is called + * automatically by AWT. If no input method is available, then the event + * will never be consumed. + * + * @param event the event to dispatch + * @throws NullPointerException if event is null + */ + public void dispatchEvent(AWTEvent event) + { + if (im != null) + im.dispatchEvent(event); + } + + /** + * Notifies the input context that a client component has been removed from + * its containment hierarchy, or that input method support has been disabled + * for the component. This method is usually called from the client + * component's {@link Component#removeNotify()} method. Potentially pending + * input from input methods for this component is discarded. If no input + * methods are available, then this method has no effect. + * + * @param client the client component + * @throws NullPointerException if client is null + */ + public void removeNotify(Component client) + { + // XXX What to do with client information? + if (im != null) + { + im.deactivate(false); + im.removeNotify(); + } + } + + /** + * Ends any input composition that may currently be going on in this + * context. Depending on the platform and possibly user preferences, this + * may commit or delete uncommitted text. Any changes to the text are + * communicated to the active component using an input method event. If no + * input methods are available, then this method has no effect. This may + * be called for a variety of reasons, such as when the user moves the + * insertion point in the client text outside the range of the composed text, + * or when text is saved to file. + */ + public void endComposition() + { + if (im != null) + im.endComposition(); + } + + /** + * Disposes of the input context and release the resources used by it. + * Called automatically by AWT for the default input context of each + * Window. If no input methods are available, then this method has no + * effect. + */ + public void dispose() + { + if (im != null) + { + im.deactivate(false); + im.dispose(); + } + } + + /** + * Returns a control object from the current input method, or null. A + * control object provides implementation-dependent methods that control + * the behavior of the input method or obtain information from the input + * method. Clients have to compare the result against known input method + * control object types. If no input methods are available or the current + * input method does not provide an input method control object, then null + * is returned. + * + * @return the control object, or null + */ + public Object getInputMethodControlObject() + { + return im == null ? null : im.getControlObject(); + } +} // class InputContext diff --git a/libjava/classpath/java/awt/im/InputMethodHighlight.java b/libjava/classpath/java/awt/im/InputMethodHighlight.java new file mode 100644 index 000000000..a2ee86d44 --- /dev/null +++ b/libjava/classpath/java/awt/im/InputMethodHighlight.java @@ -0,0 +1,189 @@ +/* InputMethodHighlight.java -- highlights the current text selection + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im; + +import java.awt.Toolkit; +import java.text.Annotation; +import java.text.AttributedCharacterIterator; +import java.util.Map; +import java.awt.font.TextAttribute; + +/** + * This describes the highlight attributes of text composed in an input method. + * The description includes an abstract level (whether text has been converted + * yet, and whether it is selected), and a concrete level (which style + * attributes are used in rendering). If no concrete level is defined, the + * renderer should use + * {@link Toolkit#mapInputMethodHighlight(InputMethodHighlight)}. An example + * of conversion state is kana -> kanji. + * + *

    Instances of this class are typically used in + * AttributedCharacterIterators, and may be wrapped in Annotations to separate + * text segments. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see AttributedCharacterIterator + * @see Annotation + * @since 1.2 + * @status updated to 1.4 + */ +public class InputMethodHighlight +{ + /** Raw text state (before conversion). */ + public static final int RAW_TEXT = 0; + + /** Converted text state (after conversion). */ + public static final int CONVERTED_TEXT = 1; + + /** Default do-nothing highlighting for unselected raw text. */ + public static final InputMethodHighlight UNSELECTED_RAW_TEXT_HIGHLIGHT + = new InputMethodHighlight(false, RAW_TEXT); + + /** Default do-nothing highlighting for selected raw text. */ + public static final InputMethodHighlight SELECTED_RAW_TEXT_HIGHLIGHT + = new InputMethodHighlight(true, RAW_TEXT); + + /** Default do-nothing highlighting for unselected converted text. */ + public static final InputMethodHighlight UNSELECTED_CONVERTED_TEXT_HIGHLIGHT + = new InputMethodHighlight(false, CONVERTED_TEXT); + + /** Default do-nothing highlighting for selected converted text. */ + public static final InputMethodHighlight SELECTED_CONVERTED_TEXT_HIGHLIGHT + = new InputMethodHighlight(true, CONVERTED_TEXT); + + /** Whether the highlighting applies to selected text. */ + private final boolean selected; + + /** The state of highlighted text. */ + private final int state; + + /** Any variation on the highlighting style. */ + private final int variation; + + /** The unmodifiable map of rendering styles. */ + private final Map style; + + /** + * Create an input method highlight style, with variation 0 and null style + * mapping. + * + * @param selected whether the text range is selected + * @param state either {@link #RAW_TEXT} or {@link #CONVERTED_TEXT} + * @throws IllegalArgumentException if state is invalid + */ + public InputMethodHighlight(boolean selected, int state) + { + this(selected, state, 0, null); + } + + /** + * Create an input method highlight style, with null style mapping. + * + * @param selected whether the text range is selected + * @param state either {@link #RAW_TEXT} or {@link #CONVERTED_TEXT} + * @param variation the style variation + * @throws IllegalArgumentException if state is invalid + */ + public InputMethodHighlight(boolean selected, int state, int variation) + { + this(selected, state, variation, null); + } + + /** + * Create an input method highlight style. + * + * @param selected whether the text range is selected + * @param state either {@link #RAW_TEXT} or {@link #CONVERTED_TEXT} + * @param variation the style variation + * @param style an unmodifiable map of rendering styles, or null + * @throws IllegalArgumentException if state is invalid + * @since 1.3 + */ + public InputMethodHighlight(boolean selected, int state, int variation, + Map style) + { + if (state != RAW_TEXT && state != CONVERTED_TEXT) + throw new IllegalArgumentException(); + this.selected = selected; + this.state = state; + this.variation = variation; + this.style = style; + } + + /** + * Return whether the highlighting applies to selected text. + * + * @return the selection status + */ + public boolean isSelected() + { + return selected; + } + + /** + * Return the conversion state of the highlighted text. + * + * @return one of {@link #RAW_TEXT} or {@link #CONVERTED_TEXT} + */ + public int getState() + { + return state; + } + + /** + * Return the highlighting style variation. + * + * @return the variation + */ + public int getVariation() + { + return variation; + } + + /** + * Return the rendering style attributes map, or null if it should be the + * default mapping. + * + * @return the style map + * @since 1.3 + */ + public Map getStyle() + { + return style; + } +} // class InputMethodHighlight diff --git a/libjava/classpath/java/awt/im/InputMethodRequests.java b/libjava/classpath/java/awt/im/InputMethodRequests.java new file mode 100644 index 000000000..0423358cc --- /dev/null +++ b/libjava/classpath/java/awt/im/InputMethodRequests.java @@ -0,0 +1,155 @@ +/* InputMethodRequests.java -- handles text insertion via input methods + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im; + +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.font.TextHitInfo; +import java.awt.event.InputMethodListener; +import java.text.AttributedCharacterIterator; +import java.text.AttributedCharacterIterator.Attribute; + +/** + * This interface handles requests made by input methods on text editing + * components. A component must specify a handler for input methods that + * implements this interface, and which supports one of two user interfaces: + *

    • on-the-spot: composed text is shown in place
    • + *
    • below-the-spot: composed text is in a separate window, + * usually below the main text window, until it is committed into place at + * the insertion point, overwriting any selected text
    + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Component#getInputMethodRequests() + * @see InputMethodListener + * @since 1.2 + * @status updated to 1.4 + */ +public interface InputMethodRequests +{ + /** + * Gets the location of a given offset of the text. This can be used to + * position a composition window near the location of where the composed + * text will be inserted. + * + *

    If the component has composed text (from the most recent + * InputMethodEvent), then offset 0 indicates the location of the first + * character of this composed text. Otherwise, the offset is ignored, and + * the location should be the beginning of the final line of selected + * text (in horizontal left-to-right text, like English, this would be the + * lower left corner of the selction; in vertical top-to-bottom text, like + * Chinese, this would be the top right corner of the selection). + * + *

    The location returned is a 0-thickness caret (either horizontal or + * vertical, depending on text flow), mapped to absolute screen coordinates. + * + * @param offset offset within composed text, or null + * @return the screen location of the caret at the offset + */ + Rectangle getTextLocation(TextHitInfo offset); + + /** + * Get the text offset for the given screen coordinate. The offset is + * relative to the composed text, and the return is null if it is outside + * the range of composed text. For example, this can be used to find + * where a mouse click should pop up a text composition window. + * + * @param x the x screen coordinate + * @param y the y screen coordinate + * @return a text hit info describing the composed text offset + */ + TextHitInfo getLocationOffset(int x, int y); + + /** + * Gets the offset where the committed text exists in the text editing + * component. This can be used to examine the text surrounding the insert + * position. + * + * @return the offset of the insert position + */ + int getInsertPositionOffset(); + + /** + * Gets an interator which provides access to the text and its attributes, + * except for the uncommitted text. The input method may provide a list of + * attributes it is interested in; and the iterator need not provide + * information on the remaining attributes. If the attribute list is null, + * the iterator must list all attributes. + * + * @param beginIndex the index of the first character in the iteration + * @param endIndex the index of the last character in the iteration + * @param attributes a list of attributes interested in, or null + * @return an iterator over the region of text with its attributes + */ + AttributedCharacterIterator getCommittedText(int beginIndex, int endIndex, + Attribute[] attributes); + + /** + * Gets the length of committed text. + * + * @return the number of committed characters + */ + int getCommittedTextLength(); + + /** + * Gets the latest committed text, and removes it from the component's text + * body. This allows an input method to provide an "Undo" command. In + * general, this should only be supported immediately after a commit, and + * not when other actions intervene; if not supported, simply return null. + * The input method may provide a list of attributes it is interested in; + * and the iterator need not provide information on the remaining attributes. + * If the attribute list is null, the iterator must list all attributes. + * + * @param attributes a list of attributes interested in, or null + * @return the latest committed text, or null + */ + AttributedCharacterIterator cancelLatestCommittedText + (Attribute[] attributes); + + /** + * Gets the currently selected text. One use of this is to implement a + * "Reconvert" feature in an input method, which modifies the selection + * based on the text in the composition window. The input method may + * provide a list of attributes it is interested in; and the iterator need + * not provide information on the remaining attributes. If the attribute + * list is null, the iterator must list all attributes. + * + * @param attributes a list of attributes interested in, or null + * @return the current selection + */ + AttributedCharacterIterator getSelectedText(Attribute[] attributes); +} // interface InputMethodRequests diff --git a/libjava/classpath/java/awt/im/InputSubset.java b/libjava/classpath/java/awt/im/InputSubset.java new file mode 100644 index 000000000..5e7d58e7f --- /dev/null +++ b/libjava/classpath/java/awt/im/InputSubset.java @@ -0,0 +1,129 @@ +/* InputSubset.java -- subsets of Unicode important in text input + Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im; + +/** + * Defines additional Unicode character blocks for use by input methods. + * These constants encompass several Unicode blocks, or portions thereof, for + * simplification over {@link Character.UnicodeBlock}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public final class InputSubset extends Character.Subset +{ + /** + * Constant for all Latin characters, including the characters in the + * BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A, LATIN_EXTENDED_B + * Unicode character blocks. + */ + public static final InputSubset LATIN = new InputSubset("LATIN"); + + /** + * Constant for the digits included in the BASIC_LATIN Unicode character + * block. + */ + public static final InputSubset LATIN_DIGITS + = new InputSubset("LATIN_DIGITS"); + + /** + * Constant for all Han characters used in writing Traditional Chinese, + * including a subset of the CJK unified ideographs as well as Traditional + * Chinese Han characters that may be defined as surrogate characters. + */ + public static final InputSubset TRADITIONAL_HANZI + = new InputSubset("TRADITIONAL_HANZI"); + + /** + * Constant for all Han characters used in writing Simplified Chinese, + * including a subset of the CJK unified ideographs as well as Simplified + * Chinese Han characters that may be defined as surrogate characters. + */ + public static final InputSubset SIMPLIFIED_HANZI + = new InputSubset("SIMPLIFIED_HANZI"); + + /** + * Constant for all Han characters used in writing Japanese, including a + * subset of the CJK unified ideographs as well as Japanese Han characters + * that may be defined as surrogate characters. + */ + public static final InputSubset KANJI = new InputSubset("KANJI"); + + /** + * Constant for all Han characters used in writing Korean, including a + * subset of the CJK unified ideographs as well as Korean Han characters + * that may be defined as surrogate characters. + */ + public static final InputSubset HANJA = new InputSubset("HANJA"); + + /** + * Constant for the halfwidth katakana subset of the Unicode halfwidth and + * fullwidth forms character block. + */ + public static final InputSubset HALFWIDTH_KATAKANA + = new InputSubset("HALFWIDTH_KATAKANA"); + + /** + * Constant for the fullwidth ASCII variants subset of the Unicode + * halfwidth and fullwidth forms character block. + * + * @since 1.3 + */ + public static final InputSubset FULLWIDTH_LATIN + = new InputSubset("FULLWIDTH_LATIN"); + + /** + * Constant for the fullwidth digits included in the Unicode halfwidth and + * fullwidth forms character block. + * + * @since 1.3 + */ + public static final InputSubset FULLWIDTH_DIGITS + = new InputSubset("FULLWIDTH_DIGITS"); + + /** + * Construct a subset. + * + * @param name the subset name + */ + private InputSubset(String name) + { + super(name); + } +} // class InputSubset diff --git a/libjava/classpath/java/awt/im/package.html b/libjava/classpath/java/awt/im/package.html new file mode 100644 index 000000000..895da66d5 --- /dev/null +++ b/libjava/classpath/java/awt/im/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.im + + +

    Support for text input methods.

    + + + diff --git a/libjava/classpath/java/awt/im/spi/InputMethod.java b/libjava/classpath/java/awt/im/spi/InputMethod.java new file mode 100644 index 000000000..ebe450841 --- /dev/null +++ b/libjava/classpath/java/awt/im/spi/InputMethod.java @@ -0,0 +1,244 @@ +/* InputMethod.java -- defines an interface for complex text input + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im.spi; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.im.InputContext; +import java.awt.im.InputMethodRequests; +import java.text.AttributedCharacterIterator.Attribute; +import java.util.Locale; + +/** + * This interface supports complex text input, often for situations where + * the text is more complex than a keyboard will accomodate. For example, + * this can be used for Chinese, Japanese, and Korean, where multiple + * keystrokes are necessary to compose text. This could also support things + * like phonetic English, or reordering Thai. + * + *

    These contexts can be loaded by the input method framework, using + * {@link InputContext#selectInputMethod(Locale)}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ +public interface InputMethod +{ + /** + * Set the input method context, which ties the input method to a client + * component. This is called once automatically when creating the input + * method. + * + * @param context the context for this input method + * @throws NullPointerException if context is null + */ + void setInputMethodContext(InputMethodContext context); + + /** + * Sets the input locale. If the input method supports that locale, it + * changes its behavior to be consistent with the locale and returns true. + * Otherwise, it returns false. This is called by + * {@link InputContext#selectInputMethod(Locale)} when the user specifies + * a locale, or when the previously selected input method had a locale. + * + * @param locale the locale to use for input + * @return true if the change is successful + * @throws NullPointerException if locale is null + */ + boolean setLocale(Locale locale); + + /** + * Returns the current input locale, or null if none is defined. This is + * called by {@link InputContext#getLocale()}, or before switching input + * methods. + * + * @return the current input locale, or null + */ + Locale getLocale(); + + /** + * Sets the allowed Unicode subsets that this input method can use. Null + * indicates that all characters are allowed. This is called after creation, + * or when switching to this input method, by + * {@link InputContext#setCharacterSubsets(Character.Subset[])}. + * + * @param subsets the accepted subsets for this input method, or null for all + */ + void setCharacterSubsets(Character.Subset[] subsets); + + /** + * Changes the enabled status of this input method. An enabled input method + * accepts incoming events for composition and control purposes, while a + * disabled input method ignores events (except for control purposes). This + * is called by {@link InputContext#setCompositionEnabled(boolean)} or when + * switching from an input method if the previous input method returned + * without exception on {@link #isCompositionEnabled()}. + * + * @param enable whether to enable this input method + * @throws UnsupportedOperationException if enabling/disabling is unsupported + * @see #isCompositionEnabled() + */ + void setCompositionEnabled(boolean enable); + + /** + * Find out if this input method is enabled. This is called by + * {@link InputContext#isCompositionEnabled()}, or when switching input + * methods via {@link InputContext#selectInputMethod(Locale)}. + * + * @return true if this input method is enabled + * @throws UnsupportedOperationException if enabling/disabling is unsupported + * @see #setCompositionEnabled(boolean) + */ + boolean isCompositionEnabled(); + + /** + * Starts a reconversion operation. The input method gets its text from the + * client, using {@link InputMethodRequests#getSelectedText(Attribute[])}. + * Then the composed and committed text produced by the operation is sent + * back to the client using a sequence of InputMethodEvents. This is called + * by {@link InputContext#reconvert()}. + * + * @throws UnsupportedOperationException if reconversion is unsupported + */ + void reconvert(); + + /** + * Dispatch an event to the input method. If input method support is enabled, + * certain events are dispatched to the input method before the client + * component or event listeners. The input method must either consume the + * event or pass it on to the component. Instances of InputEvent, including + * KeyEvent and MouseEvent, are given to this input method. This method is + * called by {@link InputContext#dispatchEvent(AWTEvent)}. + * + * @param event the event to dispatch + * @throws NullPointerException if event is null + */ + void dispatchEvent(AWTEvent event); + + /** + * Notify this input method of changes in the client window. This is called + * when notifications are enabled (see {@link + * InputMethodContext#enableClientWindowNotification(InputMethod, boolean)}, + * if {@link InputContext#removeNotify(Component)} has not been called. + * The following situations trigger a notification:

      + *
    • The client window changes in location, size, visibility, + * iconification, or is closed.
    • + *
    • When enabling client notification (or on the first activation after + * enabling if no client existed at the time).
    • + *
    • When activating a new client after removeNotify was + * called on a previous client.
    • + *
    + * + * @param bounds the client window's current bounds, or null + */ + void notifyClientWindowChange(Rectangle bounds); + + /** + * Activate this input method for input processing. If the input method + * provides its own windows, it should make them open and visible at this + * time. This method is called when a client component receives a + * FOCUS_GAINED event, or when switching to this input method from another + * one. It is only called when the input method is inactive, assuming that + * new instances begin in an inactive state. + */ + void activate(); + + /** + * Deactivate this input method, either temporarily or permanently for the + * given client. If the input method provides its own windows, it should + * only close those related to the current composition (such as a lookup + * choice panel), while leaving more persistant windows (like a control + * panel) open to avoid screen flicker. Before control is given to another + * input method, {@link #hideWindows()} will be called on this instance. + * This method is called when a client component receives a + * FOCUS_LOST event, when switching to another input method, or before + * {@link #removeNotify()} when the client is removed. + * + * @param isTemporary true if the focus change is temporary + */ + void deactivate(boolean isTemporary); + + /** + * Close or hide all windows opened by this input method. This is called + * before activating a different input method, and before calling + * {@link #dispose()} on this instance. It is only called when the input + * method is inactive. + */ + void hideWindows(); + + /** + * Notify the input method that a client component has been removed from its + * hierarchy, or that input method support has been disabled. This is + * called by {@link InputContext#removeNotify(Component)}, and only when the input + * method is inactive. + */ + void removeNotify(); + + /** + * End any input composition currently taking place. Depending on the + * platform and user preferences, this may commit or delete uncommitted text, + * using input method events. This may be called for a variety of reasons, + * such as when the user moves the insertion point in the client text outside + * the range of the composed text, or when text is saved to file. This is + * called by {@link InputContext#endComposition()}, when switching to a + * new input method, or by {@link InputContext#selectInputMethod(Locale)}. + */ + void endComposition(); + + /** + * Disposes the input method and release any resources it is using. In + * particular, the input method should dispose windows and close files. This + * is called by {@link InputContext#dispose()}, when the input method is + * inactive; and nothing will be called on this instance afterwards. + */ + void dispose(); + + /** + * Returns a control object from this input method, or null. A control object + * provides method to control the behavior of this input method, as well as + * query information about it. The object is implementation dependent, so + * clients must compare the result against known input method control + * object types. This is called by + * {@link InputContext#getInputMethodControlObject()}. + * + * @return the control object, or null + */ + Object getControlObject(); +} // interface InputMethod diff --git a/libjava/classpath/java/awt/im/spi/InputMethodContext.java b/libjava/classpath/java/awt/im/spi/InputMethodContext.java new file mode 100644 index 000000000..aed21e8d3 --- /dev/null +++ b/libjava/classpath/java/awt/im/spi/InputMethodContext.java @@ -0,0 +1,126 @@ +/* InputMethodContext.java -- communication between an input method and client + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.im.spi; + +import java.awt.HeadlessException; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.font.TextHitInfo; +import java.awt.im.InputMethodRequests; +import java.text.AttributedCharacterIterator; + +import javax.swing.JFrame; + +/** + * Provides methods for the communication context between an input method + * and the client component. This should be passed to + * {@link InputMethod#setInputMethodContext(InputMethodContext)}. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.3 + * @status updated to 1.4 + */ +public interface InputMethodContext extends InputMethodRequests +{ + /** + * Create an input method event and dispatch it to the client. + * + * @param id the event type + * @param text an iterator over the text to be committed + * @param count the count of characters to be committed + * @param caret the insertion point of the commit, or null + * @param visiblePosition the best location to make visible, or null + */ + void dispatchInputMethodEvent(int id, AttributedCharacterIterator text, + int count, TextHitInfo caret, + TextHitInfo visiblePosition); + + /** + * Creates a top-level window for use by the input method. This window should + * float above all document windows and dialogs, not receive focus, and have + * lightweight decorations (such as no title, reduced drag regions). But + * this behavior may be modified to meet the platform style. The title may + * or may not be displayed, depending on the platform. + * + *

    If attachToInputContext is true, the new window will share the input + * context of the input method, so that events in the new window are + * dispatched to the input method. Also, this supresses deactivate and + * activate calls to the input method caused by setVisible. + * + * @param title the window title, if one is displayed; null becomes "" + * @param attachToInputContext true for the window to share context with + * the input method + * @return the new window for use by the input method + * @throws HeadlessException if GraphicsEnvironment.isHeadless is true + */ + Window createInputMethodWindow(String title, boolean attachToInputContext); + + /** + * Creates a top-level Swing JFrame for use by the input method. This frame + * should float above all document windows and dialogs, not receive focus, + * and have lightweight decorations (such as no title, reduced drag + * regions). But this behavior may be modified to meet the platform style. + * The title may or may not be displayed, depending on the platform. + * + *

    If attachToInputContext is true, the new window will share the input + * context of the input method, so that events in the new window are + * dispatched to the input method. Also, this supresses deactivate and + * activate calls to the input method caused by setVisible. + * + * @param title the window title, if one is displayed; null becomes "" + * @param attachToInputContext true for the window to share context with + * the input method + * @return the new window for use by the input method + * @throws HeadlessException if GraphicsEnvironment.isHeadless is true + * @since 1.4 + */ + JFrame createInputMethodJFrame(String title, boolean attachToInputContext); + + /** + * Sets whether notification of the client window's location and state should + * be enabled for the input method. When enabled, the input method's + * {@link InputMethod#notifyClientWindowChange(Rectangle)} method is called. + * Notification is automatically disabled when the input method is disposed. + * + * @param inputMethod the method to change status of + * @param enable true to enable notification + */ + void enableClientWindowNotification(InputMethod inputMethod, boolean enable); +} // interface InputMethodContext diff --git a/libjava/classpath/java/awt/im/spi/InputMethodDescriptor.java b/libjava/classpath/java/awt/im/spi/InputMethodDescriptor.java new file mode 100644 index 000000000..ce40ab2b6 --- /dev/null +++ b/libjava/classpath/java/awt/im/spi/InputMethodDescriptor.java @@ -0,0 +1,113 @@ +/* InputMethodDescriptor.java -- enables loading and use of an input method + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.im.spi; + +import java.awt.AWTException; +import java.awt.Image; +import java.awt.im.InputContext; +import java.util.Locale; + +/** + * This interface provides information about an InputMethod before it is + * loaded. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ +public interface InputMethodDescriptor +{ + /** + * Returns the locales supported by the input method this describes. This + * allows the selection of input methods by locale (by language only, or + * also by country and variant), via + * {@link InputContext#selectInputMethod(Locale)}. The returned list should + * ignore pass-through locales, so it is usually a subset of locales for + * which {@link InputMethod#setLocale(Locale)} returns true. If + * {@link #hasDynamicLocaleList()} returns true, this is called each time + * information is needed, allowing dynamic addition or removal of supported + * locales. + * + * @return the list of supported locales + * @throws AWTException if the input method is not available + */ + Locale[] getAvailableLocales() throws AWTException; + + /** + * Test whether the input method this describes has a static or dynamic + * locale list. For example, this would return true if the list of supported + * locales depends on adapters currently loaded over a network. + * + * @return true if the locale list is dynamic + */ + boolean hasDynamicLocaleList(); + + /** + * Returns a user visible name of the input locale, displayed in the + * specified locale. The inputLocale parameter must be one obtained from + * the list in {@link #getAvailableLocales()}, or null for a + * locale-independent description of the input method. If a translation to + * the desired display language is not available, another language may be + * used. + * + * @param inputLocale the locale of the input method, or null + * @param displayLanguage the language of the result + * @return the name of the input method when using the given inputLocale + */ + String getInputMethodDisplayName(Locale inputLocale, + Locale displayLanguage); + + /** + * Returns a 16x16 icon for the input locale. The inputLocale parameter + * must be one obtained from the list in {@link #getAvailableLocales()}, or + * null for a locale-independent icon for the input method. + * + * @param inputLocale the locale of the input method, or null + * @return a 16x16 icon for the input method when using the given inputLocale + */ + Image getInputMethodIcon(Locale inputLocale); + + /** + * Creates a new instance of the input method. + * + * @return the newly created input method + * @throws Exception if anything goes wrong + */ + InputMethod createInputMethod() throws Exception; + +} // interface InputMethodDescriptor diff --git a/libjava/classpath/java/awt/im/spi/package.html b/libjava/classpath/java/awt/im/spi/package.html new file mode 100644 index 000000000..c526ee159 --- /dev/null +++ b/libjava/classpath/java/awt/im/spi/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.im.spi + + +

    Interfaces for implementation of text input methods.

    + + + diff --git a/libjava/classpath/java/awt/image/AffineTransformOp.java b/libjava/classpath/java/awt/image/AffineTransformOp.java new file mode 100644 index 000000000..460804f90 --- /dev/null +++ b/libjava/classpath/java/awt/image/AffineTransformOp.java @@ -0,0 +1,608 @@ +/* AffineTransformOp.java -- This class performs affine + transformation between two images or rasters in 2 dimensions. + Copyright (C) 2004, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +/** + * AffineTransformOp performs matrix-based transformations (translations, + * scales, flips, rotations, and shears). + * + * If interpolation is required, nearest neighbour, bilinear, and bicubic + * methods are available. + * + * @author Olga Rodimina (rodimina@redhat.com) + * @author Francis Kung (fkung@redhat.com) + */ +public class AffineTransformOp implements BufferedImageOp, RasterOp +{ + public static final int TYPE_NEAREST_NEIGHBOR = 1; + + public static final int TYPE_BILINEAR = 2; + + /** + * @since 1.5.0 + */ + public static final int TYPE_BICUBIC = 3; + + private AffineTransform transform; + private RenderingHints hints; + + /** + * Construct AffineTransformOp with the given xform and interpolationType. + * Interpolation type can be TYPE_BILINEAR, TYPE_BICUBIC or + * TYPE_NEAREST_NEIGHBOR. + * + * @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) + { + this.transform = xform; + if (xform.getDeterminant() == 0) + throw new ImagingOpException(null); + + switch (interpolationType) + { + case TYPE_BILINEAR: + hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + break; + case TYPE_BICUBIC: + hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + break; + default: + hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + } + } + + /** + * Construct AffineTransformOp with the given xform and rendering hints. + * + * @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) + { + this.transform = xform; + this.hints = hints; + if (xform.getDeterminant() == 0) + throw new ImagingOpException(null); + } + + /** + * Creates a new BufferedImage with the size equal to that of the + * transformed image and the correct number of bands. The newly created + * image is created with the specified ColorModel. + * If a ColorModel is not specified, an appropriate ColorModel is used. + * + * @param src the source image. + * @param destCM color model for the destination image (can be null). + * @return a new compatible destination image. + */ + 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()); + + else + return new BufferedImage(src.getWidth(), src.getHeight(), + BufferedImage.TYPE_INT_ARGB); + } + + /** + * Creates a new WritableRaster with the size equal to the transformed + * source raster and correct number of bands . + * + * @param src the source raster. + * @throws RasterFormatException if resulting width or height of raster is 0. + * @return a new compatible raster. + */ + public WritableRaster createCompatibleDestRaster (Raster src) + { + Rectangle2D rect = getBounds2D(src); + + if (rect.getWidth() == 0 || rect.getHeight() == 0) + throw new RasterFormatException("width or height is 0"); + + 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 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 use a compatible BufferedImage + if (dst == null) + dst = createCompatibleDestImage(src, null); + + Graphics2D gr = dst.createGraphics(); + gr.setRenderingHints(hints); + gr.drawImage(src, transform, null); + return dst; + } + + /** + * Transforms source raster using transform specified at the constructor. + * 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) + { + // Initial checks + if (dst == src) + throw new IllegalArgumentException("src image cannot be the same as" + + " the dst image"); + + if (dst == null) + dst = createCompatibleDestRaster(src); + + if (src.getNumBands() != dst.getNumBands()) + throw new IllegalArgumentException("src and dst must have same number" + + " of bands"); + + // Optimization for rasters that can be represented in the RGB colormodel: + // wrap the rasters in images, and let Cairo do the transformation + if (ColorModel.getRGBdefault().isCompatibleSampleModel(src.getSampleModel()) + && ColorModel.getRGBdefault().isCompatibleSampleModel(dst.getSampleModel())) + { + WritableRaster src2 = Raster.createWritableRaster(src.getSampleModel(), + src.getDataBuffer(), + new Point(src.getMinX(), + src.getMinY())); + BufferedImage iSrc = new BufferedImage(ColorModel.getRGBdefault(), + src2, false, null); + BufferedImage iDst = new BufferedImage(ColorModel.getRGBdefault(), dst, + false, null); + + return filter(iSrc, iDst).getRaster(); + } + + // Otherwise, we need to do the transformation in java code... + // 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++) + { + for (int y = 0; y < dst.getHeight(); y++) + { + dstPts[i++] = x; + dstPts[i++] = y; + } + } + 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; + } + + /** + * Transforms source image using transform specified at the constructor and + * returns bounds of the transformed image. + * + * @param src image to be transformed + * @return bounds of the transformed image. + */ + public final Rectangle2D getBounds2D (BufferedImage src) + { + return getBounds2D (src.getRaster()); + } + + /** + * Returns bounds of the transformed raster. + * + * @param src raster to be transformed + * @return bounds of the transformed raster. + */ + public final Rectangle2D getBounds2D (Raster src) + { + return transform.createTransformedShape(src.getBounds()).getBounds2D(); + } + + /** + * Returns interpolation type used during transformations. + * + * @return interpolation type + */ + public final int getInterpolationType () + { + 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; + } + + /** + * Returns location of the transformed source point. The resulting point + * is stored in the dstPt if one is specified. + * + * @param srcPt point to be transformed + * @param dstPt destination point + * @return the location of the transformed source point. + */ + public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) + { + return transform.transform (srcPt, dstPt); + } + + /** + * Returns rendering hints that are used during transformation. + * + * @return the rendering hints used in this Op. + */ + public final RenderingHints getRenderingHints () + { + return hints; + } + + /** + * Returns transform used in transformation between source and destination + * image. + * + * @return the transform used in this Op. + */ + public final AffineTransform getTransform () + { + 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(); + + Object xyarr = null; + Object xp1arr = null; + Object yp1arr = null; + Object xyp1arr = null; + + double xy; + double xp1; + double yp1; + double xyp1; + + double[] result = new double[src.getNumBands()]; + + // 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; + + // Get surrounding pixels used in interpolation... optimized + // to use the smallest datatype possible. + if (src.getTransferType() == DataBuffer.TYPE_DOUBLE + || src.getTransferType() == DataBuffer.TYPE_FLOAT) + { + xyarr = src.getPixel(x, y, (double[])xyarr); + xp1arr = src.getPixel(x+1, y, (double[])xp1arr); + yp1arr = src.getPixel(x, y+1, (double[])yp1arr); + xyp1arr = src.getPixel(x+1, y+1, (double[])xyp1arr); + } + else + { + xyarr = src.getPixel(x, y, (int[])xyarr); + xp1arr = src.getPixel(x+1, y, (int[])xp1arr); + yp1arr = src.getPixel(x, y+1, (int[])yp1arr); + xyp1arr = src.getPixel(x+1, y+1, (int[])xyp1arr); + } + // using + // array[] pixels = src.getPixels(x, y, 2, 2, pixels); + // instead of doing four individual src.getPixel() calls + // should be faster, but benchmarking shows that it's not... + + // Run interpolation for each band + for (int j = 0; j < src.getNumBands(); j++) + { + // Pull individual sample values out of array + if (src.getTransferType() == DataBuffer.TYPE_DOUBLE + || src.getTransferType() == DataBuffer.TYPE_FLOAT) + { + xy = ((double[])xyarr)[j]; + xp1 = ((double[])xp1arr)[j]; + yp1 = ((double[])yp1arr)[j]; + xyp1 = ((double[])xyp1arr)[j]; + } + else + { + xy = ((int[])xyarr)[j]; + xp1 = ((int[])xp1arr)[j]; + yp1 = ((int[])yp1arr)[j]; + xyp1 = ((int[])xyp1arr)[j]; + } + + // If all four samples are identical, there's no need to + // calculate anything + if (xy == xp1 && xy == yp1 && xy == xyp1) + result[j] = xy; + + // Run bilinear interpolation formula + else + result[j] = (xy * (1-xdiff) + xp1 * xdiff) + * (1-ydiff) + + (yp1 * (1-xdiff) + xyp1 * xdiff) + * ydiff; + } + + dst.setPixel((int)dpts[i] + dst.getMinX(), + (int)dpts[i+1] + dst.getMinY(), + 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(); + double[] result = new double[src.getNumBands()]; + Object pixels = null; + + // For all points on the destination raster, perform bicubic interpolation + // from corrosponding source points + 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; + 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, using the smallest + // datatype possible + if (src.getTransferType() == DataBuffer.TYPE_DOUBLE + || src.getTransferType() == DataBuffer.TYPE_FLOAT) + { + pixels = src.getPixel(srcX, srcY, (double[])pixels); + for (int j = 0; j < result.length; j++) + result[j] += ((double[])pixels)[j] * r1 * r2; + } + else + { + pixels = src.getPixel(srcX, srcY, (int[])pixels); + for (int j = 0; j < result.length; j++) + result[j] += ((int[])pixels)[j] * r1 * r2; + } + } + + // Put it all together + dst.setPixel((int)dpts[i] + dst.getMinX(), + (int)dpts[i+1] + dst.getMinY(), + result); + } + } + } +} diff --git a/libjava/classpath/java/awt/image/AreaAveragingScaleFilter.java b/libjava/classpath/java/awt/image/AreaAveragingScaleFilter.java new file mode 100644 index 000000000..db2335f1c --- /dev/null +++ b/libjava/classpath/java/awt/image/AreaAveragingScaleFilter.java @@ -0,0 +1,268 @@ +/* AreaAveragingScaleFilter.java -- Java class for filtering images + Copyright (C) 1999,2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * This filter should produce images which do not have image artifacts + * like broken lines which were originally unbroken. The cost is of + * course speed. Using bi-linear interpolation here against 4 pixel + * points should give the desired results although Sun does not + * specify what the exact algorithm should be. + *
    + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class AreaAveragingScaleFilter extends ReplicateScaleFilter +{ + /** + * Construct an instance of AreaAveragingScaleFilter which + * should be used in conjunction with a FilteredImageSource + * object. + * + * @param width the width of the destination image + * @param height the height of the destination image + */ + public AreaAveragingScaleFilter(int width, int height) { + super(width, height); + } + + /** + * The ImageProducer should call this method with a + * bit mask of hints from any of RANDOMPIXELORDER, + * TOPDOWNLEFTRIGHT, COMPLETESCANLINES, + * SINGLEPASS, SINGLEFRAME from the + * ImageConsumer interface. + *
    + * FIXME - more than likely Sun's implementation desires + * TOPDOWNLEFTRIGHT order and this method is overloaded here + * in order to assure that mask is part of the hints added to + * the consumer. + * + * @param flags a bit mask of hints + * @see ImageConsumer + */ + public void setHints(int flags) + { + if (consumer != null) + consumer.setHints(flags); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as a byte at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels array + * @param scansize the width to use in extracting pixels from the pixels array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, int scansize) + { + double rx = ((double) srcWidth) / destWidth; + double ry = ((double) srcHeight) / destHeight; + + int destScansize = (int) Math.round(scansize / rx); + + byte[] destPixels = averagePixels(x, y, w, h, + model, pixels, offset, scansize, + rx, ry, destScansize); + + if (consumer != null) + consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry), + (int) Math.ceil(w/rx), (int) Math.ceil(h/ry), + model, destPixels, 0, destScansize); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an int at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels array + * @param scansize the width to use in extracting pixels from the pixels array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, int scansize) + { + double rx = ((double) srcWidth) / destWidth; + double ry = ((double) srcHeight) / destHeight; + + int destScansize = (int) Math.round(scansize / rx); + + int[] destPixels = averagePixels(x, y, w, h, + model, pixels, offset, scansize, + rx, ry, destScansize); + + if (consumer != null) + consumer.setPixels((int) Math.floor(x/rx), (int) Math.floor(y/ry), + (int) Math.ceil(w/rx), (int) Math.ceil(h/ry), + model, destPixels, 0, destScansize); + } + + /** + * This is a really terrible implementation, + * since it uses the nearest-neighbor method. This filter is rarely used though. + * + * @param srcx, srcy - Source rectangle upper-left corner + * @param srcw, srch - Source rectangle width and height + * @param model - Pixel color model + * @param srcPixels - Source pixel data. + * @param srcOffset - Starting offset into the source pixel data array. + * @param srcScansize - Source array scanline size. + * @param rx,ry - Scaling factor. + * @param destScansize - Destination array scanline size. + */ + private byte[] averagePixels(int srcx, int srcy, int srcw, int srch, + ColorModel model, byte[] srcPixels, + int srcOffset, int srcScansize, + double rx, double ry, int destScansize) + { + int destW = (int) Math.ceil(srcw/rx); + int destH = (int) Math.ceil(srch/ry); + byte[] destPixels = new byte[ destW * destH ]; + int sx, sy; + + int w = (int)Math.ceil(rx); + int h = (int)Math.ceil(ry); + + for(int x = 0; x < destW; x++) + for(int y = 0; y < destH; y++) + { + sx = (int) (x * rx); + sy = (int) (y * ry); + + int r,g,b,a; + r = g = b = a = 0; + + for(int i = 0; i < w; i++) + { + for(int j = 0; j < h; j++) + { + int idx = srcx + sx + i + (srcy + sy + j)*srcScansize; + r += model.getRed(srcPixels[ idx ]); + g += model.getGreen(srcPixels[ idx ]); + b += model.getBlue(srcPixels[ idx ]); + a += model.getAlpha(srcPixels[ idx ]); + } + } + + r = r / (w * h); + g = g / (w * h); + b = b / (w * h); + a = a / (w * h); + + // Does this really work? + destPixels[x + destScansize*y] = (byte)model.getDataElement + (new int[]{r, g, b, a}, 0); + } + + return destPixels; + } + + /** + * This is a really terrible implementation, + * since it uses the nearest-neighbor method. This filter is rarely used though. + * + * @param srcx, srcy - Source rectangle upper-left corner + * @param srcw, srch - Source rectangle width and height + * @param model - Pixel color model + * @param srcPixels - Source pixel data. + * @param srcOffset - Starting offset into the source pixel data array. + * @param srcScansize - Source array scanline size. + * @param rx,ry - Scaling factor. + * @param destScansize - Destination array scanline size. + */ + private int[] averagePixels(int srcx, int srcy, int srcw, int srch, + ColorModel model, int[] srcPixels, + int srcOffset, int srcScansize, + double rx, double ry, int destScansize) + { + int destW = (int) Math.ceil(srcw/rx); + int destH = (int) Math.ceil(srch/ry); + int[] destPixels = new int[ destW * destH ]; + int sx, sy; + + int w = (int)Math.ceil(rx); + int h = (int)Math.ceil(ry); + + for(int x = 0; x < destW; x++) + for(int y = 0; y < destH; y++) + { + sx = (int) (x * rx); + sy = (int) (y * ry); + + int r,g,b,a; + r = g = b = a = 0; + + for(int i = 0; i < w; i++) + { + for(int j = 0; j < h; j++) + { + int idx = srcx + sx + i + (srcy + sy + j)*srcScansize; + r += model.getRed(srcPixels[ idx ]); + g += model.getGreen(srcPixels[ idx ]); + b += model.getBlue(srcPixels[ idx ]); + a += model.getAlpha(srcPixels[ idx ]); + } + } + + r = r / (w * h); + g = g / (w * h); + b = b / (w * h); + a = a / (w * h); + + destPixels[x + destScansize*y] = model.getDataElement + (new int[]{r, g, b, a}, 0); + } + + return destPixels; + } +} diff --git a/libjava/classpath/java/awt/image/BandCombineOp.java b/libjava/classpath/java/awt/image/BandCombineOp.java new file mode 100644 index 000000000..f3a6c5756 --- /dev/null +++ b/libjava/classpath/java/awt/image/BandCombineOp.java @@ -0,0 +1,218 @@ +/* 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. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +/** + * Filter Raster pixels by applying a matrix. + * + * BandCombineOp applies a matrix to each pixel to produce new pixel values. + * The width of the matrix must be the same or one more than the number of + * bands in the source Raster. If one more, the pixels in the source are + * assumed to contain an implicit 1.0 at the end. + * + * The rows of the matrix are multiplied by the pixel to produce the values + * for the destination. Therefore the destination Raster must contain the + * same number of bands as the number of rows in the filter matrix. + * + * This Op assumes that samples are integers; floating point sample types will + * be rounded to their nearest integer value during filtering. + * + * @author Jerry Quinn (jlquinn@optonline.net) + */ +public class BandCombineOp implements RasterOp +{ + private RenderingHints hints; + private float[][] matrix; + + /** + * Construct a BandCombineOp. + * + * @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 = 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. + * + * @param src The source 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) + */ + 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 + int[] spix = new int[matrix[0].length - 1]; + int[] spix2 = new int[matrix[0].length - 1]; + int[] dpix = new int[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; + src.getPixel(x, y, spix); + + // Do not re-calculate if pixel is identical to the last one + // (ie, blocks of the same colour) + if (!Arrays.equals(spix, spix2)) + { + System.arraycopy(spix, 0, spix2, 0, spix.length); + 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] * (int)matrix[i][j]; + } + } + dest.setPixel(x, y, dpix); + } + + return dest; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public final Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + /** + * Creates a new WritableRaster that can be used as the destination for this + * 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) + { + // 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. Because this is + * not a geometric operation, it simply returns a copy of the source. + * + * @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(); + + dst.setLocation(src); + return dst; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getRenderingHints() + */ + public final RenderingHints getRenderingHints() + { + return hints; + } + + /** + * Return the matrix used in this operation. + * + * @return The matrix used in this operation. + */ + public final float[][] getMatrix() + { + return matrix; + } + +} diff --git a/libjava/classpath/java/awt/image/BandedSampleModel.java b/libjava/classpath/java/awt/image/BandedSampleModel.java new file mode 100644 index 000000000..7df6c8548 --- /dev/null +++ b/libjava/classpath/java/awt/image/BandedSampleModel.java @@ -0,0 +1,759 @@ +/* Copyright (C) 2004, 2005, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import gnu.java.awt.Buffers; +import gnu.java.lang.CPStringBuilder; + +/** + * A sample model that reads each sample value from a separate band in the + * {@link DataBuffer}. + * + * @author Jerry Quinn (jlquinn@optonline.net) + */ +public final class BandedSampleModel extends ComponentSampleModel +{ + private int[] bitMasks; + private int[] bitOffsets; + private int[] sampleSize; + private int dataBitOffset; + private int elemBits; + private int numberOfBits; + private int numElems; + + private static int[] createBankArray(int size) + { + int[] result = new int[size]; + for (int i = 0; i < size; i++) + result[i] = i; + return result; + } + + /** + * Creates a new BandedSampleModel. + * + * @param dataType the data buffer type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param numBands the number of bands. + */ + public BandedSampleModel(int dataType, int w, int h, int numBands) + { + this(dataType, w, h, w, createBankArray(numBands), new int[numBands]); + } + + /** + * Creates a new BandedSampleModel. + * + * @param dataType the data buffer type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param scanlineStride the number of data elements from a pixel in one + * row to the corresponding pixel in the next row. + * @param bankIndices the bank indices. + * @param bandOffsets the band offsets. + */ + public BandedSampleModel(int dataType, int w, int h, int scanlineStride, + int[] bankIndices, int[] bandOffsets) + { + super(dataType, w, h, 1, scanlineStride, bankIndices, bandOffsets); + } + + /** + * Creates a new data buffer that is compatible with this sample model. + * + * @return The new data buffer. + */ + public DataBuffer createDataBuffer() + { + int size = scanlineStride * height; + return Buffers.createBuffer(getDataType(), size, numBanks); + } + + /** + * Creates a new SampleModel that is compatible with this + * model and has the specified width and height. + * + * @param w the width (in pixels, must be greater than zero). + * @param h the height (in pixels, must be greater than zero). + * + * @return The new sample model. + * + * @throws IllegalArgumentException if w or h is + * not greater than zero. + * @throws IllegalArgumentException if w * h exceeds + * Integer.MAX_VALUE. + */ + public SampleModel createCompatibleSampleModel(int w, int h) + { + // NOTE: blackdown 1.4.1 sets all offsets to 0. Sun's 1.4.2 docs + // disagree. + + // Compress offsets so minimum is 0, others w*scanlineStride + int[] newoffsets = new int[bandOffsets.length]; + int[] order = new int[bandOffsets.length]; + for (int i = 0; i < bandOffsets.length; i++) + order[i] = i; + // FIXME: This is N^2, but not a big issue, unless there's a lot of + // bands... + for (int i = 0; i < bandOffsets.length; i++) + for (int j = i + 1; j < bandOffsets.length; j++) + if (bankIndices[order[i]] > bankIndices[order[j]] + || (bankIndices[order[i]] == bankIndices[order[j]] + && bandOffsets[order[i]] > bandOffsets[order[j]])) + { + int t = order[i]; order[i] = order[j]; order[j] = t; + } + int bank = 0; + int offset = 0; + for (int i = 0; i < bandOffsets.length; i++) + { + if (bankIndices[order[i]] != bank) + { + bank = bankIndices[order[i]]; + offset = 0; + } + newoffsets[order[i]] = offset; + offset += w * scanlineStride; + } + + return new BandedSampleModel(dataType, w, h, w, bankIndices, newoffsets); + } + + + public SampleModel createSubsetSampleModel(int[] bands) + { + if (bands.length > bankIndices.length) + throw new + RasterFormatException("BandedSampleModel createSubsetSampleModel too" + +" many bands"); + int[] newoff = new int[bands.length]; + int[] newbanks = new int[bands.length]; + for (int i = 0; i < bands.length; i++) + { + int b = bands[i]; + newoff[i] = bandOffsets[b]; + newbanks[i] = bankIndices[b]; + } + + return new BandedSampleModel(dataType, width, height, scanlineStride, + newbanks, newoff); + } + + /** + * Extract all samples of one pixel and return in an array of transfer type. + * + * Extracts the pixel at x, y from data and stores samples into the array + * obj. If obj is null, a new array of getTransferType() is created. + * + * @param x The x-coordinate of the pixel rectangle to store in + * obj. + * @param y The y-coordinate of the pixel rectangle to store in + * obj. + * @param obj The primitive array to store the pixels into or null to force + * creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + * @see java.awt.image.SampleModel#getDataElements(int, int, + * java.lang.Object, java.awt.image.DataBuffer) + */ + public Object getDataElements(int x, int y, Object obj, DataBuffer data) + { + if (x < 0 || y < 0) + throw new ArrayIndexOutOfBoundsException( + "x and y must not be less than 0."); + int pixel = getSample(x, y, 0, data); + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + { + byte[] b = (byte[]) obj; + if (b == null) b = new byte[numBands]; + for (int i = 0; i < numBands; i++) + b[i] = (byte)getSample(x, y, i, data); + return b; + } + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + { + short[] b = (short[]) obj; + if (b == null) b = new short[numBands]; + for (int i = 0; i < numBands; i++) + b[i] = (short)getSample(x, y, i, data); + return b; + } + case DataBuffer.TYPE_INT: + { + int[] b = (int[]) obj; + if (b == null) b = new int[numBands]; + for (int i = 0; i < numBands; i++) + b[i] = getSample(x, y, i, data); + return b; + } + case DataBuffer.TYPE_FLOAT: + { + float[] b = (float[]) obj; + if (b == null) b = new float[numBands]; + for (int i = 0; i < numBands; i++) + b[i] = getSampleFloat(x, y, i, data); + return b; + } + case DataBuffer.TYPE_DOUBLE: + { + double[] b = (double[]) obj; + if (b == null) + b = new double[numBands]; + for (int i = 0; i < numBands; i++) + b[i] = getSample(x, y, i, data); + return b; + } + + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + } + + /** + * Returns all the samples for the pixel at location (x, y) + * stored in the specified data buffer. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param iArray an array that will be populated with the sample values and + * returned as the result. The size of this array should be equal to the + * number of bands in the model. If the array is null, a new + * array is created. + * @param data the data buffer (null not permitted). + * + * @return The samples for the specified pixel. + * + * @see #setPixel(int, int, int[], DataBuffer) + */ + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + if (iArray == null) + iArray = new int[numBands]; + for (int i = 0; i < numBands; i++) + iArray[i] = getSample(x, y, i, data); + + return iArray; + } + + /** + * Copy pixels from a region into an array. + * + * Copies the samples of the pixels in the rectangle starting at x, y that + * is w pixels wide and h scanlines high. When there is more than one band, + * the samples stored in order before the next pixel. This ordering isn't + * well specified in Sun's docs as of 1.4.2. + * + * If iArray is null, a new array is allocated, filled, and returned. + * + * @param x The x-coordinate of the pixel rectangle to store in + * iArray. + * @param y The y-coordinate of the pixel rectangle to store in + * iArray. + * @param w The width in pixels of the rectangle. + * @param h The height in pixels of the rectangle. + * @param iArray The int array to store the pixels into or null to force + * creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + */ + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + if (x < 0 || y < 0) + throw new ArrayIndexOutOfBoundsException( + "x and y must not be less than 0."); + if (iArray == null) + iArray = new int[w * h * numBands]; + int outOffset = 0; + int maxX = x + w; + int maxY = y + h; + for (int yy = x; yy < maxY; yy++) + { + for (int xx = x; xx < maxX; xx++) + { + for (int b = 0; b < numBands; b++) + { + int offset = bandOffsets[b] + yy * scanlineStride + xx; + iArray[outOffset++] = + data.getElem(bankIndices[b], offset); + } + } + } + return iArray; + } + + /** + * Returns a sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if data is null. + */ + public int getSample(int x, int y, int b, DataBuffer data) + { + int offset = bandOffsets[b] + y * scanlineStride + x; + return data.getElem(bankIndices[b], offset); + } + + /** + * Returns a sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if data is null. + * + * @see #getSample(int, int, int, DataBuffer) + */ + public float getSampleFloat(int x, int y, int b, DataBuffer data) + { + int offset = bandOffsets[b] + y * scanlineStride + x; + return data.getElemFloat(bankIndices[b], offset); + } + + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if data is null. + * + * @see #getSample(int, int, int, DataBuffer) + */ + public double getSampleDouble(int x, int y, int b, DataBuffer data) + { + int offset = bandOffsets[b] + y * scanlineStride + x; + return data.getElemDouble(bankIndices[b], offset); + } + + /** + * Copy one band's samples from a region into an array. + * + * Copies from one band the samples of the pixels in the rectangle starting + * at x, y that is w pixels wide and h scanlines high. + * + * If iArray is null, a new array is allocated, filled, and returned. + * + * @param x The x-coordinate of the pixel rectangle to store in + * iArray. + * @param y The y-coordinate of the pixel rectangle to store in + * iArray. + * @param w The width in pixels of the rectangle. + * @param h The height in pixels of the rectangle. + * @param b The band to retrieve. + * @param iArray The int array to store the pixels into or null to force + * creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + */ + public int[] getSamples(int x, int y, int w, int h, int b, int[] iArray, + DataBuffer data) + { + if (x < 0 || y < 0) + throw new ArrayIndexOutOfBoundsException( + "x and y must not be less than 0."); + if (iArray == null) + iArray = new int[w * h]; + int outOffset = 0; + int maxX = x + w; + int maxY = y + h; + for (int yy = y; yy < maxY; yy++) + { + for (int xx = x; xx < maxX; xx++) + { + int offset = bandOffsets[b] + yy * scanlineStride + xx; + iArray[outOffset++] = + data.getElem(bankIndices[b], offset); + } + } + return iArray; + } + + /** + * Set the pixel at x, y to the value in the first element of the primitive + * array obj. + * + * @param x The x-coordinate of the data elements in obj. + * @param y The y-coordinate of the data elements in obj. + * @param obj The primitive array containing the data elements to set. + * @param data The DataBuffer to store the data elements into. + * @see java.awt.image.SampleModel#setDataElements(int, int, int, int, java.lang.Object, java.awt.image.DataBuffer) + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) + { + int transferType = getTransferType(); + if (getTransferType() != data.getDataType()) + { + throw new IllegalArgumentException("transfer type ("+ + getTransferType()+"), "+ + "does not match data "+ + "buffer type (" + + data.getDataType() + + ")."); + } + + int offset = y * scanlineStride + x; + + try + { + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + { + DataBufferByte out = (DataBufferByte) data; + byte[] in = (byte[]) obj; + for (int i = 0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_SHORT: + { + DataBufferShort out = (DataBufferShort) data; + short[] in = (short[]) obj; + for (int i = 0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_USHORT: + { + DataBufferUShort out = (DataBufferUShort) data; + short[] in = (short[]) obj; + for (int i = 0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_INT: + { + DataBufferInt out = (DataBufferInt) data; + int[] in = (int[]) obj; + for (int i = 0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_FLOAT: + { + DataBufferFloat out = (DataBufferFloat) data; + float[] in = (float[]) obj; + for (int i = 0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + case DataBuffer.TYPE_DOUBLE: + { + DataBufferDouble out = (DataBufferDouble) data; + double[] in = (double[]) obj; + for (int i = 0; i < numBands; i++) + out.getData(bankIndices[i])[offset + bandOffsets[i]] = in[i]; + return; + } + default: + throw new ClassCastException("Unsupported data type"); + } + } + catch (ArrayIndexOutOfBoundsException aioobe) + { + String msg = "While writing data elements" + + ", x=" + x + ", y=" + y + + ", width=" + width + ", height=" + height + + ", scanlineStride=" + scanlineStride + + ", offset=" + offset + + ", data.getSize()=" + data.getSize() + + ", data.getOffset()=" + data.getOffset() + + ": " + aioobe; + throw new ArrayIndexOutOfBoundsException(msg); + } + } + + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + for (int b = 0; b < numBands; b++) + data.setElem(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, + iArray[b]); + } + + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray the pixel sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int inOffset = 0; + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = y * scanlineStride + (x + ww); + for (int b = 0; b < numBands; b++) + data.setElem(bankIndices[b], bandOffsets[b] + offset, + iArray[inOffset++]); + } + y++; + } + } + + /** + * Sets the sample value for band b of the pixel at location + * (x, y) in the specified data buffer. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param b the band index. + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, int s, DataBuffer data) + { + data.setElem(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, s); + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if data is null. + */ + public void setSample(int x, int y, int b, float s, DataBuffer data) + { + data.setElemFloat(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, + s); + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if data is null. + */ + public void setSample(int x, int y, int b, double s, DataBuffer data) + { + data.setElemDouble(bankIndices[b], bandOffsets[b] + y * scanlineStride + x, + s); + } + + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param iArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setSamples(int x, int y, int w, int h, int b, int[] iArray, + DataBuffer data) + { + if (x < 0 || y < 0) + throw new ArrayIndexOutOfBoundsException( + "x and y must not be less than 0."); + int inOffset = 0; + + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + { + DataBufferByte out = (DataBufferByte) data; + byte[] bank = out.getData(bankIndices[b]); + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + bank[offset] = (byte)iArray[inOffset++]; + } + y++; + } + return; + } + case DataBuffer.TYPE_SHORT: + { + DataBufferShort out = (DataBufferShort) data; + short[] bank = out.getData(bankIndices[b]); + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + bank[offset] = (short)iArray[inOffset++]; + } + y++; + } + return; + } + case DataBuffer.TYPE_USHORT: + { + DataBufferShort out = (DataBufferShort) data; + short[] bank = out.getData(bankIndices[b]); + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + bank[offset] = (short)iArray[inOffset++]; + } + y++; + } + return; + } + case DataBuffer.TYPE_INT: + { + DataBufferInt out = (DataBufferInt) data; + int[] bank = out.getData(bankIndices[b]); + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + bank[offset] = iArray[inOffset++]; + } + y++; + } + return; + } + case DataBuffer.TYPE_FLOAT: + case DataBuffer.TYPE_DOUBLE: + break; + default: + throw new ClassCastException("Unsupported data type"); + } + + // Default implementation probably slower for float and double + for (int hh = 0; hh < h; hh++) + { + for (int ww = 0; ww < w; ww++) + { + int offset = bandOffsets[b] + y * scanlineStride + (x + ww); + data.setElem(bankIndices[b], offset, iArray[inOffset++]); + } + y++; + } + } + + /** + * Creates a String with some information about this SampleModel. + * @return A String describing this SampleModel. + * @see java.lang.Object#toString() + */ + public String toString() + { + CPStringBuilder result = new CPStringBuilder(); + result.append(getClass().getName()); + result.append("["); + result.append("scanlineStride=").append(scanlineStride); + for(int i = 0; i < bitMasks.length; i+=1) + { + result.append(", mask[").append(i).append("]=0x").append( + Integer.toHexString(bitMasks[i])); + } + + result.append("]"); + return result.toString(); + } +} diff --git a/libjava/classpath/java/awt/image/BufferStrategy.java b/libjava/classpath/java/awt/image/BufferStrategy.java new file mode 100644 index 000000000..e86aad60f --- /dev/null +++ b/libjava/classpath/java/awt/image/BufferStrategy.java @@ -0,0 +1,124 @@ +/* BufferStrategy.java -- describes image buffering resources + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.BufferCapabilities; +import java.awt.Graphics; + +/** + * This class describes a strategy for managing image buffering + * resources on a Canvas or Window. A given buffer strategy may make + * use of hardware acceleration or take advantage of features of the + * native graphics system. Examples of buffering strategies are + * double or triple buffering using either flipping or blitting. For + * the details of these algorithms see BufferCapabilities. + * + * To use a buffer strategy, you retrieve it from either the current + * GraphicsConfiguration or from the Component on which you'd like to + * draw. Then you can query the strategy's capabilities to make sure + * they're suitable. + * + * If the strategy's capabilities are suitable, you can obtain a + * graphics object and use it to draw with this strategy. Drawing + * with a buffer strategy requires extra care, however. You'll need + * to manually cause the next buffer to be shown on the output device. + * And since buffer strategies are usually implemented with a + * VolatileImage, you must frequently check that the contents of the + * buffer are valid and that the buffer still exists. + * + * A buffer strategy is usually implemented using a VolatileImage. + * + * @see VolatileImage + * @since 1.4 + */ +public abstract class BufferStrategy +{ + /** + * Creates a new buffer strategy. + */ + public BufferStrategy() + { + } + + /** + * Retrieves the capabilities of this buffer strategy. + * + * @return this buffer strategy's capabilities + */ + public abstract BufferCapabilities getCapabilities(); + + /** + * Retrieves a graphics object that can be used to draw using this + * buffer strategy. This method may not be synchronized so be + * careful when calling it from multiple threads. You also must + * manually dispose of this graphics object. + * + * @return a graphics object that can be used to draw using this + * buffer strategy + */ + public abstract Graphics getDrawGraphics(); + + /** + * Returns whether or not the buffer's resources have been reclaimed + * by the native graphics system. If the buffer resources have been + * lost then you'll need to obtain new resources before drawing + * again. For details, see the documentation for VolatileImage. + * + * @return true if the contents were lost, false otherwise + */ + public abstract boolean contentsLost(); + + /** + * Returns whether or not the buffer's resources were re-created and + * cleared to the default background color. If the buffer's + * resources have recently been re-created and initialized then the + * buffer's image may need to be re-rendered. For details, see the + * documentation for VolatileImage. + * + * @return true if the contents were restored, false otherwise + */ + public abstract boolean contentsRestored(); + + /** + * Applies this buffer strategy. In other words, this method brings + * the contents of the back or intermediate buffers to the front + * buffer. + */ + public abstract void show(); +} diff --git a/libjava/classpath/java/awt/image/BufferedImage.java b/libjava/classpath/java/awt/image/BufferedImage.java new file mode 100644 index 000000000..094c71dd1 --- /dev/null +++ b/libjava/classpath/java/awt/image/BufferedImage.java @@ -0,0 +1,839 @@ +/* BufferedImage.java -- + Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.Buffers; +import gnu.java.awt.ClasspathGraphicsEnvironment; +import gnu.java.awt.ComponentDataBlitOp; +import gnu.java.lang.CPStringBuilder; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.util.Hashtable; +import java.util.Vector; + +/** + * A buffered image always starts at coordinates (0, 0). + * + * The buffered image is not subdivided into multiple tiles. Instead, + * the image consists of one large tile (0,0) with the width and + * height of the image. This tile is always considered to be checked + * out. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class BufferedImage extends Image + implements WritableRenderedImage, Transparency +{ + public static final int TYPE_CUSTOM = 0, + TYPE_INT_RGB = 1, + TYPE_INT_ARGB = 2, + TYPE_INT_ARGB_PRE = 3, + TYPE_INT_BGR = 4, + TYPE_3BYTE_BGR = 5, + TYPE_4BYTE_ABGR = 6, + TYPE_4BYTE_ABGR_PRE = 7, + TYPE_USHORT_565_RGB = 8, + TYPE_USHORT_555_RGB = 9, + TYPE_BYTE_GRAY = 10, + TYPE_USHORT_GRAY = 11, + TYPE_BYTE_BINARY = 12, + TYPE_BYTE_INDEXED = 13; + + /** + * Vector of TileObservers (or null) + */ + Vector tileObservers; + + /** + * The image's WritableRaster + */ + WritableRaster raster; + + /** + * The associated ColorModel + */ + ColorModel colorModel; + + /** + * The image's properties (or null) + */ + Hashtable properties; + + /** + * Whether alpha is premultiplied + */ + boolean isPremultiplied; + + /** + * The predefined type, if any. + */ + int type; + + /** + * Creates a new BufferedImage with the specified width, height + * and type. Valid type values are: + * + *
      + *
    • {@link #TYPE_INT_RGB}
    • + *
    • {@link #TYPE_INT_ARGB}
    • + *
    • {@link #TYPE_INT_ARGB_PRE}
    • + *
    • {@link #TYPE_INT_BGR}
    • + *
    • {@link #TYPE_3BYTE_BGR}
    • + *
    • {@link #TYPE_4BYTE_ABGR}
    • + *
    • {@link #TYPE_4BYTE_ABGR_PRE}
    • + *
    • {@link #TYPE_USHORT_565_RGB}
    • + *
    • {@link #TYPE_USHORT_555_RGB}
    • + *
    • {@link #TYPE_BYTE_GRAY}
    • + *
    • {@link #TYPE_USHORT_GRAY}
    • + *
    • {@link #TYPE_BYTE_BINARY}
    • + *
    • {@link #TYPE_BYTE_INDEXED}
    • + *
    + * + * @param width the width (must be > 0). + * @param height the height (must be > 0). + * @param type the image type (see the list of valid types above). + * + * @throws IllegalArgumentException if width or + * height is less than or equal to zero. + * @throws IllegalArgumentException if type is not one of the + * specified values. + */ + public BufferedImage(int width, int height, int type) + { + SampleModel sm = null; + ColorModel cm = null; + boolean premultiplied = (type == BufferedImage.TYPE_INT_ARGB_PRE + || type == BufferedImage.TYPE_4BYTE_ABGR_PRE); + + switch( type ) + { + case BufferedImage.TYPE_INT_RGB: + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, + width, height, + new int[]{ 0x00FF0000, + 0x0000FF00, + 0x000000FF } ) ; + cm = new DirectColorModel( 24, 0xff0000, 0xff00, 0xff ); + break; + + case BufferedImage.TYPE_3BYTE_BGR: + sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, + width, height, + 3, width * 3, + new int[]{ 2, 1, 0 } ); + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + false, false, + BufferedImage.OPAQUE, + DataBuffer.TYPE_BYTE); + break; + + case BufferedImage.TYPE_INT_ARGB: + case BufferedImage.TYPE_INT_ARGB_PRE: + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, + width, height, + new int[]{ 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000 } ); + if (premultiplied) + cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB), + 32, 0xff0000, 0xff00, 0xff, 0xff000000, + true, + Buffers.smallestAppropriateTransferType(32)); + else + cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 ); + + break; + + case BufferedImage.TYPE_4BYTE_ABGR: + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, + width, height, + 4, 4*width, + new int[]{3, 2, 1, 0}); + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + true, premultiplied, + BufferedImage.TRANSLUCENT, + DataBuffer.TYPE_BYTE); + break; + + case BufferedImage.TYPE_INT_BGR: + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, + width, height, + new int[]{ 0x000000FF, + 0x0000FF00, + 0x00FF0000 } ) ; + cm = new DirectColorModel( 24, 0xff, 0xff00, 0xff0000 ); + break; + + case BufferedImage.TYPE_USHORT_565_RGB: + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT, + width, height, + new int[]{ 0xF800, + 0x7E0, + 0x1F } ) ; + cm = new DirectColorModel( 16, 0xF800, 0x7E0, 0x1F ); + break; + + case BufferedImage.TYPE_USHORT_555_RGB: + sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT, + width, height, + new int[]{ 0x7C00, + 0x3E0, + 0x1F } ) ; + cm = new DirectColorModel( 15, 0x7C00, 0x3E0, 0x1F ); + break; + + case BufferedImage.TYPE_BYTE_INDEXED: + cm = createDefaultIndexedColorModel( false ); + + case BufferedImage.TYPE_BYTE_GRAY: + sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, + width, height, + 1, width, new int[]{ 0 } ); + break; + + case BufferedImage.TYPE_USHORT_GRAY: + sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_USHORT, + width, height, + 1, width, new int[]{ 0 } ); + break; + + case BufferedImage.TYPE_BYTE_BINARY: + cm = createDefaultIndexedColorModel( true ); + sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, + width, height, 1); + break; + + default: + sm = null; + } + + if( sm == null ) + throw new IllegalArgumentException("Unknown predefined image type."); + + if( cm == null ) // only for the grayscale types + { + int buftype; + int[] bits = new int[1]; + if( type == BufferedImage.TYPE_BYTE_GRAY ) + { + buftype = DataBuffer.TYPE_BYTE; + bits[0] = 8; + } + else + { + buftype = DataBuffer.TYPE_USHORT; + bits[0] = 16; + } + ColorSpace graySpace = ColorSpace.getInstance( ColorSpace.CS_GRAY ); + + cm = new ComponentColorModel( graySpace, bits, false, false, + Transparency.OPAQUE, buftype ); + } + + WritableRaster rst = null; + + // Attempt to create an accelerated backend for this image + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + if (env instanceof ClasspathGraphicsEnvironment) + rst = ((ClasspathGraphicsEnvironment)env).createRaster(cm, sm); + + // Default to a standard Java raster & databuffer if needed + if (rst == null) + rst = Raster.createWritableRaster(sm, new Point( 0, 0 ) ); + + init(cm, rst, premultiplied, + null, // no properties + type ); + } + + public BufferedImage(int w, int h, int type, IndexColorModel indexcolormodel) + { + if ((type != TYPE_BYTE_BINARY) && (type != TYPE_BYTE_INDEXED)) + throw new IllegalArgumentException("Type must be TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED"); + if( indexcolormodel.getMapSize() > 16 && type == TYPE_BYTE_BINARY ) + throw new IllegalArgumentException("Type TYPE_BYTE_BINARY cannot have a larger than 16-color palette."); + if( indexcolormodel.getMapSize() > 256 ) + throw new IllegalArgumentException("Byte type cannot have a larger than 256-color palette."); + + init( indexcolormodel, + indexcolormodel.createCompatibleWritableRaster(w, h), + indexcolormodel.isAlphaPremultiplied(), + null, // no properties + type ); + } + + public BufferedImage(ColorModel colormodel, WritableRaster writableraster, + boolean premultiplied, Hashtable properties) + { + init(colormodel, writableraster, premultiplied, properties, TYPE_CUSTOM); + } + + + private void init(ColorModel cm, WritableRaster writableraster, + boolean premultiplied, Hashtable properties, int type) + { + raster = writableraster; + colorModel = cm; + this.properties = properties; + isPremultiplied = premultiplied; + this.type = type; + } + + /** + * Creates the default palettes for the predefined indexed color types + * (256-color or black-and-white) + * + * @param binary - If true, a black and white palette, + * otherwise a default 256-color palette is returned. + */ + private IndexColorModel createDefaultIndexedColorModel( boolean binary ) + { + if( binary ) + { + byte[] t = new byte[]{ 0, (byte)255 }; + return new IndexColorModel( 1, 2, t, t, t ); + } + + byte[] r = new byte[256]; + byte[] g = new byte[256]; + byte[] b = new byte[256]; + + int index = 0; + for( int i = 0; i < 6; i++ ) + for( int j = 0; j < 6; j++ ) + for( int k = 0; k < 6; k++ ) + { + r[ index ] = (byte)(i * 51); + g[ index ] = (byte)(j * 51); + b[ index ] = (byte)(k * 51); + index++; + } + + while( index < 256 ) + { + r[ index ] = g[ index ] = b[ index ] = + (byte)(18 + (index - 216) * 6); + index++; + } + + return new IndexColorModel( 8, 256, r, g, b ); + } + + public void coerceData(boolean premultiplied) + { + colorModel = colorModel.coerceData(raster, premultiplied); + isPremultiplied = premultiplied; + } + + public WritableRaster copyData(WritableRaster dest) + { + if (dest == null) + dest = raster.createCompatibleWritableRaster(getMinX(), getMinY(), + getWidth(),getHeight()); + + int x = dest.getMinX(); + int y = dest.getMinY(); + int w = dest.getWidth(); + int h = dest.getHeight(); + + // create a src child that has the right bounds... + WritableRaster src = + raster.createWritableChild(x, y, w, h, x, y, + null); // same bands + + if (src.getSampleModel () instanceof ComponentSampleModel + && dest.getSampleModel () instanceof ComponentSampleModel) + // Refer to ComponentDataBlitOp for optimized data blitting: + ComponentDataBlitOp.INSTANCE.filter(src, dest); + + else + { + // slower path + int samples[] = src.getPixels (x, y, w, h, (int [])null); + dest.setPixels (x, y, w, h, samples); + } + return dest; + } + + public Graphics2D createGraphics() + { + GraphicsEnvironment env; + env = GraphicsEnvironment.getLocalGraphicsEnvironment (); + return env.createGraphics (this); + } + + public void flush() + { + } + + public WritableRaster getAlphaRaster() + { + return colorModel.getAlphaRaster(raster); + } + + public ColorModel getColorModel() + { + return colorModel; + } + + public Raster getData() + { + return copyData(null); + /* TODO: this might be optimized by returning the same + raster (not writable) as long as image data doesn't change. */ + } + + public Raster getData(Rectangle rectangle) + { + WritableRaster dest = + raster.createCompatibleWritableRaster(rectangle); + return copyData(dest); + } + + public Graphics getGraphics() + { + return createGraphics(); + } + + public int getHeight() + { + return raster.getHeight(); + } + + public int getHeight(ImageObserver imageobserver) + { + return getHeight(); + } + + public int getMinTileX() + { + return 0; + } + + public int getMinTileY() + { + return 0; + } + + public int getMinX() + { + return 0; + } + + public int getMinY() + { + return 0; + } + + public int getNumXTiles() + { + return 1; + } + + public int getNumYTiles() + { + return 1; + } + + /** + * Returns the value of the specified property, or + * {@link Image#UndefinedProperty} if the property is not defined. + * + * @param string the property key (null not permitted). + * + * @return The property value. + * + * @throws NullPointerException if string is null. + */ + public Object getProperty(String string) + { + if (string == null) + throw new NullPointerException("The property name cannot be null."); + Object result = Image.UndefinedProperty; + if (properties != null) + { + Object v = properties.get(string); + if (v != null) + result = v; + } + return result; + } + + public Object getProperty(String string, ImageObserver imageobserver) + { + return getProperty(string); + } + + /** + * Returns null always. + * + * @return null always. + */ + public String[] getPropertyNames() + { + // This method should always return null, see: + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4640609 + return null; + } + + public int getRGB(int x, int y) + { + Object rgbElem = raster.getDataElements(x, y, null); + return colorModel.getRGB(rgbElem); + } + + public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray, + int offset, int scanlineStride) + { + if (rgbArray == null) + { + /* + 000000000000000000 + 00000[#######----- [ = start + -----########----- ] = end + -----#######]00000 + 000000000000000000 + */ + int size = (h-1)*scanlineStride + w; + rgbArray = new int[size]; + } + + int endX = startX + w; + int endY = startY + h; + + /* *TODO*: + Opportunity for optimization by examining color models... + + Perhaps wrap the rgbArray up in a WritableRaster with packed + sRGB color model and perform optimized rendering into the + array. */ + + Object rgbElem = null; + for (int y=startY; y consumers = new Vector(); + + public void addConsumer(ImageConsumer ic) + { + if(!consumers.contains(ic)) + consumers.add(ic); + } + + public boolean isConsumer(ImageConsumer ic) + { + return consumers.contains(ic); + } + + public void removeConsumer(ImageConsumer ic) + { + consumers.remove(ic); + } + + public void startProduction(ImageConsumer ic) + { + int x = 0; + int y = 0; + int width = getWidth(); + int height = getHeight(); + int stride = width; + int offset = 0; + int[] pixels = getRGB(x, y, + width, height, + (int[])null, offset, stride); + // We already convert the color to RGB in the getRGB call, so + // we pass a simple RGB color model to the consumers. + ColorModel model = new DirectColorModel(32, 0xff0000, 0xff00, 0xff, + 0xff000000); + + consumers.add(ic); + + for(int i = 0; i < consumers.size(); i++) + { + ImageConsumer c = consumers.elementAt(i); + c.setHints(ImageConsumer.SINGLEPASS); + c.setDimensions(getWidth(), getHeight()); + c.setPixels(x, y, width, height, model, pixels, offset, stride); + c.imageComplete(ImageConsumer.STATICIMAGEDONE); + } + } + + public void requestTopDownLeftRightResend(ImageConsumer ic) + { + startProduction(ic); + } + + }; + } + + public Vector getSources() + { + return null; + } + + public BufferedImage getSubimage(int x, int y, int w, int h) + { + WritableRaster subRaster = + getRaster().createWritableChild(x, y, w, h, 0, 0, null); + + return new BufferedImage(getColorModel(), subRaster, isPremultiplied, + properties); + } + + public Raster getTile(int tileX, int tileY) + { + return getWritableTile(tileX, tileY); + } + + public int getTileGridXOffset() + { + return 0; // according to javadocs + } + + public int getTileGridYOffset() + { + return 0; // according to javadocs + } + + public int getTileHeight() + { + return getHeight(); // image is one big tile + } + + public int getTileWidth() + { + return getWidth(); // image is one big tile + } + + public int getType() + { + return type; + } + + public int getWidth() + { + return raster.getWidth(); + } + + public int getWidth(ImageObserver imageobserver) + { + return getWidth(); + } + + public WritableRaster getWritableTile(int tileX, int tileY) + { + isTileWritable(tileX, tileY); // for exception + return raster; + } + + private static final Point[] tileIndices = { new Point() }; + + public Point[] getWritableTileIndices() + { + return tileIndices; + } + + public boolean hasTileWriters() + { + return true; + } + + public boolean isAlphaPremultiplied() + { + return isPremultiplied; + } + + public boolean isTileWritable(int tileX, int tileY) + { + if ((tileX != 0) || (tileY != 0)) + throw new ArrayIndexOutOfBoundsException("only tile is (0,0)"); + return true; + } + + public void releaseWritableTile(int tileX, int tileY) + { + isTileWritable(tileX, tileY); // for exception + } + + //public void removeTileObserver(TileObserver tileobserver) {} + + public void setData(Raster src) + { + int x = src.getMinX(); + int y = src.getMinY(); + int w = src.getWidth(); + int h = src.getHeight(); + + // create a dest child that has the right bounds... + WritableRaster dest = + raster.createWritableChild(x, y, w, h, x, y, null); + + if (src.getSampleModel () instanceof ComponentSampleModel + && dest.getSampleModel () instanceof ComponentSampleModel) + + // Refer to ComponentDataBlitOp for optimized data blitting: + ComponentDataBlitOp.INSTANCE.filter(src, dest); + else + { + // slower path + int samples[] = src.getPixels (x, y, w, h, (int [])null); + dest.setPixels (x, y, w, h, samples); + } + } + + public void setRGB(int x, int y, int argb) + { + Object rgbElem = colorModel.getDataElements(argb, null); + raster.setDataElements(x, y, rgbElem); + } + + public void setRGB(int startX, int startY, int w, int h, + int[] argbArray, int offset, int scanlineStride) + { + int endX = startX + w; + int endY = startY + h; + + Object rgbElem = null; + for (int y=startY; yBufferedImage (the + * source) producing a new BufferedImage (the destination). + */ +public interface BufferedImageOp +{ + /** + * Performs an operation on the source image, returning the result in a + * BufferedImage. If dest is null, a + * new BufferedImage will be created by calling the + * {@link #createCompatibleDestImage} method. If dest + * is not null, the result is written to dest then + * returned (this avoids creating a new BufferedImage each + * time this method is called). + * + * @param src the source image. + * @param dst the destination image (null permitted). + * + * @return The filterd image. + */ + BufferedImage filter(BufferedImage src, BufferedImage dst); + + /** + * Returns the bounds of the destination image on the basis of this + * BufferedImageOp being applied to the specified source image. + * + * @param src the source image. + * + * @return The destination bounds. + */ + Rectangle2D getBounds2D(BufferedImage src); + + /** + * Returns a new BufferedImage that can be used by this + * BufferedImageOp as the destination image when filtering + * the specified source image. + * + * @param src the source image. + * @param dstCM the color model for the destination image. + * + * @return A new image that can be used as the destination image. + */ + BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM); + + /** + * Returns the point on the destination image that corresponds to the given + * point on the source image. + * + * @param src the source point. + * @param dst the destination point (null permitted). + * + * @return The destination point. + */ + Point2D getPoint2D(Point2D src, Point2D dst); + + /** + * Returns the rendering hints for this operation. + * + * @return The rendering hints. + */ + RenderingHints getRenderingHints(); + +} diff --git a/libjava/classpath/java/awt/image/ByteLookupTable.java b/libjava/classpath/java/awt/image/ByteLookupTable.java new file mode 100644 index 000000000..89804c55b --- /dev/null +++ b/libjava/classpath/java/awt/image/ByteLookupTable.java @@ -0,0 +1,175 @@ +/* ByteLookupTable.java -- Java class for a pixel translation table. + Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * ByteLookupTable represents translation arrays for pixel values. It wraps + * one or more data arrays for each layer (or component) in an image, such as + * Alpha, R, G, and B. When doing translation, the offset is subtracted from + * the pixel values to allow a subset of an array to be used. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @version 1.0 + */ +public class ByteLookupTable extends LookupTable +{ + // Array of translation tables. + private byte data[][]; + + /** + * Creates a new ByteLookupTable instance. + * + * Offset is subtracted from pixel values when looking up in the translation + * tables. If data.length is one, the same table is applied to all pixel + * components. + * + * @param offset Offset to be subtracted. + * @param data Array of lookup tables (null not permitted). + * @exception IllegalArgumentException if offset < 0 or data.length < 1. + */ + public ByteLookupTable(int offset, byte[][] data) + throws IllegalArgumentException + { + super(offset, data.length); + + // tests show that Sun's implementation creates a new array to store the + // references from the incoming 'data' array - not sure why, but we'll + // match that behaviour just in case it matters... + this.data = new byte[data.length][]; + for (int i = 0; i < data.length; i++) + this.data[i] = data[i]; + } + + /** + * Creates a new ByteLookupTable instance. + * + * Offset is subtracted from pixel values when looking up in the translation + * table. The same table is applied to all pixel components. + * + * @param offset Offset to be subtracted. + * @param data Lookup table for all components (null not + * permitted). + * @exception IllegalArgumentException if offset < 0. + */ + public ByteLookupTable(int offset, byte[] data) + throws IllegalArgumentException + { + super(offset, 1); + if (data == null) + throw new NullPointerException("Null 'data' argument."); + this.data = new byte[][] {data}; + } + + /** + * Return the lookup tables. + * + * @return the tables + */ + public final byte[][] getTable() + { + return data; + } + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dst Destination array for values, or null. + * @return Translated values for the pixel. + */ + public int[] lookupPixel(int[] src, int[] dst) + throws ArrayIndexOutOfBoundsException + { + if (dst == null) + dst = new int[src.length]; + + if (data.length == 1) + for (int i=0; i < src.length; i++) + dst[i] = data[0][src[i] - offset]; + else + for (int i=0; i < src.length; i++) + dst[i] = data[i][src[i] - offset]; + + return dst; + } + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dst Destination array for values, or null. + * @return Translated values for the pixel. + */ + public byte[] lookupPixel(byte[] src, byte[] dst) + throws ArrayIndexOutOfBoundsException + { + if (dst == null) + dst = new byte[src.length]; + + if (data.length == 1) + for (int i=0; i < src.length; i++) + dst[i] = data[0][((int)src[i]) - offset]; + else + for (int i=0; i < src.length; i++) + dst[i] = data[i][((int)src[i]) - offset]; + + return dst; + + } +} diff --git a/libjava/classpath/java/awt/image/ColorConvertOp.java b/libjava/classpath/java/awt/image/ColorConvertOp.java new file mode 100644 index 000000000..b1d6f857d --- /dev/null +++ b/libjava/classpath/java/awt/image/ColorConvertOp.java @@ -0,0 +1,537 @@ +/* ColorConvertOp.java -- + Copyright (C) 2004, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.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; +import java.awt.color.ICC_Profile; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * 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 + * color conversion, and premultiplication applied if the destination + * requires it. + * + * Color rendering and dithering hints may be applied if specified. This is + * likely platform-dependent. + * + * @author jlquinn@optonline.net + */ +public class ColorConvertOp implements BufferedImageOp, RasterOp +{ + private RenderingHints hints; + private ICC_Profile[] profiles = null; + private ColorSpace[] spaces; + + + /** + * Convert a BufferedImage through a ColorSpace. + * + * 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, if any (may be null) + * @throws NullPointerException if the ColorSpace is null. + */ + public ColorConvertOp(ColorSpace cspace, RenderingHints hints) + { + if (cspace == null) + throw new NullPointerException(); + spaces = new ColorSpace[]{cspace}; + this.hints = hints; + } + + /** + * 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) + { + if (srcCspace == null || dstCspace == null) + throw new NullPointerException(); + spaces = new ColorSpace[]{srcCspace, dstCspace}; + this.hints = hints; + } + + /** + * Convert from a source colorspace to a destinatino colorspace. + * + * This constructor builds a ColorConvertOp from an array of ICC_Profiles. + * 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, an IllegalArgumentException is thrown. + * + * 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 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; + + // 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 color space to destination color space. + * + * Only valid for BufferedImage objects, this Op converts from the source + * image's color space to the destination image's color space. + * + * 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; + spaces = new ColorSpace[0]; + } + + /** + * 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) + { + // TODO: The plan is to create a scanline buffer for intermediate buffers. + // For now we just suck it up and create intermediate buffers. + + if (dst == null && spaces.length == 0) + 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; + } + + // Convert through defined intermediate conversions + BufferedImage tmp; + for (int i = 0; i < spaces.length; i++) + { + if (src.getColorModel().getColorSpace().getType() != spaces[i].getType()) + { + tmp = createCompatibleDestImage(src, + createCompatibleColorModel(src, + spaces[i])); + copyimage(src, tmp); + src = tmp; + } + } + + // No implicit conversion to destination type needed; return result from the + // last intermediate conversions (which was left in src) + if (dst == null) + dst = src; + + // Implicit conversion to destination image's color space + else + copyimage(src, dst); + + return dst; + } + + /** + * 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) + { + 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, + createCompatibleDestRaster(src.getRaster(), + dstCM.getColorSpace(), + src.getColorModel().hasAlpha, + dstCM.getTransferType()), + src.isPremultiplied, null); + } + + /** + * 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) + { + if (spaces.length < 2) + throw new IllegalArgumentException("Not enough destination colorspace " + + "information"); + + // 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); + } + + /** + * 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 ICC_Profile[] getICC_Profiles() + { + return profiles; + } + + /** + * Returns the rendering hints for this op. + * + * @return The rendering hints for this Op, or null. + */ + public final RenderingHints getRenderingHints() + { + return hints; + } + + /** + * Returns the corresponding destination point for a source point. + * Because this is not a geometric operation, the destination and source + * points will be identical. + * + * @param src The source point. + * @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(); + + dst.setLocation(src); + return dst; + } + + /** + * 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(); + } + + /** + * 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(); + } + + /** + * 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 + // setRenderingHints on the Graphics2D object. + if (hints != null) + gg.setRenderingHints(hints); + + gg.drawImage(src, 0, 0, null); + gg.dispose(); + } + + /** + * 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 != 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++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + dst.setPixel(x, y, + dcs.fromCIEXYZ(scs.toCIEXYZ(src.getPixel(x, y, sbuf)))); + } + else + { + // use rgb - it's probably faster + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + dst.setPixel(x, y, + dcs.fromRGB(scs.toRGB(src.getPixel(x, y, sbuf)))); + } + } + + /** + * 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/libjava/classpath/java/awt/image/ColorModel.java b/libjava/classpath/java/awt/image/ColorModel.java new file mode 100644 index 000000000..ede9e31d8 --- /dev/null +++ b/libjava/classpath/java/awt/image/ColorModel.java @@ -0,0 +1,794 @@ +/* ColorModel.java -- + Copyright (C) 1999, 2000, 2002, 2003, 2004, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.Buffers; + +import java.awt.Point; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.util.Arrays; + +/** + * A color model operates with colors in several formats: + * + *
      + *
    • normalized: component samples are in range [0.0, 1.0].
    • + * + *
    • color model pixel value: all the color component samples for a + * sigle pixel packed/encoded in a way natural for the color + * model.
    • + * + *
    • color model pixel int value: only makes sense if the natural + * encoding of a single pixel can fit in a single int value.
    • + * + *
    • array of transferType containing a single pixel: the pixel is + * encoded in the natural way of the color model, taking up as many + * array elements as needed.
    • + * + *
    • sRGB pixel int value: a pixel in sRGB color space, encoded in + * default 0xAARRGGBB format, assumed not alpha premultiplied.
    • + * + *
    • single [0, 255] scaled int samples from default sRGB color + * space. These are always assumed to be alpha non-premultiplied.
    • + * + *
    • arrays of unnormalized component samples of single pixel: these + * samples are scaled and multiplied according to the color model, but + * is otherwise not packed or encoded. Each element of the array is one + * separate component sample. The color model only operate on the + * components from one pixel at a time, but using offsets, allows + * manipulation of arrays that contain the components of more than one + * pixel.
    • + * + *
    + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author C. Brian Jones (cbj@gnu.org) + */ +public abstract class ColorModel implements Transparency +{ + protected int pixel_bits; + protected int transferType; + + int[] bits; + ColorSpace cspace; + int transparency; + boolean hasAlpha; + boolean isAlphaPremultiplied; + + /** + * The standard color model for the common sRGB. + */ + private static final ColorModel S_RGB_MODEL = new SRGBColorModel(); + + static int[] nArray(int value, int times) + { + int[] array = new int[times]; + java.util.Arrays.fill(array, value); + return array; + } + + static byte[] nArray(byte value, int times) + { + byte[] array = new byte[times]; + java.util.Arrays.fill(array, value); + return array; + } + + /** + * Constructs the default color model. The default color model + * can be obtained by calling getRGBdefault of this + * class. + * @param bits the number of bits wide used for bit size of pixel values + */ + public ColorModel(int bits) + { + this(bits * 4, // total bits, sRGB, four channels + nArray(bits, 4), // bits for each channel + ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB + true, // has alpha + false, // not premultiplied + TRANSLUCENT, + Buffers.smallestAppropriateTransferType(bits * 4)); + } + + /** + * Constructs a ColorModel that translates pixel values to + * color/alpha components. + * + * @exception IllegalArgumentException If the length of the bit array is less + * than the number of color or alpha components in this ColorModel, or if the + * transparency is not a valid value, or if the sum of the number of bits in + * bits is less than 1 or if any of the elements in bits is less than 0. + */ + protected ColorModel(int pixel_bits, int[] bits, ColorSpace cspace, + boolean hasAlpha, boolean isAlphaPremultiplied, + int transparency, int transferType) + { + int bits_sum = 0; + for (int i = 0; i < bits.length; i++) + { + if (bits [i] < 0) + throw new IllegalArgumentException (); + + bits_sum |= bits [i]; + } + + if ((bits.length < cspace.getNumComponents()) + || (bits_sum < 1)) + throw new IllegalArgumentException (); + + this.pixel_bits = pixel_bits; + this.bits = bits; + this.cspace = cspace; + this.hasAlpha = hasAlpha; + this.isAlphaPremultiplied = isAlphaPremultiplied; + this.transparency = transparency; + this.transferType = transferType; + } + + public void finalize() + { + // Do nothing here. + } + + /** + * Returns the default color model which in Sun's case is an instance + * of DirectColorModel. + */ + public static ColorModel getRGBdefault() + { + return S_RGB_MODEL; + } + + public final boolean hasAlpha() + { + return hasAlpha; + } + + public final boolean isAlphaPremultiplied() + { + return isAlphaPremultiplied; + } + + /** + * Get get number of bits wide used for the bit size of pixel values + */ + public int getPixelSize() + { + return pixel_bits; + } + + public int getComponentSize(int componentIdx) + { + return bits[componentIdx]; + } + + public int[] getComponentSize() + { + return bits; + } + + public int getTransparency() + { + return transparency; + } + + public int getNumComponents() + { + return getNumColorComponents() + (hasAlpha ? 1 : 0); + } + + public int getNumColorComponents() + { + return cspace.getNumComponents(); + } + + /** + * Converts pixel value to sRGB and extract red int sample scaled + * to range [0, 255]. + * + * @param pixel pixel value that will be interpreted according to + * the color model, (assumed alpha premultiplied if color model says + * so.) + * + * @return red sample scaled to range [0, 255], from default color + * space sRGB, alpha non-premultiplied. + */ + public abstract int getRed(int pixel); + + /** + * Converts pixel value to sRGB and extract green int sample + * scaled to range [0, 255]. + * + * @see #getRed(int) + */ + public abstract int getGreen(int pixel); + + /** + * Converts pixel value to sRGB and extract blue int sample + * scaled to range [0, 255]. + * + * @see #getRed(int) + */ + public abstract int getBlue(int pixel); + + /** + * Extract alpha int sample from pixel value, scaled to [0, 255]. + * + * @param pixel pixel value that will be interpreted according to + * the color model. + * + * @return alpha sample, scaled to range [0, 255]. + */ + public abstract int getAlpha(int pixel); + + /** + * Converts a pixel int value of the color space of the color + * model to a sRGB pixel int value. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. + * + * @param pixel pixel value that will be interpreted according to + * the color model. + * + * @return a pixel in sRGB color space, encoded in default + * 0xAARRGGBB format. */ + public int getRGB(int pixel) + { + return + ((getAlpha(pixel) & 0xff) << 24) | + (( getRed(pixel) & 0xff) << 16) | + ((getGreen(pixel) & 0xff) << 8) | + (( getBlue(pixel) & 0xff) << 0); + } + + + /** + * In this color model we know that the whole pixel value will + * always be contained within the first element of the pixel + * array. + */ + final int getPixelFromArray(Object inData) { + DataBuffer data = + Buffers.createBufferFromData(transferType, inData, 1); + Object da = Buffers.getData(data); + + return data.getElem(0); + } + + /** + * Converts pixel in the given array to sRGB and extract blue int + * sample scaled to range [0-255]. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. + * + * @param inData array of transferType containing a single pixel. The + * pixel should be encoded in the natural way of the color model. + */ + public int getRed(Object inData) + { + return getRed(getPixelFromArray(inData)); + } + + /** + * @see #getRed(Object) + */ + public int getGreen(Object inData) + { + return getGreen(getPixelFromArray(inData)); + } + + /** + * @see #getRed(Object) + */ + public int getBlue(Object inData) { + return getBlue(getPixelFromArray(inData)); + } + + /** + * @see #getRed(Object) + */ + public int getAlpha(Object inData) { + return getAlpha(getPixelFromArray(inData)); + } + + /** + * Converts a pixel in the given array of the color space of the + * color model to an sRGB pixel int value. + * + *

    This method performs the inverse function of + * getDataElements(int rgb, Object pixel). + * I.e. (rgb == cm.getRGB(cm.getDataElements(rgb, + * null))). + * + * @param inData array of transferType containing a single pixel. The + * pixel should be encoded in the natural way of the color model. + * + * @return a pixel in sRGB color space, encoded in default + * 0xAARRGGBB format. + * + * @see #getDataElements(int, Object) + */ + public int getRGB(Object inData) + { + return + ((getAlpha(inData) & 0xff) << 24) | + (( getRed(inData) & 0xff) << 16) | + ((getGreen(inData) & 0xff) << 8) | + (( getBlue(inData) & 0xff) << 0); + } + + /** + * Converts an sRGB pixel int value to an array containing a + * single pixel of the color space of the color model. + * + *

    This method performs the inverse function of + * getRGB(Object inData). + * + * Outline of conversion process: + * + *

      + * + *
    1. Convert rgb to normalized [0.0, 1.0] sRGB values.
    2. + * + *
    3. Convert to color space components using fromRGB in + * ColorSpace.
    4. + * + *
    5. If color model has alpha and should be premultiplied, + * multiply color space components with alpha value
    6. + * + *
    7. Scale the components to the correct number of bits.
    8. + * + *
    9. Arrange the components in the output array
    10. + * + *
    + * + * @param rgb The color to be converted to dataElements. A pixel + * in sRGB color space, encoded in default 0xAARRGGBB format, + * assumed not alpha premultiplied. + * + * @param pixel to avoid needless creation of arrays, an array to + * use to return the pixel can be given. If null, a suitable array + * will be created. + * + * @return An array of transferType values representing the color, + * in the color model format. The color model defines whether the + * + * @see #getRGB(Object) + */ + public Object getDataElements(int rgb, Object pixel) + { + // subclasses has to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Fills an array with the unnormalized component samples from a + * pixel value. I.e. decompose the pixel, but not perform any + * color conversion. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. + * + * @param pixel pixel value encoded according to the color model. + * + * @return arrays of unnormalized component samples of single + * pixel. The scale and multiplication state of the samples are + * according to the color model. Each component sample is stored + * as a separate element in the array. + */ + public int[] getComponents(int pixel, int[] components, int offset) + { + // subclasses has to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Fills an array with the unnormalized component samples from an + * array of transferType containing a single pixel. I.e. decompose + * the pixel, but not perform any color conversion. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. + * + * @param pixel an array of transferType containing a single pixel. The + * pixel should be encoded in the natural way of the color model. If + * this argument is not an array, as expected, a {@link ClassCastException} + * will be thrown. + * @param components an array that will be filled with the color component + * of the pixel. If this is null, a new array will be allocated + * @param offset index into the components array at which the result + * will be stored + * + * @return arrays of unnormalized component samples of single + * pixel. The scale and multiplication state of the samples are + * according to the color model. Each component sample is stored + * as a separate element in the array. + */ + public int[] getComponents(Object pixel, int[] components, int offset) + { + // subclasses has to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Convert normalized components to unnormalized components. + */ + public int[] getUnnormalizedComponents(float[] normComponents, + int normOffset, + int[] components, + int offset) + { + int numComponents = getNumComponents(); + if (components == null) + { + components = new int[offset + numComponents]; + } + + for (int i=0; igetComponents(int pixel, int[] components, + * int offset). I.e. + * + * (pixel == cm.getDataElement(cm.getComponents(pixel, null, + * 0), 0)). + * + * This method is overriden in subclasses since this abstract class throws + * UnsupportedOperationException(). + * + * @param components Array of unnormalized component samples of single + * pixel. The scale and multiplication state of the samples are according + * to the color model. Each component sample is stored as a separate element + * in the array. + * @param offset Position of the first value of the pixel in components. + * + * @return pixel value encoded according to the color model. + */ + public int getDataElement(int[] components, int offset) + { + // subclasses have to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Converts the normalized component samples from an array to a pixel + * value. I.e. composes the pixel from component samples, but does not + * perform any color conversion or scaling of the samples. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. The method provided by this abstract + * class converts the components to unnormalized form and returns + * getDataElement(int[], int). + * + * @param components Array of normalized component samples of single pixel. + * The scale and multiplication state of the samples are according to the + * color model. Each component sample is stored as a separate element in the + * array. + * @param offset Position of the first value of the pixel in components. + * + * @return pixel value encoded according to the color model. + * @since 1.4 + */ + public int getDataElement (float[] components, int offset) + { + return + getDataElement(getUnnormalizedComponents(components, offset, null, 0), + 0); + } + + public Object getDataElements(int[] components, int offset, Object obj) + { + // subclasses have to implement this method. + throw new UnsupportedOperationException(); + } + + /** + * Converts the normalized component samples from an array to an array of + * TransferType values. I.e. composes the pixel from component samples, but + * does not perform any color conversion or scaling of the samples. + * + * If obj is null, a new array of TransferType is allocated and returned. + * Otherwise the results are stored in obj and obj is returned. If obj is + * not long enough, ArrayIndexOutOfBounds is thrown. If obj is not an array + * of primitives, ClassCastException is thrown. + * + * This method is typically overriden in subclasses to provide a + * more efficient implementation. The method provided by this abstract + * class converts the components to unnormalized form and returns + * getDataElement(int[], int, Object). + * + * @param components Array of normalized component samples of single pixel. + * The scale and multiplication state of the samples are according to the + * color model. Each component sample is stored as a separate element in the + * array. + * @param offset Position of the first value of the pixel in components. + * @param obj Array of TransferType or null. + * + * @return pixel value encoded according to the color model. + * @throws ArrayIndexOutOfBoundsException + * @throws ClassCastException + * @since 1.4 + */ + public Object getDataElements(float[] components, int offset, Object obj) + { + return + getDataElements(getUnnormalizedComponents(components, offset, null, 0), + 0, obj); + } + + public boolean equals(Object obj) + { + if (!(obj instanceof ColorModel)) return false; + + ColorModel o = (ColorModel) obj; + return + (pixel_bits == o.pixel_bits) && + (transferType == o.transferType) && + (transparency == o.transparency) && + (hasAlpha == o.hasAlpha) && + (isAlphaPremultiplied == o.isAlphaPremultiplied) && + Arrays.equals(bits, o.bits) && + (cspace.equals(o.cspace)); + } + + public final ColorSpace getColorSpace() + { + return cspace; + } + + public ColorModel coerceData(WritableRaster raster, + boolean isAlphaPremultiplied) + { + // This method should always be overridden, but is not abstract. + throw new UnsupportedOperationException(); + } + + void coerceDataWorker(WritableRaster raster, + boolean isAlphaPremultiplied) + { + int w = raster.getWidth(); + int h = raster.getHeight(); + int x = raster.getMinX(); + int y = raster.getMinY(); + int size = w * h; + int numColors = getNumColorComponents(); + int numComponents = getNumComponents(); + int alphaScale = (1 << getComponentSize(numColors)) - 1; + double[] pixels = raster.getPixels(x, y, w, h, (double[]) null); + + for (int i = 0; i < size; i++) + { + double alpha = pixels[i * numComponents + numColors] / alphaScale; + for (int c = 0; c < numColors; c++) + { + int offset = i * numComponents + c; + if (isAlphaPremultiplied) + pixels[offset] = Math.round(pixels[offset] * alpha); + else + pixels[offset] = Math.round(pixels[offset] / alpha); + } + } + + raster.setPixels(0, 0, w, h, pixels); + } + + /** + * Checks if the given raster has a compatible data-layout (SampleModel). + * @param raster The Raster to test. + * @return true if raster is compatible. + */ + public boolean isCompatibleRaster(Raster raster) + { + SampleModel sampleModel = raster.getSampleModel(); + return isCompatibleSampleModel(sampleModel); + } + + // Typically overridden + public WritableRaster createCompatibleWritableRaster(int w, int h) + { + return new WritableRaster(createCompatibleSampleModel(w, h), + new Point(0, 0)); + } + + // Typically overridden + public SampleModel createCompatibleSampleModel(int w, int h) + { + throw new UnsupportedOperationException(); + } + + // Typically overridden + public boolean isCompatibleSampleModel(SampleModel sm) + { + return sm.getTransferType() == transferType; + } + + public final int getTransferType () + { + return transferType; + } + + /** + * Subclasses must override this method if it is possible for the + * color model to have an alpha channel. + * + * @return null, as per JDK 1.3 doc. Subclasses will only return + * null if no alpha raster exists. + */ + public WritableRaster getAlphaRaster(WritableRaster raster) + { + return null; + + /* It is a mystery to me why we couldn't use the following code... + + + if (!hasAlpha()) return null; + + SampleModel sm = raster.getSampleModel(); + int[] alphaBand = { sm.getNumBands() - 1 }; + SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand); + DataBuffer buffer = raster.getDataBuffer(); + Point origin = new Point(0, 0); + return Raster.createWritableRaster(alphaModel, buffer, origin); + + + ...here, and avoided overriding the method in subclasses, + but the Sun docs state that this method always will return + null, and that overriding is required. Oh, well. + */ + } + + String stringParam() + { + return "pixel_bits=" + pixel_bits + + ", cspace=" + cspace + + ", transferType=" + transferType + + ", transparency=" + transparency + + ", hasAlpha=" + hasAlpha + + ", isAlphaPremultiplied=" + isAlphaPremultiplied; + } + + public String toString() + { + return getClass().getName() + "[" + stringParam() + "]"; + } + + /** + * A color model optimized for standard sRGB. + */ + private static class SRGBColorModel + extends DirectColorModel + { + + SRGBColorModel() + { + super(32,0x00FF0000,0x0000FF00,0x000000FF,0xFF000000); + } + + public int getAlpha(Object inData) + { + return ((((int[]) inData)[0]) >> 24) & 0xFF; + } + + public int getBlue(Object inData) + { + return ((((int[]) inData)[0])) & 0xFF; + } + + public int getGreen(Object inData) + { + return ((((int[]) inData)[0]) >> 8) & 0xFF; + } + + public int getRed(Object inData) + { + return ((((int[]) inData)[0]) >> 16) & 0xFF; + } + + public int getRGB(Object inData) + { + return ((int[]) inData)[0]; + } + + public Object getDataElements(int rgb, Object pixel) + { + if(pixel == null) + { + pixel = new int[]{rgb}; + } + else + { + ((int[]) pixel)[0] = rgb; + } + + return pixel; + } + } +} diff --git a/libjava/classpath/java/awt/image/ComponentColorModel.java b/libjava/classpath/java/awt/image/ComponentColorModel.java new file mode 100644 index 000000000..ef0b84f00 --- /dev/null +++ b/libjava/classpath/java/awt/image/ComponentColorModel.java @@ -0,0 +1,408 @@ +/* ComponentColorModel.java -- + Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +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; + for (int i=0; i1) throw new IllegalArgumentException(); + return (int) getRGBFloat(pixel)[0]; + } + + public int getGreen(int pixel) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + return (int) getRGBFloat(pixel)[0]; + } + + public int getBlue(int pixel) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + return (int) getRGBFloat(pixel)[0]; + } + + public int getAlpha(int pixel) + { + if (getNumComponents()>1) throw new IllegalArgumentException(); + int shift = 8 - getComponentSize(getNumColorComponents()); + if (shift >= 0) return pixel << shift; + return pixel >> (-shift); + } + + public int getRGB(int pixel) + { + float[] rgb = getRGBFloat(pixel); + int ret = getRGB(rgb); + if (hasAlpha()) ret |= getAlpha(pixel) << 24; + return ret; + } + + + /* Note, it's OK to pass a to large array to toRGB(). Extra + elements are ignored. */ + + private float[] getRGBFloat(int pixel) + { + float[] data = { pixel }; + return cspace.toRGB(data); + } + + private float[] getRGBFloat(Object inData) + { + DataBuffer buffer = + Buffers.createBufferFromData(transferType, inData, + getNumComponents()); + int colors = getNumColorComponents(); + float[] data = new float[colors]; + + // FIXME: unpremultiply data that is premultiplied + for (int i=0; i= 0) return alpha << shift; + return alpha >> (-shift); + } + + private int getRGB(float[] rgb) + { + /* NOTE: We could cast to byte instead of int here. This would + avoid bits spilling over from one bit field to + another. But, if we assume that floats are in the [0.0, + 1.0] range, this will never happen anyway. */ + + /* Remember to multiply BEFORE casting to int, otherwise, decimal + point data will be lost. */ + int ret = + (((int) (rgb[0]*255F)) << 16) | + (((int) (rgb[1]*255F)) << 8) | + (((int) (rgb[2]*255F)) << 0); + return ret; + } + + /** + * @param inData pixel data of transferType, as returned by the + * getDataElements method in SampleModel. + */ + public int getRGB(Object inData) + { + float[] rgb = getRGBFloat(inData); + int ret = getRGB(rgb); + if (hasAlpha()) ret |= getAlpha(inData) << 24; + return ret; + } + + public Object getDataElements(int rgb, Object pixel) + { + // Convert rgb to [0.0, 1.0] sRGB values. + float[] rgbFloats = { + ((rgb >> 16)&0xff)/255.0F, + ((rgb >> 8)&0xff)/255.0F, + ((rgb >> 0)&0xff)/255.0F + }; + + // Convert from rgb to color space components. + float[] data = cspace.fromRGB(rgbFloats); + DataBuffer buffer = Buffers.createBuffer(transferType, pixel, + getNumComponents()); + int numColors = getNumColorComponents(); + + if (hasAlpha()) + { + float alpha = ((rgb >> 24)&0xff)/255.0F; + + /* If color model has alpha and should be premultiplied, multiply + color space components with alpha value. */ + if (isAlphaPremultiplied()) { + for (int i=0; i1) throw new IllegalArgumentException(); + if (components == null) + components = new int[getNumComponents() + offset]; + components[offset] = pixel; + return components; + } + + public int[] getComponents(Object pixel, int[] components, int offset) + { + DataBuffer buffer = Buffers.createBuffer(transferType, pixel, + getNumComponents()); + int numComponents = getNumComponents(); + + if (components == null) + components = new int[numComponents + offset]; + + for (int i=0; i1) throw new IllegalArgumentException(); + return components[offset]; + } + + public Object getDataElements(int[] components, int offset, Object obj) + { + DataBuffer buffer = Buffers.createBuffer(transferType, obj, + getNumComponents()); + int numComponents = getNumComponents(); + + for (int i=0; iSampleModel whose arrangement of pixel + * data is compatible to this ColorModel. + * + * @param w the number of pixels in the horizontal direction. + * @param h the number of pixels in the vertical direction. + */ + public SampleModel createCompatibleSampleModel(int w, int h) + { + int pixelStride, scanlineStride; + int[] bandOffsets; + + pixelStride = getNumComponents(); + scanlineStride = pixelStride * w; + + /* We might be able to re-use the same bandOffsets array among + * multiple calls to this method. However, this optimization does + * not seem worthwile because setting up descriptive data + * structures (such as SampleModels) is neglectible in comparision + * to shuffling around masses of pixel data. + */ + bandOffsets = new int[pixelStride]; + for (int i = 0; i < pixelStride; i++) + bandOffsets[i] = i; + + /* FIXME: Think about whether it would make sense to return the + * possibly more efficient PixelInterleavedSampleModel for other + * transferTypes as well. It seems unlikely that this would break + * any user applications, so the Mauve tests on this method + * might be too restrictive. + */ + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + return new PixelInterleavedSampleModel(transferType, w, h, + pixelStride, + scanlineStride, + bandOffsets); + + default: + return new ComponentSampleModel(transferType, w, h, + pixelStride, + scanlineStride, + bandOffsets); + } + } + + + public boolean isCompatibleSampleModel(SampleModel sm) + { + return + (sm instanceof ComponentSampleModel) && + super.isCompatibleSampleModel(sm); + } + + public WritableRaster getAlphaRaster(WritableRaster raster) + { + if (!hasAlpha()) return null; + + SampleModel sm = raster.getSampleModel(); + int[] alphaBand = { sm.getNumBands() - 1 }; + SampleModel alphaModel = sm.createSubsetSampleModel(alphaBand); + DataBuffer buffer = raster.getDataBuffer(); + Point origin = new Point(0, 0); + return Raster.createWritableRaster(alphaModel, buffer, origin); + } + + public boolean equals(Object obj) + { + if (!(obj instanceof ComponentColorModel)) return false; + return super.equals(obj); + } +} diff --git a/libjava/classpath/java/awt/image/ComponentSampleModel.java b/libjava/classpath/java/awt/image/ComponentSampleModel.java new file mode 100644 index 000000000..f32eae319 --- /dev/null +++ b/libjava/classpath/java/awt/image/ComponentSampleModel.java @@ -0,0 +1,755 @@ +/* Copyright (C) 2000, 2002, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import java.util.Arrays; + +/** + * ComponentSampleModel supports a flexible organization of pixel samples in + * memory, permitting pixel samples to be interleaved by band, by scanline, + * and by pixel. + * + * A DataBuffer for this sample model has K banks of data. Pixels have N + * samples, so there are N bands in the DataBuffer. Each band is completely + * contained in one bank of data, but a bank may contain more than one band. + * Each pixel sample is stored in a single data element. + * + * Within a bank, each band begins at an offset stored in bandOffsets. The + * banks containing the band is given by bankIndices. Within the bank, there + * are three dimensions - band, pixel, and scanline. The dimension ordering + * is controlled by bandOffset, pixelStride, and scanlineStride, which means + * that any combination of interleavings is supported. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class ComponentSampleModel extends SampleModel +{ + /** The offsets to the first sample for each band. */ + protected int[] bandOffsets; + + /** The indices of the bank used to store each band in a data buffer. */ + protected int[] bankIndices; + + /** + * The number of bands in the image. + * @specnote This field shadows the protected numBands in SampleModel. + */ + protected int numBands; + + /** Used when creating data buffers. */ + protected int numBanks; + + /** + * The number of data elements between a sample in one row and the + * corresponding sample in the next row. + */ + protected int scanlineStride; + + /** + * The number of data elements between a sample for one pixel and the + * corresponding sample for the next pixel in the same row. + */ + protected int pixelStride; + + /** + * Creates a new sample model that assumes that all bands are stored in a + * single bank of the {@link DataBuffer}. + *

    + * Note that the bandOffsets array is copied to internal storage + * to prevent subsequent changes to the array from affecting this object. + * + * @param dataType the data type (one of {@link DataBuffer#TYPE_BYTE}, + * {@link DataBuffer#TYPE_USHORT}, {@link DataBuffer#TYPE_SHORT}, + * {@link DataBuffer#TYPE_INT}, {@link DataBuffer#TYPE_FLOAT} or + * {@link DataBuffer#TYPE_DOUBLE}). + * @param w the width in pixels. + * @param h the height in pixels. + * @param pixelStride the number of data elements in the step from a sample + * in one pixel to the corresponding sample in the next pixel. + * @param scanlineStride the number of data elements in the step from a + * sample in a pixel to the corresponding sample in the pixel in the next + * row. + * @param bandOffsets the offset to the first element for each band, with + * the size of the array defining the number of bands (null + * not permitted). + * + * @throws IllegalArgumentException if dataType is not one of + * the specified values. + * @throws IllegalArgumentException if w is less than or equal + * to zero. + * @throws IllegalArgumentException if h is less than or equal + * to zero. + * @throws IllegalArgumentException if w * h exceeds + * {@link Integer#MAX_VALUE}. + * @throws IllegalArgumentException if pixelStride is negative. + * @throws IllegalArgumentException if scanlineStride is less + * than or equal to zero. + * @throws IllegalArgumentException if bandOffsets has zero + * length. + */ + public ComponentSampleModel(int dataType, + int w, int h, + int pixelStride, + int scanlineStride, + int[] bandOffsets) + { + this(dataType, w, h, pixelStride, scanlineStride, + new int[bandOffsets.length], bandOffsets); + } + + /** + * Creates a new sample model that assumes that all bands are stored in a + * single bank of the {@link DataBuffer}. + * + * @param dataType the data type (one of {@link DataBuffer#TYPE_BYTE}, + * {@link DataBuffer#TYPE_USHORT}, {@link DataBuffer#TYPE_SHORT}, + * {@link DataBuffer#TYPE_INT}, {@link DataBuffer#TYPE_FLOAT} or + * {@link DataBuffer#TYPE_DOUBLE}). + * @param w the width in pixels. + * @param h the height in pixels. + * @param pixelStride the number of data elements in the step from a sample + * in one pixel to the corresponding sample in the next pixel. + * @param scanlineStride the number of data elements in the step from a + * sample in a pixel to the corresponding sample in the pixel in the next + * row. + * @param bankIndices the index of the bank in which each band is stored + * (null not permitted). This array is copied to internal + * storage so that subsequent updates to the array do not affect the sample + * model. + * @param bandOffsets the offset to the first element for each band, with + * the size of the array defining the number of bands (null + * not permitted). This array is copied to internal storage so that + * subsequent updates to the array do not affect the sample model. + * + * @throws IllegalArgumentException if dataType is not one of + * the specified values. + * @throws IllegalArgumentException if w is less than or equal + * to zero. + * @throws IllegalArgumentException if h is less than or equal + * to zero. + * @throws IllegalArgumentException if w * h exceeds + * {@link Integer#MAX_VALUE}. + * @throws IllegalArgumentException if pixelStride is negative. + * @throws IllegalArgumentException if scanlineStride is less + * than or equal to zero. + * @throws IllegalArgumentException if bandOffsets has zero + * length. + */ + public ComponentSampleModel(int dataType, + int w, int h, + int pixelStride, + int scanlineStride, + int[] bankIndices, + int[] bandOffsets) + { + super(dataType, w, h, bandOffsets.length); + + // super permits DataBuffer.TYPE_UNDEFINED but this class doesn't... + if (dataType == DataBuffer.TYPE_UNDEFINED) + throw new IllegalArgumentException("Unsupported dataType."); + + if ((pixelStride < 0) || (scanlineStride < 0) || (bandOffsets.length < 1) + || (bandOffsets.length != bankIndices.length)) + throw new IllegalArgumentException(); + + this.bandOffsets = (int[]) bandOffsets.clone(); + this.bankIndices = (int[]) bankIndices.clone(); + this.numBands = bandOffsets.length; + + this.numBanks = 0; + for (int b = 0; b < bankIndices.length; b++) + this.numBanks = Math.max(this.numBanks, bankIndices[b] + 1); + + this.scanlineStride = scanlineStride; + this.pixelStride = pixelStride; + + } + + /** + * Creates a new sample model that is compatible with this one, but with the + * specified dimensions. + * + * @param w the width (must be greater than zero). + * @param h the height (must be greater than zero). + * + * @return A new sample model. + */ + public SampleModel createCompatibleSampleModel(int w, int h) + { + return new ComponentSampleModel(dataType, w, h, pixelStride, + scanlineStride, bankIndices, + bandOffsets); + } + + /** + * Creates a new sample model that provides access to a subset of the bands + * that this sample model supports. + * + * @param bands the bands (null not permitted). + * + * @return The new sample model. + */ + public SampleModel createSubsetSampleModel(int[] bands) + { + int numBands = bands.length; + + int[] bankIndices = new int[numBands]; + int[] bandOffsets = new int[numBands]; + for (int b = 0; b < numBands; b++) + { + bankIndices[b] = this.bankIndices[bands[b]]; + bandOffsets[b] = this.bandOffsets[bands[b]]; + } + + return new ComponentSampleModel(dataType, width, height, pixelStride, + scanlineStride, bankIndices, + bandOffsets); + } + + /** + * Creates a new data buffer that is compatible with this sample model. + * + * @return The new data buffer. + */ + public DataBuffer createDataBuffer() + { + // Maybe this value should be precalculated in the constructor? + int highestOffset = 0; + for (int b = 0; b < numBands; b++) + highestOffset = Math.max(highestOffset, bandOffsets[b]); + int size = pixelStride * (width - 1) + scanlineStride * (height - 1) + + highestOffset + 1; + + DataBuffer buffer = null; + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + buffer = new DataBufferByte(size, numBanks); + break; + case DataBuffer.TYPE_SHORT: + buffer = new DataBufferShort(size, numBanks); + break; + case DataBuffer.TYPE_USHORT: + buffer = new DataBufferUShort(size, numBanks); + break; + case DataBuffer.TYPE_INT: + buffer = new DataBufferInt(size, numBanks); + break; + case DataBuffer.TYPE_FLOAT: + buffer = new DataBufferFloat(size, numBanks); + break; + case DataBuffer.TYPE_DOUBLE: + buffer = new DataBufferDouble(size, numBanks); + break; + } + return buffer; + } + + /** + * Returns the offset of the sample in band 0 for the pixel at location + * (x, y). This offset can be used to read a sample value from + * a {@link DataBuffer}. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * + * @return The offset. + * + * @see #getOffset(int, int, int) + */ + public int getOffset(int x, int y) + { + return getOffset(x, y, 0); + } + + /** + * Returns the offset of the sample in band b for the pixel at + * location (x, y). This offset can be used to read a sample + * value from a {@link DataBuffer}. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param b the band index. + * + * @return The offset. + */ + public int getOffset(int x, int y, int b) + { + return bandOffsets[b] + pixelStride * x + scanlineStride * y; + } + + /** + * Returns the size in bits for each sample (one per band). For this sample + * model, each band has the same sample size and this is determined by the + * data type for the sample model. + * + * @return The sample sizes. + * + * @see SampleModel#getDataType() + */ + public final int[] getSampleSize() + { + int size = DataBuffer.getDataTypeSize(getDataType()); + int[] sizes = new int[numBands]; + + java.util.Arrays.fill(sizes, size); + return sizes; + } + + /** + * Returns the size in bits for the samples in the specified band. In this + * class, the sample size is the same for every band and is determined from + * the data type for the model. + * + * @param band the band index (ignored here). + * + * @return The sample size in bits. + * + * @see SampleModel#getDataType() + */ + public final int getSampleSize(int band) + { + return DataBuffer.getDataTypeSize(getDataType()); + } + + /** + * Returns the indices of the bank(s) in the {@link DataBuffer} used to + * store the samples for each band. The returned array is a copy, so that + * altering it will not impact the sample model. + * + * @return The bank indices. + */ + public final int[] getBankIndices() + { + return (int[]) bankIndices.clone(); + } + + /** + * Returns the offsets to the first sample in each band. The returned array + * is a copy, so that altering it will not impact the sample model. + * + * @return The offsets. + */ + public final int[] getBandOffsets() + { + return (int[]) bandOffsets.clone(); + } + + /** + * Returns the distance (in terms of element indices) between the sample for + * one pixel and the corresponding sample for the equivalent pixel in the + * next row. This is used in the calculation of the element offset for + * retrieving samples from a {@link DataBuffer}. + * + * @return The distance between pixel samples in consecutive rows. + */ + public final int getScanlineStride() + { + return scanlineStride; + } + + /** + * Returns the distance (in terms of element indices) between the sample for + * one pixel and the corresponding sample for the next pixel in a row. This + * is used in the calculation of the element offset for retrieving samples + * from a {@link DataBuffer}. + * + * @return The distance between pixel samples in the same row. + */ + public final int getPixelStride() + { + return pixelStride; + } + + /** + * Returns the number of data elements used to store the samples for one + * pixel. In this model, this is the same as the number of bands. + * + * @return The number of data elements used to store the samples for one + * pixel. + */ + public final int getNumDataElements() + { + return numBands; + } + + /** + * Returns the samples for the pixel at location (x, y) in + * a primitive array (the array type is determined by the data type for + * this model). The obj argument provides an option to supply + * an existing array to hold the result, if this is null a new + * array will be allocated. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param obj a primitive array that, if not null, will be + * used to store and return the sample values. + * @param data the data buffer (null not permitted). + * + * @return An array of sample values for the specified pixel. + */ + public Object getDataElements(int x, int y, Object obj, DataBuffer data) + { + int type = getTransferType(); + int numDataEls = getNumDataElements(); + int offset = y * scanlineStride + x * pixelStride; + switch (type) + { + case DataBuffer.TYPE_BYTE: + byte[] bData; + if (obj == null) + bData = new byte[numDataEls]; + else + bData = (byte[]) obj; + for (int i = 0; i < numDataEls; i++) + { + bData[i] = (byte) data.getElem(bankIndices[i], + offset + bandOffsets[i]); + } + obj = bData; + break; + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + short[] sData; + if (obj == null) + sData = new short[numDataEls]; + else + sData = (short[]) obj; + for (int i = 0; i < numDataEls; i++) + { + sData[i] = (short) data.getElem(bankIndices[i], + offset + bandOffsets[i]); + } + obj = sData; + break; + case DataBuffer.TYPE_INT: + int[] iData; + if (obj == null) + iData = new int[numDataEls]; + else + iData = (int[]) obj; + for (int i = 0; i < numDataEls; i++) + { + iData[i] = data.getElem(bankIndices[i], offset + bandOffsets[i]); + } + obj = iData; + break; + case DataBuffer.TYPE_FLOAT: + float[] fData; + if (obj == null) + fData = new float[numDataEls]; + else + fData = (float[]) obj; + for (int i = 0; i < numDataEls; i++) + { + fData[i] = data.getElemFloat(bankIndices[i], + offset + bandOffsets[i]); + } + obj = fData; + break; + case DataBuffer.TYPE_DOUBLE: + double[] dData; + if (obj == null) + dData = new double[numDataEls]; + else + dData = (double[]) obj; + for (int i = 0; i < numDataEls; i++) + { + dData[i] = data.getElemDouble(bankIndices[i], + offset + bandOffsets[i]); + } + obj = dData; + break; + } + return obj; + } + + + /** + * Returns all the samples for the pixel at location (x, y) + * stored in the specified data buffer. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param iArray an array that will be populated with the sample values and + * returned as the result. The size of this array should be equal to the + * number of bands in the model. If the array is null, a new + * array is created. + * @param data the data buffer (null not permitted). + * + * @return The samples for the specified pixel. + * + * @see #setPixel(int, int, int[], DataBuffer) + */ + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + if (x < 0 || x >= width || y < 0 || y >= height) + throw new ArrayIndexOutOfBoundsException("Pixel (" + x + ", " + y + + ") is out of bounds."); + int offset = pixelStride * x + scanlineStride * y; + if (iArray == null) + iArray = new int[numBands]; + for (int b = 0; b < numBands; b++) + { + iArray[b] = data.getElem(bankIndices[b], offset + bandOffsets[b]); + } + return iArray; + } + + /** + * Returns the samples for all the pixels in a rectangular region. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + * @param iArray an array that if non-null will be populated + * with the sample values and returned as the result. + * @param data the data buffer (null not permitted). + * + * @return The samples for all the pixels in the rectangle. + */ + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int offset = pixelStride * x + scanlineStride * y; + if (iArray == null) + iArray = new int[numBands * w * h]; + int outOffset = 0; + for (y = 0; y < h; y++) + { + int lineOffset = offset; + for (x = 0; x < w; x++) + { + for (int b = 0; b < numBands; b++) + { + iArray[outOffset++] + = data.getElem(bankIndices[b], lineOffset+bandOffsets[b]); + } + lineOffset += pixelStride; + } + offset += scanlineStride; + } + return iArray; + } + + /** + * Returns the sample for band b of the pixel at + * (x, y) that is stored in the specified data buffer. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param b the band index. + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws ArrayIndexOutOfBoundsException if (x, y) is outside + * the bounds [0, 0, width, height]. + * + * @see #setSample(int, int, int, int, DataBuffer) + */ + public int getSample(int x, int y, int b, DataBuffer data) + { + if (x < 0 || x >= width || y < 0 || y >= height) + throw new ArrayIndexOutOfBoundsException("Sample (" + x + ", " + y + + ") is out of bounds."); + return data.getElem(bankIndices[b], getOffset(x, y, b)); + } + + /** + * Sets the samples for the pixel at location (x, y) from the + * supplied primitive array (the array type must be consistent with the data + * type for this model). + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param obj a primitive array containing the pixel's sample values. + * @param data the data buffer (null not permitted). + * + * @see #setDataElements(int, int, Object, DataBuffer) + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) + { + int type = getTransferType(); + int numDataEls = getNumDataElements(); + int offset = y * scanlineStride + x * pixelStride; + switch (type) + { + case DataBuffer.TYPE_BYTE: + byte[] bData = (byte[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElem(bankIndices[i], offset + bandOffsets[i], + ((int) bData[i]) & 0xFF); + } + break; + case DataBuffer.TYPE_SHORT: + case DataBuffer.TYPE_USHORT: + short[] sData = (short[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElem(bankIndices[i], offset + bandOffsets[i], + ((int) sData[i]) & 0xFFFF); + } + break; + case DataBuffer.TYPE_INT: + int[] iData = (int[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElem(bankIndices[i], offset + bandOffsets[i], iData[i]); + } + break; + case DataBuffer.TYPE_FLOAT: + float[] fData = (float[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElemFloat(bankIndices[i], offset + bandOffsets[i], + fData[i]); + } + break; + case DataBuffer.TYPE_DOUBLE: + double[] dData = (double[]) obj; + for (int i = 0; i < numDataEls; i++) + { + data.setElemDouble(bankIndices[i], offset + bandOffsets[i], + dData[i]); + } + break; + } + } + + /** + * Sets the sample values for the pixel at location (x, y) + * stored in the specified data buffer. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param iArray the pixel sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @see #getPixel(int, int, int[], DataBuffer) + */ + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + int offset = pixelStride * x + scanlineStride * y; + for (int b = 0; b < numBands; b++) + data.setElem(bankIndices[b], offset + bandOffsets[b], iArray[b]); + } + + /** + * Sets the sample value for band b of the pixel at location + * (x, y) in the specified data buffer. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param b the band index. + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @see #getSample(int, int, int, DataBuffer) + */ + public void setSample(int x, int y, int b, int s, DataBuffer data) + { + data.setElem(bankIndices[b], getOffset(x, y, b), s); + } + + /** + * Tests this sample model for equality with an arbitrary object. Returns + * true if and only if: + *

      + *
    • obj is not null;
    • + *
    • obj is an instance of ComponentSampleModel; + *
    • + *
    • both models have the same values for the dataType, + * width, height, pixelStride, + * scanlineStride, bandOffsets and + * bankIndices fields.
    • + *
    + * + * @param obj the object to test (null permitted). + * + * @return true if this sample model is equal to + * obj, and false otherwise. + */ + public boolean equals(Object obj) + { + if (obj == null) + return false; + if (! (obj instanceof ComponentSampleModel)) + return false; + ComponentSampleModel that = (ComponentSampleModel) obj; + if (this.dataType != that.dataType) + return false; + if (this.width != that.width) + return false; + if (this.height != that.height) + return false; + if (this.pixelStride != that.pixelStride) + return false; + if (this.scanlineStride != that.scanlineStride) + return false; + if (! Arrays.equals(this.bandOffsets, that.bandOffsets)) + return false; + if (! Arrays.equals(this.bankIndices, that.bankIndices)) + return false; + // couldn't find any difference, so... + return true; + } + + /** + * Returns a hash code for this sample model. + * + * @return The hash code. + */ + public int hashCode() + { + // this computation is based on the method described in Chapter 3 + // of Joshua Bloch's Effective Java... + int result = 17; + result = 37 * result + dataType; + result = 37 * result + width; + result = 37 * result + height; + result = 37 * result + pixelStride; + result = 37 * result + scanlineStride; + for (int i = 0; i < bandOffsets.length; i++) + result = 37 * result + bandOffsets[i]; + for (int i = 0; i < bankIndices.length; i++) + result = 37 * result + bankIndices[i]; + return result; + } +} diff --git a/libjava/classpath/java/awt/image/ConvolveOp.java b/libjava/classpath/java/awt/image/ConvolveOp.java new file mode 100644 index 000000000..10b85f446 --- /dev/null +++ b/libjava/classpath/java/awt/image/ConvolveOp.java @@ -0,0 +1,380 @@ +/* ConvolveOp.java -- + Copyright (C) 2004, 2005, 2006, Free Software Foundation -- ConvolveOp + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * Convolution filter. + * + * ConvolveOp convolves the source image with a Kernel to generate a + * destination image. This involves multiplying each pixel and its neighbors + * 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. + * + * 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 + */ +public class ConvolveOp implements BufferedImageOp, RasterOp +{ + /** Edge pixels are set to 0. */ + public static final int EDGE_ZERO_FILL = 0; + + /** Edge pixels are copied from the source. */ + public static final int EDGE_NO_OP = 1; + + private Kernel kernel; + private int edge; + private RenderingHints hints; + + /** + * Construct a ConvolveOp. + * + * The edge condition specifies that pixels outside the area that can be + * filtered are either set to 0 or copied from the source image. + * + * @param kernel The kernel to convolve with. + * @param edgeCondition Either EDGE_ZERO_FILL or EDGE_NO_OP. + * @param hints Rendering hints for color conversion, or null. + */ + public ConvolveOp(Kernel kernel, + int edgeCondition, + RenderingHints hints) + { + this.kernel = kernel; + edge = edgeCondition; + this.hints = hints; + } + + /** + * Construct a ConvolveOp. + * + * The edge condition defaults to EDGE_ZERO_FILL. + * + * @param kernel The kernel to convolve with. + */ + public ConvolveOp(Kernel kernel) + { + this.kernel = kernel; + edge = EDGE_ZERO_FILL; + hints = null; + } + + /** + * 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("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; + // 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 (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) + new ColorConvertOp(hints).filter(dst1, dst); + + return dst; + } + + /** + * 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) + { + if (dstCM != null) + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isAlphaPremultiplied(), null); + + return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getRenderingHints() + */ + public final RenderingHints getRenderingHints() + { + return hints; + } + + /** + * Get the edge condition for this Op. + * + * @return The edge condition. + */ + public int getEdgeCondition() + { + return edge; + } + + /** + * Returns (a clone of) the convolution kernel. + * + * @return The convolution kernel. + */ + public final Kernel getKernel() + { + return (Kernel) kernel.clone(); + } + + /** + * 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) + { + if (src == dest) + throw new IllegalArgumentException("src == dest is not allowed."); + if (kernel.getWidth() > src.getWidth() + || kernel.getHeight() > src.getHeight()) + throw new ImagingOpException("The kernel is too large."); + if (dest == null) + dest = createCompatibleDestRaster(src); + else if (src.getNumBands() != dest.getNumBands()) + throw new ImagingOpException("src and dest have different band counts."); + + // calculate the borders that the op can't reach... + int kWidth = kernel.getWidth(); + int kHeight = kernel.getHeight(); + int left = kernel.getXOrigin(); + int right = Math.max(kWidth - left - 1, 0); + int top = kernel.getYOrigin(); + int bottom = Math.max(kHeight - top - 1, 0); + + // Calculate max sample values for clipping + int[] maxValue = src.getSampleModel().getSampleSize(); + for (int i = 0; i < maxValue.length; i++) + maxValue[i] = (int)Math.pow(2, maxValue[i]) - 1; + + // process the region that is reachable... + int regionW = src.width - left - right; + int regionH = src.height - top - bottom; + float[] kvals = kernel.getKernelData(null); + float[] tmp = new float[kWidth * kHeight]; + + for (int x = 0; x < regionW; x++) + { + for (int y = 0; y < regionH; y++) + { + // FIXME: This needs a much more efficient implementation + for (int b = 0; b < src.getNumBands(); b++) + { + float v = 0; + src.getSamples(x, y, kWidth, kHeight, b, tmp); + for (int i = 0; i < tmp.length; i++) + v += tmp[tmp.length - i - 1] * kvals[i]; + // FIXME: in the above line, I've had to reverse the order of + // the samples array to make the tests pass. I haven't worked + // out why this is necessary. + + // This clipping is is undocumented, but determined by testing. + if (v > maxValue[b]) + v = maxValue[b]; + else if (v < 0) + v = 0; + + dest.setSample(x + kernel.getXOrigin(), y + kernel.getYOrigin(), + b, v); + } + } + } + + // fill in the top border + fillEdge(src, dest, 0, 0, src.width, top, edge); + + // fill in the bottom border + fillEdge(src, dest, 0, src.height - bottom, src.width, bottom, edge); + + // fill in the left border + fillEdge(src, dest, 0, top, left, regionH, edge); + + // fill in the right border + fillEdge(src, dest, src.width - right, top, right, regionH, edge); + + return dest; + } + + /** + * Fills a range of pixels (typically at the edge of a raster) with either + * zero values (if edgeOp is EDGE_ZERO_FILL) or the + * corresponding pixel values from the source raster (if edgeOp + * is EDGE_NO_OP). This utility method is called by the + * {@link #fillEdge(Raster, WritableRaster, int, int, int, int, int)} method. + * + * @param src the source raster. + * @param dest the destination raster. + * @param x the x-coordinate of the top left pixel in the range. + * @param y the y-coordinate of the top left pixel in the range. + * @param w the width of the pixel range. + * @param h the height of the pixel range. + * @param edgeOp indicates how to determine the values for the range + * (either {@link #EDGE_ZERO_FILL} or {@link #EDGE_NO_OP}). + */ + private void fillEdge(Raster src, WritableRaster dest, int x, int y, int w, + int h, int edgeOp) + { + if (w <= 0) + return; + if (h <= 0) + return; + if (edgeOp == EDGE_ZERO_FILL) // fill region with zeroes + { + float[] zeros = new float[src.getNumBands() * w * h]; + dest.setPixels(x, y, w, h, zeros); + } + else // copy pixels from source + { + float[] pixels = new float[src.getNumBands() * w * h]; + src.getPixels(x, y, w, h, pixels); + dest.setPixels(x, y, w, h, pixels); + } + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + */ + public WritableRaster createCompatibleDestRaster(Raster src) + { + return src.createCompatibleWritableRaster(); + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + */ + public final Rectangle2D getBounds2D(BufferedImage src) + { + return src.getRaster().getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public final Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + /** + * Returns the corresponding destination point for a source point. Because + * this is not a geometric operation, the destination and source points will + * be identical. + * + * @param src The source point. + * @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(); + dst.setLocation(src); + return dst; + } +} diff --git a/libjava/classpath/java/awt/image/CropImageFilter.java b/libjava/classpath/java/awt/image/CropImageFilter.java new file mode 100644 index 000000000..4d8fb624f --- /dev/null +++ b/libjava/classpath/java/awt/image/CropImageFilter.java @@ -0,0 +1,184 @@ +/* CropImageFilter.java -- Java class for cropping image filter + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Rectangle; +import java.util.Hashtable; + +/** + * Currently this filter does almost nothing and needs to be implemented. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class CropImageFilter extends ImageFilter +{ + int x; + int y; + int width; + int height; + + /** + * Construct a new CropImageFilter instance. + * + * @param x the x-coordinate location of the top-left of the cropped rectangle + * @param y the y-coordinate location of the top-left of the cropped rectangle + * @param width the width of the cropped rectangle + * @param height the height of the cropped rectangle + */ + public CropImageFilter(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + /** + * An ImageProducer indicates the size of the image + * being produced using this method. This filter overrides this + * method in order to set the dimentions to the size of the + * cropped rectangle instead of the size of the image. + * + * @param width the width of the image + * @param height the height of the image + */ + public void setDimensions(int width, int height) + { + if (consumer != null) + consumer.setDimensions(this.width, this.height); + } + + /** + * An ImageProducer can set a list of properties + * associated with this image by using this method. + *
    + * FIXME - What property is set for this class? + * + * @param props the list of properties associated with this image + */ + public void setProperties(Hashtable props) + { + Hashtable prop2 = (Hashtable) props; + prop2.put("filters", "CropImageFilter"); + if (consumer != null) + consumer.setProperties(prop2); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as a byte at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels array + * @param scansize the width to use in extracting pixels from the pixels array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, int scansize) + { + Rectangle filterBounds = new Rectangle(this.x, this.y, + this.width, this.height); + Rectangle pixelBounds = new Rectangle(x, y, w, h); + + if (filterBounds.intersects(pixelBounds)) + { + Rectangle bounds = filterBounds.intersection(pixelBounds); + + byte[] cropped = new byte[bounds.width * bounds.height]; + for (int i = 0; i < bounds.height; i++) + { + int start = (bounds.y - pixelBounds.y + i) * scansize + offset; + + for (int j = 0; j < bounds.width; j++) + cropped[i * bounds.width + j] = pixels[start + bounds.x + j]; + } + + if (consumer != null) + consumer.setPixels(0, 0, + bounds.width, bounds.height, + model, cropped, 0, bounds.width); + } + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an int at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels array + * @param scansize the width to use in extracting pixels from the pixels array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, int scansize) + { + Rectangle filterBounds = new Rectangle(this.x, this.y, + this.width, this.height); + Rectangle pixelBounds = new Rectangle(x, y, w, h); + + if (filterBounds.intersects(pixelBounds)) + { + Rectangle bounds = filterBounds.intersection(pixelBounds); + + int[] cropped = new int[bounds.width * bounds.height]; + for (int i = 0; i < bounds.height; i++) + { + int start = (bounds.y - pixelBounds.y + i) * scansize + offset; + + for (int j = 0; j < bounds.width; j++) + cropped[i * bounds.width + j] = pixels[start + bounds.x + j]; + } + + if (consumer != null) + consumer.setPixels(0, 0, + bounds.width, bounds.height, + model, cropped, 0, bounds.width); + } + } + +} diff --git a/libjava/classpath/java/awt/image/DataBuffer.java b/libjava/classpath/java/awt/image/DataBuffer.java new file mode 100644 index 000000000..78bc75ba5 --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBuffer.java @@ -0,0 +1,437 @@ +/* Copyright (C) 2000, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/** + * Class that manages arrays of data elements. A data buffer consists + * of one or more banks. A bank is a continuous region of data + * elements. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public abstract class DataBuffer +{ + /** + * A constant representing a data type that uses byte primitives + * as the storage unit. + */ + public static final int TYPE_BYTE = 0; + + /** + * A constant representing a data type that uses short + * primitives as the storage unit. + */ + public static final int TYPE_USHORT = 1; + + /** + * A constant representing a data type that uses short + * primitives as the storage unit. + */ + public static final int TYPE_SHORT = 2; + + /** + * A constant representing a data type that uses int + * primitives as the storage unit. + */ + public static final int TYPE_INT = 3; + + /** + * A constant representing a data type that uses float + * primitives as the storage unit. + */ + public static final int TYPE_FLOAT = 4; + + /** + * A constant representing a data type that uses double + * primitives as the storage unit. + */ + public static final int TYPE_DOUBLE = 5; + + /** + * A constant representing an undefined data type. + */ + public static final int TYPE_UNDEFINED = 32; + + /** The type of the data elements stored in the data buffer. */ + protected int dataType; + + /** The number of banks in this buffer. */ + protected int banks = 1; + + /** Offset into the default (0'th) bank). */ + protected int offset; // FIXME: Is offsets[0] always mirrored in offset? + + /** The size of the banks. */ + protected int size; + + /** Offset into each bank. */ + protected int[] offsets; + + /** + * Creates a new DataBuffer with the specified data type and + * size. The dataType should be one of the constants + * {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, {@link #TYPE_USHORT}, + * {@link #TYPE_INT}, {@link #TYPE_FLOAT} and {@link #TYPE_DOUBLE}. + *

    + * The physical (array-based) storage is allocated by a subclass. + * + * @param dataType the data type. + * @param size the number of elements in the buffer. + */ + protected DataBuffer(int dataType, int size) + { + this(dataType, size, 1); + } + + /** + * Creates a new DataBuffer with the specified data type, + * size and number of banks. The dataType should be one of + * the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + *

    + * The physical (array-based) storage is allocated by a subclass. + * + * @param dataType the data type. + * @param size the number of elements in the buffer. + * @param numBanks the number of data banks. + */ + protected DataBuffer(int dataType, int size, int numBanks) { + this(dataType, size, numBanks, 0); + } + + /** + * Creates a new DataBuffer with the specified data type, + * size and number of banks. An offset (which applies to all banks) is + * also specified. The dataType should be one of + * the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + *

    + * The physical (array-based) storage is allocated by a subclass. + * + * @param dataType the data type. + * @param size the number of elements in the buffer. + * @param numBanks the number of data banks. + * @param offset the offset to the first element for all banks. + */ + protected DataBuffer(int dataType, int size, int numBanks, int offset) { + banks = numBanks; + this.dataType = dataType; + this.size = size; + this.offset = offset; + + offsets = new int[ numBanks ]; + for(int i = 0; i < numBanks; i++ ) + offsets[i] = offset; + } + + /** + * Creates a new DataBuffer with the specified data type, + * size and number of banks. An offset (which applies to all banks) is + * also specified. The dataType should be one of + * the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + *

    + * The physical (array-based) storage is allocated by a subclass. + * + * @param dataType the data type. + * @param size the number of elements in the buffer. + * @param numBanks the number of data banks. + * @param offsets the offsets to the first element for all banks. + * + * @throws ArrayIndexOutOfBoundsException if + * numBanks != offsets.length. + */ + protected DataBuffer(int dataType, int size, int numBanks, int[] offsets) { + if (numBanks != offsets.length) + throw new ArrayIndexOutOfBoundsException(); + + this.dataType = dataType; + this.size = size; + banks = numBanks; + this.offsets = offsets; + + offset = offsets[0]; + } + + /** + * Returns the size (number of bits) of the specified data type. Valid types + * are defined by the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + * + * @param dataType the data type. + * @return The number of bits for the specified data type. + * @throws IllegalArgumentException if dataType < 0 or + * dataType > TYPE_DOUBLE. + */ + public static int getDataTypeSize(int dataType) { + // Maybe this should be a lookup table instead. + switch (dataType) + { + case TYPE_BYTE: + return 8; + case TYPE_USHORT: + case TYPE_SHORT: + return 16; + case TYPE_INT: + case TYPE_FLOAT: + return 32; + case TYPE_DOUBLE: + return 64; + default: + throw new IllegalArgumentException(); + } + } + + /** + * Returns the type of the data elements in the data buffer. Valid types + * are defined by the constants {@link #TYPE_BYTE}, {@link #TYPE_SHORT}, + * {@link #TYPE_USHORT}, {@link #TYPE_INT}, {@link #TYPE_FLOAT} and + * {@link #TYPE_DOUBLE}. + * + * @return The type. + */ + public int getDataType() + { + return dataType; + } + + /** + * Returns the size of the data buffer. + * + * @return The size. + */ + public int getSize() + { + return size; + } + + /** + * Returns the element offset for the first data bank. + * + * @return The element offset. + */ + public int getOffset() + { + return offset; + } + + /** + * Returns the offsets for all the data banks used by this + * DataBuffer. + * + * @return The offsets. + */ + public int[] getOffsets() + { + if (offsets == null) + { + // is this necessary? + offsets = new int[1]; + offsets[0] = offset; + } + return offsets; + } + + /** + * Returns the number of data banks for this DataBuffer. + * @return The number of data banks. + */ + public int getNumBanks() + { + return banks; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return getElem(0, i); + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public abstract int getElem(int bank, int i); + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + setElem(0, i, val); + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public abstract void setElem(int bank, int i, int val); + + /** + * Returns an element from the first data bank, converted to a + * float. The offset (specified in the constructor) is added + * to i before accessing the underlying data array. + * + * @param i the element index. + * @return The element. + */ + public float getElemFloat(int i) + { + return getElem(i); + } + + /** + * Returns an element from a particular data bank, converted to a + * float. The offset (specified in the constructor) is + * added to i before accessing the underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public float getElemFloat(int bank, int i) + { + return getElem(bank, i); + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElemFloat(int i, float val) + { + setElem(i, (int) val); + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElemFloat(int bank, int i, float val) + { + setElem(bank, i, (int) val); + } + + /** + * Returns an element from the first data bank, converted to a + * double. The offset (specified in the constructor) is added + * to i before accessing the underlying data array. + * + * @param i the element index. + * @return The element. + */ + public double getElemDouble(int i) + { + return getElem(i); + } + + /** + * Returns an element from a particular data bank, converted to a + * double. The offset (specified in the constructor) is + * added to i before accessing the underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public double getElemDouble(int bank, int i) + { + return getElem(bank, i); + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElemDouble(int i, double val) + { + setElem(i, (int) val); + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElemDouble(int bank, int i, double val) + { + setElem(bank, i, (int) val); + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferByte.java b/libjava/classpath/java/awt/image/DataBufferByte.java new file mode 100644 index 000000000..01364b07a --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferByte.java @@ -0,0 +1,245 @@ +/* Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of byte primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public final class DataBufferByte extends DataBuffer +{ + private byte[] data; + private byte[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of byte elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferByte(int size) + { + super(TYPE_BYTE, size, 1, 0); + bankData = new byte[1][]; + data = new byte[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of byte elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferByte(int size, int numBanks) + { + super(TYPE_BYTE, size, numBanks); + bankData = new byte[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferByte(byte[] dataArray, int size) + { + super(TYPE_BYTE, size, 1, 0); + bankData = new byte[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferByte(byte[] dataArray, int size, int offset) + { + super(TYPE_BYTE, size, 1, offset); + bankData = new byte[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferByte(byte[][] dataArray, int size) + { + super(TYPE_BYTE, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferByte(byte[][] dataArray, int size, int[] offsets) + { + super(TYPE_BYTE, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public byte[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public byte[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this DataBuffer. + * + * @return The data banks. + */ + public byte[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return data[i+offset] & 0xff; // get unsigned byte as int + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + // get unsigned byte as int + return bankData[bank][i+offsets[bank]] & 0xff; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = (byte) val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = (byte) val; + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferDouble.java b/libjava/classpath/java/awt/image/DataBufferDouble.java new file mode 100644 index 000000000..31c5ebd8c --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferDouble.java @@ -0,0 +1,288 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of double primitives + * to represent each of its banks. + * + * @since 1.4 + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class DataBufferDouble + extends DataBuffer +{ + private double[] data; + private double[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of double elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferDouble(int size) + { + super(TYPE_DOUBLE, size, 1, 0); + bankData = new double[1][]; + data = new double[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of double elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferDouble(int size, int numBanks) + { + super(TYPE_DOUBLE, size, numBanks); + bankData = new double[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferDouble(double[] dataArray, int size) + { + super(TYPE_DOUBLE, size, 1, 0); + bankData = new double[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferDouble(double[] dataArray, int size, int offset) + { + super(TYPE_DOUBLE, size, 1, offset); + bankData = new double[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferDouble(double[][] dataArray, int size) + { + super(TYPE_DOUBLE, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferDouble(double[][] dataArray, int size, int[] offsets) + { + super(TYPE_DOUBLE, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public double[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public double[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this DataBuffer. + * + * @return The data banks. + */ + public double[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return (int) data[i+offset]; + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + return (int) bankData[bank][i+offsets[bank]]; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = val; + } + + public float getElemFloat(int i) + { + return (float) data[i+offset]; + } + + public float getElemFloat(int bank, int i) + { + return (float) bankData[bank][i+offsets[bank]]; + } + + public void setElemFloat(int i, float val) + { + data[i+offset] = val; + } + + public void setElemFloat(int bank, int i, float val) + { + bankData[bank][i+offsets[bank]] = val; + } + + public double getElemDouble(int i) + { + return data[i + offset]; + } + + public double getElemDouble(int bank, int i) + { + return bankData[bank][i + offsets[bank]]; + } + + public void setElemDouble(int i, double val) + { + data[i + offset] = val; + } + + public void setElemDouble(int bank, int i, double val) + { + bankData[bank][i + offsets[bank]] = val; + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferFloat.java b/libjava/classpath/java/awt/image/DataBufferFloat.java new file mode 100644 index 000000000..44a0a3892 --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferFloat.java @@ -0,0 +1,286 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of float primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public final class DataBufferFloat + extends DataBuffer +{ + private float[] data; + private float[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of float elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferFloat(int size) + { + super(TYPE_FLOAT, size, 1, 0); + bankData = new float[1][]; + data = new float[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of float elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferFloat(int size, int numBanks) + { + super(TYPE_FLOAT, size, numBanks); + bankData = new float[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferFloat(float[] dataArray, int size) + { + super(TYPE_FLOAT, size, 1, 0); + bankData = new float[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferFloat(float[] dataArray, int size, int offset) + { + super(TYPE_FLOAT, size, 1, offset); + bankData = new float[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferFloat(float[][] dataArray, int size) + { + super(TYPE_FLOAT, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferFloat(float[][] dataArray, int size, int[] offsets) + { + super(TYPE_FLOAT, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public float[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public float[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this DataBuffer. + * + * @return The data banks. + */ + public float[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return (int) data[i+offset]; + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + return (int) bankData[bank][i+offsets[bank]]; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = val; + } + + public float getElemFloat(int i) + { + return data[i+offset]; + } + + public float getElemFloat(int bank, int i) + { + return bankData[bank][i+offsets[bank]]; + } + + public void setElemFloat(int i, float val) + { + data[i+offset] = val; + } + + public void setElemFloat(int bank, int i, float val) + { + bankData[bank][i+offsets[bank]] = val; + } + + public double getElemDouble(int i) + { + return getElemFloat(i); + } + + public double getElemDouble(int bank, int i) + { + return getElemFloat(bank, i); + } + + public void setElemDouble(int i, double val) + { + setElemFloat(i, (float) val); + } + + public void setElemDouble(int bank, int i, double val) + { + setElemFloat(bank, i, (float) val); + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferInt.java b/libjava/classpath/java/awt/image/DataBufferInt.java new file mode 100644 index 000000000..db9d5bc30 --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferInt.java @@ -0,0 +1,244 @@ +/* Copyright (C) 2000, 2002, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of int primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public final class DataBufferInt extends DataBuffer +{ + private int[] data; + private int[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of int elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferInt(int size) + { + super(TYPE_INT, size, 1, 0); + bankData = new int[1][]; + data = new int[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of int elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferInt(int size, int numBanks) + { + super(TYPE_INT, size, numBanks); + bankData = new int[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferInt(int[] dataArray, int size) + { + super(TYPE_INT, size, 1, 0); + bankData = new int[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferInt(int[] dataArray, int size, int offset) + { + super(TYPE_INT, size, 1, offset); + bankData = new int[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferInt(int[][] dataArray, int size) + { + super(TYPE_INT, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferInt(int[][] dataArray, int size, int[] offsets) + { + super(TYPE_INT, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public int[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public int[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this DataBuffer. + * + * @return The data banks. + */ + public int[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset is + * added to the specified index before accessing the underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return data[i+offset]; + } + + /** + * Returns an element from a particular data bank. The offset + * is added to the specified index before accessing the underlying data + * array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + // get unsigned int as int + return bankData[bank][i+offsets[bank]]; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = val; + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferShort.java b/libjava/classpath/java/awt/image/DataBufferShort.java new file mode 100644 index 000000000..2156d7571 --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferShort.java @@ -0,0 +1,245 @@ +/* DataBufferShort.java -- + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of short primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public final class DataBufferShort extends DataBuffer +{ + private short[] data; + private short[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of short elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferShort(int size) + { + super(TYPE_SHORT, size, 1, 0); + bankData = new short[1][]; + data = new short[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of short elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferShort(int size, int numBanks) + { + super(TYPE_SHORT, size, numBanks); + bankData = new short[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + */ + public DataBufferShort(short[] dataArray, int size) + { + super(TYPE_SHORT, size, 1, 0); + bankData = new short[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + *

    + * Note: there is no exception when dataArray is + * null, but in that case an exception will be thrown + * later if you attempt to access the data buffer. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + */ + public DataBufferShort(short[] dataArray, int size, int offset) + { + super(TYPE_SHORT, size, 1, offset); + bankData = new short[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferShort(short[][] dataArray, int size) + { + super(TYPE_SHORT, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferShort(short[][] dataArray, int size, int[] offsets) + { + super(TYPE_SHORT, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public short[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public short[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this DataBuffer. + * + * @return The data banks. + */ + public short[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return data[i+offset]; + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + return bankData[bank][i+offsets[bank]]; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = (short) val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = (short) val; + } +} diff --git a/libjava/classpath/java/awt/image/DataBufferUShort.java b/libjava/classpath/java/awt/image/DataBufferUShort.java new file mode 100644 index 000000000..d2cadcf0b --- /dev/null +++ b/libjava/classpath/java/awt/image/DataBufferUShort.java @@ -0,0 +1,246 @@ +/* DataBufferUShort.java -- + Copyright (C) 2000, 2002, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/* This is one of several classes that are nearly identical. Maybe we + should have a central template and generate all these files. This + is one of the cases where templates or macros would have been + useful to have in Java. + + This file has been created using search-replace. My only fear is + that these classes will grow out-of-sync as of a result of changes + that are not propagated to the other files. As always, mirroring + code is a maintenance nightmare. */ + +/** + * A {@link DataBuffer} that uses an array of short primitives + * to represent each of its banks. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public final class DataBufferUShort extends DataBuffer +{ + private short[] data; + private short[][] bankData; + + /** + * Creates a new data buffer with a single data bank containing the + * specified number of short elements. + * + * @param size the number of elements in the data bank. + */ + public DataBufferUShort(int size) + { + super(TYPE_USHORT, size, 1, 0); + bankData = new short[1][]; + data = new short[size]; + bankData[0] = data; + } + + /** + * Creates a new data buffer with the specified number of data banks, + * each containing the specified number of short elements. + * + * @param size the number of elements in the data bank. + * @param numBanks the number of data banks. + */ + public DataBufferUShort(int size, int numBanks) + { + super(TYPE_USHORT, size, numBanks); + bankData = new short[numBanks][size]; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data bank. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if dataArray is null + */ + public DataBufferUShort(short[] dataArray, int size) + { + super(TYPE_USHORT, size, 1, 0); + if (dataArray == null) + throw new NullPointerException(); + bankData = new short[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data bank, with + * the specified offset to the first element. + * + * @param dataArray the data bank. + * @param size the number of elements in the data bank. + * @param offset the offset to the first element in the array. + * + * @throws NullPointerException if dataArray is null + */ + public DataBufferUShort(short[] dataArray, int size, int offset) + { + super(TYPE_USHORT, size, 1, offset); + if (dataArray == null) + throw new NullPointerException(); + bankData = new short[1][]; + data = dataArray; + bankData[0] = data; + } + + /** + * Creates a new data buffer backed by the specified data banks. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferUShort(short[][] dataArray, int size) + { + super(TYPE_USHORT, size, dataArray.length); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Creates a new data buffer backed by the specified data banks, with + * the specified offsets to the first element in each bank. + * + * @param dataArray the data banks. + * @param size the number of elements in the data bank. + * @param offsets the offsets to the first element in each data bank. + * + * @throws NullPointerException if dataArray is + * null. + */ + public DataBufferUShort(short[][] dataArray, int size, int[] offsets) + { + super(TYPE_USHORT, size, dataArray.length, offsets); + bankData = dataArray; + data = bankData[0]; + } + + /** + * Returns the first data bank. + * + * @return The first data bank. + */ + public short[] getData() + { + return data; + } + + /** + * Returns a data bank. + * + * @param bank the bank index. + * @return A data bank. + */ + public short[] getData(int bank) + { + return bankData[bank]; + } + + /** + * Returns the array underlying this DataBuffer. + * + * @return The data banks. + */ + public short[][] getBankData() + { + return bankData; + } + + /** + * Returns an element from the first data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param i the element index. + * @return The element. + */ + public int getElem(int i) + { + return data[i+offset] & 0xffff; // get unsigned short as int + } + + /** + * Returns an element from a particular data bank. The offset (specified in + * the constructor) is added to i before accessing the + * underlying data array. + * + * @param bank the bank index. + * @param i the element index. + * @return The element. + */ + public int getElem(int bank, int i) + { + // get unsigned short as int + return bankData[bank][i+offsets[bank]] & 0xffff; + } + + /** + * Sets an element in the first data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int i, int val) + { + data[i+offset] = (short) val; + } + + /** + * Sets an element in a particular data bank. The offset (specified in the + * constructor) is added to i before updating the underlying + * data array. + * + * @param bank the data bank index. + * @param i the element index. + * @param val the new element value. + */ + public void setElem(int bank, int i, int val) + { + bankData[bank][i+offsets[bank]] = (short) val; + } +} diff --git a/libjava/classpath/java/awt/image/DirectColorModel.java b/libjava/classpath/java/awt/image/DirectColorModel.java new file mode 100644 index 000000000..a464fed30 --- /dev/null +++ b/libjava/classpath/java/awt/image/DirectColorModel.java @@ -0,0 +1,435 @@ +/* DirectColorModel.java -- + Copyright (C) 1999, 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.Buffers; + +import java.awt.Point; +import java.awt.Transparency; +import java.awt.color.ColorSpace; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + * @author C. Brian Jones (cbj@gnu.org) + * @author Mark Benvenuto (mcb54@columbia.edu) + */ +public class DirectColorModel extends PackedColorModel +{ + /** + * For the color model created with this constructor the pixels + * will have fully opaque alpha components with a value of 255. + * Each mask should describe a fully contiguous set of bits in the + * most likely order of alpha, red, green, blue from the most significant + * byte to the least significant byte. + * + * @param pixelBits the number of bits wide used for bit size of pixel values + * @param rmask the bits describing the red component of a pixel + * @param gmask the bits describing the green component of a pixel + * @param bmask the bits describing the blue component of a pixel + */ + public DirectColorModel(int pixelBits, int rmask, int gmask, int bmask) + { + this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits, + rmask, gmask, bmask, 0, + false, // not alpha premultiplied + Buffers.smallestAppropriateTransferType(pixelBits) // find type + ); + } + + /** + * For the color model created with this constructor the pixels + * will have fully opaque alpha components with a value of 255. + * Each mask should describe a fully contiguous set of bits in the + * most likely order of red, green, blue from the most significant + * byte to the least significant byte. + * + * @param pixelBits the number of bits wide used for bit size of pixel values + * @param rmask the bits describing the red component of a pixel + * @param gmask the bits describing the green component of a pixel + * @param bmask the bits describing the blue component of a pixel + * @param amask the bits describing the alpha component of a pixel + */ + public DirectColorModel(int pixelBits, + int rmask, int gmask, int bmask, int amask) + { + this(ColorSpace.getInstance(ColorSpace.CS_sRGB), pixelBits, + rmask, gmask, bmask, amask, + false, // not alpha premultiplied + Buffers.smallestAppropriateTransferType(pixelBits) // find type + ); + } + + public DirectColorModel(ColorSpace cspace, int pixelBits, + int rmask, int gmask, int bmask, int amask, + boolean isAlphaPremultiplied, + int transferType) + { + super(cspace, pixelBits, + rmask, gmask, bmask, amask, isAlphaPremultiplied, + ((amask == 0) ? Transparency.OPAQUE : Transparency.TRANSLUCENT), + transferType); + } + + public final int getRedMask() + { + return getMask(0); + } + + public final int getGreenMask() + { + return getMask(1); + } + + public final int getBlueMask() + { + return getMask(2); + } + + public final int getAlphaMask() + { + return hasAlpha() ? getMask(3) : 0; + } + + /** + * Get the red component of the given pixel. + *
    + */ + public final int getRed(int pixel) + { + return extractAndNormalizeSample(pixel, 0); + } + + /** + * Get the green component of the given pixel. + *
    + */ + public final int getGreen(int pixel) + { + return extractAndNormalizeSample(pixel, 1); + } + + /** + * Get the blue component of the given pixel. + *
    + */ + public final int getBlue(int pixel) + { + return extractAndNormalizeSample(pixel, 2); + } + + /** + * Get the alpha component of the given pixel. + *
    + */ + public final int getAlpha(int pixel) + { + if (!hasAlpha()) + return 255; + return extractAndScaleSample(pixel, 3); + } + + private int extractAndNormalizeSample(int pixel, int component) + { + int value = extractAndScaleSample(pixel, component); + if (hasAlpha() && isAlphaPremultiplied() && getAlpha(pixel) != 0) + value = value*255/getAlpha(pixel); + return value; + } + + private int extractAndScaleSample(int pixel, int component) + { + int field = pixel & getMask(component); + int to8BitShift = + 8 - shifts[component] - getComponentSize(component); + return (to8BitShift>0) ? + (field << to8BitShift) : + (field >>> (-to8BitShift)); + } + + /** + * Get the RGB color value of the given pixel using the default + * RGB color model. + *
    + * + * @param pixel a pixel value + */ + public final int getRGB(int pixel) + { + /* FIXME: The Sun docs show that this method is overridden, but I + don't see any way to improve on the superclass + implementation. */ + return super.getRGB(pixel); + } + + public int getRed(Object inData) + { + return getRed(getPixelFromArray(inData)); + } + + public int getGreen(Object inData) + { + return getGreen(getPixelFromArray(inData)); + } + + public int getBlue(Object inData) + { + return getBlue(getPixelFromArray(inData)); + } + + public int getAlpha(Object inData) + { + return getAlpha(getPixelFromArray(inData)); + } + + public int getRGB(Object inData) + { + return getRGB(getPixelFromArray(inData)); + } + + /** + * Converts a normalized pixel int value in the sRGB color + * space to an array containing a single pixel of the color space + * of the color model. + * + *

    This method performs the inverse function of + * getRGB(Object inData). + * + * @param rgb pixel as a normalized sRGB, 0xAARRGGBB value. + * + * @param pixel to avoid needless creation of arrays, an array to + * use to return the pixel can be given. If null, a suitable array + * will be created. + * + * @return array of transferType containing a single pixel. The + * pixel should be encoded in the natural way of the color model. + * + * @see #getRGB(Object) + */ + public Object getDataElements(int rgb, Object pixel) + { + // FIXME: handle alpha multiply + + int pixelValue = 0; + int a = 0; + if (hasAlpha()) { + a = (rgb >>> 24) & 0xff; + pixelValue = valueToField(a, 3, 8); + } + + if (hasAlpha() && isAlphaPremultiplied()) + { + int r, g, b; + /* if r=0xff and a=0xff, then resulting + value will be (r*a)>>>8 == 0xfe... This seems wrong. + We should divide by 255 rather than shifting >>>8 after + multiplying. + + Too bad, shifting is probably less expensive. + r = ((rgb >>> 16) & 0xff)*a; + g = ((rgb >>> 8) & 0xff)*a; + b = ((rgb >>> 0) & 0xff)*a; */ + /* The r, g, b values we calculate are 16 bit. This allows + us to avoid discarding the lower 8 bits obtained if + multiplying with the alpha band. */ + + // using 16 bit values + r = ((rgb >>> 8) & 0xff00)*a/255; + g = ((rgb >>> 0) & 0xff00)*a/255; + b = ((rgb << 8) & 0xff00)*a/255; + pixelValue |= + valueToField(r, 0, 16) | // Red + valueToField(g, 1, 16) | // Green + valueToField(b, 2, 16); // Blue + } + else + { + int r, g, b; + // using 8 bit values + r = (rgb >>> 16) & 0xff; + g = (rgb >>> 8) & 0xff; + b = (rgb >>> 0) & 0xff; + + pixelValue |= + valueToField(r, 0, 8) | // Red + valueToField(g, 1, 8) | // Green + valueToField(b, 2, 8); // Blue + } + + /* In this color model, the whole pixel fits in the first element + of the array. */ + DataBuffer buffer = Buffers.createBuffer(transferType, pixel, 1); + buffer.setElem(0, pixelValue); + return Buffers.getData(buffer); + } + + /** + * Converts a value to the correct field bits based on the + * information derived from the field masks. + * + * @param highBit the position of the most significant bit in the + * val parameter. + */ + private int valueToField(int val, int component, int highBit) + { + int toFieldShift = + getComponentSize(component) + shifts[component] - highBit; + int ret = (toFieldShift>0) ? + (val << toFieldShift) : + (val >>> (-toFieldShift)); + return ret & getMask(component); + } + + /** + * Converts a 16 bit value to the correct field bits based on the + * information derived from the field masks. + */ + private int value16ToField(int val, int component) + { + int toFieldShift = getComponentSize(component) + shifts[component] - 16; + return (toFieldShift>0) ? + (val << toFieldShift) : + (val >>> (-toFieldShift)); + } + + /** + * Fills an array with the unnormalized component samples from a + * pixel value. I.e. decompose the pixel, but not perform any + * color conversion. + */ + public final int[] getComponents(int pixel, int[] components, int offset) + { + int numComponents = getNumComponents(); + if (components == null) components = new int[offset + numComponents]; + + for (int b=0; b>> shifts[b]; + + return components; + } + + public final int[] getComponents(Object pixel, int[] components, + int offset) + { + return getComponents(getPixelFromArray(pixel), components, offset); + } + + /** + * Creates a WriteableRaster that has a SampleModel + * that is compatible with this ColorModel. + * + * @param w the width of the writeable raster to create + * @param h the height of the writeable raster to create + * + * @throws IllegalArgumentException if w or h + * is less than or equal to zero + */ + public final WritableRaster createCompatibleWritableRaster(int w, int h) + { + // Sun also makes this check here. + if(w <= 0 || h <= 0) + throw new IllegalArgumentException("width (=" + w + ") and height (=" + + h + ") must be > 0"); + + SampleModel sm = createCompatibleSampleModel(w, h); + Point origin = new Point(0, 0); + return Raster.createWritableRaster(sm, origin); + } + + public int getDataElement(int[] components, int offset) + { + int numComponents = getNumComponents(); + int pixelValue = 0; + + for (int c=0; cImageProducer. By default the setColorModel + * method of the consumer is called with the specified model. + * + * @param model the color model to be used most often by setPixels + * + * @see ColorModel + */ + public void setColorModel(ColorModel model) + { + consumer.setColorModel(model); + } + + /** + * The ImageProducer should call this method with a + * bit mask of hints from any of RANDOMPIXELORDER, + * TOPDOWNLEFTRIGHT, COMPLETESCANLINES, + * SINGLEPASS, SINGLEFRAME from the + * ImageConsumer interface. + * + * @param flags a bit mask of hints + * @see ImageConsumer + */ + public void setHints(int flags) + { + consumer.setHints(flags); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as a byte at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels array + * @param scansize the width to use in extracting pixels from the pixels array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, + int scansize) + { + consumer.setPixels(x, y, w, h, model, pixels, offset, scansize); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an int at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels array + * @param scansize the width to use in extracting pixels from the pixels array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, + int scansize) + { + consumer.setPixels(x, y, w, h, model, pixels, offset, scansize); + } + + /** + * The ImageProducer calls this method to indicate a + * single frame or the entire image is complete. The method is + * also used to indicate an error in loading or producing the + * image. + */ + public void imageComplete(int status) + { + consumer.imageComplete(status); + } +} diff --git a/libjava/classpath/java/awt/image/ImageObserver.java b/libjava/classpath/java/awt/image/ImageObserver.java new file mode 100644 index 000000000..e63d4bba1 --- /dev/null +++ b/libjava/classpath/java/awt/image/ImageObserver.java @@ -0,0 +1,129 @@ +/* ImageObserver.java -- Java interface for asynchronous updates to an image + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Image; + +/** + * An object implementing the ImageObserver interface can + * receive updates on image construction from an + * ImageProducer asynchronously. + * + * @see ImageProducer + * @author C. Brian Jones (cbj@gnu.org) + */ +public interface ImageObserver +{ + /** + * The width of the image has been provided as the + * width argument to imageUpdate. + * + * @see #imageUpdate + */ + int WIDTH = 1; + + /** + * The height of the image has been provided as the + * height argument to imageUpdate. + * + * @see #imageUpdate + */ + int HEIGHT = 2; + + /** + * The properties of the image have been provided. + * + * @see #imageUpdate + * @see java.awt.Image#getProperty (java.lang.String, java.awt.image.ImageObserver) + */ + int PROPERTIES = 4; + + /** + * More pixels are now available for drawing a scaled variation of + * the image. + * + * @see #imageUpdate + */ + int SOMEBITS = 8; + + /** + * All the pixels needed to draw a complete frame of a multi-frame + * image are available. + * + * @see #imageUpdate + */ + int FRAMEBITS = 16; + + /** + * An image with a single frame, a static image, is complete. + * + * @see #imageUpdate + */ + int ALLBITS = 32; + + /** + * An error was encountered while producing the image. + * + * @see #imageUpdate + */ + int ERROR = 64; + + /** + * Production of the image was aborted. + * + * @see #imageUpdate + */ + int ABORT = 128; + + /** + * This is a callback method for an asynchronous image producer to + * provide updates on the production of the image as it happens. + * + * @param image the image the update refers to + * @param flags a bit mask indicating what is provided with this update + * @param x the x coordinate of the image + * @param y the y coordinate of the image + * @param width the width of the image + * @param height the height of the image + * + * @see java.awt.Image + */ + boolean imageUpdate(Image image, int flags, int x, + int y, int width, int height); +} diff --git a/libjava/classpath/java/awt/image/ImageProducer.java b/libjava/classpath/java/awt/image/ImageProducer.java new file mode 100644 index 000000000..c0f9ad4c7 --- /dev/null +++ b/libjava/classpath/java/awt/image/ImageProducer.java @@ -0,0 +1,84 @@ +/* ImageProducer.java -- Java interface for image production + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * An object implementing the ImageProducer interface can + * produce data for images. Each image has a corresponding + * ImageProducer which is needed for things such as + * resizing the image. + * + * @see ImageConsumer + * @author C. Brian Jones (cbj@gnu.org) + */ +public interface ImageProducer +{ + /** + * Used to register an ImageConsumer with this + * ImageProducer. + */ + void addConsumer(ImageConsumer ic); + + /** + * Used to determine if the given ImageConsumer is + * already registered with this ImageProducer. + */ + boolean isConsumer(ImageConsumer ic); + + /** + * Used to remove an ImageConsumer from the list of + * registered consumers for this ImageProducer. + */ + void removeConsumer(ImageConsumer ic); + + /** + * Used to register an ImageConsumer with this + * ImageProducer and then immediately start + * reconstruction of the image data to be delivered to all + * registered consumers. + */ + void startProduction(ImageConsumer ic); + + /** + * Used to register an ImageConsumer with this + * ImageProducer and then request that this producer + * resend the image data in the order top-down, left-right. + */ + void requestTopDownLeftRightResend(ImageConsumer ic); +} diff --git a/libjava/classpath/java/awt/image/ImagingOpException.java b/libjava/classpath/java/awt/image/ImagingOpException.java new file mode 100644 index 000000000..ca40e9ed3 --- /dev/null +++ b/libjava/classpath/java/awt/image/ImagingOpException.java @@ -0,0 +1,66 @@ +/* ImagingOpException.java -- indicates an imaging filter failure + Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * This exception is thrown when BufferedImageOp or + * RasterOp filters cannot process an image. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see BufferedImageOp + * @see RasterOp + * @status updated to 1.4 + */ +public class ImagingOpException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8026288481846276658L; + + /** + * Create a new instance with a descriptive error message. + * + * @param message the descriptive error message + */ + public ImagingOpException(String message) + { + super(message); + } +} // class ImagingOpException diff --git a/libjava/classpath/java/awt/image/IndexColorModel.java b/libjava/classpath/java/awt/image/IndexColorModel.java new file mode 100644 index 000000000..340d38e89 --- /dev/null +++ b/libjava/classpath/java/awt/image/IndexColorModel.java @@ -0,0 +1,736 @@ +/* IndexColorModel.java -- Java class for interpreting Pixel objects + Copyright (C) 1999, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import gnu.java.awt.Buffers; + +import java.awt.color.ColorSpace; +import java.math.BigInteger; + +/** + * Color model similar to pseudo visual in X11. + *

    + * This color model maps linear pixel values to actual RGB and alpha colors. + * Thus, pixel values are indexes into the color map. Each color component is + * an 8-bit unsigned value. + *

    + * The IndexColorModel supports a map of valid pixels, allowing + * the representation of holes in the the color map. The valid map is + * represented as a {@link BigInteger} where each bit indicates the validity + * of the map entry with the same index. + *

    + * Colors can have alpha components for transparency support. If alpha + * component values aren't given, color values are opaque. The model also + * supports a reserved pixel value to represent completely transparent colors, + * no matter what the actual color component values are. + *

    + * IndexColorModel supports anywhere from 1 to 16 bit index + * values. The allowed transfer types are {@link DataBuffer#TYPE_BYTE} and + * {@link DataBuffer#TYPE_USHORT}. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class IndexColorModel extends ColorModel +{ + private int map_size; + private boolean opaque; // no alpha, but doesn't account for trans + private int trans = -1; + private int[] rgb; + private BigInteger validBits = BigInteger.ZERO; + + /** + * Creates a new indexed color model for size color elements + * with no alpha component. Each array must contain at least + * size elements. For each array, the i-th color is described + * by reds[i], greens[i] and blues[i]. + * + * @param bits the number of bits needed to represent size + * colors. + * @param size the number of colors in the color map. + * @param reds the red component of all colors. + * @param greens the green component of all colors. + * @param blues the blue component of all colors. + * + * @throws IllegalArgumentException if bits < 1 or + * bits > 16. + * @throws NullPointerException if any of the arrays is null. + * @throws ArrayIndexOutOfBoundsException if size is greater + * than the length of the component arrays. + */ + public IndexColorModel(int bits, int size, byte[] reds, byte[] greens, + byte[] blues) + { + this(bits, size, reds, greens, blues, (byte[]) null); + } + + /** + * Creates a new indexed color model for size color elements. + * Each array must contain at least size elements. For each + * array, the i-th color is described by reds[i], greens[i] and blues[i]. + * All the colors are opaque except for the transparent color. + * + * @param bits the number of bits needed to represent size + * colors + * @param size the number of colors in the color map + * @param reds the red component of all colors + * @param greens the green component of all colors + * @param blues the blue component of all colors + * @param trans the index of the transparent color (use -1 for no + * transparent color). + * + * @throws IllegalArgumentException if bits < 1 or + * bits > 16. + * @throws NullPointerException if any of the arrays is null. + * @throws ArrayIndexOutOfBoundsException if size is greater + * than the length of the component arrays. + */ + public IndexColorModel(int bits, int size, byte[] reds, byte[] greens, + byte[] blues, int trans) + { + super(bits, nArray(8, (0 <= trans && trans < size) ? 4 : 3), + ColorSpace.getInstance(ColorSpace.CS_sRGB), + (0 <= trans && trans < size), // hasAlpha + false, OPAQUE, + Buffers.smallestAppropriateTransferType(bits)); + if (bits < 1) + throw new IllegalArgumentException("bits < 1"); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + rgb = createColorMap(bits, size); + for (int i = 0; i < size; i++) + { + rgb[i] = (0xff000000 + | ((reds[i] & 0xff) << 16) + | ((greens[i] & 0xff) << 8) + | (blues[i] & 0xff)); + } + + setTransparentPixel(trans); + + // Generate a bigint with 1's for every pixel + validBits = validBits.setBit(size).subtract(BigInteger.ONE); + } + + /** + * Creates a new indexed color model for size color elements + * including alpha. Each array must contain at least size + * elements. For each array, the i-th color is described + * by reds[i], greens[i], blues[i] and alphas[i]. + * + * @param bits the number of bits needed to represent size + * colors. + * @param size the number of colors in the color map. + * @param reds the red component of all colors. + * @param greens the green component of all colors. + * @param blues the blue component of all colors. + * @param alphas the alpha component of all colors (null + * permitted). + * + * @throws IllegalArgumentException if bits < 1 or + * bits > 16. + * @throws NullPointerException if reds, greens or + * blues is null. + * @throws ArrayIndexOutOfBoundsException if size is greater + * than the length of the component arrays. + */ + public IndexColorModel(int bits, int size, byte[] reds, byte[] greens, + byte[] blues, byte[] alphas) + { + super(bits, nArray(8, (alphas == null ? 3 : 4)), + ColorSpace.getInstance(ColorSpace.CS_sRGB), + (alphas != null), false, TRANSLUCENT, + Buffers.smallestAppropriateTransferType(bits)); + if (bits < 1) + throw new IllegalArgumentException("bits < 1"); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + opaque = (alphas == null); + + rgb = createColorMap(bits, size); + if (alphas == null) + { + for (int i = 0; i < size; i++) + { + rgb[i] = (0xff000000 + | ((reds[i] & 0xff) << 16) + | ((greens[i] & 0xff) << 8) + | (blues[i] & 0xff)); + } + transparency = OPAQUE; + } + else + { + byte alphaZero = (byte) 0x00; + byte alphaOne = (byte) 0xFF; + for (int i = 0; i < size; i++) + { + alphaZero = (byte) (alphaZero | alphas[i]); + alphaOne = (byte) (alphaOne & alphas[i]); + rgb[i] = ((alphas[i] & 0xff) << 24 + | ((reds[i] & 0xff) << 16) + | ((greens[i] & 0xff) << 8) + | (blues[i] & 0xff)); + } + if ((alphaZero == (byte) 0x00) || (alphaOne == (byte) 0xFF)) + transparency = BITMASK; + else + transparency = TRANSLUCENT; + } + + // Generate a bigint with 1's for every pixel + validBits = validBits.setBit(size).subtract(BigInteger.ONE); + } + + /** + * Creates a new indexed color model using the color components in + * cmap. If hasAlpha is true then + * cmap contains an alpha component after each of the red, green + * and blue components. + * + * @param bits the number of bits needed to represent size + * colors + * @param size the number of colors in the color map + * @param cmap packed color components + * @param start the offset of the first color component in cmap + * @param hasAlpha cmap has alpha values + * @throws IllegalArgumentException if bits < 1, bits > 16, or size + * < 1. + * @throws NullPointerException if cmap is null. + */ + public IndexColorModel(int bits, int size, byte[] cmap, int start, + boolean hasAlpha) + { + this(bits, size, cmap, start, hasAlpha, -1); + } + + /** + * Construct an IndexColorModel from an array of red, green, blue, and + * optional alpha components. The component values are interleaved as RGB(A). + * + * @param bits the number of bits needed to represent size + * colors + * @param size the number of colors in the color map + * @param cmap interleaved color components + * @param start the offset of the first color component in cmap + * @param hasAlpha cmap has alpha values + * @param trans the index of the transparent color + * @throws IllegalArgumentException if bits < 1, bits > 16, or size + * < 1. + * @throws NullPointerException if cmap is null. + */ + public IndexColorModel(int bits, int size, byte[] cmap, int start, + boolean hasAlpha, int trans) + { + super(bits, nArray(8, hasAlpha || (0 <= trans && trans < size) ? 4 : 3), + ColorSpace.getInstance(ColorSpace.CS_sRGB), + hasAlpha || (0 <= trans && trans < size), false, OPAQUE, + Buffers.smallestAppropriateTransferType(bits)); + if (bits < 1) + throw new IllegalArgumentException("bits < 1"); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + opaque = !hasAlpha; + + rgb = createColorMap(bits, size); + if (hasAlpha) + { + int alpha; + int alphaZero = 0x00; // use to detect all zeros + int alphaOne = 0xff; // use to detect all ones + for (int i = 0; i < size; i++) { + alpha = cmap[4 * i + 3 + start] & 0xff; + alphaZero = alphaZero | alpha; + alphaOne = alphaOne & alpha; + rgb[i] = + ( alpha << 24 + // red + | ((cmap[4 * i + start] & 0xff) << 16) + // green + | ((cmap[4 * i + 1 + start] & 0xff) << 8) + // blue + | (cmap[4 * i + 2 + start] & 0xff)); + } + if (alphaZero == 0) + transparency = BITMASK; + else if (alphaOne == 255) + transparency = (trans != -1 ? BITMASK : OPAQUE); + else + transparency = TRANSLUCENT; + } + else + { + for (int i = 0; i < size; i++) + rgb[i] = (0xff000000 + // red + | ((cmap[3 * i + start] & 0xff) << 16) + // green + | ((cmap[3 * i + 1 + start] & 0xff) << 8) + // blue + | (cmap[3 * i + 2 + start] & 0xff)); + if (trans != -1) + transparency = BITMASK; + } + + setTransparentPixel(trans); + + // Generate a bigint with 1's for every pixel + validBits = validBits.setBit(size).subtract(BigInteger.ONE); + } + + /** + * Construct an IndexColorModel from an array of size packed + * colors. Each int element contains 8-bit red, green, blue, and optional + * alpha values packed in order. If hasAlpha is false, then all the colors + * are opaque except for the transparent color. + * + * @param bits the number of bits needed to represent size + * colors + * @param size the number of colors in the color map + * @param cmap packed color components + * @param start the offset of the first color component in cmap + * @param hasAlpha cmap has alpha values + * @param trans the index of the transparent color + * @param transferType {@link DataBuffer#TYPE_BYTE} or + {@link DataBuffer#TYPE_USHORT}. + * @throws IllegalArgumentException if bits < 1, bits > 16, or size + * < 1. + * @throws IllegalArgumentException if transferType is something + * other than {@link DataBuffer#TYPE_BYTE} or + * {@link DataBuffer#TYPE_USHORT}. + */ + public IndexColorModel(int bits, int size, int[] cmap, int start, + boolean hasAlpha, int trans, int transferType) + { + super(bits, + nArray(8, 4), // bits for each channel + ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB + true, // has alpha + false, // not premultiplied + TRANSLUCENT, transferType); + if (transferType != DataBuffer.TYPE_BYTE + && transferType != DataBuffer.TYPE_USHORT) + throw new IllegalArgumentException(); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + opaque = !hasAlpha; + rgb = createColorMap(bits, size); + if (!hasAlpha) + for (int i = 0; i < size; i++) + rgb[i] = cmap[i + start] | 0xff000000; + else + System.arraycopy(cmap, start, rgb, 0, size); + + setTransparentPixel(trans); + + // Generate a bigint with 1's for every pixel + validBits = validBits.setBit(size).subtract(BigInteger.ONE); + } + + /** + * Construct an IndexColorModel using a colormap with holes. + *

    + * The IndexColorModel is built from the array of ints defining the + * colormap. Each element contains red, green, blue, and alpha + * components. The ColorSpace is sRGB. The transparency value is + * automatically determined. + *

    + * This constructor permits indicating which colormap entries are valid, + * using the validBits argument. Each entry in cmap is valid if the + * corresponding bit in validBits is set. + * + * @param bits the number of bits needed to represent size + * colors. + * @param size the number of colors in the color map. + * @param cmap packed color components. + * @param start the offset of the first color component in cmap. + * @param transferType {@link DataBuffer#TYPE_BYTE} or + * {@link DataBuffer#TYPE_USHORT}. + * @param validBits a map of the valid entries in cmap. + * @throws IllegalArgumentException if bits < 1, bits > 16, or size + * < 1. + * @throws IllegalArgumentException if transferType is something other than + * {@link DataBuffer#TYPE_BYTE} or {@link DataBuffer#TYPE_USHORT}. + */ + public IndexColorModel(int bits, int size, int[] cmap, int start, + int transferType, BigInteger validBits) + { + super(bits, // total bits, sRGB, four channels + nArray(8, 4), // bits for each channel + ColorSpace.getInstance(ColorSpace.CS_sRGB), // sRGB + true, // has alpha + false, // not premultiplied + TRANSLUCENT, transferType); + if (transferType != DataBuffer.TYPE_BYTE + && transferType != DataBuffer.TYPE_USHORT) + throw new IllegalArgumentException(); + if (bits > 16) + throw new IllegalArgumentException("bits > 16"); + if (size < 1) + throw new IllegalArgumentException("size < 1"); + map_size = size; + opaque = false; + this.trans = -1; + this.validBits = validBits; + + rgb = createColorMap(bits, size); + if (!hasAlpha) + for (int i = 0; i < size; i++) + rgb[i] = cmap[i + start] | 0xff000000; + else + System.arraycopy(cmap, start, rgb, 0, size); + } + + /** + * Returns the size of the color lookup table. + * + * @return The size of the color lookup table. + */ + public final int getMapSize() + { + return map_size; + } + + /** + * Get the index of the transparent color in this color model. + * + * @return The index of the color that is considered transparent, or -1 if + * there is no transparent color. + */ + public final int getTransparentPixel() + { + return trans; + } + + /** + * Fills the supplied array with the red component of each color in the + * lookup table. + * + * @param r an array that is at least as large as {@link #getMapSize()}. + * @throws NullPointerException if r is null. + * @throws ArrayIndexOutOfBoundsException if r has less + * than {@link #getMapSize()} elements. + */ + public final void getReds(byte[] r) + { + int i; + for (i = 0; i < map_size; i++) + r[i] = (byte) ((0x00FF0000 & rgb[i]) >> 16); + } + + /** + * Fills the supplied array with the green component of each color in the + * lookup table. + * + * @param g an array that is at least as large as {@link #getMapSize()}. + * @throws NullPointerException if g is null. + * @throws ArrayIndexOutOfBoundsException if g has less + * than {@link #getMapSize()} elements. + */ + public final void getGreens(byte[] g) + { + int i; + for (i = 0; i < map_size; i++) + g[i] = (byte) ((0x0000FF00 & rgb[i]) >> 8); + } + + /** + * Fills the supplied array with the blue component of each color in the + * lookup table. + * + * @param b an array that is at least as large as {@link #getMapSize()}. + * @throws NullPointerException if b is null. + * @throws ArrayIndexOutOfBoundsException if b has less + * than {@link #getMapSize()} elements. + */ + public final void getBlues(byte[] b) + { + int i; + for (i = 0; i < map_size; i++) + b[i] = (byte) (0x000000FF & rgb[i]); + } + + /** + * Fills the supplied array with the alpha component of each color in the + * lookup table. If the model has a transparent pixel specified, the alpha + * for that pixel will be 0. + * + * @param a an array that is at least as large as {@link #getMapSize()}. + * @throws NullPointerException if a is null. + * @throws ArrayIndexOutOfBoundsException if a has less + * than {@link #getMapSize()} elements. + */ + public final void getAlphas(byte[] a) + { + int i; + for (i = 0; i < map_size; i++) + if (i == trans) + a[i] = (byte) 0; + else + a[i] = (byte) ((0xFF000000 & rgb[i]) >> 24); + } + + /** + * Returns the red component of the color in the lookup table for the + * given pixel value. + * + * @param pixel the pixel lookup value. + * + * @return The red component of the color in the lookup table. + * @throws ArrayIndexOutOfBoundsException if pixel is negative. + */ + public final int getRed(int pixel) + { + if (pixel < map_size) + return (0x00FF0000 & rgb[pixel]) >> 16; + + return 0; + } + + /** + * Returns the green component of the color in the lookup table for the + * given pixel value. + * + * @param pixel the pixel lookup value. + * + * @return The green component of the color in the lookup table. + * @throws ArrayIndexOutOfBoundsException if pixel is negative. + */ + public final int getGreen(int pixel) + { + if (pixel < map_size) + return (0x0000FF00 & rgb[pixel]) >> 8; + + return 0; + } + + /** + * Returns the blue component of the color in the lookup table for the + * given pixel value. + * + * @param pixel the pixel lookup value. + * + * @return The blue component of the color in the lookup table. + * @throws ArrayIndexOutOfBoundsException if pixel is negative. + */ + public final int getBlue(int pixel) + { + if (pixel < map_size) + return 0x000000FF & rgb[pixel]; + + return 0; + } + + /** + * Returns the alpha component of the color in the lookup table for the + * given pixel value. If no alpha channel was specified when the color model + * was created, then 255 is returned for all pixels except the transparent + * pixel (if one is defined - see {@link #getTransparentPixel()}) which + * returns an alpha of 0. + * + * @param pixel the pixel lookup value. + * + * @return The alpha component of the color in the lookup table (in the + * range 0 to 255). + * @throws ArrayIndexOutOfBoundsException if pixel is negative. + */ + public final int getAlpha(int pixel) + { + return (rgb[pixel] >> 24) & 0xFF; + } + + /** + * Get the RGB color value of the given pixel using the default + * RGB color model. + * + * @param pixel the pixel lookup value. + * @return The RGB color value. + * @throws ArrayIndexOutOfBoundsException if pixel is negative. + */ + public final int getRGB(int pixel) + { + if (pixel >= 0 && pixel < map_size) + return rgb[pixel]; + + return 0; + } + + /** + * Get the RGB color values of all pixels in the map using the default + * RGB color model. + * + * @param rgb The destination array. + */ + public final void getRGBs(int[] rgb) + { + System.arraycopy(this.rgb, 0, rgb, 0, map_size); + } + + /** + * Return true if the lookup table contains valid data for + * pixel, and false otherwise. + * + * @param pixel the pixel value used to index the color lookup table. + * @return true if pixel is valid, + * false otherwise. + */ + public boolean isValid(int pixel) + { + if (pixel >= 0) + return validBits.testBit(pixel); + return false; + } + + /** + * Return true if all pixels are valid, false + * otherwise. + * + * @return true if all pixels are valid, false + * otherwise. + */ + public boolean isValid() + { + // Generate a bigint with 1's for every pixel + BigInteger allbits = new BigInteger("0"); + allbits = allbits.setBit(map_size); + allbits = allbits.subtract(new BigInteger("1")); + return allbits.equals(validBits); + } + + /** + * Returns a binary value ({@link BigInteger}) where each bit represents an + * entry in the color lookup table. If the bit is on, the entry is valid. + * + * @return The binary value. + */ + public BigInteger getValidPixels() + { + return validBits; + } + + /** + * Construct a {@link BufferedImage} with rgb pixel values from a + * {@link Raster}. + * + * Constructs a new BufferedImage in which each pixel is an RGBA int from + * a Raster with index-valued pixels. If this model has no alpha component + * or transparent pixel, the type of the new BufferedImage is TYPE_INT_RGB. + * Otherwise the type is TYPE_INT_ARGB. If forceARGB is true, the type is + * forced to be TYPE_INT_ARGB no matter what. + * + * @param raster The source of pixel values. + * @param forceARGB True if type must be TYPE_INT_ARGB. + * @return New BufferedImage with RBGA int pixel values. + */ + public BufferedImage convertToIntDiscrete(Raster raster, boolean forceARGB) + { + int type = forceARGB ? BufferedImage.TYPE_INT_ARGB + : ((opaque && trans == -1) ? BufferedImage.TYPE_INT_RGB : + BufferedImage.TYPE_INT_ARGB); + + // FIXME: assuming that raster has only 1 band since pixels are supposed + // to be int indexes. + // FIXME: it would likely be more efficient to fetch a complete array, + // but it would take much more memory. + // FIXME: I'm not sure if transparent pixels or alpha values need special + // handling here. + BufferedImage im = new BufferedImage(raster.width, raster.height, type); + for (int x = raster.minX; x < raster.width + raster.minX; x++) + for (int y = raster.minY; y < raster.height + raster.minY; y++) + im.setRGB(x, y, rgb[raster.getSample(x, y, 0)]); + + return im; + } + + /** + * Creates a {@link SampleModel} that is compatible to this color model. + * This will be a {@link MultiPixelPackedSampleModel} for bits/pixel of + * 1, 2 or 4, or a {@link ComponentColorModel} for the other cases. + * + * @param w the width of the sample model to create + * @param h the height of the sample model to create + * + * @return a compatible sample model + */ + public SampleModel createCompatibleSampleModel(int w, int h) + { + SampleModel sm; + if (pixel_bits == 1 || pixel_bits == 2 || pixel_bits == 4) + sm = new MultiPixelPackedSampleModel(transferType, w, h, pixel_bits); + else + sm = new ComponentSampleModel(transferType, w, h, 1, w, new int[]{0}); + return sm; + } + + /** + * Sets the transparent pixel. This is called by the various constructors. + * + * @param t the transparent pixel + */ + private void setTransparentPixel(int t) + { + if (t >= 0 && t < map_size) + { + rgb[t] &= 0xffffff; // Make the value transparent. + trans = t; + if (transparency == OPAQUE) + { + transparency = BITMASK; + hasAlpha = true; + } + } + } + + private int[] createColorMap(int bits, int size) + { + // According to a Mauve test, the RI allocates at least 256 entries here. + int realSize = Math.max(256, Math.max(1 << bits, size)); + return new int[realSize]; + } +} diff --git a/libjava/classpath/java/awt/image/Kernel.java b/libjava/classpath/java/awt/image/Kernel.java new file mode 100644 index 000000000..ae0ff1469 --- /dev/null +++ b/libjava/classpath/java/awt/image/Kernel.java @@ -0,0 +1,171 @@ +/* Kernel.java -- Java class for an image processing kernel + Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * Kernel represents an image processing kernel. It gets used to hold + * convolution filters among other purposes. It stores an array of float + * values representing a 2-dimensional array in row-major order. + * + * @author Jerry Quinn (jlquinn@optonline.net) + */ +public class Kernel implements Cloneable +{ + /** The kernel width. */ + private final int width; + + /** The kernel height. */ + private final int height; + + /** Internal storage for the kernel's values. */ + private final float[] data; + + /** + * Creates a new Kernel instance with the specified dimensions + * and values. The first width * height values in the specified + * data array are copied to internal storage. + * + * @param width the kernel width. + * @param height the kernel height. + * @param data the source data array (null not permitted). + * + * @throws IllegalArgumentException if data.length is less than + * width * height. + * @throws IllegalArgumentException if width or + * height is less than zero. + * @throws NullPointerException if data is null. + */ + public Kernel(int width, int height, float[] data) + throws IllegalArgumentException + { + this.width = width; + this.height = height; + if (data.length < width * height || width < 0 || height < 0) + throw new IllegalArgumentException(); + this.data = new float[width * height]; + System.arraycopy(data, 0, this.data, 0, width * height); + } + + /** + * Returns the x-origin for the kernel, which is calculated as + * (width - 1) / 2. + * + * @return The x-origin for the kernel. + */ + public final int getXOrigin() + { + return (width - 1) / 2; + } + + /** + * Returns the y-origin for the kernel, which is calculated as + * (height - 1) / 2. + * + * @return The y-origin for the kernel. + */ + public final int getYOrigin() + { + return (height - 1) / 2; + } + + /** + * Returns the kernel width (as supplied to the constructor). + * + * @return The kernel width. + */ + public final int getWidth() + { + return width; + } + + /** + * Returns the kernel height (as supplied to the constructor). + * + * @return The kernel height. + */ + public final int getHeight() + { + return height; + } + + /** + * Returns an array containing a copy of the kernel data. If the + * data argument is non-null, the kernel values + * are copied into it and then data is returned as the result. + * If the data argument is null, this method + * allocates a new array then populates and returns it. + * + * @param data an array to copy the return values into (if + * null, a new array is allocated). + * + * @return The array with copied values. + * + * @throws IllegalArgumentException if data.length is less than + * the kernel's width * height. + */ + public final float[] getKernelData(float[] data) + throws IllegalArgumentException + { + if (data == null) + return (float[]) this.data.clone(); + + if (data.length < this.data.length) + throw new IllegalArgumentException(); + + System.arraycopy(this.data, 0, data, 0, this.data.length); + return data; + } + + /** + * Returns a clone of this kernel. + * + * @return a clone of this Kernel. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // Impossible + } + } +} diff --git a/libjava/classpath/java/awt/image/LookupOp.java b/libjava/classpath/java/awt/image/LookupOp.java new file mode 100644 index 000000000..0bc79a00b --- /dev/null +++ b/libjava/classpath/java/awt/image/LookupOp.java @@ -0,0 +1,307 @@ +/* LookupOp.java -- Filter that converts each pixel using a lookup table. + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * LookupOp is a filter that converts each pixel using a lookup table. + * + * For filtering Rasters, the lookup table must have either one component + * that is applied to all bands, or one component for every band in the + * Rasters. + * + * For BufferedImages, the lookup table may apply to both color and alpha + * components. If the lookup table contains one component, or if there are + * the same number of components as color components in the source, the table + * applies to all color components. Otherwise the table applies to all + * components including alpha. Alpha premultiplication is ignored during the + * lookup filtering. + * + * After filtering, if color conversion is necessary, the conversion happens, + * taking alpha premultiplication into account. + * + * @author jlquinn + */ +public class LookupOp implements BufferedImageOp, RasterOp +{ + private LookupTable lut; + private RenderingHints hints; + + /** + * Construct a new LookupOp using the given LookupTable. + * + * @param lookup LookupTable to use. + * @param hints Rendering hints (can be null). + */ + public LookupOp(LookupTable lookup, RenderingHints hints) + { + lut = lookup; + this.hints = hints; + } + + /** + * Converts the source image using the lookup table 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 image cannot use an IndexColorModel, and the destination image + * (if one is provided) must have the same size. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @throws ArrayIndexOutOfBoundsException if a pixel in the source is not + * contained in the LookupTable. + * @return The convolved image. + */ + public final BufferedImage filter(BufferedImage src, BufferedImage dst) + { + if (src.getColorModel() instanceof IndexColorModel) + throw new IllegalArgumentException("LookupOp.filter: IndexColorModel " + + "not allowed"); + + if (lut.getNumComponents() != 1 + && lut.getNumComponents() != src.getColorModel().getNumComponents() + && lut.getNumComponents() != src.getColorModel().getNumColorComponents()) + throw new IllegalArgumentException("LookupOp.filter: Incompatible " + + "lookup table and source image"); + + if (dst == null) + dst = createCompatibleDestImage(src, null); + + else if (src.getHeight() != dst.getHeight() || src.getWidth() != dst.getWidth()) + throw new IllegalArgumentException("Source and destination images are " + + "different sizes."); + + // Set up for potential colormodel mismatch + BufferedImage tgt; + if (dst.getColorModel().equals(src.getColorModel())) + tgt = dst; + else + tgt = createCompatibleDestImage(src, src.getColorModel()); + + Raster sr = src.getRaster(); + WritableRaster dr = tgt.getRaster(); + + if (src.getColorModel().hasAlpha() && + (lut.getNumComponents() == 1 || + lut.getNumComponents() == src.getColorModel().getNumColorComponents())) + { + // Need to ignore alpha for lookup + int[] dbuf = new int[src.getColorModel().getNumComponents()]; + int tmpBands = src.getColorModel().getNumColorComponents(); + int[] tmp = new int[tmpBands]; + + // Filter the pixels + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + { + // Filter only color components, but also copy alpha + sr.getPixel(x, y, dbuf); + System.arraycopy(dbuf, 0, tmp, 0, tmpBands); + dr.setPixel(x, y, lut.lookupPixel(tmp, dbuf)); + + /* The reference implementation does not use LookupTable.lookupPixel, + * but rather it seems to copy the table into a native array. The + * effect of this (a probable bug in their implementation) is that + * an out-of-bounds lookup on a ByteLookupTable will *not* throw an + * out of bounds exception, but will instead return random garbage. + * A bad lookup on a ShortLookupTable, however, will throw an + * exception. + * + * Instead of mimicing this behaviour, we always throw an + * ArrayOutofBoundsException by virtue of using + * LookupTable.lookupPixle. + */ + } + } + else + { + // No alpha to ignore + int[] dbuf = new int[src.getColorModel().getNumComponents()]; + + // Filter the pixels + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + dr.setPixel(x, y, lut.lookupPixel(sr.getPixel(x, y, dbuf), dbuf)); + } + + if (tgt != dst) + new ColorConvertOp(hints).filter(tgt, dst); + + return dst; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + */ + public final Rectangle2D getBounds2D(BufferedImage src) + { + return src.getRaster().getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel) + */ + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel dstCM) + { + if (dstCM != null) + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isAlphaPremultiplied(), null); + + // This is a strange exception, done for compatibility with the reference + // (as demonstrated by a mauve testcase) + int imgType = src.getType(); + if (imgType == BufferedImage.TYPE_USHORT_GRAY) + imgType = BufferedImage.TYPE_BYTE_GRAY; + + return new BufferedImage(src.getWidth(), src.getHeight(), imgType); + } + + /** + * Returns the corresponding destination point for a given source point. + * + * This Op will return the source point unchanged. + * + * @param src The source point. + * @param dst The destination point. + */ + public final Point2D getPoint2D(Point2D src, Point2D dst) + { + if (dst == null) + return (Point2D) src.clone(); + + dst.setLocation(src); + return dst; + } + + /** + * Return the LookupTable for this op. + * + * @return The lookup table. + */ + public final LookupTable getTable() + { + return lut; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getRenderingHints() + */ + public final RenderingHints getRenderingHints() + { + return hints; + } + + /** + * Filter a raster through a lookup table. + * + * Applies the lookup table for this Rasterop to each pixel of src and + * puts the results in dest. If dest is null, a new Raster is created and + * returned. + * + * @param src The source raster. + * @param dest The destination raster. + * @return The WritableRaster with the filtered pixels. + * @throws IllegalArgumentException if lookup table has more than one + * component but not the same as src and dest. + * @throws ArrayIndexOutOfBoundsException if a pixel in the source is not + * contained in the LookupTable. + */ + public final WritableRaster filter(Raster src, WritableRaster dest) + { + if (dest == null) + // Allocate a raster if needed + dest = createCompatibleDestRaster(src); + else + if (src.getNumBands() != dest.getNumBands()) + throw new IllegalArgumentException("Source and destination rasters " + + "are incompatible."); + + if (lut.getNumComponents() != 1 + && lut.getNumComponents() != src.getNumBands()) + throw new IllegalArgumentException("Lookup table is incompatible with " + + "this raster."); + + // Allocate pixel storage. + int[] tmp = new int[src.getNumBands()]; + + // Filter the pixels + for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) + for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) + dest.setPixel(x, y, lut.lookupPixel(src.getPixel(x, y, tmp), tmp)); + + /* The reference implementation does not use LookupTable.lookupPixel, + * but rather it seems to copy the table into a native array. The + * effect of this (a probable bug in their implementation) is that + * an out-of-bounds lookup on a ByteLookupTable will *not* throw an + * out of bounds exception, but will instead return random garbage. + * A bad lookup on a ShortLookupTable, however, will throw an + * exception. + * + * Instead of mimicing this behaviour, we always throw an + * ArrayOutofBoundsException by virtue of using + * LookupTable.lookupPixle. + */ + return dest; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public final Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + */ + public WritableRaster createCompatibleDestRaster(Raster src) + { + return src.createCompatibleWritableRaster(); + } + +} diff --git a/libjava/classpath/java/awt/image/LookupTable.java b/libjava/classpath/java/awt/image/LookupTable.java new file mode 100644 index 000000000..d104b2fd5 --- /dev/null +++ b/libjava/classpath/java/awt/image/LookupTable.java @@ -0,0 +1,109 @@ +/* LookupTable.java -- Java class for a pixel translation table. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * LookupTable represents translation arrays for pixel values. It wraps one + * or more data arrays for each layer (or component) in an image, such as + * Alpha, R, G, and B. When doing translation, the offset is subtracted from + * the pixel values to allow a subset of an array to be used. + * + * @see ByteLookupTable + * @see ShortLookupTable + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @version 1.0 + */ +public abstract class LookupTable +{ + // Not protected since that's part of the public API. + int offset; + int numComponents; + + /** + * Creates a new LookupTable instance. + * + * If numComponents is 1, the same translation table is used for all pixel + * components. + * + * @param offset Offset to be subtracted. + * @param numComponents Number of image components. + * @exception IllegalArgumentException if offset < 0 or numComponents < 1. + */ + protected LookupTable(int offset, int numComponents) + throws IllegalArgumentException + { + if (offset < 0 || numComponents < 1) + throw new IllegalArgumentException(); + this.offset = offset; + this.numComponents = numComponents; + } + + /** Return the number of components. */ + public int getNumComponents() + { + return numComponents; + } + + /** Return the offset. */ + public int getOffset() + { + return offset; + } + + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dest Destination array for values, or null. + * @return Translated values for the pixel. + */ + public abstract int[] lookupPixel(int[] src, int[] dest); +} diff --git a/libjava/classpath/java/awt/image/MemoryImageSource.java b/libjava/classpath/java/awt/image/MemoryImageSource.java new file mode 100644 index 000000000..e8780f6f5 --- /dev/null +++ b/libjava/classpath/java/awt/image/MemoryImageSource.java @@ -0,0 +1,429 @@ +/* MemoryImageSource.java -- Java class for providing image data + Copyright (C) 1999, 2004, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.util.Hashtable; +import java.util.Vector; + +/** + * An image producer that delivers image data from an array. + */ +public class MemoryImageSource implements ImageProducer +{ + private boolean animated = false; + private boolean fullbuffers = false; + private int[] pixeli; + private int width; + private int height; + private int offset; + private int scansize; + private byte[] pixelb; + private ColorModel cm; + private Hashtable props = new Hashtable(); + private Vector consumers = new Vector(); + + /** + * Construct an image producer that reads image data from a byte + * array. + * + * @param w width of image + * @param h height of image + * @param cm the color model used to represent pixel values + * @param pix a byte array of pixel values + * @param off the offset into the array at which the first pixel is stored + * @param scan the number of array elements that represents a single pixel row + */ + public MemoryImageSource(int w, int h, ColorModel cm, byte[] pix, int off, + int scan) + { + this(w, h, cm, pix, off, scan, null); + } + + /** + * Constructs an ImageProducer from memory. + * + * @param w the image width. + * @param h the image height. + * @param cm the color model. + * @param pix the image data. + * @param off the offset to the first pixel in the array. + * @param scan the number of array elements from a pixel on one row to the + * corresponding pixel on the next row. + * @param props image properties (null permitted). + */ + public MemoryImageSource(int w, int h, ColorModel cm, byte[] pix, int off, + int scan, Hashtable props) + { + width = w; + height = h; + this.cm = cm; + offset = off; + scansize = scan; + this.props = props; + int max = ((scansize > width) ? scansize : width); + pixelb = pix; + } + + /** + * Construct an image producer that reads image data from an + * integer array. + * + * @param w width of image + * @param h height of image + * @param cm the color model used to represent pixel values + * @param pix an integer array of pixel values + * @param off the offset into the array at which the first pixel is stored + * @param scan the number of array elements that represents a single pixel row + */ + public MemoryImageSource(int w, int h, ColorModel cm, int[] pix, int off, + int scan) + { + this(w, h, cm, pix, off, scan, null); + } + + /** + * Constructs an ImageProducer from memory + * + * @param w the image width. + * @param h the image height. + * @param cm the color model. + * @param pix the image data. + * @param off the offset to the first pixel in the array. + * @param scan the number of array elements from a pixel on one row to the + * corresponding pixel on the next row. + * @param props image properties (null permitted). + */ + public MemoryImageSource(int w, int h, ColorModel cm, int[] pix, int off, + int scan, Hashtable props) + { + width = w; + height = h; + this.cm = cm; + offset = off; + scansize = scan; + this.props = props; + int max = ((scansize > width) ? scansize : width); + pixeli = pix; + } + + /** + * Constructs an ImageProducer from memory using the default RGB ColorModel. + * + * @param w the image width. + * @param h the image height. + * @param pix the image data. + * @param off the offset to the first pixel in the array. + * @param scan the number of array elements from a pixel on one row to the + * corresponding pixel on the next row. + * @param props image properties (null permitted). + + */ + public MemoryImageSource(int w, int h, int[] pix, int off, int scan, + Hashtable props) + { + this(w, h, ColorModel.getRGBdefault(), pix, off, scan, props); + } + + /** + * Constructs an ImageProducer from memory using the default RGB ColorModel. + * + * @param w the image width. + * @param h the image height. + * @param pix the image data. + * @param off the offset to the first pixel in the array. + * @param scan the number of array elements from a pixel on one row to the + * corresponding pixel on the next row. + */ + public MemoryImageSource(int w, int h, int[] pix, int off, int scan) + { + this(w, h, ColorModel.getRGBdefault(), pix, off, scan, null); + } + + /** + * Used to register an ImageConsumer with this + * ImageProducer. + * + * @param ic the image consumer. + */ + public synchronized void addConsumer(ImageConsumer ic) + { + if (consumers.contains(ic)) + return; + + consumers.addElement(ic); + } + + /** + * Used to determine if the given ImageConsumer is + * already registered with this ImageProducer. + * + * @param ic the image consumer. + */ + public synchronized boolean isConsumer(ImageConsumer ic) + { + if (consumers.contains(ic)) + return true; + return false; + } + + /** + * Used to remove an ImageConsumer from the list of + * registered consumers for this ImageProducer. + * + * @param ic the image consumer. + */ + public synchronized void removeConsumer(ImageConsumer ic) + { + consumers.removeElement(ic); + } + + /** + * Used to register an ImageConsumer with this + * ImageProducer and then immediately start + * reconstruction of the image data to be delivered to all + * registered consumers. + */ + public void startProduction(ImageConsumer ic) + { + if (! (consumers.contains(ic))) + consumers.addElement(ic); + + Vector list = (Vector) consumers.clone(); + for (int i = 0; i < list.size(); i++) + { + ic = (ImageConsumer) list.elementAt(i); + sendPicture(ic); + if (animated) + ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + else + ic.imageComplete(ImageConsumer.STATICIMAGEDONE); + } + } + + /** + * Used to register an ImageConsumer with this + * ImageProducer and then request that this producer + * resend the image data in the order top-down, left-right. + * + * @param ic the image consumer. + */ + public void requestTopDownLeftRightResend(ImageConsumer ic) + { + startProduction(ic); + } + + /** + * Changes a flag to indicate whether this MemoryImageSource supports + * animations. + * + * @param animated A flag indicating whether this class supports animations + */ + public synchronized void setAnimated(boolean animated) + { + this.animated = animated; + } + + /** + * A flag to indicate whether or not to send full buffer updates when + * sending animation. If this flag is set then full buffers are sent + * in the newPixels methods instead of just regions. + * + * @param fullbuffers a flag indicating whether to send the full buffers + */ + public synchronized void setFullBufferUpdates(boolean fullbuffers) + { + this.fullbuffers = fullbuffers; + } + + /** + * Send an animation frame to the image consumers. + */ + public void newPixels() + { + if (animated == true) + { + ImageConsumer ic; + Vector list = (Vector) consumers.clone(); + for (int i = 0; i < list.size(); i++) + { + ic = (ImageConsumer) list.elementAt(i); + sendPicture(ic); + ic.imageComplete(ImageConsumer.SINGLEFRAME); + } + } + } + + private void sendPicture(ImageConsumer ic) + { + ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT); + if (props != null) + ic.setProperties(props); + ic.setDimensions(width, height); + ic.setColorModel(cm); + if (pixeli != null) + ic.setPixels(0, 0, width, height, cm, pixeli, offset, scansize); + else + ic.setPixels(0, 0, width, height, cm, pixelb, offset, scansize); + } + + /** + * Send an animation frame to the image consumers containing the specified + * pixels unless setFullBufferUpdates is set. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + */ + public synchronized void newPixels(int x, int y, int w, int h) + { + if (animated == true) + { + if (fullbuffers) + newPixels(); + else + { + ImageConsumer ic; + Vector list = (Vector) consumers.clone(); + for (int i = 0; i < list.size(); i++) + { + ic = (ImageConsumer) list.elementAt(i); + ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT); + if (props != null) + ic.setProperties(props); + if (pixeli != null) + { + int[] pixelbuf = new int[w * h]; + for (int row = y; row < y + h; row++) + System.arraycopy(pixeli, row * scansize + x + offset, + pixelbuf, 0, w * h); + ic.setPixels(x, y, w, h, cm, pixelbuf, 0, w); + } + else + { + byte[] pixelbuf = new byte[w * h]; + for (int row = y; row < y + h; row++) + System.arraycopy(pixelb, row * scansize + x + offset, + pixelbuf, 0, w * h); + + ic.setPixels(x, y, w, h, cm, pixelbuf, 0, w); + } + ic.imageComplete(ImageConsumer.SINGLEFRAME); + } + } + } + } + + /** + * Send an animation frame to the image consumers containing the specified + * pixels unless setFullBufferUpdates is set. + * + * If framenotify is set then a notification is sent when the frame + * is sent otherwise no status is sent. + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * @param w the width. + * @param h the height. + * @param framenotify send notification? + */ + public synchronized void newPixels(int x, int y, int w, int h, + boolean framenotify) + { + if (animated == true) + { + if (fullbuffers) + newPixels(); + else + { + ImageConsumer ic; + Vector list = (Vector) consumers.clone(); + for (int i = 0; i < list.size(); i++) + { + ic = (ImageConsumer) list.elementAt(i); + ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT); + if (props != null) + ic.setProperties(props); + if (pixeli != null) + { + int[] pixelbuf = new int[w * h]; + for (int row = y; row < y + h; row++) + System.arraycopy(pixeli, row * scansize + x + offset, + pixelbuf, 0, w * h); + ic.setPixels(x, y, w, h, cm, pixelbuf, 0, w); + } + else + { + byte[] pixelbuf = new byte[w * h]; + for (int row = y; row < y + h; row++) + System.arraycopy(pixelb, row * scansize + x + offset, + pixelbuf, 0, w * h); + ic.setPixels(x, y, w, h, cm, pixelbuf, 0, w); + } + if (framenotify == true) + ic.imageComplete(ImageConsumer.SINGLEFRAME); + } + } + } + } + + public synchronized void newPixels(byte[] newpix, ColorModel newmodel, + int offset, int scansize) + { + pixeli = null; + pixelb = newpix; + cm = newmodel; + this.offset = offset; + this.scansize = scansize; + if (animated == true) + newPixels(); + } + + public synchronized void newPixels(int[] newpix, ColorModel newmodel, + int offset, int scansize) + { + pixelb = null; + pixeli = newpix; + cm = newmodel; + this.offset = offset; + this.scansize = scansize; + if (animated == true) + newPixels(); + } +} diff --git a/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java b/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java new file mode 100644 index 000000000..592611cbb --- /dev/null +++ b/libjava/classpath/java/awt/image/MultiPixelPackedSampleModel.java @@ -0,0 +1,603 @@ +/* Copyright (C) 2004, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import gnu.java.awt.Buffers; +import gnu.java.lang.CPStringBuilder; + +/** + * MultiPixelPackedSampleModel provides a single band model that supports + * multiple pixels in a single unit. Pixels have 2^n bits and 2^k pixels fit + * per data element. + * + * @author Jerry Quinn (jlquinn@optonline.net) + */ +public class MultiPixelPackedSampleModel extends SampleModel +{ + private int scanlineStride; + private int[] bitMasks; + private int[] bitOffsets; + private int[] sampleSize; + private int dataBitOffset; + private int elemBits; + private int numberOfBits; + private int numElems; + + /** + * Creates a new MultiPixelPackedSampleModel with the specified + * data type, which should be one of: + *

      + *
    • {@link DataBuffer#TYPE_BYTE};
    • + *
    • {@link DataBuffer#TYPE_USHORT};
    • + *
    • {@link DataBuffer#TYPE_INT};
    • + *
    + * + * @param dataType the data type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param numberOfBits the number of bits per pixel (must be a power of 2). + */ + public MultiPixelPackedSampleModel(int dataType, int w, int h, + int numberOfBits) + { + this(dataType, w, h, numberOfBits, 0, 0); + } + + /** + * Creates a new MultiPixelPackedSampleModel with the specified + * data type, which should be one of: + *
      + *
    • {@link DataBuffer#TYPE_BYTE};
    • + *
    • {@link DataBuffer#TYPE_USHORT};
    • + *
    • {@link DataBuffer#TYPE_INT};
    • + *
    + * + * @param dataType the data type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param numberOfBits the number of bits per pixel (must be a power of 2). + * @param scanlineStride the number of data elements from a pixel on one + * row to the corresponding pixel in the next row. + * @param dataBitOffset the offset to the first data bit. + */ + public MultiPixelPackedSampleModel(int dataType, int w, int h, + int numberOfBits, int scanlineStride, + int dataBitOffset) + { + super(dataType, w, h, 1); + + switch (dataType) + { + case DataBuffer.TYPE_BYTE: + elemBits = 8; + break; + case DataBuffer.TYPE_USHORT: + elemBits = 16; + break; + case DataBuffer.TYPE_INT: + elemBits = 32; + break; + default: + throw new IllegalArgumentException("MultiPixelPackedSampleModel" + + " unsupported dataType"); + } + + this.dataBitOffset = dataBitOffset; + + this.numberOfBits = numberOfBits; + if (numberOfBits > elemBits) + throw new RasterFormatException("MultiPixelPackedSampleModel pixel size" + + " larger than dataType"); + switch (numberOfBits) + { + case 1: case 2: case 4: case 8: case 16: case 32: break; + default: + throw new RasterFormatException("MultiPixelPackedSampleModel pixel" + + " size not 2^n bits"); + } + numElems = elemBits / numberOfBits; + + // Compute scan line large enough for w pixels. + if (scanlineStride == 0) + scanlineStride = ((dataBitOffset + w * numberOfBits) - 1) / elemBits + 1; + this.scanlineStride = scanlineStride; + + + sampleSize = new int[1]; + sampleSize[0] = numberOfBits; + + bitMasks = new int[numElems]; + bitOffsets = new int[numElems]; + for (int i=0; i < numElems; i++) + { + bitOffsets[numElems - i- 1] = numberOfBits * i; + bitMasks[numElems - i - 1] = ((1 << numberOfBits) - 1) << + bitOffsets[numElems - i - 1]; + } + } + + /** + * Creates a new MultiPixelPackedSample model with the same + * data type and bits per pixel as this model, but with the specified + * dimensions. + * + * @param w the width (in pixels). + * @param h the height (in pixels). + * + * @return The new sample model. + */ + public SampleModel createCompatibleSampleModel(int w, int h) + { + /* FIXME: We can avoid recalculation of bit offsets and sample + sizes here by passing these from the current instance to a + special private constructor. */ + return new MultiPixelPackedSampleModel(dataType, w, h, numberOfBits); + } + + /** + * Creates a DataBuffer for holding pixel data in the format and + * layout described by this SampleModel. The returned buffer will + * consist of one single bank. + * + * @return A new data buffer. + */ + public DataBuffer createDataBuffer() + { + int size = scanlineStride * height; + if (dataBitOffset > 0) + size += (dataBitOffset - 1) / elemBits + 1; + return Buffers.createBuffer(getDataType(), size); + } + + /** + * Returns the number of data elements required to transfer a pixel in the + * get/setDataElements() methods. + * + * @return 1. + */ + public int getNumDataElements() + { + return 1; + } + + /** + * Returns an array containing the size (in bits) of the samples in each + * band. The MultiPixelPackedSampleModel class supports only + * one band, so this method returns an array with length 1. + * + * @return An array containing the size (in bits) of the samples in band zero. + * + * @see #getSampleSize(int) + */ + public int[] getSampleSize() + { + return (int[]) sampleSize.clone(); + } + + /** + * Returns the size of the samples in the specified band. Note that the + * MultiPixelPackedSampleModel supports only one band -- this + * method ignored the band argument, and always returns the size + * of band zero. + * + * @param band the band (this parameter is ignored). + * + * @return The size of the samples in band zero. + * + * @see #getSampleSize() + */ + public int getSampleSize(int band) + { + return sampleSize[0]; + } + + /** + * Returns the index in the data buffer that stores the pixel at (x, y). + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * + * @return The index in the data buffer that stores the pixel at (x, y). + * + * @see #getBitOffset(int) + */ + public int getOffset(int x, int y) + { + return scanlineStride * y + ((dataBitOffset + x * numberOfBits) / elemBits); + } + + /** + * The bit offset (within an element in the data buffer) of the pixels with + * the specified x-coordinate. + * + * @param x the x-coordinate. + * + * @return The bit offset. + */ + public int getBitOffset(int x) + { + return (dataBitOffset + x * numberOfBits) % elemBits; + } + + /** + * Returns the offset to the first data bit. + * + * @return The offset to the first data bit. + */ + public int getDataBitOffset() + { + return dataBitOffset; + } + + /** + * Returns the number of data elements from a pixel in one row to the + * corresponding pixel in the next row. + * + * @return The scanline stride. + */ + public int getScanlineStride() + { + return scanlineStride; + } + + /** + * Returns the number of bits per pixel. + * + * @return The number of bits per pixel. + */ + public int getPixelBitStride() + { + return numberOfBits; + } + + /** + * Returns the transfer type, which is one of the following (depending on + * the number of bits per sample for this model): + *
      + *
    • {@link DataBuffer#TYPE_BYTE};
    • + *
    • {@link DataBuffer#TYPE_USHORT};
    • + *
    • {@link DataBuffer#TYPE_INT};
    • + *
    + * + * @return The transfer type. + */ + public int getTransferType() + { + if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_BYTE)) + return DataBuffer.TYPE_BYTE; + else if (numberOfBits <= DataBuffer.getDataTypeSize(DataBuffer.TYPE_USHORT)) + return DataBuffer.TYPE_USHORT; + return DataBuffer.TYPE_INT; + } + + /** + * Normally this method returns a sample model for accessing a subset of + * bands of image data, but since MultiPixelPackedSampleModel + * only supports a single band, this overridden implementation just returns + * a new instance of MultiPixelPackedSampleModel, with the same + * attributes as this instance. + * + * @param bands the bands to include in the subset (this is ignored, except + * that if it is non-null a check is made to ensure that the + * array length is equal to 1). + * + * @throws RasterFormatException if bands is not + * null and bands.length != 1. + */ + public SampleModel createSubsetSampleModel(int[] bands) + { + if (bands != null && bands.length != 1) + throw new RasterFormatException("MultiPixelPackedSampleModel only" + + " supports one band"); + return new MultiPixelPackedSampleModel(dataType, width, height, + numberOfBits, scanlineStride, dataBitOffset); + } + + /** + * Extract one pixel and return in an array of transfer type. + * + * Extracts the pixel at x, y from data and stores into the 0th index of the + * array obj, since there is only one band. If obj is null, a new array of + * getTransferType() is created. + * + * @param x The x-coordinate of the pixel rectangle to store in + * obj. + * @param y The y-coordinate of the pixel rectangle to store in + * obj. + * @param obj The primitive array to store the pixels into or null to force + * creation. + * @param data The DataBuffer that is the source of the pixel data. + * @return The primitive array containing the pixel data. + * @see java.awt.image.SampleModel#getDataElements(int, int, Object, + * DataBuffer) + */ + public Object getDataElements(int x, int y, Object obj, DataBuffer data) + { + int pixel = getSample(x, y, 0, data); + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + if (obj == null) + obj = new byte[1]; + ((byte[]) obj)[0] = (byte) pixel; + return obj; + case DataBuffer.TYPE_USHORT: + if (obj == null) + obj = new short[1]; + ((short[]) obj)[0] = (short) pixel; + return obj; + case DataBuffer.TYPE_INT: + if (obj == null) + obj = new int[1]; + ((int[]) obj)[0] = pixel; + return obj; + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + } + + /** + * Returns an array (of length 1) containing the sample for the pixel at + * (x, y) in the specified data buffer. If iArray is not + * null, it will be populated with the sample value and + * returned as the result of this function (this avoids allocating a new + * array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return An array containing the pixel sample value. + * + * @throws NullPointerException if data is null. + */ + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + if (iArray == null) + iArray = new int[1]; + iArray[0] = getSample(x, y, 0, data); + return iArray; + } + + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if data is null. + */ + public int getSample(int x, int y, int b, DataBuffer data) + { + int pos = + ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits; + int offset = getOffset(x, y); + int samples = data.getElem(offset); + return (samples & bitMasks[pos]) >>> bitOffsets[pos]; + } + + /** + * Set the pixel at x, y to the value in the first element of the primitive + * array obj. + * + * @param x The x-coordinate of the data elements in obj. + * @param y The y-coordinate of the data elements in obj. + * @param obj The primitive array containing the data elements to set. + * @param data The DataBuffer to store the data elements into. + */ + public void setDataElements(int x, int y, Object obj, DataBuffer data) + { + int transferType = getTransferType(); + try + { + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + { + byte[] in = (byte[]) obj; + setSample(x, y, 0, in[0] & 0xFF, data); + return; + } + case DataBuffer.TYPE_USHORT: + { + short[] in = (short[]) obj; + setSample(x, y, 0, in[0] & 0xFFFF, data); + return; + } + case DataBuffer.TYPE_INT: + { + int[] in = (int[]) obj; + setSample(x, y, 0, in[0], data); + return; + } + default: + throw new ClassCastException("Unsupported data type"); + } + } + catch (ArrayIndexOutOfBoundsException aioobe) + { + String msg = "While writing data elements" + + ", x=" + x + ", y=" + y + + ", width=" + width + ", height=" + height + + ", scanlineStride=" + scanlineStride + + ", offset=" + getOffset(x, y) + + ", data.getSize()=" + data.getSize() + + ", data.getOffset()=" + data.getOffset() + + ": " + aioobe; + throw new ArrayIndexOutOfBoundsException(msg); + } + } + + /** + * Sets the sample value for the pixel at (x, y) in the specified data + * buffer to the specified value. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample value (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + * + * @see #setSample(int, int, int, int, DataBuffer) + */ + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + setSample(x, y, 0, iArray[0], data); + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if data is null. + */ + public void setSample(int x, int y, int b, int s, DataBuffer data) + { + int bitpos = + ((dataBitOffset + x * numberOfBits) % elemBits) / numberOfBits; + int offset = getOffset(x, y); + + s = s << bitOffsets[bitpos]; + s = s & bitMasks[bitpos]; + + int sample = data.getElem(offset); + sample |= s; + data.setElem(offset, sample); + } + + /** + * Tests this sample model for equality with an arbitrary object. This + * method returns true if and only if: + *
      + *
    • obj is not null; + *
    • obj is an instance of + * MultiPixelPackedSampleModel; + *
    • both models have the same: + *
        + *
      • dataType; + *
      • width; + *
      • height; + *
      • numberOfBits; + *
      • scanlineStride; + *
      • dataBitOffsets. + *
      + *
    • + *
    + * + * @param obj the object (null permitted) + * + * @return true if this model is equal to obj, and + * false otherwise. + */ + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (! (obj instanceof MultiPixelPackedSampleModel)) + return false; + MultiPixelPackedSampleModel that = (MultiPixelPackedSampleModel) obj; + if (this.dataType != that.dataType) + return false; + if (this.width != that.width) + return false; + if (this.height != that.height) + return false; + if (this.numberOfBits != that.numberOfBits) + return false; + if (this.scanlineStride != that.scanlineStride) + return false; + if (this.dataBitOffset != that.dataBitOffset) + return false; + return true; + } + + /** + * Returns a hash code for this MultiPixelPackedSampleModel. + * + * @return A hash code. + */ + public int hashCode() + { + // this hash code won't match Sun's, but that shouldn't matter... + int result = 193; + result = 37 * result + dataType; + result = 37 * result + width; + result = 37 * result + height; + result = 37 * result + numberOfBits; + result = 37 * result + scanlineStride; + result = 37 * result + dataBitOffset; + return result; + } + + /** + * Creates a String with some information about this SampleModel. + * @return A String describing this SampleModel. + * @see java.lang.Object#toString() + */ + public String toString() + { + CPStringBuilder result = new CPStringBuilder(); + result.append(getClass().getName()); + result.append("["); + result.append("scanlineStride=").append(scanlineStride); + for(int i=0; i < bitMasks.length; i+=1) + { + result.append(", mask[").append(i).append("]=0x").append(Integer.toHexString(bitMasks[i])); + } + + result.append("]"); + return result.toString(); + } +} diff --git a/libjava/classpath/java/awt/image/PackedColorModel.java b/libjava/classpath/java/awt/image/PackedColorModel.java new file mode 100644 index 000000000..a89d73b8c --- /dev/null +++ b/libjava/classpath/java/awt/image/PackedColorModel.java @@ -0,0 +1,188 @@ +/* Copyright (C) 2000, 2002, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.awt.BitMaskExtent; + +import java.awt.Point; +import java.awt.color.ColorSpace; + +/** + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public abstract class PackedColorModel extends ColorModel +{ + private int masks[]; + + /* Package accessibility, the DirectColorModel needs this array */ + int shifts[]; + + public PackedColorModel(ColorSpace cspace, int pixelBits, + int[] colorMaskArray, int alphaMask, + boolean isAlphaPremultiplied, + int transparency, + int transferType) + { + super(pixelBits, calcBitsPerComponent(colorMaskArray, alphaMask), + cspace, (alphaMask != 0), isAlphaPremultiplied, transparency, + transferType); + initMasks(colorMaskArray, alphaMask); + if ((pixelBits<1) || (pixelBits>32)) { + throw new IllegalArgumentException("pixels per bits must be " + + "in the range [1, 32]"); + } + } + + private static int[] calcBitsPerComponent(int[] colorMaskArray, + int alphaMask) + { + int numComponents = colorMaskArray.length; + if (alphaMask != 0) numComponents++; + + int[] bitsPerComponent = new int[numComponents]; + + BitMaskExtent extent = new BitMaskExtent(); + for (int b=0; b props; + + int int_pixel_buffer[]; + boolean ints_delivered = false; + byte byte_pixel_buffer[]; + boolean bytes_delivered = false; + + ImageProducer ip; + int observerStatus; + int consumerStatus; + + private Thread grabberThread; + boolean grabbing = false; + + /** + * Construct a PixelGrabber that will retrieve RGB data from a given + * Image. + * + * The RGB data will be retrieved from a rectangular region + * (x, y, w, h) within the image. The data will be + * stored in the provided pix array, which must have + * been initialized to a size of at least w * h. The + * data for a pixel (m, n) in the grab rectangle will be stored at + * pix[(n - y) * scansize + (m - x) + off]. + * + * @param img the Image from which to grab pixels + * @param x the x coordinate, relative to img's + * top-left corner, of the grab rectangle's top-left pixel + * @param y the y coordinate, relative to img's + * top-left corner, of the grab rectangle's top-left pixel + * @param w the width of the grab rectangle, in pixels + * @param h the height of the grab rectangle, in pixels + * @param pix the array in which to store grabbed RGB pixel data + * @param off the offset into the pix array at which to + * start storing RGB data + * @param scansize a set of scansize consecutive + * elements in the pix array represents one row of + * pixels in the grab rectangle + */ + public PixelGrabber(Image img, int x, int y, int w, int h, + int pix[], int off, int scansize) + { + this (img.getSource(), x, y, w, h, pix, off, scansize); + } + + /** + * Construct a PixelGrabber that will retrieve RGB data from a given + * ImageProducer. + * + * The RGB data will be retrieved from a rectangular region + * (x, y, w, h) within the image produced by + * ip. The data will be stored in the provided + * pix array, which must have been initialized to a + * size of at least w * h. The data for a pixel (m, n) + * in the grab rectangle will be stored at + * pix[(n - y) * scansize + (m - x) + off]. + * + * @param ip the ImageProducer from which to grab pixels. This can + * be null. + * @param x the x coordinate of the grab rectangle's top-left pixel, + * specified relative to the top-left corner of the image produced + * by ip + * @param y the y coordinate of the grab rectangle's top-left pixel, + * specified relative to the top-left corner of the image produced + * by ip + * @param w the width of the grab rectangle, in pixels + * @param h the height of the grab rectangle, in pixels + * @param pix the array in which to store grabbed RGB pixel data + * @param off the offset into the pix array at which to + * start storing RGB data + * @param scansize a set of scansize consecutive + * elements in the pix array represents one row of + * pixels in the grab rectangle + */ + public PixelGrabber(ImageProducer ip, int x, int y, int w, int h, + int pix[], int off, int scansize) + { + this.ip = ip; + this.x = x; + this.y = y; + this.width = w; + this.height = h; + this.offset = off; + this.scansize = scansize; + + int_pixel_buffer = pix; + // Initialize the byte array in case ip sends us byte-formatted + // pixel data. + byte_pixel_buffer = new byte[pix.length * 4]; + } + + /** + * Construct a PixelGrabber that will retrieve data from a given + * Image. + * + * The RGB data will be retrieved from a rectangular region + * (x, y, w, h) within the image. The data will be + * stored in an internal array which can be accessed by calling + * getPixels. The data for a pixel (m, n) in the grab + * rectangle will be stored in the returned array at index + * (n - y) * scansize + (m - x) + off. + * If forceRGB is false, then the returned data will be not be + * converted to RGB from its format in img. + * + * If w is negative, the width of the grab region will + * be from x to the right edge of the image. Likewise, if + * h is negative, the height of the grab region will be + * from y to the bottom edge of the image. + * + * @param img the Image from which to grab pixels + * @param x the x coordinate, relative to img's + * top-left corner, of the grab rectangle's top-left pixel + * @param y the y coordinate, relative to img's + * top-left corner, of the grab rectangle's top-left pixel + * @param w the width of the grab rectangle, in pixels + * @param h the height of the grab rectangle, in pixels + * @param forceRGB true to force conversion of the rectangular + * region's pixel data to RGB + */ + public PixelGrabber(Image img, + int x, int y, + int w, int h, + boolean forceRGB) + { + this.ip = img.getSource(); + + if (this.ip == null) + throw new NullPointerException("The ImageProducer must not be null."); + + this.x = x; + this.y = y; + width = w; + height = h; + // If width or height is negative, postpone pixel buffer + // initialization until setDimensions is called back by ip. + if (width >= 0 && height >= 0) + { + int_pixel_buffer = new int[width * height]; + byte_pixel_buffer = new byte[width * height]; + } + this.forceRGB = forceRGB; + } + + /** + * Start grabbing pixels. + * + * Spawns an image production thread that calls back to this + * PixelGrabber's ImageConsumer methods. + */ + public synchronized void startGrabbing() + { + // Make sure we're not already grabbing. + if (grabbing == false) + { + grabbing = true; + grabberThread = new Thread () + { + public void run () + { + try + { + ip.startProduction (PixelGrabber.this); + } + catch (Exception ex) + { + imageComplete(ImageConsumer.IMAGEABORTED); + } + } + }; + grabberThread.start (); + } + } + + /** + * Abort pixel grabbing. + */ + public synchronized void abortGrabbing() + { + if (grabbing) + { + // Interrupt the grabbing thread. + Thread moribund = grabberThread; + grabberThread = null; + moribund.interrupt(); + + imageComplete (ImageConsumer.IMAGEABORTED); + } + } + + /** + * Have our Image or ImageProducer start sending us pixels via our + * ImageConsumer methods and wait for all pixels in the grab + * rectangle to be delivered. + * + * @return true if successful, false on abort or error + * + * @throws InterruptedException if interrupted by another thread. + */ + public synchronized boolean grabPixels() throws InterruptedException + { + return grabPixels(0); + } + + /** + * grabPixels's behavior depends on the value of ms. + * + * If ms < 0, return true if all pixels from the source image have + * been delivered, false otherwise. Do not wait. + * + * If ms >= 0 then we request that our Image or ImageProducer start + * delivering pixels to us via our ImageConsumer methods. + * + * If ms > 0, wait at most ms milliseconds for + * delivery of all pixels within the grab rectangle. + * + * If ms == 0, wait until all pixels have been delivered. + * + * @return true if all pixels from the source image have been + * delivered, false otherwise + * + * @throws InterruptedException if this thread is interrupted while + * we are waiting for pixels to be delivered + */ + public synchronized boolean grabPixels(long ms) throws InterruptedException + { + if (ms < 0) + return ((observerStatus & (ImageObserver.FRAMEBITS + | ImageObserver.ALLBITS)) != 0); + + // Spawn a new ImageProducer thread to send us the image data via + // our ImageConsumer methods. + startGrabbing(); + + if (ms > 0) + { + long stop_time = System.currentTimeMillis() + ms; + long time_remaining; + while (grabbing) + { + time_remaining = stop_time - System.currentTimeMillis(); + if (time_remaining <= 0) + break; + wait (time_remaining); + } + abortGrabbing (); + } + else + wait (); + + // If consumerStatus is non-zero then the image is done loading or + // an error has occurred. + if (consumerStatus != 0) + return setObserverStatus (); + + return ((observerStatus & (ImageObserver.FRAMEBITS + | ImageObserver.ALLBITS)) != 0); + } + + // Set observer status flags based on the current consumer status + // flags. Return true if the consumer flags indicate that the + // image was loaded successfully, or false otherwise. + private synchronized boolean setObserverStatus () + { + boolean retval = false; + + if ((consumerStatus & IMAGEERROR) != 0) + observerStatus |= ImageObserver.ERROR; + + if ((consumerStatus & IMAGEABORTED) != 0) + observerStatus |= ImageObserver.ABORT; + + if ((consumerStatus & STATICIMAGEDONE) != 0) + { + observerStatus |= ImageObserver.ALLBITS; + retval = true; + } + + if ((consumerStatus & SINGLEFRAMEDONE) != 0) + { + observerStatus |= ImageObserver.FRAMEBITS; + retval = true; + } + + return retval; + } + + /** + * @return the status of the pixel grabbing thread, represented by a + * bitwise OR of ImageObserver flags + */ + public synchronized int getStatus() + { + return observerStatus; + } + + /** + * @return the width of the grab rectangle in pixels, or a negative + * number if the ImageProducer has not yet called our setDimensions + * method + */ + public synchronized int getWidth() + { + return width; + } + + /** + * @return the height of the grab rectangle in pixels, or a negative + * number if the ImageProducer has not yet called our setDimensions + * method + */ + public synchronized int getHeight() + { + return height; + } + + /** + * @return a byte array of pixel data if ImageProducer delivered + * pixel data using the byte[] variant of setPixels, or an int array + * otherwise + */ + public synchronized Object getPixels() + { + if (ints_delivered) + return int_pixel_buffer; + else if (bytes_delivered) + return byte_pixel_buffer; + else + return null; + } + + /** + * @return the ColorModel currently being used for the majority of + * pixel data conversions + */ + public synchronized ColorModel getColorModel() + { + return model; + } + + /** + * Our ImageProducer calls this method to indicate the + * size of the image being produced. + * + * setDimensions is an ImageConsumer method. None of PixelGrabber's + * ImageConsumer methods should be called by code that instantiates + * a PixelGrabber. They are only made public so they can be called + * by the PixelGrabber's ImageProducer. + * + * @param width the width of the image + * @param height the height of the image + */ + public synchronized void setDimensions(int width, int height) + { + // Our width wasn't set when we were constructed. Set our width + // so that the grab region includes all pixels from x to the right + // edge of the source image. + if (this.width < 0) + this.width = width - x; + + // Our height wasn't set when we were constructed. Set our height + // so that the grab region includes all pixels from y to the + // bottom edge of the source image. + if (this.height < 0) + this.height = height - y; + + if (scansize < 0) + scansize = this.width; + + if (int_pixel_buffer == null) + int_pixel_buffer = new int[this.width * this.height]; + + if (byte_pixel_buffer == null) + byte_pixel_buffer = new byte[this.width * this.height]; + } + + /** + * Our ImageProducer may call this method to send us a + * list of its image's properties. + * + * setProperties is an ImageConsumer method. None of PixelGrabber's + * ImageConsumer methods should be called by code that instantiates + * a PixelGrabber. They are only made public so they can be called + * by the PixelGrabber's ImageProducer. + * + * @param props a list of properties associated with the image being + * produced + */ + public synchronized void setProperties(Hashtable props) + { + this.props = props; + } + + /** + * Our ImageProducer will call setColorModel to + * indicate the model used by the majority of calls to + * setPixels. Each call to setPixels + * could however indicate a different ColorModel. + * + * setColorModel is an ImageConsumer method. None of PixelGrabber's + * ImageConsumer methods should be called by code that instantiates + * a PixelGrabber. They are only made public so they can be called + * by the PixelGrabber's ImageProducer. + * + * @param model the color model to be used most often by setPixels + * + * @see ColorModel + */ + public synchronized void setColorModel(ColorModel model) + { + this.model = model; + } + + /** + * Our ImageProducer may call this method with a + * bit mask of hints from any of RANDOMPIXELORDER, + * TOPDOWNLEFTRIGHT, COMPLETESCANLINES, + * SINGLEPASS, SINGLEFRAME. + * + * setHints is an ImageConsumer method. None of PixelGrabber's + * ImageConsumer methods should be called by code that instantiates + * a PixelGrabber. They are only made public so they can be called + * by the PixelGrabber's ImageProducer. + * + * @param flags a bit mask of hints + */ + public synchronized void setHints(int flags) + { + hints = flags; + } + + /** + * Our ImageProducer calls setPixels to deliver a subset of its + * pixels. + * + * Each element of the pixels array represents one pixel. The + * pixel data is formatted according to the color model model. + * The x and y parameters are the coordinates of the rectangular + * region of pixels being delivered to this ImageConsumer, + * specified relative to the top left corner of the image being + * produced. Likewise, w and h are the pixel region's dimensions. + * + * @param x x coordinate of pixel block + * @param y y coordinate of pixel block + * @param w width of pixel block + * @param h height of pixel block + * @param model color model used to interpret pixel data + * @param pixels pixel block data + * @param offset offset into pixels array + * @param scansize width of one row in the pixel block + */ + public synchronized void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, + int offset, int scansize) + { + ColorModel currentModel; + if (model != null) + currentModel = model; + else + currentModel = this.model; + + for(int yp = y; yp < (y + h); yp++) + { + for(int xp = x; xp < (x + w); xp++) + { + // Check if the coordinates (xp, yp) are within the + // pixel block that we are grabbing. + if(xp >= this.x + && yp >= this.y + && xp < (this.x + this.width) + && yp < (this.y + this.height)) + { + int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; + int p = (yp - y) * scansize + (xp - x) + offset; + if (forceRGB) + { + ints_delivered = true; + + int_pixel_buffer[i] = currentModel.getRGB (pixels[p] & 0xFF); + } + else + { + bytes_delivered = true; + + byte_pixel_buffer[i] = pixels[p]; + } + } + } + } + } + + /** + * Our ImageProducer calls setPixels to deliver a subset of its + * pixels. + * + * Each element of the pixels array represents one pixel. The + * pixel data is formatted according to the color model model. + * The x and y parameters are the coordinates of the rectangular + * region of pixels being delivered to this ImageConsumer, + * specified relative to the top left corner of the image being + * produced. Likewise, w and h are the pixel region's dimensions. + * + * @param x x coordinate of pixel block + * @param y y coordinate of pixel block + * @param w width of pixel block + * @param h height of pixel block + * @param model color model used to interpret pixel data + * @param pixels pixel block data + * @param offset offset into pixels array + * @param scansize width of one row in the pixel block + */ + public synchronized void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, + int offset, int scansize) + { + ColorModel currentModel; + if (model != null) + currentModel = model; + else + currentModel = this.model; + + ints_delivered = true; + + for(int yp = y; yp < (y + h); yp++) + { + for(int xp = x; xp < (x + w); xp++) + { + // Check if the coordinates (xp, yp) are within the + // pixel block that we are grabbing. + if(xp >= this.x + && yp >= this.y + && xp < (this.x + this.width) + && yp < (this.y + this.height)) + { + int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset; + int p = (yp - y) * scansize + (xp - x) + offset; + if (forceRGB) + int_pixel_buffer[i] = currentModel.getRGB (pixels[p]); + else + int_pixel_buffer[i] = pixels[p]; + } + } + } + } + + /** + * Our ImageProducer calls this method to inform us + * that a single frame or the entire image is complete. The method + * is also used to inform us of an error in loading or producing the + * image. + * + * @param status the status of image production, represented by a + * bitwise OR of ImageConsumer flags + */ + public synchronized void imageComplete(int status) + { + consumerStatus = status; + setObserverStatus (); + grabbing = false; + if (ip != null) + ip.removeConsumer (this); + + notifyAll (); + } + + /** + * @return the return value of getStatus + * + * @specnote The newer getStatus should be used in place of status. + */ + public synchronized int status() + { + return getStatus(); + } +} diff --git a/libjava/classpath/java/awt/image/PixelInterleavedSampleModel.java b/libjava/classpath/java/awt/image/PixelInterleavedSampleModel.java new file mode 100644 index 000000000..13134b413 --- /dev/null +++ b/libjava/classpath/java/awt/image/PixelInterleavedSampleModel.java @@ -0,0 +1,123 @@ +/* PixelInterleavedSampleModel.java + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + + +/** + * A SampleModel that uses exactly one element of the + * raster’s {@link DataBuffer} per pixel, holds all bands in a + * single bank, and stores band data in pixel-interleaved manner. + * + * @since 1.2 + * + * @author Sascha Brawer (brawer@dandelis.ch) + */ +public class PixelInterleavedSampleModel + extends ComponentSampleModel +{ + public PixelInterleavedSampleModel(int dataType, int width, int height, + int pixelStride, int scanlineStride, + int[] bandOffsets) + { + super(dataType, width, height, pixelStride, scanlineStride, + bandOffsets); + } + + + /** + * Creates a new SampleModel that is like this one, but + * uses the specified width and height. + * + * @param width the number of pixels in the horizontal direction. + * + * @param height the number of pixels in the vertical direction. + */ + public SampleModel createCompatibleSampleModel(int width, int height) + { + // Find minimum band offset. + int minBandoff = bandOffsets[0]; + int numBands = bandOffsets.length; + for (int i = 1; i < numBands; i++) + { + if (bandOffsets[i] < minBandoff) + { + minBandoff = bandOffsets[i]; + } + } + // Adjust band offsets so that minimum offset is at 0. + int[] bandOff; + if (minBandoff > 0) + { + bandOff = new int[numBands]; + for (int i = 0; i < numBands; i++) + { + bandOff[i] = bandOffsets[i] - minBandoff; + } + } + else + { + bandOff = bandOffsets; + } + // Adjust scanline stride for new width. + return new PixelInterleavedSampleModel(dataType, width, height, + pixelStride, pixelStride * width, + bandOff); + } + + + /** + * Creates a new SampleModel that is like this one, but + * uses only a subset of its bands. + * + * @param bands an array whose elements indicate which bands shall + * be part of the subset. For example, [0, 2, 3] would + * create a SampleModel containing bands #0, #2 and #3. + */ + public SampleModel createSubsetSampleModel(int[] bands) + { + int[] subOffsets; + + subOffsets = new int[bands.length]; + for (int i = 0; i < bands.length; i++) + subOffsets[i] = bandOffsets[bands[i]]; + + return new PixelInterleavedSampleModel(dataType, width, height, + pixelStride, scanlineStride, + subOffsets); + } +} diff --git a/libjava/classpath/java/awt/image/RGBImageFilter.java b/libjava/classpath/java/awt/image/RGBImageFilter.java new file mode 100644 index 000000000..1354c3a80 --- /dev/null +++ b/libjava/classpath/java/awt/image/RGBImageFilter.java @@ -0,0 +1,265 @@ +/* RGBImageFilter.java -- Java class for filtering Pixels by RGB values + Copyright (C) 1999, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * A filter designed to filter images in the default RGBColorModel regardless of + * the ImageProducer's ColorModel. + * + * @author Mark Benvenuto (mcb54@columbia.edu) + */ +public abstract class RGBImageFilter extends ImageFilter +{ + protected ColorModel origmodel; + + protected ColorModel newmodel; + + /** + * Specifies whether to apply the filter to the index entries of the + * IndexColorModel. Subclasses should set this to true if the filter + * does not depend on the pixel's coordinate. + */ + protected boolean canFilterIndexColorModel = false; + + /** + * Construct new RGBImageFilter. + */ + public RGBImageFilter() + { + } + + /** + * Sets the ColorModel used to filter with. If the specified ColorModel is + * IndexColorModel and canFilterIndexColorModel is true, we subsitute the + * ColorModel for a filtered one here and in setPixels whenever the original + * one appears. Otherwise overrides the default ColorModel of ImageProducer + * and specifies the default RGBColorModel + * + * @param model the color model to be used most often by setPixels + * + * @see ColorModel + */ + public void setColorModel(ColorModel model) + { + if ((model instanceof IndexColorModel) && canFilterIndexColorModel) + { + ColorModel newCM = filterIndexColorModel((IndexColorModel) model); + substituteColorModel(model, newCM); + consumer.setColorModel(newmodel); + } + else + { + consumer.setColorModel(ColorModel.getRGBdefault()); + } + } + + /** + * Registers a new ColorModel to subsitute for the old ColorModel when + * setPixels encounters the a pixel with the old ColorModel. The pixel + * remains unchanged except for a new ColorModel. + * + * @param oldcm the old ColorModel + * @param newcm the new ColorModel + */ + public void substituteColorModel(ColorModel oldcm, ColorModel newcm) + { + origmodel = oldcm; + newmodel = newcm; + } + + /** + * Filters an IndexColorModel through the filterRGB function. Uses + * coordinates of -1 to indicate its filtering an index and not a pixel. + * + * @param icm an IndexColorModel to filter + */ + public IndexColorModel filterIndexColorModel(IndexColorModel icm) + { + int len = icm.getMapSize(); + byte[] reds = new byte[len]; + byte[] greens = new byte[len]; + byte[] blues = new byte[len]; + byte[] alphas = new byte[len]; + + icm.getAlphas( alphas ); + icm.getReds( reds ); + icm.getGreens( greens ); + icm.getBlues( blues ); + + int transparent = icm.getTransparentPixel(); + boolean needAlpha = false; + for( int i = 0; i < len; i++ ) + { + int rgb = filterRGB(-1, -1, icm.getRGB(i)); + alphas[i] = (byte) (rgb >> 24); + if (alphas[i] != ((byte) 0xff) && i != transparent) + needAlpha = true; + reds[i] = (byte) (rgb >> 16); + greens[i] = (byte) (rgb >> 8); + blues[i] = (byte) (rgb); + } + IndexColorModel newIcm; + if (needAlpha) + newIcm = new IndexColorModel(icm.getPixelSize(), len, reds, greens, + blues, alphas); + else + newIcm = new IndexColorModel(icm.getPixelSize(), len, reds, greens, + blues, transparent); + return newIcm; + } + + /** + * This functions filters a set of RGB pixels through filterRGB. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the + * pixels array + * @param scansize the width to use in extracting pixels from the + * pixels array + */ + public void filterRGBPixels(int x, int y, int w, int h, int[] pixels, + int offset, int scansize) + { + int index = offset; + for (int yp = 0; yp < h; yp++) + { + for (int xp = 0; xp < w; xp++) + { + pixels[index] = filterRGB(xp + x, yp + y, pixels[index]); + index++; + } + index += scansize - w; + } + consumer.setPixels(x, y, w, h, ColorModel.getRGBdefault(), pixels, offset, + scansize); + } + + /** + * If the ColorModel is the same ColorModel which as already converted + * then it converts it the converted ColorModel. Otherwise it passes the + * array of pixels through filterRGBpixels. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels + * array + * @param scansize the width to use in extracting pixels from the + * pixels array + */ + public void setPixels(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int offset, int scansize) + { + if (model == origmodel) + { + consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize); + } + else + { + int[] filtered = new int[w]; + int index = offset; + for (int yp = 0; yp < h; yp++) + { + for (int xp = 0; xp < w; xp++) + { + filtered[xp] = model.getRGB((pixels[index] & 0xff)); + index++; + } + index += scansize - w; + filterRGBPixels(x, y + yp, w, 1, filtered, 0, w); + } + } + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an int at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels + * array + * @param scansize the width to use in extracting pixels from the + * pixels array + */ + public void setPixels(int x, int y, int w, int h, ColorModel model, + int[] pixels, int offset, int scansize) + { + if (model == origmodel) + { + consumer.setPixels(x, y, w, h, newmodel, pixels, offset, scansize); + } + else + { + int[] filtered = new int[w]; + int index = offset; + for (int yp = 0; yp < h; yp++) + { + for (int xp = 0; xp < w; xp++) + { + filtered[xp] = model.getRGB((pixels[index])); + index++; + } + index += scansize - w; + filterRGBPixels(x, y + yp, w, 1, filtered, 0, w); + } + } + } + + /** + * Filters a single pixel from the default ColorModel. + * + * @param x x-coordinate + * @param y y-coordinate + * @param rgb color + */ + public abstract int filterRGB(int x, int y, int rgb); +} diff --git a/libjava/classpath/java/awt/image/Raster.java b/libjava/classpath/java/awt/image/Raster.java new file mode 100644 index 000000000..6d99c3683 --- /dev/null +++ b/libjava/classpath/java/awt/image/Raster.java @@ -0,0 +1,973 @@ +/* Copyright (C) 2000, 2002, 2003, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import gnu.java.lang.CPStringBuilder; + +import java.awt.Point; +import java.awt.Rectangle; + +/** + * A rectangular collection of pixels composed from a {@link DataBuffer} which + * stores the pixel values, and a {@link SampleModel} which is used to retrieve + * the pixel values. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class Raster +{ + /** The sample model used to access the pixel values. */ + protected SampleModel sampleModel; + + /** The data buffer used to store the pixel values. */ + protected DataBuffer dataBuffer; + + /** The x-coordinate of the top left corner of the raster. */ + protected int minX; + + /** The y-coordinate of the top left corner of the raster. */ + protected int minY; + + /** The width of the raster. */ + protected int width; + + /** The height of the raster. */ + protected int height; + + protected int sampleModelTranslateX; + + protected int sampleModelTranslateY; + + /** The number of bands. */ + protected int numBands; + + protected int numDataElements; + + /** The raster's parent. */ + protected Raster parent; + + /** + * Creates a new raster. + * + * @param sampleModel the sample model. + * @param origin the origin. + */ + protected Raster(SampleModel sampleModel, Point origin) + { + this(sampleModel, sampleModel.createDataBuffer(), origin); + } + + /** + * Creates a new raster. + * + * @param sampleModel the sample model. + * @param dataBuffer the data buffer. + * @param origin the origin. + */ + protected Raster(SampleModel sampleModel, DataBuffer dataBuffer, + Point origin) + { + this(sampleModel, dataBuffer, new Rectangle(origin.x, origin.y, + sampleModel.getWidth(), sampleModel.getHeight()), origin, null); + } + + /** + * Creates a new raster. + * + * @param sampleModel the sample model. + * @param dataBuffer the data buffer. + * @param aRegion the raster's bounds. + * @param sampleModelTranslate the translation (null permitted). + * @param parent the raster's parent. + */ + protected Raster(SampleModel sampleModel, DataBuffer dataBuffer, + Rectangle aRegion, Point sampleModelTranslate, Raster parent) + { + this.sampleModel = sampleModel; + this.dataBuffer = dataBuffer; + this.minX = aRegion.x; + this.minY = aRegion.y; + this.width = aRegion.width; + this.height = aRegion.height; + + // If sampleModelTranslate is null, use (0,0). Methods such as + // Raster.createRaster are specified to allow for a null argument. + if (sampleModelTranslate != null) + { + this.sampleModelTranslateX = sampleModelTranslate.x; + this.sampleModelTranslateY = sampleModelTranslate.y; + } + + this.numBands = sampleModel.getNumBands(); + this.numDataElements = sampleModel.getNumDataElements(); + this.parent = parent; + } + + /** + * Creates an interleaved raster using the specified data type. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param bands the number of bands. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createInterleavedRaster(int dataType, + int w, int h, int bands, Point location) + { + int[] bandOffsets = new int[bands]; + // TODO: Maybe not generate this every time. + for (int b = 0; b < bands; b++) + bandOffsets[b] = b; + + int scanlineStride = bands * w; + return createInterleavedRaster(dataType, w, h, scanlineStride, bands, + bandOffsets, location); + } + + /** + * Creates an interleaved raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param pixelStride the number of elements from a sample in one pixel to + * the corresponding sample in the next pixel. + * @param bandOffsets the band offsets. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createInterleavedRaster(int dataType, + int w, int h, int scanlineStride, int pixelStride, int[] bandOffsets, + Point location) + { + SampleModel sm = new ComponentSampleModel(dataType, w, h, pixelStride, + scanlineStride, bandOffsets); + return createWritableRaster(sm, location); + } + + /** + * Creates a new banded raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param bands the number of bands. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createBandedRaster(int dataType, int w, int h, + int bands, Point location) + { + SampleModel sm = new BandedSampleModel(dataType, w, h, bands); + return createWritableRaster(sm, location); + } + + /** + * Creates a new banded raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param bankIndices the index for each bank. + * @param bandOffsets the offset for each band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createBandedRaster(int dataType, int w, int h, + int scanlineStride, int[] bankIndices, int[] bandOffsets, Point location) + { + SampleModel sm = new BandedSampleModel(dataType, w, h, scanlineStride, + bankIndices, bandOffsets); + return createWritableRaster(sm, location); + } + + /** + * Creates a new packed raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param bandMasks the bit mask for each band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createPackedRaster(int dataType, int w, int h, + int[] bandMasks, Point location) + { + SampleModel sm = new SinglePixelPackedSampleModel(dataType, w, h, + bandMasks); + return createWritableRaster(sm, location); + } + + /** + * Creates a new raster. + * + * @param dataType the data type. + * @param w the width. + * @param h the height. + * @param bands the number of bands. + * @param bitsPerBand the number of bits per band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createPackedRaster(int dataType, + int w, int h, int bands, int bitsPerBand, Point location) + { + if (bands <= 0 || (bands * bitsPerBand > getTypeBits(dataType))) + throw new IllegalArgumentException(); + + SampleModel sm; + + if (bands == 1) + sm = new MultiPixelPackedSampleModel(dataType, w, h, bitsPerBand); + else + { + int[] bandMasks = new int[bands]; + int mask = 0x1; + for (int bits = bitsPerBand; --bits != 0;) + mask = (mask << 1) | 0x1; + for (int i = 0; i < bands; i++) + { + bandMasks[i] = mask; + mask <<= bitsPerBand; + } + + sm = new SinglePixelPackedSampleModel(dataType, w, h, bandMasks); + } + return createWritableRaster(sm, location); + } + + /** + * Creates a new interleaved raster. + * + * @param dataBuffer the data buffer. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param pixelStride the number of elements from a sample in one pixel to + * the corresponding sample in the next pixel. + * @param bandOffsets the offset for each band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createInterleavedRaster(DataBuffer dataBuffer, + int w, int h, int scanlineStride, int pixelStride, int[] bandOffsets, + Point location) + { + SampleModel sm = new ComponentSampleModel(dataBuffer.getDataType(), + w, h, pixelStride, scanlineStride, bandOffsets); + return createWritableRaster(sm, dataBuffer, location); + } + + /** + * Creates a new banded raster. + * + * @param dataBuffer the data buffer. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param bankIndices the index for each bank. + * @param bandOffsets the band offsets. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createBandedRaster(DataBuffer dataBuffer, + int w, int h, int scanlineStride, int[] bankIndices, int[] bandOffsets, + Point location) + { + SampleModel sm = new BandedSampleModel(dataBuffer.getDataType(), + w, h, scanlineStride, bankIndices, bandOffsets); + return createWritableRaster(sm, dataBuffer, location); + } + + /** + * Creates a new packed raster. + * + * @param dataBuffer the data buffer. + * @param w the width. + * @param h the height. + * @param scanlineStride the number of data elements from a sample on one + * row to the corresponding sample on the next row. + * @param bandMasks the bit mask for each band. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createPackedRaster(DataBuffer dataBuffer, + int w, int h, int scanlineStride, int[] bandMasks, Point location) + { + SampleModel sm = new SinglePixelPackedSampleModel(dataBuffer.getDataType(), + w, h, scanlineStride, bandMasks); + return createWritableRaster(sm, dataBuffer, location); + } + + /** + * Creates a new packed raster. + * + * @param dataBuffer the data buffer. + * @param w the width. + * @param h the height. + * @param bitsPerPixel the number of bits per pixel. + * @param location + * + * @return The new raster. + */ + public static WritableRaster createPackedRaster(DataBuffer dataBuffer, + int w, int h, int bitsPerPixel, Point location) + { + SampleModel sm = new MultiPixelPackedSampleModel(dataBuffer.getDataType(), + w, h, bitsPerPixel); + return createWritableRaster(sm, dataBuffer, location); + } + + /** + * Creates a new raster. + * + * @param sm the sample model. + * @param db the data buffer. + * @param location + * + * @return The new raster. + */ + public static Raster createRaster(SampleModel sm, DataBuffer db, + Point location) + { + return new Raster(sm, db, location); + } + + /** + * Creates a new writable raster. + * + * @param sm the sample model. + * @param location + * + * @return The new writable raster. + */ + public static WritableRaster createWritableRaster(SampleModel sm, + Point location) + { + return new WritableRaster(sm, location); + } + + /** + * Creates a new writable raster. + * + * @param sm the sample model. + * @param db the data buffer. + * @param location + * + * @return The new writable raster. + */ + public static WritableRaster createWritableRaster(SampleModel sm, + DataBuffer db, Point location) + { + return new WritableRaster(sm, db, location); + } + + /** + * Returns the raster's parent. + * + * @return The raster's parent. + */ + public Raster getParent() + { + return parent; + } + + /** + * Returns the x-translation. + * + * @return The x-translation. + */ + public final int getSampleModelTranslateX() + { + return sampleModelTranslateX; + } + + /** + * Returns the y-translation. + * + * @return The y-translation. + */ + public final int getSampleModelTranslateY() + { + return sampleModelTranslateY; + } + + /** + * Creates a new writable raster that is compatible with this raster. + * + * @return A new writable raster. + */ + public WritableRaster createCompatibleWritableRaster() + { + return new WritableRaster(getSampleModel(), new Point(minX, minY)); + } + + /** + * Creates a new writable raster that is compatible with this raster. + * + * @param w the width. + * @param h the height. + * + * @return A new writable raster. + */ + public WritableRaster createCompatibleWritableRaster(int w, int h) + { + return createCompatibleWritableRaster(minX, minY, w, h); + } + + /** + * Creates a new writable raster that is compatible with this raster, with + * the specified bounds. + * + * @param rect the raster bounds. + * + * @return A new writable raster. + */ + public WritableRaster createCompatibleWritableRaster(Rectangle rect) + { + return createCompatibleWritableRaster(rect.x, rect.y, + rect.width, rect.height); + } + + /** + * Creates a new writable raster that is compatible with this raster, with + * the specified bounds. + * + * @param x the x-coordinate of the top-left corner of the raster. + * @param y the y-coordinate of the top-left corner of the raster. + * @param w the raster width. + * @param h the raster height. + * + * @return A new writable raster. + */ + public WritableRaster createCompatibleWritableRaster(int x, int y, + int w, int h) + { + SampleModel sm = getSampleModel().createCompatibleSampleModel(w, h); + return new WritableRaster(sm, sm.createDataBuffer(), new Point(x, y)); + } + + public Raster createTranslatedChild(int childMinX, int childMinY) { + int tcx = sampleModelTranslateX - minX + childMinX; + int tcy = sampleModelTranslateY - minY + childMinY; + + return new Raster(sampleModel, dataBuffer, + new Rectangle(childMinX, childMinY, width, height), + new Point(tcx, tcy), this); + } + + public Raster createChild(int parentX, int parentY, int width, + int height, int childMinX, int childMinY, + int[] bandList) + { + if (parentX < minX || parentX + width > minX + this.width + || parentY < minY || parentY + height > minY + this.height) + throw new RasterFormatException("Child raster extends beyond parent"); + + SampleModel sm = (bandList == null) ? + sampleModel : + sampleModel.createSubsetSampleModel(bandList); + + /* + data origin + / + +------------------------- + |\. __ parent trans + | \`. + | \ `. parent origin + | \ `. / + | /\ +-------- - - + |trans\ /<\-- deltaTrans + |child +-+-\---- - - + | /|`| \__ parent [x, y] + |child | |`. \ + |origin| : `.\ + | | / `\ + | : / + + | child [x, y] + + parent_xy - parent_trans = child_xy - child_trans + + child_trans = parent_trans + child_xy - parent_xy + */ + + return new Raster(sm, dataBuffer, + new Rectangle(childMinX, childMinY, width, height), + new Point(sampleModelTranslateX + childMinX - parentX, + sampleModelTranslateY + childMinY - parentY), + this); + } + + /** + * Returns a new rectangle containing the bounds of this raster. + * + * @return A new rectangle containing the bounds of this raster. + */ + public Rectangle getBounds() + { + return new Rectangle(minX, minY, width, height); + } + + /** + * Returns the x-coordinate of the top left corner of the raster. + * + * @return The x-coordinate of the top left corner of the raster. + */ + public final int getMinX() + { + return minX; + } + + /** + * Returns the t-coordinate of the top left corner of the raster. + * + * @return The t-coordinate of the top left corner of the raster. + */ + public final int getMinY() + { + return minY; + } + + /** + * Returns the width of the raster. + * + * @return The width of the raster. + */ + public final int getWidth() + { + return width; + } + + /** + * Returns the height of the raster. + * + * @return The height of the raster. + */ + public final int getHeight() + { + return height; + } + + /** + * Returns the number of bands for this raster. + * + * @return The number of bands. + */ + public final int getNumBands() + { + return numBands; + } + + public final int getNumDataElements() + { + return numDataElements; + } + + /** + * Returns the transfer type for the raster (this is determined by the + * raster's sample model). + * + * @return The transfer type. + */ + public final int getTransferType() + { + return sampleModel.getTransferType(); + } + + /** + * Returns the data buffer that stores the pixel data for this raster. + * + * @return The data buffer. + */ + public DataBuffer getDataBuffer() + { + return dataBuffer; + } + + /** + * Returns the sample model that accesses the data buffer (to extract pixel + * data) for this raster. + * + * @return The sample model. + */ + public SampleModel getSampleModel() + { + return sampleModel; + } + + public Object getDataElements(int x, int y, Object outData) + { + return sampleModel.getDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, outData, dataBuffer); + } + + public Object getDataElements(int x, int y, int w, int h, Object outData) + { + return sampleModel.getDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, outData, dataBuffer); + } + + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * raster. If iArray is not null, it will be + * populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The pixel sample values. + */ + public int[] getPixel(int x, int y, int[] iArray) + { + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, iArray, dataBuffer); + } + + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * raster. If fArray is not null, it will be + * populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param fArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The pixel sample values. + */ + public float[] getPixel(int x, int y, float[] fArray) + { + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, fArray, dataBuffer); + } + + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * raster. If dArray is not null, it will be + * populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param dArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The pixel sample values. + */ + public double[] getPixel(int x, int y, double[] dArray) + { + return sampleModel.getPixel(x - sampleModelTranslateX, + y - sampleModelTranslateY, dArray, dataBuffer); + } + + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the raster. The array is ordered by pixels + * (that is, all the samples for the first pixel are grouped together, + * followed by all the samples for the second pixel, and so on). + * If iArray is not null, it will be populated + * with the sample values and returned as the result of this function (this + * avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The pixel sample values. + */ + public int[] getPixels(int x, int y, int w, int h, int[] iArray) + { + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, iArray, dataBuffer); + } + + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the raster. The array is ordered by pixels + * (that is, all the samples for the first pixel are grouped together, + * followed by all the samples for the second pixel, and so on). + * If fArray is not null, it will be populated + * with the sample values and returned as the result of this function (this + * avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param fArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The pixel sample values. + */ + public float[] getPixels(int x, int y, int w, int h, float[] fArray) + { + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, fArray, dataBuffer); + } + + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the raster. The array is ordered by pixels + * (that is, all the samples for the first pixel are grouped together, + * followed by all the samples for the second pixel, and so on). + * If dArray is not null, it will be populated + * with the sample values and returned as the result of this function (this + * avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param dArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The pixel sample values. + */ + public double[] getPixels(int x, int y, int w, int h, double[] dArray) + { + return sampleModel.getPixels(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, dArray, dataBuffer); + } + + /** + * Returns the sample value for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * + * @return The sample value. + */ + public int getSample(int x, int y, int b) + { + return sampleModel.getSample(x - sampleModelTranslateX, + y - sampleModelTranslateY, b, dataBuffer); + } + + /** + * Returns the sample value for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * + * @return The sample value. + * + * @see #getSample(int, int, int) + */ + public float getSampleFloat(int x, int y, int b) + { + return sampleModel.getSampleFloat(x - sampleModelTranslateX, + y - sampleModelTranslateY, b, dataBuffer); + } + + /** + * Returns the sample value for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * + * @return The sample value. + * + * @see #getSample(int, int, int) + */ + public double getSampleDouble(int x, int y, int b) + { + return sampleModel.getSampleDouble(x - sampleModelTranslateX, + y - sampleModelTranslateY, b, dataBuffer); + } + + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the raster. If + * iArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The sample values. + */ + public int[] getSamples(int x, int y, int w, int h, int b, + int[] iArray) + { + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, b, iArray, dataBuffer); + } + + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the raster. If + * fArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param fArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The sample values. + */ + public float[] getSamples(int x, int y, int w, int h, int b, float[] fArray) + { + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, b, fArray, dataBuffer); + } + + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the raster. If + * dArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param dArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * + * @return The sample values. + */ + public double[] getSamples(int x, int y, int w, int h, int b, + double[] dArray) + { + return sampleModel.getSamples(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, b, dArray, dataBuffer); + } + + /** + * Create a String representing the state of this Raster. + * + * @return A String representing the stat of this Raster. + */ + public String toString() + { + CPStringBuilder result = new CPStringBuilder(); + + result.append(getClass().getName()); + result.append("[("); + result.append(minX).append(",").append(minY).append("), "); + result.append(width).append(" x ").append(height).append(","); + result.append(sampleModel).append(","); + result.append(dataBuffer); + result.append("]"); + + return result.toString(); + } + + /** + * Returns the number of bits used to represent the specified data type. + * Valid types are: + *
      + *
    • {@link DataBuffer#TYPE_BYTE};
    • + *
    • {@link DataBuffer#TYPE_USHORT};
    • + *
    • {@link DataBuffer#TYPE_SHORT};
    • + *
    • {@link DataBuffer#TYPE_INT};
    • + *
    • {@link DataBuffer#TYPE_FLOAT};
    • + *
    • {@link DataBuffer#TYPE_DOUBLE};
    • + *
    + * This method returns 0 for invalid data types. + * + * @param dataType the data type. + * + * @return The number of bits used to represent the specified data type. + */ + private static int getTypeBits(int dataType) + { + switch (dataType) + { + case DataBuffer.TYPE_BYTE: + return 8; + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_SHORT: + return 16; + case DataBuffer.TYPE_INT: + case DataBuffer.TYPE_FLOAT: + return 32; + case DataBuffer.TYPE_DOUBLE: + return 64; + default: + return 0; + } + } +} diff --git a/libjava/classpath/java/awt/image/RasterFormatException.java b/libjava/classpath/java/awt/image/RasterFormatException.java new file mode 100644 index 000000000..582c2ae5a --- /dev/null +++ b/libjava/classpath/java/awt/image/RasterFormatException.java @@ -0,0 +1,65 @@ +/* RasterFormatException.java -- indicates invalid layout in Raster + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * This exception is thrown when there is invalid layout information in + * Raster + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Raster + * @status updated to 1.4 + */ +public class RasterFormatException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 96598996116164315L; + + /** + * Create a new instance with a descriptive error message. + * + * @param message the descriptive error message + */ + public RasterFormatException(String message) + { + super(message); + } +} // class RasterFormatException diff --git a/libjava/classpath/java/awt/image/RasterOp.java b/libjava/classpath/java/awt/image/RasterOp.java new file mode 100644 index 000000000..2b2c0731f --- /dev/null +++ b/libjava/classpath/java/awt/image/RasterOp.java @@ -0,0 +1,104 @@ +/* RasterOp.java -- + Copyright (C) 2000, 2002, 2004, 2005, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * An operation that is performed on one raster (the source) producing a new + * raster (the destination). + */ +public interface RasterOp +{ + /** + * Performs an operation on the source raster, returning the result in a + * writable raster. If dest is null, a new + * WritableRaster will be created by calling the + * {@link #createCompatibleDestRaster(Raster)} method. If dest + * is not null, the result is written to dest then + * returned (this avoids creating a new WritableRaster each + * time this method is called). + * + * @param src the source raster. + * @param dest the destination raster (null permitted). + * + * @return The filtered raster. + */ + WritableRaster filter(Raster src, WritableRaster dest); + + /** + * Returns the bounds of the destination raster on the basis of this + * RasterOp being applied to the specified source raster. + * + * @param src the source raster. + * + * @return The destination bounds. + */ + Rectangle2D getBounds2D(Raster src); + + /** + * Returns a raster that can be used by this RasterOp as the + * destination raster when operating on the specified source raster. + * + * @param src the source raster. + * + * @return A new writable raster that can be used as the destination raster. + */ + WritableRaster createCompatibleDestRaster(Raster src); + + /** + * Returns the point on the destination raster that corresponds to the given + * point on the source raster. + * + * @param srcPoint the source point. + * @param destPoint the destination point (null permitted). + * + * @return The destination point. + */ + Point2D getPoint2D(Point2D srcPoint, Point2D destPoint); + + /** + * Returns the rendering hints for this operation. + * + * @return The rendering hints. + */ + RenderingHints getRenderingHints(); +} diff --git a/libjava/classpath/java/awt/image/RenderedImage.java b/libjava/classpath/java/awt/image/RenderedImage.java new file mode 100644 index 000000000..fd18871a9 --- /dev/null +++ b/libjava/classpath/java/awt/image/RenderedImage.java @@ -0,0 +1,70 @@ +/* RenderedImage.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Rectangle; +import java.util.Vector; + +/** + * NEEDS DOCUMENTATION + */ +public interface RenderedImage +{ + Vector getSources(); + Object getProperty(String name); + String[] getPropertyNames(); + ColorModel getColorModel(); + SampleModel getSampleModel(); + int getWidth(); + int getHeight(); + int getMinX(); + int getMinY(); + int getNumXTiles(); + int getNumYTiles(); + int getMinTileX(); + int getMinTileY(); + int getTileWidth(); + int getTileHeight(); + int getTileGridXOffset(); + int getTileGridYOffset(); + Raster getTile(int x, int y); + Raster getData(); + Raster getData(Rectangle r); + WritableRaster copyData(WritableRaster raster); +} // interface RenderedImage diff --git a/libjava/classpath/java/awt/image/ReplicateScaleFilter.java b/libjava/classpath/java/awt/image/ReplicateScaleFilter.java new file mode 100644 index 000000000..94aad85dd --- /dev/null +++ b/libjava/classpath/java/awt/image/ReplicateScaleFilter.java @@ -0,0 +1,257 @@ +/* ReplicateScaleFilter.java -- Java class for filtering images + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.util.Hashtable; + +/** + * This filter should be used for fast scaling of images where the result + * does not need to ensure straight lines are still straight, etc. The + * exact method is not defined by Sun but some sort of fast Box filter should + * probably be correct. + *
    + * Currently this filter does nothing and needs to be implemented. + * + * @author C. Brian Jones (cbj@gnu.org) + */ +public class ReplicateScaleFilter extends ImageFilter +{ + public ReplicateScaleFilter(int width, int height) { + destHeight = height; + destWidth = width; + } + + /** + * The height of the destination image. + */ + protected int destHeight; + + /** + * The width of the destination image. + */ + protected int destWidth; + + /** + * The height of the source image. + */ + protected int srcHeight; + + /** + * The width of the source image. + */ + protected int srcWidth; + + /** + * + */ + protected int srcrows[]; + + /** + * + */ + protected int srccols[]; + + /** + * + */ + protected Object outpixbuf; + + /** + * An ImageProducer indicates the size of the image + * being produced using this method. A filter can override this + * method to intercept these calls from the producer in order to + * change either the width or the height before in turn calling + * the consumer's setDimensions method. + * + * @param width the width of the image + * @param height the height of the image + */ + public void setDimensions(int width, int height) + { + srcWidth = width; + srcHeight = height; + + /* If either destHeight or destWidth is < 0, the image should + maintain its original aspect ratio. When both are < 0, + just maintain the original width and height. */ + if (destWidth < 0 && destHeight < 0) + { + destWidth = width; + destHeight = height; + } + else if (destWidth < 0) + { + destWidth = width * destHeight / srcHeight; + } + else if (destHeight < 0) + { + destHeight = height * destWidth / srcWidth; + } + + if (consumer != null) + consumer.setDimensions(destWidth, destHeight); + } + + /** + * An ImageProducer can set a list of properties + * associated with this image by using this method. + * + * @param props the list of properties associated with this image + */ + public void setProperties(Hashtable props) + { + Hashtable prop2 = (Hashtable) props; + prop2.put("filters", "ReplicateScaleFilter"); + if (consumer != null) + consumer.setProperties(prop2); + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as a byte at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels array + * @param scansize the width to use in extracting pixels from the pixels array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, byte[] pixels, int offset, int scansize) + { + if (srcrows == null || srccols == null) + setupSources(); + int dx1 = (2 * x * destWidth + srcWidth - 1) / (2 * destWidth); + int dy1 = (2 * y * destHeight + srcHeight - 1) / (2 * destHeight); + byte[] pix; + if (outpixbuf != null && outpixbuf instanceof byte[]) + { + pix = (byte[]) outpixbuf; + } + else + { + pix = new byte[destWidth]; + outpixbuf = pix; + } + int sy, sx; + for (int yy = dy1; (sy = srcrows[yy]) < y + h; yy++) + { + int offs = offset + scansize * (sy - y); + int xx; + for (xx = dx1; (sx = srccols[xx]) < x + w; xx++) + { + pix[xx] = pixels[offs + sx - x]; + } + if (xx > dx1) + { + consumer.setPixels(dx1, yy, xx - dx1, 1, model, pix, dx1, + destWidth); + } + } + } + + /** + * This function delivers a rectangle of pixels where any + * pixel(m,n) is stored in the array as an int at + * index (n * scansize + m + offset). + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @param model the ColorModel used to translate the pixels + * @param pixels the array of pixel values + * @param offset the index of the first pixels in the pixels array + * @param scansize the width to use in extracting pixels from the pixels array + */ + public void setPixels(int x, int y, int w, int h, + ColorModel model, int[] pixels, int offset, int scansize) + { + if (srcrows == null || srccols == null) + setupSources(); + int dx1 = (2 * x * destWidth + srcWidth - 1) / (2 * destWidth); + int dy1 = (2 * y * destHeight + srcHeight - 1) / (2 * destHeight); + int[] pix; + if (outpixbuf != null && outpixbuf instanceof int[]) + { + pix = (int[]) outpixbuf; + } + else + { + pix = new int[destWidth]; + outpixbuf = pix; + } + int sy, sx; + for (int yy = dy1; (sy = srcrows[yy]) < y + h; yy++) + { + int offs = offset + scansize * (sy - y); + int xx; + for (xx = dx1; (sx = srccols[xx]) < x + w; xx++) + { + pix[xx] = pixels[offs + sx - x]; + } + if (xx > dx1) + { + consumer.setPixels(dx1, yy, xx - dx1, 1, model, pix, dx1, + destWidth); + } + } + } + + /** + * Sets up the srcrows and srccols arrays. + */ + private void setupSources() + { + srcrows = new int[destHeight + 1]; + for (int y = 0; y <= destHeight; y++) + { + srcrows[y] = (2 * y * srcHeight + srcHeight) / (2 * destHeight); + } + srccols = new int[destWidth + 1]; + for (int x = 0; x <= destWidth; x++) + { + srccols[x] = (2 * x * srcWidth + srcWidth) / (2 * destWidth); + } + } +} diff --git a/libjava/classpath/java/awt/image/RescaleOp.java b/libjava/classpath/java/awt/image/RescaleOp.java new file mode 100644 index 000000000..efab40b7e --- /dev/null +++ b/libjava/classpath/java/awt/image/RescaleOp.java @@ -0,0 +1,385 @@ +/* Copyright (C) 2004, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Arrays; + +/** + * RescaleOp is a filter that changes each pixel by a scaling factor and offset. + * + * For filtering Rasters, either one scaling factor and offset can be specified, + * which will be applied to all bands; or a scaling factor and offset can be + * specified for each band. + * + * For BufferedImages, the scaling may apply to both color and alpha components. + * If only one scaling factor is provided, or if the number of factors provided + * equals the number of color components, the scaling is performed on all color + * components. Otherwise, the scaling is performed on all components including + * alpha. Alpha premultiplication is ignored. + * + * After filtering, if color conversion is necessary, the conversion happens, + * taking alpha premultiplication into account. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Francis Kung (fkung@redhat.com) + */ +public class RescaleOp implements BufferedImageOp, RasterOp +{ + private float[] scale; + private float[] offsets; + private RenderingHints hints = null; + + /** + * Create a new RescaleOp object using the given scale factors and offsets. + * + * The length of the arrays must be equal to the number of bands (or number of + * data or color components) of the raster/image that this Op will be used on, + * otherwise an IllegalArgumentException will be thrown when calling the + * filter method. + * + * @param scaleFactors an array of scale factors. + * @param offsets an array of offsets. + * @param hints any rendering hints to use (can be null). + * @throws NullPointerException if the scaleFactors or offsets array is null. + */ + public RescaleOp(float[] scaleFactors, + float[] offsets, + RenderingHints hints) + { + int length = Math.min(scaleFactors.length, offsets.length); + + scale = new float[length]; + System.arraycopy(scaleFactors, 0, this.scale, 0, length); + + this.offsets = new float[length]; + System.arraycopy(offsets, 0, this.offsets, 0, length); + + this.hints = hints; + } + + /** + * Create a new RescaleOp object using the given scale factor and offset. + * + * The same scale factor and offset will be used on all bands/components. + * + * @param scaleFactor the scale factor to use. + * @param offset the offset to use. + * @param hints any rendering hints to use (can be null). + */ + public RescaleOp(float scaleFactor, + float offset, + RenderingHints hints) + { + scale = new float[]{ scaleFactor }; + offsets = new float[]{offset}; + this.hints = hints; + } + + /** + * Returns the scaling factors. This method accepts an optional array, which + * will be used to store the factors if not null (this avoids allocating a + * new array). If this array is too small to hold all the scaling factors, + * the array will be filled and the remaining factors discarded. + * + * @param scaleFactors array to store the scaling factors in (can be null). + * @return an array of scaling factors. + */ + public final float[] getScaleFactors(float[] scaleFactors) + { + if (scaleFactors == null) + scaleFactors = new float[scale.length]; + System.arraycopy(scale, 0, scaleFactors, 0, Math.min(scale.length, + scaleFactors.length)); + return scaleFactors; + } + + /** + * Returns the offsets. This method accepts an optional array, which + * will be used to store the offsets if not null (this avoids allocating a + * new array). If this array is too small to hold all the offsets, the array + * will be filled and the remaining factors discarded. + * + * @param offsets array to store the offsets in (can be null). + * @return an array of offsets. + */ + public final float[] getOffsets(float[] offsets) + { + if (offsets == null) + offsets = new float[this.offsets.length]; + System.arraycopy(this.offsets, 0, offsets, 0, Math.min(this.offsets.length, + offsets.length)); + return offsets; + } + + /** + * Returns the number of scaling factors / offsets. + * + * @return the number of scaling factors / offsets. + */ + public final int getNumFactors() + { + return scale.length; + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getRenderingHints() + */ + public final RenderingHints getRenderingHints() + { + return hints; + } + + /** + * Converts the source image using the scale factors and offsets 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 image cannot use an IndexColorModel, and the destination image + * (if one is provided) must have the same size. + * + * If the final value of a sample is beyond the range of the color model, it + * will be clipped to the appropriate maximum / minimum. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The rescaled image. + */ + public final BufferedImage filter(BufferedImage src, BufferedImage dst) + { + // Initial checks + if (scale.length != 1 + && scale.length != src.getColorModel().getNumComponents() + && (scale.length != src.getColorModel().getNumColorComponents())) + throw new IllegalArgumentException("Source image has wrong number of " + + "bands for these scaling factors."); + + if (dst == null) + dst = createCompatibleDestImage(src, null); + else if (src.getHeight() != dst.getHeight() + || src.getWidth() != dst.getWidth()) + throw new IllegalArgumentException("Source and destination images are " + + "different sizes."); + + // Prepare for possible colorspace conversion + BufferedImage dst2 = dst; + if (dst.getColorModel().getColorSpace().getType() != src.getColorModel().getColorSpace().getType()) + dst2 = createCompatibleDestImage(src, src.getColorModel()); + + // Figure out how many bands to scale + int numBands = scale.length; + if (scale.length == 1) + numBands = src.getColorModel().getNumColorComponents(); + boolean[] bands = new boolean[numBands]; + // this assumes the alpha, if present, is the last band + Arrays.fill(bands, true); + + // Perform rescaling + filter(src.getRaster(), dst2.getRaster(), bands); + + // Copy alpha band if needed (ie if it exists and wasn't scaled) + // NOTE: This assumes the alpha component is the last band! + if (src.getColorModel().hasAlpha() + && numBands == src.getColorModel().getNumColorComponents()) + { + + dst2.getRaster().setSamples(0, 0, src.getWidth(), src.getHeight(), + numBands, + src.getRaster().getSamples(0, 0, + src.getWidth(), + src.getHeight(), + numBands, + (int[]) null)); + } + + // Perform colorspace conversion if needed + if (dst != dst2) + new ColorConvertOp(hints).filter(dst2, dst); + + return dst; + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster) + */ + public final WritableRaster filter(Raster src, WritableRaster dest) + { + // Required sanity checks + if (scale.length != 1 && scale.length != src.numBands) + throw new IllegalArgumentException("Number of rasters is incompatible " + + "with the number of scaling " + + "factors provided."); + + if (dest == null) + dest = src.createCompatibleWritableRaster(); + else if (src.getHeight() != dest.getHeight() + || src.getWidth() != dest.getWidth()) + throw new IllegalArgumentException("Source and destination rasters are " + + "different sizes."); + else if (src.numBands != dest.numBands) + throw new IllegalArgumentException("Source and destination rasters " + + "are incompatible."); + + // Filter all bands + boolean[] bands = new boolean[src.getNumBands()]; + Arrays.fill(bands, true); + return filter(src, dest, bands); + } + + /** + * Perform raster-based filtering on a selected number of bands. + * + * The length of the bands array should equal the number of bands; a true + * element indicates filtering should happen on the corresponding band, while + * a false element will skip the band. + * + * The rasters are assumed to be compatible and non-null. + * + * @param src the source raster. + * @param dest the destination raster. + * @param bands an array indicating which bands to filter. + * @throws NullPointerException if any parameter is null. + * @throws ArrayIndexOutOfBoundsException if the bands array is too small. + * @return the destination raster. + */ + private WritableRaster filter(Raster src, WritableRaster dest, boolean[] bands) + { + int[] values = new int[src.getHeight() * src.getWidth()]; + float scaleFactor, offset; + + // Find max sample value, to be used for clipping later + int[] maxValue = src.getSampleModel().getSampleSize(); + for (int i = 0; i < maxValue.length; i++) + maxValue[i] = (int)Math.pow(2, maxValue[i]) - 1; + + // TODO: can this be optimized further? + // Filter all samples of all requested bands + for (int band = 0; band < bands.length; band++) + if (bands[band]) + { + values = src.getSamples(src.getMinX(), src.getMinY(), src.getWidth(), + src.getHeight(), band, values); + + if (scale.length == 1) + { + scaleFactor = scale[0]; + offset = offsets[0]; + } + else + { + scaleFactor = scale[band]; + offset = offsets[band]; + } + + for (int i = 0; i < values.length; i++) + { + values[i] = (int) (values[i] * scaleFactor + offset); + + // Clip if needed + if (values[i] < 0) + values[i] = 0; + if (values[i] > maxValue[band]) + values[i] = maxValue[band]; + } + + dest.setSamples(dest.getMinX(), dest.getMinY(), dest.getWidth(), + dest.getHeight(), band, values); + } + + return dest; + } + + /* + * (non-Javadoc) + * + * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, + * java.awt.image.ColorModel) + */ + public BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel dstCM) + { + if (dstCM == null) + return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); + + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isAlphaPremultiplied(), null); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + */ + public WritableRaster createCompatibleDestRaster(Raster src) + { + return src.createCompatibleWritableRaster(); + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + */ + public final Rectangle2D getBounds2D(BufferedImage src) + { + return src.getRaster().getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + */ + public final Rectangle2D getBounds2D(Raster src) + { + return src.getBounds(); + } + + /* (non-Javadoc) + * @see java.awt.image.BufferedImageOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D) + */ + public final Point2D getPoint2D(Point2D src, Point2D dst) + { + if (dst == null) + dst = (Point2D) src.clone(); + else + dst.setLocation(src); + + return dst; + } + +} diff --git a/libjava/classpath/java/awt/image/SampleModel.java b/libjava/classpath/java/awt/image/SampleModel.java new file mode 100644 index 000000000..7e0b18c55 --- /dev/null +++ b/libjava/classpath/java/awt/image/SampleModel.java @@ -0,0 +1,974 @@ +/* Copyright (C) 2000, 2001, 2002, 2005, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +/** + * A SampleModel is used to access pixel data from a + * {@link DataBuffer}. This is used by the {@link Raster} class. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public abstract class SampleModel +{ + /** Width of image described. */ + protected int width; + + /** Height of image described. */ + protected int height; + + /** Number of bands in the image described. Package-private here, + shadowed by ComponentSampleModel. */ + protected int numBands; + + /** + * The DataBuffer type that is used to store the data of the image + * described. + */ + protected int dataType; + + /** + * Creates a new sample model with the specified attributes. + * + * @param dataType the data type (one of {@link DataBuffer#TYPE_BYTE}, + * {@link DataBuffer#TYPE_USHORT}, {@link DataBuffer#TYPE_SHORT}, + * {@link DataBuffer#TYPE_INT}, {@link DataBuffer#TYPE_FLOAT}, + * {@link DataBuffer#TYPE_DOUBLE} or {@link DataBuffer#TYPE_UNDEFINED}). + * @param w the width in pixels (must be greater than zero). + * @param h the height in pixels (must be greater than zero). + * @param numBands the number of bands (must be greater than zero). + * + * @throws IllegalArgumentException if dataType is not one of + * the listed values. + * @throws IllegalArgumentException if w is less than or equal + * to zero. + * @throws IllegalArgumentException if h is less than or equal + * to zero. + * @throws IllegalArgumentException if w * h is greater than + * {@link Integer#MAX_VALUE}. + */ + public SampleModel(int dataType, int w, int h, int numBands) + { + if (dataType != DataBuffer.TYPE_UNDEFINED) + if (dataType < DataBuffer.TYPE_BYTE || dataType > DataBuffer.TYPE_DOUBLE) + throw new IllegalArgumentException("Unrecognised 'dataType' argument."); + + if ((w <= 0) || (h <= 0)) + throw new IllegalArgumentException((w <= 0 ? " width<=0" : " width is ok") + + (h <= 0 ? " height<=0" : " height is ok")); + + long area = (long) w * (long) h; + if (area > Integer.MAX_VALUE) + throw new IllegalArgumentException("w * h exceeds Integer.MAX_VALUE."); + + if (numBands <= 0) + throw new IllegalArgumentException("Requires numBands > 0."); + + this.dataType = dataType; + this.width = w; + this.height = h; + this.numBands = numBands; + } + + /** + * Returns the width of the pixel data accessible via this + * SampleModel. + * + * @return The width. + * + * @see #getHeight() + */ + public final int getWidth() + { + return width; + } + + /** + * Returns the height of the pixel data accessible via this + * SampleModel. + * + * @return The height. + * + * @see #getWidth() + */ + public final int getHeight() + { + return height; + } + + /** + * Returns the number of bands for this SampleModel. + * + * @return The number of bands. + */ + public final int getNumBands() + { + return numBands; + } + + public abstract int getNumDataElements(); + + /** + * Returns the type of the {@link DataBuffer} that this + * SampleModel accesses. + * + * @return The data buffer type. + */ + public final int getDataType() + { + return dataType; + } + + public int getTransferType() + { + // FIXME: Is this a reasonable default implementation? + return dataType; + } + + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * specified data buffer. If iArray is not null, + * it will be populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if data is null. + */ + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + if (iArray == null) + iArray = new int[numBands]; + for (int b = 0; b < numBands; b++) + iArray[b] = getSample(x, y, b, data); + return iArray; + } + + /** + * + * This method is provided as a faster alternative to getPixel(), + * that can be used when there is no need to decode the pixel into + * separate sample values. + * + * @param obj An array to return the pixel data in. If null, an + * array of the right type and size will be created. + * + * @return A single pixel as an array object of a primitive type, + * based on the transfer type. Eg. if transfer type is + * DataBuffer.TYPE_USHORT, then a short[] object is returned. + */ + public abstract Object getDataElements(int x, int y, Object obj, + DataBuffer data); + + + public Object getDataElements(int x, int y, int w, int h, Object obj, + DataBuffer data) + { + int size = w * h; + int numDataElements = getNumDataElements(); + int dataSize = numDataElements * size; + + if (obj == null) + { + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + obj = new byte[dataSize]; + break; + case DataBuffer.TYPE_USHORT: + obj = new short[dataSize]; + break; + case DataBuffer.TYPE_INT: + obj = new int[dataSize]; + break; + default: + // Seems like the only sensible thing to do. + throw new ClassCastException(); + } + } + Object pixelData = null; + int outOffset = 0; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + pixelData = getDataElements(xx, yy, pixelData, data); + System.arraycopy(pixelData, 0, obj, outOffset, + numDataElements); + outOffset += numDataElements; + } + } + return obj; + } + + public abstract void setDataElements(int x, int y, Object obj, + DataBuffer data); + + public void setDataElements(int x, int y, int w, int h, + Object obj, DataBuffer data) + { + int numDataElements = getNumDataElements(); + + Object pixelData; + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + pixelData = new byte[numDataElements]; + break; + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_SHORT: + pixelData = new short[numDataElements]; + break; + case DataBuffer.TYPE_INT: + pixelData = new int[numDataElements]; + break; + case DataBuffer.TYPE_FLOAT: + pixelData = new float[numDataElements]; + break; + case DataBuffer.TYPE_DOUBLE: + pixelData = new double[numDataElements]; + break; + default: + // The RI silently igores invalid types. + pixelData = null; + } + + int inOffset = 0; + if (pixelData != null) + { + for (int yy=y; yy<(y+h); yy++) + { + for (int xx=x; xx<(x+w); xx++) + { + System.arraycopy(obj, inOffset, pixelData, 0, numDataElements); + setDataElements(xx, yy, pixelData, data); + inOffset += numDataElements; + } + } + } + } + + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * specified data buffer. If fArray is not null, + * it will be populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param fArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if data is null. + */ + public float[] getPixel(int x, int y, float[] fArray, DataBuffer data) + { + if (fArray == null) + fArray = new float[numBands]; + + for (int b = 0; b < numBands; b++) + { + fArray[b] = getSampleFloat(x, y, b, data); + } + return fArray; + } + + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * specified data buffer. If dArray is not null, + * it will be populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param dArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if data is null. + */ + public double[] getPixel(int x, int y, double[] dArray, DataBuffer data) { + if (dArray == null) + dArray = new double[numBands]; + for (int b = 0; b < numBands; b++) + { + dArray[b] = getSampleDouble(x, y, b, data); + } + return dArray; + } + + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). If iArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if data is null. + */ + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int size = w * h; + int outOffset = 0; + int[] pixel = null; + if (iArray == null) + iArray = new int[w * h * numBands]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + pixel = getPixel(xx, yy, pixel, data); + System.arraycopy(pixel, 0, iArray, outOffset, numBands); + outOffset += numBands; + } + } + return iArray; + } + + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). If fArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param fArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if data is null. + */ + public float[] getPixels(int x, int y, int w, int h, float[] fArray, + DataBuffer data) + { + int size = w * h; + int outOffset = 0; + float[] pixel = null; + if (fArray == null) fArray = new float[w * h * numBands]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + pixel = getPixel(xx, yy, pixel, data); + System.arraycopy(pixel, 0, fArray, outOffset, numBands); + outOffset += numBands; + } + } + return fArray; + } + + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). If dArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param dArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if data is null. + */ + public double[] getPixels(int x, int y, int w, int h, double[] dArray, + DataBuffer data) + { + int size = w * h; + int outOffset = 0; + double[] pixel = null; + if (dArray == null) + dArray = new double[w * h * numBands]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + pixel = getPixel(xx, yy, pixel, data); + System.arraycopy(pixel, 0, dArray, outOffset, numBands); + outOffset += numBands; + } + } + return dArray; + } + + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if data is null. + */ + public abstract int getSample(int x, int y, int b, DataBuffer data); + + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if data is null. + * + * @see #getSample(int, int, int, DataBuffer) + */ + public float getSampleFloat(int x, int y, int b, DataBuffer data) + { + return getSample(x, y, b, data); + } + + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if data is null. + * + * @see #getSample(int, int, int, DataBuffer) + */ + public double getSampleDouble(int x, int y, int b, DataBuffer data) + { + return getSampleFloat(x, y, b, data); + } + + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the specified data buffer. If + * iArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The sample values. + * + * @throws NullPointerException if data is null. + */ + public int[] getSamples(int x, int y, int w, int h, int b, + int[] iArray, DataBuffer data) + { + int size = w * h; + int outOffset = 0; + if (iArray == null) + iArray = new int[size]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + iArray[outOffset++] = getSample(xx, yy, b, data); + } + } + return iArray; + } + + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the specified data buffer. If + * fArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param fArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The sample values. + * + * @throws NullPointerException if data is null. + */ + public float[] getSamples(int x, int y, int w, int h, int b, + float[] fArray, DataBuffer data) + { + int size = w * h; + int outOffset = 0; + if (fArray == null) + fArray = new float[size]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + fArray[outOffset++] = getSampleFloat(xx, yy, b, data); + } + } + return fArray; + } + + /** + * Returns an array containing the samples from one band for the pixels in + * the region specified by (x, y, w, h) in the specified data buffer. If + * dArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param dArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The sample values. + * + * @throws NullPointerException if data is null. + */ + public double[] getSamples(int x, int y, int w, int h, int b, + double[] dArray, DataBuffer data) + { + int size = w * h; + int outOffset = 0; + if (dArray == null) + dArray = new double[size]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + dArray[outOffset++] = getSampleDouble(xx, yy, b, data); + } + } + return dArray; + } + + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + for (int b = 0; b < numBands; b++) + setSample(x, y, b, iArray[b], data); + } + + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param fArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either fArray or + * data is null. + */ + public void setPixel(int x, int y, float[] fArray, DataBuffer data) + { + for (int b = 0; b < numBands; b++) + setSample(x, y, b, fArray[b], data); + } + + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param dArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either dArray or + * data is null. + */ + public void setPixel(int x, int y, double[] dArray, DataBuffer data) + { + for (int b = 0; b < numBands; b++) + setSample(x, y, b, dArray[b], data); + } + + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray the pixel sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int inOffset = 0; + int[] pixel = new int[numBands]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + System.arraycopy(iArray, inOffset, pixel, 0, numBands); + setPixel(xx, yy, pixel, data); + inOffset += numBands; + } + } + } + + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param fArray the pixel sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either fArray or + * data is null. + */ + public void setPixels(int x, int y, int w, int h, float[] fArray, + DataBuffer data) + { + int inOffset = 0; + float[] pixel = new float[numBands]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + System.arraycopy(fArray, inOffset, pixel, 0, numBands); + setPixel(xx, yy, pixel, data); + inOffset += numBands; + } + } + } + + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param dArray the pixel sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either dArray or + * data is null. + */ + public void setPixels(int x, int y, int w, int h, double[] dArray, + DataBuffer data) + { + int inOffset = 0; + double[] pixel = new double[numBands]; + for (int yy = y; yy < (y + h); yy++) + { + for (int xx = x; xx < (x + w); xx++) + { + System.arraycopy(dArray, inOffset, pixel, 0, numBands); + setPixel(xx, yy, pixel, data); + inOffset += numBands; + } + } + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if data is null. + */ + public abstract void setSample(int x, int y, int b, int s, + DataBuffer data); + + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if data is null. + */ + public void setSample(int x, int y, int b, float s, + DataBuffer data) + { + setSample(x, y, b, (int) s, data); + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if data is null. + */ + public void setSample(int x, int y, int b, double s, + DataBuffer data) + { + setSample(x, y, b, (float) s, data); + } + + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param iArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setSamples(int x, int y, int w, int h, int b, + int[] iArray, DataBuffer data) + { + int size = w * h; + int inOffset = 0; + for (int yy = y; yy < (y + h); yy++) + for (int xx = x; xx < (x + w); xx++) + setSample(xx, yy, b, iArray[inOffset++], data); + } + + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param fArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setSamples(int x, int y, int w, int h, int b, + float[] fArray, DataBuffer data) + { + int size = w * h; + int inOffset = 0; + for (int yy = y; yy < (y + h); yy++) + for (int xx = x; xx < (x + w); xx++) + setSample(xx, yy, b, fArray[inOffset++], data); + + } + + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param dArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setSamples(int x, int y, int w, int h, int b, + double[] dArray, DataBuffer data) { + int size = w * h; + int inOffset = 0; + for (int yy = y; yy < (y + h); yy++) + for (int xx = x; xx < (x + w); xx++) + setSample(xx, yy, b, dArray[inOffset++], data); + } + + /** + * Creates a new SampleModel that is compatible with this + * model and has the specified width and height. + * + * @param w the width (in pixels). + * @param h the height (in pixels). + * + * @return The new sample model. + */ + public abstract SampleModel createCompatibleSampleModel(int w, int h); + + /** + * Return a SampleModel with a subset of the bands in this model. + * + * Selects bands.length bands from this sample model. The bands chosen + * are specified in the indices of bands[]. This also permits permuting + * the bands as well as taking a subset. Thus, giving an array with + * 1, 2, 3, ..., numbands, will give an identical sample model. + * + * @param bands Array with band indices to include. + * @return A new sample model + */ + public abstract SampleModel createSubsetSampleModel(int[] bands); + + /** + * Creates a new {@link DataBuffer} of the correct type and size for this + * SampleModel. + * + * @return The data buffer. + */ + public abstract DataBuffer createDataBuffer(); + + /** + * Returns an array containing the size (in bits) for each band accessed by + * the SampleModel. + * + * @return An array. + * + * @see #getSampleSize(int) + */ + public abstract int[] getSampleSize(); + + /** + * Returns the size (in bits) of the samples for the specified band. + * + * @param band the band (in the range 0 to + * getNumBands() - 1). + * + * @return The sample size (in bits). + */ + public abstract int getSampleSize(int band); +} diff --git a/libjava/classpath/java/awt/image/ShortLookupTable.java b/libjava/classpath/java/awt/image/ShortLookupTable.java new file mode 100644 index 000000000..3e276fe9f --- /dev/null +++ b/libjava/classpath/java/awt/image/ShortLookupTable.java @@ -0,0 +1,177 @@ +/* ShortLookupTable.java -- Java class for a pixel translation table. + Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * ShortLookupTable represents translation arrays for pixel values. It wraps + * one or more data arrays for each layer (or component) in an image, such as + * Alpha, R, G, and B. When doing translation, the offset is subtracted from + * the pixel values to allow a subset of an array to be used. + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @version 1.0 + */ +public class ShortLookupTable extends LookupTable +{ + // Array of translation tables. + private short data[][]; + + /** + * Creates a new ShortLookupTable instance. + * + * Offset is subtracted from pixel values when looking up in the translation + * tables. If data.length is one, the same table is applied to all pixel + * components. + * + * @param offset Offset to be subtracted. + * @param data Array of lookup tables. + * @exception IllegalArgumentException if offset < 0 or data.length < 1. + */ + public ShortLookupTable(int offset, short[][] data) + throws IllegalArgumentException + { + super(offset, data.length); + + // tests show that Sun's implementation creates a new array to store the + // references from the incoming 'data' array - not sure why, but we'll + // match that behaviour just in case it matters... + this.data = new short[data.length][]; + for (int i = 0; i < data.length; i++) + this.data[i] = data[i]; + } + + /** + * Creates a new ShortLookupTable instance. + * + * Offset is subtracted from pixel values when looking up in the translation + * table. The same table is applied to all pixel components. + * + * @param offset Offset to be subtracted. + * @param data Lookup table for all components (null not + * permitted). + * @exception IllegalArgumentException if offset < 0. + */ + public ShortLookupTable(int offset, short[] data) + throws IllegalArgumentException + { + super(offset, 1); + if (data == null) + throw new NullPointerException("Null 'data' argument."); + this.data = new short[][] {data}; + } + + /** + * Return the lookup tables. This is a reference to the actual table, so + * modifying the contents of the returned array will modify the lookup table. + * + * @return The lookup table. + */ + public final short[][] getTable() + { + return data; + } + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dst Destination array for values, or null. + * @return Translated values for the pixel. + */ + public int[] lookupPixel(int[] src, int[] dst) + throws ArrayIndexOutOfBoundsException + { + if (dst == null) + dst = new int[src.length]; + + if (data.length == 1) + for (int i = 0; i < src.length; i++) + dst[i] = data[0][src[i] - offset]; + else + for (int i = 0; i < src.length; i++) + dst[i] = data[i][src[i] - offset]; + + return dst; + } + + /** + * Return translated values for a pixel. + * + * For each value in the pixel src, use the value minus offset as an index + * in the component array and copy the value there to the output for the + * component. If dest is null, the output is a new array, otherwise the + * translated values are written to dest. Dest can be the same array as + * src. + * + * For example, if the pixel src is [2, 4, 3], and offset is 1, the output + * is [comp1[1], comp2[3], comp3[2]], where comp1, comp2, and comp3 are the + * translation arrays. + * + * @param src Component values of a pixel. + * @param dst Destination array for values, or null. + * @return Translated values for the pixel. + * + */ + public short[] lookupPixel(short[] src, short[] dst) + throws ArrayIndexOutOfBoundsException + { + if (dst == null) + dst = new short[src.length]; + + if (data.length == 1) + for (int i = 0; i < src.length; i++) + dst[i] = data[0][((int) src[i]) - offset]; + else + for (int i = 0; i < src.length; i++) + dst[i] = data[i][((int) src[i]) - offset]; + + return dst; + + } +} diff --git a/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java new file mode 100644 index 000000000..143581db9 --- /dev/null +++ b/libjava/classpath/java/awt/image/SinglePixelPackedSampleModel.java @@ -0,0 +1,586 @@ +/* Copyright (C) 2000, 2002, 2003, 2004, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.image; + +import java.util.Arrays; + +import gnu.java.awt.BitMaskExtent; +import gnu.java.lang.CPStringBuilder; + +/** + * A SampleModel used when all samples are stored in a single + * data element in the {@link DataBuffer}, and each data element contains + * samples for one pixel only. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class SinglePixelPackedSampleModel extends SampleModel +{ + private int scanlineStride; + private int[] bitMasks; + private int[] bitOffsets; + private int[] sampleSize; + + /** + * Creates a new SinglePixelPackedSampleModel. + * + * @param dataType the data buffer type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param bitMasks an array containing the bit mask used to extract the + * sample value for each band. + */ + public SinglePixelPackedSampleModel(int dataType, int w, int h, + int[] bitMasks) + { + this(dataType, w, h, w, bitMasks); + } + + /** + * Creates a new SinglePixelPackedSampleModel. + * + * @param dataType the data buffer type. + * @param w the width (in pixels). + * @param h the height (in pixels). + * @param scanlineStride the number of data elements between a pixel on one + * row and the corresponding pixel on the next row. + * @param bitMasks an array containing the bit mask used to extract the + * sample value for each band. + */ + public SinglePixelPackedSampleModel(int dataType, int w, int h, + int scanlineStride, int[] bitMasks) + { + super(dataType, w, h, bitMasks.length); + + switch (dataType) + { + case DataBuffer.TYPE_BYTE: + case DataBuffer.TYPE_USHORT: + case DataBuffer.TYPE_INT: + break; + default: + throw new IllegalArgumentException( + "SinglePixelPackedSampleModel unsupported dataType"); + } + + this.scanlineStride = scanlineStride; + this.bitMasks = bitMasks; + + bitOffsets = new int[numBands]; + sampleSize = new int[numBands]; + + BitMaskExtent extent = new BitMaskExtent(); + for (int b = 0; b < numBands; b++) + { + // the mask is an unsigned integer + long mask = bitMasks[b] & 0xFFFFFFFFL; + extent.setMask(mask); + sampleSize[b] = extent.bitWidth; + bitOffsets[b] = extent.leastSignificantBit; + } + } + + /** + * Returns the number of data elements. + * + * @return 1. + */ + public int getNumDataElements() + { + return 1; + } + + /** + * Creates a new SampleModel that is compatible with this + * model and has the specified width and height. + * + * @param w the width (in pixels). + * @param h the height (in pixels). + * + * @return The new sample model. + */ + public SampleModel createCompatibleSampleModel(int w, int h) + { + /* FIXME: We can avoid recalculation of bit offsets and sample + sizes here by passing these from the current instance to a + special private constructor. */ + return new SinglePixelPackedSampleModel(dataType, w, h, bitMasks); + } + + + /** + * Creates a DataBuffer for holding pixel data in the format and + * layout described by this SampleModel. The returned buffer will + * consist of one single bank. + * + * @return The data buffer. + */ + public DataBuffer createDataBuffer() + { + // We can save (scanlineStride - width) pixels at the very end of + // the buffer. The Sun reference implementation (J2SE 1.3.1 and + // 1.4.1_01) seems to do this; tested with Mauve test code. + int size = scanlineStride * (height - 1) + width; + + DataBuffer buffer = null; + switch (getTransferType()) + { + case DataBuffer.TYPE_BYTE: + buffer = new DataBufferByte(size); + break; + case DataBuffer.TYPE_USHORT: + buffer = new DataBufferUShort(size); + break; + case DataBuffer.TYPE_INT: + buffer = new DataBufferInt(size); + break; + } + return buffer; + } + + /** + * Returns an array containing the size (in bits) for each band accessed by + * the SampleModel. + * + * @return An array. + * + * @see #getSampleSize(int) + */ + public int[] getSampleSize() + { + return (int[]) sampleSize.clone(); + } + + /** + * Returns the size (in bits) of the samples for the specified band. + * + * @param band the band (in the range 0 to + * getNumBands() - 1). + * + * @return The sample size (in bits). + */ + public int getSampleSize(int band) + { + return sampleSize[band]; + } + + /** + * Returns the index in the data buffer that stores the pixel at (x, y). + * + * @param x the x-coordinate. + * @param y the y-coordinate. + * + * @return The index in the data buffer that stores the pixel at (x, y). + */ + public int getOffset(int x, int y) + { + return scanlineStride*y + x; + } + + public int[] getBitOffsets() + { + return bitOffsets; + } + + public int[] getBitMasks() + { + return bitMasks; + } + + /** + * Returns the number of data elements from a pixel in one row to the + * corresponding pixel in the next row. + * + * @return The scanline stride. + */ + public int getScanlineStride() + { + return scanlineStride; + } + + /** + * Creates a new SinglePixelPackedSampleModel that accesses + * the specified subset of bands. + * + * @param bands an array containing band indices (null not + * permitted). + * + * @return A new sample model. + * + * @throws NullPointerException if bands is null. + * @throws RasterFormatException if bands.length is greater + * than the number of bands in this model. + */ + public SampleModel createSubsetSampleModel(int[] bands) + { + if (bands.length > numBands) + throw new RasterFormatException("Too many bands."); + + int numBands = bands.length; + + int[] bitMasks = new int[numBands]; + + for (int b = 0; b < numBands; b++) + bitMasks[b] = this.bitMasks[bands[b]]; + + return new SinglePixelPackedSampleModel(dataType, width, height, + scanlineStride, bitMasks); + } + + public Object getDataElements(int x, int y, Object obj, + DataBuffer data) + { + int type = getTransferType(); + Object ret = null; + switch (type) + { + case DataBuffer.TYPE_BYTE: + { + byte[] in = (byte[]) obj; + if (in == null) + in = new byte[1]; + in[0] = (byte) data.getElem(x + y * scanlineStride); + ret = in; + } + break; + case DataBuffer.TYPE_USHORT: + { + short[] in = (short[]) obj; + if (in == null) + in = new short[1]; + in[0] = (short) data.getElem(x + y * scanlineStride); + ret = in; + } + break; + case DataBuffer.TYPE_INT: + { + int[] in = (int[]) obj; + if (in == null) + in = new int[1]; + in[0] = data.getElem(x + y * scanlineStride); + ret = in; + } + break; + } + return ret; + } + + /** + * Returns an array containing the samples for the pixel at (x, y) in the + * specified data buffer. If iArray is not null, + * it will be populated with the sample values and returned as the result of + * this function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if data is null. + */ + public int[] getPixel(int x, int y, int[] iArray, DataBuffer data) + { + int offset = scanlineStride*y + x; + if (iArray == null) iArray = new int[numBands]; + int samples = data.getElem(offset); + + for (int b = 0; b < numBands; b++) + iArray[b] = (samples & bitMasks[b]) >>> bitOffsets[b]; + + return iArray; + } + + /** + * Returns an array containing the samples for the pixels in the region + * specified by (x, y, w, h) in the specified data buffer. The array is + * ordered by pixels (that is, all the samples for the first pixel are + * grouped together, followed by all the samples for the second pixel, and so + * on). If iArray is not null, it will be + * populated with the sample values and returned as the result of this + * function (this avoids allocating a new array instance). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray an array to populate with the sample values and return as + * the result (if null, a new array will be allocated). + * @param data the data buffer (null not permitted). + * + * @return The pixel sample values. + * + * @throws NullPointerException if data is null. + */ + public int[] getPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int offset = scanlineStride*y + x; + if (iArray == null) iArray = new int[numBands*w*h]; + int outOffset = 0; + for (y = 0; y < h; y++) + { + int lineOffset = offset; + for (x = 0; x < w; x++) + { + int samples = data.getElem(lineOffset++); + for (int b = 0; b < numBands; b++) + iArray[outOffset++] = (samples & bitMasks[b]) >>> bitOffsets[b]; + } + offset += scanlineStride; + } + return iArray; + } + + /** + * Returns the sample value for the pixel at (x, y) in the specified data + * buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param data the data buffer (null not permitted). + * + * @return The sample value. + * + * @throws NullPointerException if data is null. + */ + public int getSample(int x, int y, int b, DataBuffer data) + { + int offset = scanlineStride*y + x; + int samples = data.getElem(offset); + return (samples & bitMasks[b]) >>> bitOffsets[b]; + } + + public void setDataElements(int x, int y, Object obj, DataBuffer data) + { + int transferType = getTransferType(); + switch (transferType) + { + case DataBuffer.TYPE_BYTE: + { + byte[] in = (byte[]) obj; + data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xff); + } + break; + case DataBuffer.TYPE_USHORT: + { + short[] in = (short[]) obj; + data.setElem(y * scanlineStride + x, ((int) in[0]) & 0xffff); + } + break; + case DataBuffer.TYPE_INT: + { + int[] in = (int[]) obj; + data.setElem(y * scanlineStride + x, in[0]); + break; + } + } + } + + /** + * Sets the samples for the pixel at (x, y) in the specified data buffer to + * the specified values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample values (null not permitted). + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if either iArray or + * data is null. + */ + public void setPixel(int x, int y, int[] iArray, DataBuffer data) + { + int offset = scanlineStride*y + x; + + int samples = 0; + for (int b = 0; b < numBands; b++) + samples |= (iArray[b] << bitOffsets[b]) & bitMasks[b]; + + data.setElem(offset, samples); + } + + /** + * This method implements a more efficient way to set pixels than the default + * implementation of the super class. It copies the pixel components directly + * from the input array instead of creating a intermediate buffer. + * @param x The x-coordinate of the pixel rectangle in obj. + * @param y The y-coordinate of the pixel rectangle in obj. + * @param w The width of the pixel rectangle in obj. + * @param h The height of the pixel rectangle in obj. + * @param iArray The primitive array containing the pixels to set. + * @param data The DataBuffer to store the pixels into. + * @see java.awt.image.SampleModel#setPixels(int, int, int, int, int[], + * java.awt.image.DataBuffer) + */ + public void setPixels(int x, int y, int w, int h, int[] iArray, + DataBuffer data) + { + int inOffset = 0; + for (int yy=y; yy<(y+h); yy++) + { + int offset = scanlineStride*yy + x; + for (int xx=x; xx<(x+w); xx++) + { + int samples = 0; + for (int b = 0; b < numBands; b++) + samples |= (iArray[inOffset+b] << bitOffsets[b]) & bitMasks[b]; + data.setElem(0, offset, samples); + inOffset += numBands; + offset += 1; + } + } + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the + * specified data buffer. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + * @param data the data buffer (null not permitted). + * + * @throws NullPointerException if data is null. + */ + public void setSample(int x, int y, int b, int s, DataBuffer data) + { + int offset = scanlineStride*y + x; + int samples = data.getElem(offset); + int bitMask = bitMasks[b]; + samples &= ~bitMask; + samples |= (s << bitOffsets[b]) & bitMask; + data.setElem(offset, samples); + } + + /** + * Tests this sample model for equality with an arbitrary object. This + * method returns true if and only if: + *
      + *
    • obj is not null; + *
    • obj is an instance of + * SinglePixelPackedSampleModel; + *
    • both models have the same: + *
        + *
      • dataType; + *
      • width; + *
      • height; + *
      • numBands; + *
      • scanlineStride; + *
      • bitMasks; + *
      • bitOffsets. + *
      + *
    • + *
    + * + * @param obj the object (null permitted) + * + * @return true if this model is equal to obj, and + * false otherwise. + */ + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (! (obj instanceof SinglePixelPackedSampleModel)) + return false; + SinglePixelPackedSampleModel that = (SinglePixelPackedSampleModel) obj; + if (this.dataType != that.dataType) + return false; + if (this.width != that.width) + return false; + if (this.height != that.height) + return false; + if (this.numBands != that.numBands) + return false; + if (this.scanlineStride != that.scanlineStride) + return false; + if (!Arrays.equals(this.bitMasks, that.bitMasks)) + return false; + if (!Arrays.equals(this.bitOffsets, that.bitOffsets)) + return false; + return true; + } + + /** + * Returns a hash code for this SinglePixelPackedSampleModel. + * + * @return A hash code. + */ + public int hashCode() + { + // this hash code won't match Sun's, but that shouldn't matter... + int result = 193; + result = 37 * result + dataType; + result = 37 * result + width; + result = 37 * result + height; + result = 37 * result + numBands; + result = 37 * result + scanlineStride; + for (int i = 0; i < bitMasks.length; i++) + result = 37 * result + bitMasks[i]; + for (int i = 0; i < bitOffsets.length; i++) + result = 37 * result + bitOffsets[i]; + return result; + } + + /** + * Creates a String with some information about this SampleModel. + * @return A String describing this SampleModel. + * @see java.lang.Object#toString() + */ + public String toString() + { + CPStringBuilder result = new CPStringBuilder(); + result.append(getClass().getName()); + result.append("["); + result.append("scanlineStride=").append(scanlineStride); + for(int i = 0; i < bitMasks.length; i+=1) + { + result.append(", mask[").append(i).append("]=0x").append( + Integer.toHexString(bitMasks[i])); + } + + result.append("]"); + return result.toString(); + } +} diff --git a/libjava/classpath/java/awt/image/TileObserver.java b/libjava/classpath/java/awt/image/TileObserver.java new file mode 100644 index 000000000..769888e40 --- /dev/null +++ b/libjava/classpath/java/awt/image/TileObserver.java @@ -0,0 +1,47 @@ +/* TileObserver.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +/** + * NEEDS DOCUMENTATION + */ +public interface TileObserver +{ + void tileUpdate(WritableRenderedImage src, int x, int y, boolean writable); +} // interface TileObserver diff --git a/libjava/classpath/java/awt/image/VolatileImage.java b/libjava/classpath/java/awt/image/VolatileImage.java new file mode 100644 index 000000000..308654162 --- /dev/null +++ b/libjava/classpath/java/awt/image/VolatileImage.java @@ -0,0 +1,253 @@ +/* VolatileImage.java -- a hardware-accelerated image buffer + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Transparency; +import java.awt.ImageCapabilities; + +/** + * VolatileImage represents a hardware-accelerated graphics buffer. + * The native graphics system may free or damage the resources + * occupied by a VolatileImage at any time. As such, one must + * frequently check the "validity" of the image buffer's resources. + * + * A volatile image's "validity" depends on multiple factors. Its + * resources may have become unavailble in which case you must + * reallocate them. If you move the image from one output device to + * another, you may need to recreate the image's resources if the new + * output device's capabilities don't match the old one's. Finally, + * if the contents of the image's buffer have been damaged you must + * re-render the image. + * + * VolatileImages should always be created using either + * Component.createVolatileImage or + * GraphicsConfiguration.createCompatibleVolatileImage. + */ +public abstract class VolatileImage extends Image + implements Transparency +{ + /** + * One of validate's possible return values. Indicates that the + * image buffer matches its graphics configuration's capabilities + * and that its resources are initialized and ready to be drawn + * into. Also implies that any existing image rendered to the + * buffer is intact and need not be re-rendered. + */ + public static final int IMAGE_OK = 0; + + /** + * One of validate's possible return values. Indicates that the + * image buffer has been restored, meaning that it is valid and + * ready-to-use but that its previous contents have been lost. This + * return value implies that the image needs to be re-rendered. + */ + public static final int IMAGE_RESTORED = 1; + + /** + * One of validate's possible return values. Indicates that the + * image buffer type is unsupported by the current graphics + * configuration. The graphics configuration may have changed, for + * example if the image moved from one output device to another. + * This return value implies that the image buffer's resources + * should be re-acquired. + */ + public static final int IMAGE_INCOMPATIBLE = 2; + + /** + * This image's transparency type. One of Transparency.BITMASK, + * Transparency.OPAQUE or Transparency.TRANSLUCENT. + * + * @since 1.5 + */ + protected int transparency; + + /** + * Default constructor. VolatileImages should not be created + * directly. Rather, you should use Component.createVolatileImage + * or GraphicsConfiguration.createCompatibleVolatileImage. + */ + public VolatileImage() + { + } + + /** + * Returns an image representing the current state of the volatile + * image buffer. The returned image is static meaning that it is + * not updated after being created. It is a snapshot of the + * volatile image buffer at the time getSnapshot is called. + * + * This method, which reads pixels from the volatile image buffer, + * may be less-performant than methods that write pixels since + * VolatileImages are typically optimized for writing. + * + * @return a BufferedImage representing this VolatileImage + */ + public abstract BufferedImage getSnapshot(); + + /** + * Returns the width of this image buffer. + * + * @return the width of this VolatileImage + */ + public abstract int getWidth(); + + /** + * Returns the height of this image buffer. + * + * @return the height of this VolatileImage + */ + public abstract int getHeight(); + + /** + * Calling this method is equivalent to calling + * getSnapshot().getSource(). The ImageProducer produces pixels + * from the BufferedImage snapshot and not from the VolatileImage + * itself. Thus, changes to the VolatileImage that occur after this + * ImageProducer has been retrieved will not be reflected in the + * produced pixels. + * + * This method, which reads pixels from the volatile image buffer, + * may be less-performant than methods that write pixels since + * VolatileImages are typically optimized for writing. + * + * @return an ImageProducer for a static BufferedImage snapshot of + * this image buffer + */ + public ImageProducer getSource() + { + return getSnapshot().getSource(); + } + + /** + * Releases the system resources taken by this image. + */ + public void flush() + { + } + + /** + * Returns a Graphics2D object that can be used to draw onto this + * image. This method is present for backwards-compatibility. It + * simply returns the result of createGraphics. + * + * @return a Graphics2D object that can be used to draw onto this + * image + */ + public Graphics getGraphics() + { + return createGraphics(); + } + + /** + * Returns a Graphics2D object that can be used to draw onto this + * image. + * + * @return a Graphics2D object that can be used to draw onto this + * image + */ + public abstract Graphics2D createGraphics(); + + /** + * Validates and restores this image. If the image buffer has + * become unavailable for further use since the last call to + * validate, validate will allocate a new image buffer. The image + * is also "validated" against the GraphicsConfiguration parameter. + * + * "Validating" the image means checking that the capabilities it + * requires of the output device are indeed supported by the given + * output device. If the image's characteristics, which can be + * highly output device-specific, are not supported by the graphics + * configuration, then IMAGE_INCOMPATIBLE will be returned. This + * can happen, for example, if this image was created on one output + * device, then validated against a different output device with + * different capabilities. Calling validate with a NULL gc argument + * causes validate to skip the validation test. + * + * @param gc graphics configuration against which to validate or + * NULL + * + * @return a code indicating the result of validation. One of: + *
      + *
    • IMAGE_OK if the image did not need to be + * validated and didn't need to be restored
    • + *
    • IMAGE_RESTORED if the image may need to be + * re-rendered.
    • + *
    • IMAGE_INCOMPATIBLE if this image's + * requirements are not fulfilled by the graphics configuration + * parameter. This implies that you need to create a new + * VolatileImage for the different GraphicsConfiguration or + * Component. This return value implies nothing about whether the + * image is valid or needs to be re-rendered.
    • + *
    + */ + public abstract int validate(GraphicsConfiguration gc); + + /** + * Returns true if the contents of the image buffer have been + * damaged or if the image buffer's resources have been reclaimed by + * the graphics system. You should call this method after a series + * of rendering operations to or from the image, to see if the image + * buffer needs to be revalidated or the image re-rendered. + * + * @return true if the validate should be called, false otherwise + */ + public abstract boolean contentsLost(); + + /** + * Returns the capabilities of this image buffer. + * + * @return the capabilities of this image buffer + */ + public abstract ImageCapabilities getCapabilities(); + + /** + * Returns the transparency type of this image. + * + * @return Transparency.OPAQUE, Transparency.BITMASK or + * Transparency.TRANSLUCENT + */ + public int getTransparency() + { + return transparency; + } +} diff --git a/libjava/classpath/java/awt/image/WritableRaster.java b/libjava/classpath/java/awt/image/WritableRaster.java new file mode 100644 index 000000000..68774f785 --- /dev/null +++ b/libjava/classpath/java/awt/image/WritableRaster.java @@ -0,0 +1,448 @@ +/* Copyright (C) 2000, 2002, 2003, 2006, Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Point; +import java.awt.Rectangle; + +/** + * A raster with methods to support updating pixel values. + * + * @author Rolf W. Rasmussen (rolfwr@ii.uib.no) + */ +public class WritableRaster extends Raster +{ + /** + * Creates a new WritableRaster. + * + * @param sampleModel the sample model. + * @param origin the origin. + */ + protected WritableRaster(SampleModel sampleModel, Point origin) + { + this(sampleModel, sampleModel.createDataBuffer(), origin); + } + + /** + * Creates a new WritableRaster instance. + * + * @param sampleModel the sample model. + * @param dataBuffer the data buffer. + * @param origin the origin. + */ + protected WritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, + Point origin) + { + this(sampleModel, dataBuffer, + new Rectangle(origin != null ? origin.x : 0, + origin != null ? origin.y : 0, + sampleModel.getWidth(), sampleModel.getHeight()), + origin, null); + } + + /** + * Creates a new WritableRaster instance. + * + * @param sampleModel the sample model. + * @param dataBuffer the data buffer. + * @param aRegion the raster's bounds. + * @param sampleModelTranslate the translation. + * @param parent the parent. + */ + protected WritableRaster(SampleModel sampleModel, + DataBuffer dataBuffer, + Rectangle aRegion, + Point sampleModelTranslate, + WritableRaster parent) + { + super(sampleModel, dataBuffer, aRegion, sampleModelTranslate, parent); + } + + /** + * Returns the raster's parent, cast as a {@link WritableRaster}. + * + * @return The raster's parent. + */ + public WritableRaster getWritableParent() + { + return (WritableRaster) getParent(); + } + + /** + * @param childMinX + * @param childMinY + * @return + */ + public WritableRaster createWritableTranslatedChild(int childMinX, + int childMinY) + { + return createWritableChild(minX, minY, width, height, + childMinX, childMinY, null); + } + + /** + * + * @param parentX + * @param parentY + * @param w + * @param h + * @param childMinX + * @param childMinY + * @param bandList + * @return + */ + public WritableRaster createWritableChild(int parentX, int parentY, + int w, int h, int childMinX, int childMinY, int[] bandList) + { + // This mirrors the code from the super class + + if (parentX < minX || parentX + w > minX + width + || parentY < minY || parentY + h > minY + height) + throw new RasterFormatException("Child raster extends beyond parent"); + + SampleModel sm = (bandList == null) ? + sampleModel : + sampleModel.createSubsetSampleModel(bandList); + + return new WritableRaster(sm, getDataBuffer(), + new Rectangle(childMinX, childMinY, w, h), + new Point(sampleModelTranslateX + childMinX - + parentX, + sampleModelTranslateY + childMinY - + parentY), + this); + } + + public Raster createChild(int parentX, int parentY, int width, + int height, int childMinX, int childMinY, + int[] bandList) + { + if (parentX < minX || parentX + width > minX + this.width + || parentY < minY || parentY + height > minY + this.height) + throw new RasterFormatException("Child raster extends beyond parent"); + + SampleModel sm = (bandList == null) ? + sampleModel : + sampleModel.createSubsetSampleModel(bandList); + + return new WritableRaster(sm, dataBuffer, + new Rectangle(childMinX, childMinY, width, height), + new Point(sampleModelTranslateX + childMinX - parentX, + sampleModelTranslateY + childMinY - parentY), + this); + } + + public void setDataElements(int x, int y, Object inData) + { + sampleModel.setDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, inData, dataBuffer); + } + + public void setDataElements(int x, int y, Raster inRaster) + { + Object dataElements = getDataElements(0, 0, inRaster.getWidth(), + inRaster.getHeight(), null); + setDataElements(x, y, dataElements); + } + + public void setDataElements(int x, int y, int w, int h, Object inData) + { + sampleModel.setDataElements(x - sampleModelTranslateX, + y - sampleModelTranslateY, w, h, inData, dataBuffer); + } + + /** + * + * @param srcRaster + */ + public void setRect(Raster srcRaster) + { + setRect(0, 0, srcRaster); + } + + /** + * + * @param dx + * @param dy + * @param srcRaster + */ + public void setRect(int dx, int dy, Raster srcRaster) + { + Rectangle targetUnclipped = new Rectangle(srcRaster.getMinX() + dx, + srcRaster.getMinY() + dy, srcRaster.getWidth(), srcRaster.getHeight()); + + Rectangle target = getBounds().intersection(targetUnclipped); + + if (target.isEmpty()) return; + + int sx = target.x - dx; + int sy = target.y - dy; + + // FIXME: Do tests on rasters and use get/set data instead. + + /* The JDK documentation seems to imply this implementation. + (the trucation of higher bits), but an implementation using + get/setDataElements would be more efficient. None of the + implementations would do anything sensible when the sample + models don't match. + + But this is probably not the place to consider such + optimizations.*/ + + int[] pixels = srcRaster.getPixels(sx, sy, target.width, target.height, + (int[]) null); + + setPixels(target.x, target.y, target.width, target.height, pixels); + } + + /** + * Sets the samples for the pixel at (x, y) in the raster to the specified + * values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param iArray the sample values (null not permitted). + * + * @throws NullPointerException if iArray is null. + */ + public void setPixel(int x, int y, int[] iArray) + { + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, + iArray, dataBuffer); + } + + /** + * Sets the samples for the pixel at (x, y) in the raster to the specified + * values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param fArray the sample values (null not permitted). + * + * @throws NullPointerException if fArray is null. + */ + public void setPixel(int x, int y, float[] fArray) + { + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, + fArray, dataBuffer); + } + + /** + * Sets the samples for the pixel at (x, y) in the raster to the specified + * values. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param dArray the sample values (null not permitted). + * + * @throws NullPointerException if dArray is null. + */ + public void setPixel(int x, int y, double[] dArray) + { + sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, + dArray, dataBuffer); + } + + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the raster. The array is ordered by pixels (that is, all + * the samples for the first pixel are grouped together, followed by all the + * samples for the second pixel, and so on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param iArray the pixel sample values (null not permitted). + * + * @throws NullPointerException if iArray is null. + */ + public void setPixels(int x, int y, int w, int h, int[] iArray) + { + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, iArray, dataBuffer); + } + + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the raster. The array is ordered by pixels (that is, all + * the samples for the first pixel are grouped together, followed by all the + * samples for the second pixel, and so on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param fArray the pixel sample values (null not permitted). + * + * @throws NullPointerException if fArray is null. + */ + public void setPixels(int x, int y, int w, int h, float[] fArray) + { + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, fArray, dataBuffer); + } + + /** + * Sets the sample values for the pixels in the region specified by + * (x, y, w, h) in the raster. The array is ordered by pixels (that is, all + * the samples for the first pixel are grouped together, followed by all the + * samples for the second pixel, and so on). + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param dArray the pixel sample values (null not permitted). + * + * @throws NullPointerException if dArray is null. + */ + public void setPixels(int x, int y, int w, int h, double[] dArray) + { + sampleModel.setPixels(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, dArray, dataBuffer); + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + */ + public void setSample(int x, int y, int b, int s) + { + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, + b, s, dataBuffer); + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + */ + public void setSample(int x, int y, int b, float s) + { + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, + b, s, dataBuffer); + } + + /** + * Sets the sample value for a band for the pixel at (x, y) in the raster. + * + * @param x the x-coordinate of the pixel. + * @param y the y-coordinate of the pixel. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param s the sample value. + */ + public void setSample(int x, int y, int b, double s) + { + sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, + b, s, dataBuffer); + } + + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the raster. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param iArray the sample values (null not permitted). + * + * @throws NullPointerException if iArray is null. + */ + public void setSamples(int x, int y, int w, int h, int b, + int[] iArray) + { + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, b, iArray, dataBuffer); + } + + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the raster. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param fArray the sample values (null not permitted). + * + * @throws NullPointerException if fArray is null. + */ + public void setSamples(int x, int y, int w, int h, int b, + float[] fArray) + { + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, b, fArray, dataBuffer); + } + + /** + * Sets the sample values for one band for the pixels in the region + * specified by (x, y, w, h) in the raster. + * + * @param x the x-coordinate of the top-left pixel. + * @param y the y-coordinate of the top-left pixel. + * @param w the width of the region of pixels. + * @param h the height of the region of pixels. + * @param b the band (in the range 0 to + * getNumBands() - 1). + * @param dArray the sample values (null not permitted). + * + * @throws NullPointerException if dArray is null. + */ + public void setSamples(int x, int y, int w, int h, int b, + double[] dArray) + { + sampleModel.setSamples(x - sampleModelTranslateX, y - sampleModelTranslateY, + w, h, b, dArray, dataBuffer); + } +} diff --git a/libjava/classpath/java/awt/image/WritableRenderedImage.java b/libjava/classpath/java/awt/image/WritableRenderedImage.java new file mode 100644 index 000000000..9142fe004 --- /dev/null +++ b/libjava/classpath/java/awt/image/WritableRenderedImage.java @@ -0,0 +1,56 @@ +/* WritableRenderedImage.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image; + +import java.awt.Point; + +/** + * NEEDS DOCUMENTATION + */ +public interface WritableRenderedImage extends RenderedImage +{ + void addTileObserver(TileObserver to); + void removeTileObserver(TileObserver to); + WritableRaster getWritableTile(int x, int y); + void releaseWritableTile(int x, int y); + boolean isTileWritable(int x, int y); + Point[] getWritableTileIndices(); + boolean hasTileWriters(); + void setData(Raster r); +} // interface WritableRenderedImage diff --git a/libjava/classpath/java/awt/image/package.html b/libjava/classpath/java/awt/image/package.html new file mode 100644 index 000000000..50fa99d13 --- /dev/null +++ b/libjava/classpath/java/awt/image/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.image + + +

    Image consumers, producers and filters.

    + + + diff --git a/libjava/classpath/java/awt/image/renderable/ContextualRenderedImageFactory.java b/libjava/classpath/java/awt/image/renderable/ContextualRenderedImageFactory.java new file mode 100644 index 000000000..8852bef1c --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/ContextualRenderedImageFactory.java @@ -0,0 +1,56 @@ +/* ContextualRenderedImageFactory.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.geom.Rectangle2D; +import java.awt.image.RenderedImage; + +/** + * STUBBED + */ +public interface ContextualRenderedImageFactory extends RenderedImageFactory +{ + RenderContext mapRenderContext(int i, RenderContext context, + ParameterBlock block, RenderableImage image); + RenderedImage create(RenderContext context, ParameterBlock block); + Rectangle2D getBounds2D(ParameterBlock block); + Object getProperty(ParameterBlock block, String name); + String[] getPropertyNames(); + boolean isDynamic(); +} // interface ContextualRenderedImageFactory diff --git a/libjava/classpath/java/awt/image/renderable/ParameterBlock.java b/libjava/classpath/java/awt/image/renderable/ParameterBlock.java new file mode 100644 index 000000000..f38077816 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/ParameterBlock.java @@ -0,0 +1,308 @@ +/* ParameterBlock.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.image.RenderedImage; +import java.io.Serializable; +import java.util.Vector; + +public class ParameterBlock implements Cloneable, Serializable +{ + private static final long serialVersionUID = -7577115551785240750L; + protected Vector sources; + protected Vector parameters; + + public ParameterBlock() + { + this(new Vector(), new Vector()); + } + + public ParameterBlock(Vector sources) + { + this(sources, new Vector()); + } + + public ParameterBlock(Vector sources, Vector parameters) + { + this.sources = sources; + this.parameters = parameters; + } + + public Object shallowClone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // impossible + } + } + + public Object clone() + { + ParameterBlock pb = (ParameterBlock) shallowClone(); + if (sources != null) + pb.sources = (Vector) sources.clone(); + if (parameters != null) + pb.parameters = (Vector) parameters.clone(); + return pb; + } + + public ParameterBlock addSource(Object source) + { + sources.add(source); + return this; + } + + public Object getSource(int index) + { + return sources.get(index); + } + + public ParameterBlock setSource(Object source, int index) + { + sources.ensureCapacity(index); + sources.set(index, source); + return this; + } + + public RenderedImage getRenderedSource(int index) + { + return (RenderedImage) sources.get(index); + } + + public RenderableImage getRenderableSource(int index) + { + return (RenderableImage) sources.get(index); + } + + public int getNumSources() + { + return sources.size(); + } + + public Vector getSources() + { + return sources; + } + + public void setSources(Vector sources) + { + this.sources = sources; + } + + public void removeSources() + { + if (sources != null) + sources.clear(); + } + + public int getNumParameters() + { + return parameters.size(); + } + + public Vector getParameters() + { + return parameters; + } + + public void setParameters(Vector parameters) + { + this.parameters = parameters; + } + + public void removeParameters() + { + if (parameters != null) + parameters.clear(); + } + + public ParameterBlock add(Object o) + { + parameters.add(o); + return this; + } + + public ParameterBlock add(byte b) + { + return add(new Byte(b)); + } + + public ParameterBlock add(char c) + { + return add(new Character(c)); + } + + public ParameterBlock add(short s) + { + return add(new Short(s)); + } + + public ParameterBlock add(int i) + { + return add(new Integer(i)); + } + + public ParameterBlock add(long l) + { + return add(new Long(l)); + } + + public ParameterBlock add(float f) + { + return add(new Float(f)); + } + + public ParameterBlock add(double d) + { + return add(new Double(d)); + } + + public ParameterBlock set(Object o, int index) + { + parameters.ensureCapacity(index); + parameters.set(index, o); + return this; + } + + public ParameterBlock set(byte b, int index) + { + return set(new Byte(b), index); + } + + public ParameterBlock set(char c, int index) + { + return set(new Character(c), index); + } + + public ParameterBlock set(short s, int index) + { + return set(new Short(s), index); + } + + public ParameterBlock set(int i, int index) + { + return set(new Integer(i), index); + } + + public ParameterBlock set(long l, int index) + { + return set(new Long(l), index); + } + + public ParameterBlock set(float f, int index) + { + return set(new Float(f), index); + } + + public ParameterBlock set(double d, int index) + { + return set(new Double(d), index); + } + + public Object getObjectParameter(int index) + { + return parameters.get(index); + } + + public byte getByteParameter(int index) + { + return ((Byte) parameters.get(index)).byteValue(); + } + + public char getCharParameter(int index) + { + return ((Character) parameters.get(index)).charValue(); + } + + public short getShortParameter(int index) + { + return ((Short) parameters.get(index)).shortValue(); + } + + public int getIntParameter(int index) + { + return ((Integer) parameters.get(index)).intValue(); + } + + public long getLongParameter(int index) + { + return ((Long) parameters.get(index)).longValue(); + } + + public float getFloatParameter(int index) + { + return ((Float) parameters.get(index)).floatValue(); + } + + public double getDoubleParameter(int index) + { + return ((Double) parameters.get(index)).doubleValue(); + } + + public Class[] getParamClasses() + { + int i = parameters.size(); + Class[] result = new Class[i]; + while (--i >= 0) + { + Class c = parameters.get(i).getClass(); + if (c == Byte.class) + result[i] = byte.class; + else if (c == Character.class) + result[i] = char.class; + else if (c == Short.class) + result[i] = short.class; + else if (c == Integer.class) + result[i] = int.class; + else if (c == Long.class) + result[i] = long.class; + else if (c == Float.class) + result[i] = float.class; + else if (c == Double.class) + result[i] = double.class; + else + result[i] = c; + } + return result; + } +} // class ParameterBlock diff --git a/libjava/classpath/java/awt/image/renderable/RenderContext.java b/libjava/classpath/java/awt/image/renderable/RenderContext.java new file mode 100644 index 000000000..8017c1acf --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderContext.java @@ -0,0 +1,141 @@ +/* RenderContext.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; + +public class RenderContext implements Cloneable +{ + private AffineTransform xform; + private Shape aoi; + private RenderingHints hints; + + public RenderContext(AffineTransform xform, Shape aoi, RenderingHints hints) + { + this.xform = xform; + this.aoi = aoi; + this.hints = hints; + } + + public RenderContext(AffineTransform xform) + { + this(xform, null, null); + } + + public RenderContext(AffineTransform xform, RenderingHints hints) + { + this(xform, null, hints); + } + + public RenderContext(AffineTransform xform, Shape aoi) + { + this(xform, aoi, null); + } + + public RenderingHints getRenderingHints() + { + return hints; + } + + public void setRenderingHints(RenderingHints hints) + { + this.hints = hints; + } + + public void setTransform(AffineTransform xform) + { + this.xform = xform; + } + + public void preConcatenateTransform(AffineTransform pre) + { + preConcetenateTransform (pre); + } + + /** @deprecated */ + public void preConcetenateTransform(AffineTransform pre) + { + xform.preConcatenate (pre); + } + + public void concatenateTransform(AffineTransform post) + { + concetenateTransform (post); + } + + /** @deprecated */ + public void concetenateTransform(AffineTransform post) + { + xform.concatenate (post); + } + + public AffineTransform getTransform() + { + return xform; + } + + public void setAreaOfInterest(Shape aoi) + { + this.aoi = aoi; + } + + public Shape getAreaOfInterest() + { + return aoi; + } + + public Object clone() + { + try + { + RenderContext copy = (RenderContext) super.clone(); + if (xform != null) + copy.xform = (AffineTransform) xform.clone(); + if (hints != null) + copy.hints = (RenderingHints) hints.clone(); + return copy; + } + catch (CloneNotSupportedException e) + { + throw (Error) new InternalError().initCause(e); // impossible + } + } +} // class RenderContext diff --git a/libjava/classpath/java/awt/image/renderable/RenderableImage.java b/libjava/classpath/java/awt/image/renderable/RenderableImage.java new file mode 100644 index 000000000..31767af2c --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderableImage.java @@ -0,0 +1,61 @@ +/* RenderableImage.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.image.RenderedImage; +import java.util.Vector; + +public interface RenderableImage +{ + String HINTS_OBSERVED = "HINTS_OBSERVED"; + + Vector getSources(); + Object getProperty(String name); + String[] getPropertyNames(); + boolean isDynamic(); + float getWidth(); + float getHeight(); + float getMinX(); + float getMinY(); + RenderedImage createScaledRendering(int w, int h, RenderingHints hints); + RenderedImage createDefaultRendering(); + RenderedImage createRendering(RenderContext context); + +} // interface RenderableImage diff --git a/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java b/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java new file mode 100644 index 000000000..81e537cd5 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderableImageOp.java @@ -0,0 +1,157 @@ +/* RenderableImageOp.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.image.RenderedImage; +import java.util.Vector; + +public class RenderableImageOp implements RenderableImage +{ + private final ContextualRenderedImageFactory crif; + private ParameterBlock block; + + public RenderableImageOp(ContextualRenderedImageFactory crif, + ParameterBlock block) + { + this.crif = crif; + this.block = (ParameterBlock) block.clone(); + } + + public Vector getSources() + { + if (block.sources == null) + return null; + int size = block.sources.size(); + Vector v = new Vector(); + for (int i = 0; i < size; i++) + { + Object o = block.sources.get(i); + if (o instanceof RenderableImage) + v.add(o); + } + return v; + } + + public Object getProperty(String name) + { + return crif.getProperty(block, name); + } + + public String[] getPropertyNames() + { + return crif.getPropertyNames(); + } + + public boolean isDynamic() + { + return crif.isDynamic(); + } + + public float getWidth() + { + return (float) crif.getBounds2D(block).getWidth(); + } + + public float getHeight() + { + return (float) crif.getBounds2D(block).getHeight(); + } + + public float getMinX() + { + return (float) crif.getBounds2D(block).getX(); + } + + public float getMinY() + { + return (float) crif.getBounds2D(block).getY(); + } + + public ParameterBlock setParameterBlock(ParameterBlock block) + { + ParameterBlock result = this.block; + this.block = (ParameterBlock) block.clone(); + return result; + } + + public ParameterBlock getParameterBlock() + { + return block; + } + + public RenderedImage createScaledRendering(int w, int h, + RenderingHints hints) + { + if (w == 0) + if (h == 0) + throw new IllegalArgumentException(); + else + w = Math.round(h * getWidth() / getHeight()); + if (h == 0) + h = Math.round(w * getHeight() / getWidth()); + AffineTransform xform = AffineTransform.getScaleInstance(w * getWidth(), + h * getHeight()); + return createRendering(new RenderContext(xform, hints)); + } + + public RenderedImage createDefaultRendering() + { + return createRendering(new RenderContext(new AffineTransform())); + } + + public RenderedImage createRendering(RenderContext context) + { + ParameterBlock copy = (ParameterBlock) block.clone(); + int i = block.sources.size(); + while (--i >= 0) + { + Object o = block.sources.get(i); + if (o instanceof RenderableImage) + { + RenderableImage ri = (RenderableImage) o; + RenderContext rc = crif.mapRenderContext(i, context, block, ri); + copy.sources.set(i, ri.createRendering(rc)); + } + } + // Now copy.sources should be only RenderedImages. + return crif.create(context, copy); + } +} // class RenderableImageOp diff --git a/libjava/classpath/java/awt/image/renderable/RenderableImageProducer.java b/libjava/classpath/java/awt/image/renderable/RenderableImageProducer.java new file mode 100644 index 000000000..bd3b507cb --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderableImageProducer.java @@ -0,0 +1,166 @@ +/* RenderableImageProducer.java -- + Copyright (C) 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.ImageConsumer; +import java.awt.image.ImageProducer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.util.ArrayList; +import java.util.Iterator; + +public class RenderableImageProducer implements ImageProducer, Runnable +{ + private RenderableImage image; + private RenderContext context; + private ArrayList consumers = new ArrayList(); + + public RenderableImageProducer(RenderableImage image, RenderContext context) + { + this.image = image; + this.context = context; + } + + public void setRenderContext(RenderContext context) + { + this.context = context; + } + + public void addConsumer(ImageConsumer consumer) + { + synchronized (consumers) + { + if (! consumers.contains(consumer)) + consumers.add(consumer); + } + } + + public boolean isConsumer(ImageConsumer consumer) + { + synchronized (consumers) + { + return consumers.contains(consumer); + } + } + + public void removeConsumer(ImageConsumer consumer) + { + synchronized (consumers) + { + consumers.remove(consumer); + } + } + + public void startProduction(ImageConsumer consumer) + { + addConsumer(consumer); + Thread t = new Thread(this, "RenderableImageProducerWorker"); + t.start(); + } + + public void requestTopDownLeftRightResend(ImageConsumer consumer) + { + // Do nothing. The contract says we can ignore this call, so we do. + } + + public void run() + { + // This isn't ideal but it avoids fail-fast problems. + // Alternatively, we could clone 'consumers' here. + synchronized (consumers) + { + RenderedImage newImage; + if (context == null) + newImage = image.createDefaultRendering(); + else + newImage = image.createRendering(context); + Raster newData = newImage.getData(); + ColorModel colorModel = newImage.getColorModel(); + if (colorModel == null) + colorModel = ColorModel.getRGBdefault(); + SampleModel sampleModel = newData.getSampleModel(); + DataBuffer dataBuffer = newData.getDataBuffer(); + int width = newData.getWidth(); + int height = newData.getHeight(); + + // Initialize the consumers. + Iterator it = consumers.iterator(); + while (it.hasNext()) + { + ImageConsumer target = (ImageConsumer) it.next(); + target.setHints(ImageConsumer.COMPLETESCANLINES + | ImageConsumer.SINGLEFRAME + | ImageConsumer.SINGLEPASS + | ImageConsumer.TOPDOWNLEFTRIGHT); + target.setDimensions(width, height); + } + + // Work in scan-line order. + int[] newLine = new int[width]; + int[] bands = new int[sampleModel.getNumBands()]; + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + sampleModel.getPixel(x, y, bands, dataBuffer); + newLine[x] = colorModel.getDataElement(bands, 0); + } + + // Tell the consumers about the new scan line. + it = consumers.iterator(); + while (it.hasNext()) + { + ImageConsumer target = (ImageConsumer) it.next(); + target.setPixels(0, y, width, 1, colorModel, newLine, 0, width); + } + } + + // Tell the consumers that we're done. + it = consumers.iterator(); + while (it.hasNext()) + { + ImageConsumer target = (ImageConsumer) it.next(); + target.imageComplete(ImageConsumer.STATICIMAGEDONE); + } + } + } +} // class RenderableImageProducer diff --git a/libjava/classpath/java/awt/image/renderable/RenderedImageFactory.java b/libjava/classpath/java/awt/image/renderable/RenderedImageFactory.java new file mode 100644 index 000000000..ea2bd69f7 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/RenderedImageFactory.java @@ -0,0 +1,47 @@ +/* RenderedImageFactory.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.image.renderable; + +import java.awt.RenderingHints; +import java.awt.image.RenderedImage; + +public interface RenderedImageFactory +{ + RenderedImage create(ParameterBlock block, RenderingHints hints); +} // interface RenderedImageFactory diff --git a/libjava/classpath/java/awt/image/renderable/package.html b/libjava/classpath/java/awt/image/renderable/package.html new file mode 100644 index 000000000..a24237e72 --- /dev/null +++ b/libjava/classpath/java/awt/image/renderable/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.image.renderable + + +

    + + + diff --git a/libjava/classpath/java/awt/package.html b/libjava/classpath/java/awt/package.html new file mode 100644 index 000000000..c5ff9881a --- /dev/null +++ b/libjava/classpath/java/awt/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt + + +

    Abstract Window Toolkit classes.

    + + + diff --git a/libjava/classpath/java/awt/peer/ButtonPeer.java b/libjava/classpath/java/awt/peer/ButtonPeer.java new file mode 100644 index 000000000..6e2510ff5 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ButtonPeer.java @@ -0,0 +1,45 @@ +/* ButtonPeer.java -- Peer interface for buttons + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface ButtonPeer extends ComponentPeer +{ + void setLabel (String label); + +} // interface ButtonPeer diff --git a/libjava/classpath/java/awt/peer/CanvasPeer.java b/libjava/classpath/java/awt/peer/CanvasPeer.java new file mode 100644 index 000000000..241a0e4e3 --- /dev/null +++ b/libjava/classpath/java/awt/peer/CanvasPeer.java @@ -0,0 +1,44 @@ +/* CanvasPeer.java -- Peer interface for a canvas + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface CanvasPeer extends ComponentPeer +{ + +} // interface CanvasPeer diff --git a/libjava/classpath/java/awt/peer/CheckboxMenuItemPeer.java b/libjava/classpath/java/awt/peer/CheckboxMenuItemPeer.java new file mode 100644 index 000000000..3cc8f72a6 --- /dev/null +++ b/libjava/classpath/java/awt/peer/CheckboxMenuItemPeer.java @@ -0,0 +1,45 @@ +/* CheckboxMenuItemPeer.java -- Peer interface for checkbox menu items + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface CheckboxMenuItemPeer extends MenuItemPeer +{ + void setState (boolean state); + +} // interface CheckboxMenuItemPeer diff --git a/libjava/classpath/java/awt/peer/CheckboxPeer.java b/libjava/classpath/java/awt/peer/CheckboxPeer.java new file mode 100644 index 000000000..639614891 --- /dev/null +++ b/libjava/classpath/java/awt/peer/CheckboxPeer.java @@ -0,0 +1,51 @@ +/* CheckboxPeer.java -- Interface for checkbox peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.CheckboxGroup; + +public interface CheckboxPeer extends ComponentPeer +{ + void setCheckboxGroup (CheckboxGroup group); + + void setLabel (String label); + + void setState (boolean state); + +} // interface CheckboxPeer diff --git a/libjava/classpath/java/awt/peer/ChoicePeer.java b/libjava/classpath/java/awt/peer/ChoicePeer.java new file mode 100644 index 000000000..3605a97ef --- /dev/null +++ b/libjava/classpath/java/awt/peer/ChoicePeer.java @@ -0,0 +1,53 @@ +/* ChoicePeer.java -- Peer for choice box + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface ChoicePeer extends ComponentPeer +{ + void add (String item, int index); + + void addItem (String item, int index); + + void remove (int index); + + void removeAll(); + + void select (int index); + +} // interface ChoicePeer diff --git a/libjava/classpath/java/awt/peer/ComponentPeer.java b/libjava/classpath/java/awt/peer/ComponentPeer.java new file mode 100644 index 000000000..736cdf376 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ComponentPeer.java @@ -0,0 +1,532 @@ +/* ComponentPeer.java -- Toplevel component peer + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.BufferCapabilities; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.event.PaintEvent; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.awt.image.VolatileImage; + +import sun.awt.CausedFocusEvent; + +/** + * Defines the methods that a component peer is required to implement. + */ +public interface ComponentPeer +{ + /** + * Returns the construction status of the specified image. This is called + * by {@link Component#checkImage(Image, int, int, ImageObserver)}. + * + * @param img the image + * @param width the width of the image + * @param height the height of the image + * @param ob the image observer to be notified of updates of the status + * + * @return a bitwise ORed set of ImageObserver flags + */ + int checkImage(Image img, int width, int height, + ImageObserver ob); + + /** + * Creates an image by starting the specified image producer. This is called + * by {@link Component#createImage(ImageProducer)}. + * + * @param prod the image producer to be used to create the image + * + * @return the created image + */ + Image createImage(ImageProducer prod); + + /** + * Creates an empty image with the specified width and + * height. + * + * @param width the width of the image to be created + * @param height the height of the image to be created + * + * @return the created image + */ + Image createImage(int width, int height); + + /** + * Disables the component. This is called by {@link Component#disable()}. + */ + void disable(); + + /** + * Disposes the component peer. This should release all resources held by the + * peer. This is called when the component is no longer in use. + */ + void dispose(); + + /** + * Enables the component. This is called by {@link Component#enable()}. + */ + void enable(); + + /** + * Returns the color model of the component. This is currently not used. + * + * @return the color model of the component + */ + ColorModel getColorModel(); + + /** + * Returns the font metrics for the specified font. This is called by + * {@link Component#getFontMetrics(Font)}. + * + * @param f the font for which to query the font metrics + * + * @return the font metrics for the specified font + */ + FontMetrics getFontMetrics(Font f); + + /** + * Returns a {@link Graphics} object suitable for drawing on this component. + * This is called by {@link Component#getGraphics()}. + * + * @return a graphics object suitable for drawing on this component + */ + Graphics getGraphics(); + + /** + * Returns the location of this component in screen coordinates. This is + * called by {@link Component#getLocationOnScreen()}. + * + * @return the location of this component in screen coordinates + */ + Point getLocationOnScreen(); + + /** + * Returns the minimum size for the component. This is called by + * {@link Component#getMinimumSize()}. + * + * @return the minimum size for the component + * + * @specnote Presumably this method got added to replace minimumSize(). + * However, testing shows that this is never called in the RI + * (tested with JDK5), but instead minimumSize() is called + * directly. It is advisable to implement this method to delegate + * to minimumSize() and put the real implementation in there. + */ + Dimension getMinimumSize(); + + /** + * Returns the preferred size for the component. This is called by + * {@link Component#getPreferredSize()}. + * + * @return the preferred size for the component + * + * @specnote Presumably this method got added to replace preferredSize(). + * However, testing shows that this is never called in the RI + * (tested with JDK5), but instead preferredSize() is called + * directly. It is advisable to implement this method to delegate + * to preferredSize() and put the real implementation in there. + */ + Dimension getPreferredSize(); + + /** + * Returns the toolkit that created this peer. + * + * @return the toolkit that created this peer + */ + Toolkit getToolkit(); + + /** + * Handles the given event. This is called from + * {@link Component#dispatchEvent(AWTEvent)} to give the peer a chance to + * react to events for the component. + * + * @param e the event + */ + void handleEvent(AWTEvent e); + + /** + * Makes the component invisible. This is called from + * {@link Component#hide()}. + */ + void hide(); + + /** + * Returns true if the component can receive keyboard input + * focus. This is called from {@link Component#isFocusTraversable()}. + * + * @specnote Part of the earlier 1.1 API, replaced by isFocusable(). + */ + boolean isFocusTraversable(); + + /** + * Returns true if the component can receive keyboard input + * focus. This is called from {@link Component#isFocusable()}. + */ + boolean isFocusable(); + + /** + * Returns the minimum size for the component. This is called by + * {@link Component#minimumSize()}. + * + * @return the minimum size for the component + */ + Dimension minimumSize(); + + /** + * Returns the preferred size for the component. This is called by + * {@link Component#getPreferredSize()}. + * + * @return the preferred size for the component + */ + Dimension preferredSize(); + + void paint(Graphics graphics); + + /** + * Prepares an image for rendering on this component. This is called by + * {@link Component#prepareImage(Image, int, int, ImageObserver)}. + * + * @param img the image to prepare + * @param width the desired width of the rendered image + * @param height the desired height of the rendered image + * @param ob the image observer to be notified of updates in the preparation + * process + * + * @return true if the image has been fully prepared, + * false otherwise (in which case the image observer + * receives updates) + */ + boolean prepareImage(Image img, int width, int height, + ImageObserver ob); + + void print(Graphics graphics); + + /** + * Repaints the specified rectangle of this component. This is called from + * {@link Component#repaint(long, int, int, int, int)}. + * + * @param tm number of milliseconds to wait with repainting + * @param x the X coordinate of the upper left corner of the damaged rectangle + * @param y the Y coordinate of the upper left corner of the damaged rectangle + * @param width the width of the damaged rectangle + * @param height the height of the damaged rectangle + */ + void repaint(long tm, int x, int y, int width, int height); + + /** + * Requests that this component receives the focus. This is called from + * {@link Component#requestFocus()}. + * + * @specnote Part of the earlier 1.1 API, apparently replaced by argument + * form of the same method. + */ + void requestFocus(); + + /** + * Requests that this component receives the focus. This is called from + * {@link Component#requestFocus()}. + * + * This method is only called for heavyweight component's peers. Lightweight + * components ask their nearest heavyweight component to request focus. + * It's up to the heavyweight peer to decide if any of it's lightweight + * descendants are allowed to receive keyboard input focus or not. If the + * focus request is finally approved, then the peer must post a FOCUS_GAINED + * event for the requested component. + * + * @param request the component for which the focus is requested + * @param temporary indicates if the focus change is temporary (true) or + * permanent (false) + * @param allowWindowFocus indicates if it's allowed to change window focus + * @param time the timestamp + */ + boolean requestFocus(Component request, boolean temporary, + boolean allowWindowFocus, long time); + + /** + * Notifies the peer that the bounds of this component have changed. This + * is called by {@link Component#reshape(int, int, int, int)}. + * + * @param x the X coordinate of the upper left corner of the component + * @param y the Y coordinate of the upper left corner of the component + * @param width the width of the component + * @param height the height of the component + */ + void reshape(int x, int y, int width, int height); + + /** + * Sets the background color of the component. This is called by + * {@link Component#setBackground(Color)}. + * + * @param color the background color to set + */ + void setBackground(Color color); + + /** + * Notifies the peer that the bounds of this component have changed. This + * is called by {@link Component#setBounds(int, int, int, int)}. + * + * @param x the X coordinate of the upper left corner of the component + * @param y the Y coordinate of the upper left corner of the component + * @param width the width of the component + * @param height the height of the component + */ + void setBounds(int x, int y, int width, int height); + + /** + * Sets the cursor of the component. This is called by + * {@link Component#setCursor(Cursor)}. + * + * @specnote Part of the earlier 1.1 API, apparently no longer needed. + */ + void setCursor(Cursor cursor); + + /** + * Sets the enabled/disabled state of this component. This is called by + * {@link Component#setEnabled(boolean)}. + * + * @param enabled true to enable the component, + * false to disable it + */ + void setEnabled(boolean enabled); + + /** + * Sets the font of the component. This is called by + * {@link Component#setFont(Font)}. + * + * @param font the font to set + */ + void setFont(Font font); + + /** + * Sets the foreground color of the component. This is called by + * {@link Component#setForeground(Color)}. + * + * @param color the foreground color to set + */ + void setForeground(Color color); + + /** + * Sets the visibility state of the component. This is called by + * {@link Component#setVisible(boolean)}. + * + * @param visible true to make the component visible, + * false to make it invisible + */ + void setVisible(boolean visible); + + /** + * Makes the component visible. This is called by {@link Component#show()}. + */ + void show(); + + /** + * Get the graphics configuration of the component. The color model + * of the component can be derived from the configuration. + * + * @return the graphics configuration of the component + */ + GraphicsConfiguration getGraphicsConfiguration(); + + /** + * Part of an older API, no longer needed. + */ + void setEventMask(long mask); + + /** + * Returns true if this component has been obscured, + * false otherwise. This will only work if + * {@link #canDetermineObscurity()} also returns true. + * + * @return true if this component has been obscured, + * false otherwise. + */ + boolean isObscured(); + + /** + * Returns true if this component peer can determine if the + * component has been obscured, false otherwise. + * + * @return true if this component peer can determine if the + * component has been obscured, false otherwise + */ + boolean canDetermineObscurity(); + + /** + * Coalesces the specified paint event. + * + * @param e the paint event + */ + void coalescePaintEvent(PaintEvent e); + + /** + * Updates the cursor. + */ + void updateCursorImmediately(); + + /** + * Returns true, if this component can handle wheel scrolling, + * false otherwise. + * + * @return true, if this component can handle wheel scrolling, + * false otherwise + */ + boolean handlesWheelScrolling(); + + /** + * A convenience method that creates a volatile image. The volatile + * image is created on the screen device on which this component is + * displayed, in the device's current graphics configuration. + * + * @param width width of the image + * @param height height of the image + * + * @see VolatileImage + * + * @since 1.2 + */ + VolatileImage createVolatileImage(int width, int height); + + /** + * Create a number of image buffers that implement a buffering + * strategy according to the given capabilities. + * + * @param numBuffers the number of buffers + * @param caps the buffering capabilities + * + * @throws AWTException if the specified buffering strategy is not + * implemented + * + * @since 1.2 + */ + void createBuffers(int numBuffers, BufferCapabilities caps) + throws AWTException; + + /** + * Return the back buffer of this component. + * + * @return the back buffer of this component. + * + * @since 1.2 + */ + Image getBackBuffer(); + + /** + * Perform a page flip, leaving the contents of the back buffer in + * the specified state. + * + * @param contents the state in which to leave the back buffer + * + * @since 1.2 + */ + void flip(BufferCapabilities.FlipContents contents); + + /** + * Destroy the resources created by createBuffers. + * + * @since 1.2 + */ + void destroyBuffers(); + + /** + * Get the bounds of this component peer. + * + * @return component peer bounds + * @since 1.5 + */ + Rectangle getBounds(); + + /** + * Reparent this component under another container. + * + * @param parent + * @since 1.5 + */ + void reparent(ContainerPeer parent); + + /** + * Set the bounds of this component peer. + * + * @param x the new x co-ordinate + * @param y the new y co-ordinate + * @param width the new width + * @param height the new height + * @param z the new stacking level + * @since 1.5 + */ + void setBounds (int x, int y, int width, int height, int z); + + /** + * Check if this component supports being reparented. + * + * @return true if this component can be reparented, + * false otherwise. + * @since 1.5 + */ + boolean isReparentSupported(); + + /** + * Layout this component peer. + * + * @since 1.5 + */ + void layout(); + + + /** + * Requests the focus on the component. + */ + boolean requestFocus(Component lightweightChild, boolean temporary, + boolean focusedWindowChangeAllowed, long time, + CausedFocusEvent.Cause cause); + +} diff --git a/libjava/classpath/java/awt/peer/ContainerPeer.java b/libjava/classpath/java/awt/peer/ContainerPeer.java new file mode 100644 index 000000000..497abdc32 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ContainerPeer.java @@ -0,0 +1,83 @@ +/* ContainerPeer.java -- Interface for container peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.peer; + +import java.awt.Insets; + +public interface ContainerPeer extends ComponentPeer +{ + Insets insets(); + + Insets getInsets(); + + void beginValidate(); + + void endValidate(); + + void beginLayout(); + + void endLayout(); + + boolean isPaintPending(); + + /** + * Check if this container peer can be restacked. + * + * @return true if this container peer supports being restacked, false otherwise + * @since 1.5 + */ + boolean isRestackSupported(); + + /** + * Cancel a pending paint event on a region of this container. + * + * @param x the x co-ordinate of the region + * @param y the y co-ordinate of the region + * @param width the width of the region + * @param height the height of the region + * @since 1.5 + */ + void cancelPendingPaint(int x, int y, int width, int height); + + /** + * Restack the component peers in this container peer. + * + * @since 1.5 + */ + void restack(); +} // interface ContainerPeer diff --git a/libjava/classpath/java/awt/peer/DesktopPeer.java b/libjava/classpath/java/awt/peer/DesktopPeer.java new file mode 100644 index 000000000..626ea840e --- /dev/null +++ b/libjava/classpath/java/awt/peer/DesktopPeer.java @@ -0,0 +1,64 @@ +/* DesktopPeer.java -- Interface to enable access to common applications + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + +package java.awt.peer; + +import java.awt.Desktop.Action; +import java.io.File; +import java.io.IOException; +import java.net.URI; + +/** + * @author Mario Torre + * + */ +public interface DesktopPeer +{ + public void browse(URI url) throws IOException; + + public void edit(File file) throws IOException; + + public boolean isSupported(Action action); + + public void mail(URI mailtoURL) throws IOException; + + public void mail() throws IOException; + + public void open(File file) throws IOException; + + public void print(File file) throws IOException; +} diff --git a/libjava/classpath/java/awt/peer/DialogPeer.java b/libjava/classpath/java/awt/peer/DialogPeer.java new file mode 100644 index 000000000..b0963eceb --- /dev/null +++ b/libjava/classpath/java/awt/peer/DialogPeer.java @@ -0,0 +1,47 @@ +/* DialogPeer.java -- Interface for dialog box peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface DialogPeer extends WindowPeer +{ + void setResizable (boolean resizeable); + + void setTitle (String title); + +} // interface DialogPeer diff --git a/libjava/classpath/java/awt/peer/FileDialogPeer.java b/libjava/classpath/java/awt/peer/FileDialogPeer.java new file mode 100644 index 000000000..608b9c5d6 --- /dev/null +++ b/libjava/classpath/java/awt/peer/FileDialogPeer.java @@ -0,0 +1,51 @@ +/* FileDialogPeer.java -- Interface for file selection dialog box peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.io.FilenameFilter; + +public interface FileDialogPeer extends DialogPeer +{ + void setFile (String file); + + void setDirectory (String dir); + + void setFilenameFilter (FilenameFilter ff); + +} // interface FileDialogPeer diff --git a/libjava/classpath/java/awt/peer/FontPeer.java b/libjava/classpath/java/awt/peer/FontPeer.java new file mode 100644 index 000000000..388e56175 --- /dev/null +++ b/libjava/classpath/java/awt/peer/FontPeer.java @@ -0,0 +1,44 @@ +/* FontPeer.java -- Interface for font peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface FontPeer +{ + +} // interface FontPeer diff --git a/libjava/classpath/java/awt/peer/FramePeer.java b/libjava/classpath/java/awt/peer/FramePeer.java new file mode 100644 index 000000000..f00236819 --- /dev/null +++ b/libjava/classpath/java/awt/peer/FramePeer.java @@ -0,0 +1,77 @@ +/* FramePeer.java -- Interface for frame peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Image; +import java.awt.MenuBar; +import java.awt.Rectangle; + +public interface FramePeer extends WindowPeer +{ + void setIconImage(Image image); + void setMenuBar(MenuBar mb); + void setResizable(boolean resizable); + void setTitle(String title); + int getState(); + void setState(int state); + void setMaximizedBounds(Rectangle r); + + /** + * Check if this frame peer supports being restacked. + * + * @return true if this frame peer can be restacked, + * false otherwise + * @since 1.5 + */ + boolean isRestackSupported(); + + /** + * Sets the bounds of this frame peer. + * + * @param x the new x co-ordinate + * @param y the new y co-ordinate + * @param width the new width + * @param height the new height + * @since 1.5 + */ + void setBoundsPrivate(int x, int y, int width, int height); + + Rectangle getBoundsPrivate(); + +} // interface FramePeer diff --git a/libjava/classpath/java/awt/peer/LabelPeer.java b/libjava/classpath/java/awt/peer/LabelPeer.java new file mode 100644 index 000000000..b0c5c346d --- /dev/null +++ b/libjava/classpath/java/awt/peer/LabelPeer.java @@ -0,0 +1,45 @@ +/* LabelPeer.java -- Interface for simple text lable peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface LabelPeer extends ComponentPeer +{ + void setAlignment(int alignment); + void setText(String text); +} // interface LabelPeer diff --git a/libjava/classpath/java/awt/peer/LightweightPeer.java b/libjava/classpath/java/awt/peer/LightweightPeer.java new file mode 100644 index 000000000..38333b0aa --- /dev/null +++ b/libjava/classpath/java/awt/peer/LightweightPeer.java @@ -0,0 +1,44 @@ +/* LightweightPeer.java -- Interface for lightweight peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface LightweightPeer extends ComponentPeer +{ + +} // interface LightweightPeer diff --git a/libjava/classpath/java/awt/peer/ListPeer.java b/libjava/classpath/java/awt/peer/ListPeer.java new file mode 100644 index 000000000..55ff92913 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ListPeer.java @@ -0,0 +1,60 @@ +/* ListPeer.java -- Interface for list box peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Dimension; + +public interface ListPeer extends ComponentPeer +{ + void add(String item, int index); + void addItem(String item, int index); + void clear(); + void delItems(int start_index, int end_index); + void deselect(int index); + int[] getSelectedIndexes(); + void makeVisible(int index); + Dimension minimumSize(int s); + Dimension preferredSize(int s); + void removeAll(); + void select(int index); + void setMultipleMode(boolean multi); + void setMultipleSelections(boolean multi); + Dimension getPreferredSize(int s); + Dimension getMinimumSize(int s); +} // interface ListPeer diff --git a/libjava/classpath/java/awt/peer/MenuBarPeer.java b/libjava/classpath/java/awt/peer/MenuBarPeer.java new file mode 100644 index 000000000..ae43af3cc --- /dev/null +++ b/libjava/classpath/java/awt/peer/MenuBarPeer.java @@ -0,0 +1,48 @@ +/* MenuBarPeer.java -- Interface for menu bar peer + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Menu; + +public interface MenuBarPeer extends MenuComponentPeer +{ + void addMenu(Menu m); + void addHelpMenu(Menu menu); + void delMenu(int index); +} // interface MenuBarPeer diff --git a/libjava/classpath/java/awt/peer/MenuComponentPeer.java b/libjava/classpath/java/awt/peer/MenuComponentPeer.java new file mode 100644 index 000000000..7369aa52e --- /dev/null +++ b/libjava/classpath/java/awt/peer/MenuComponentPeer.java @@ -0,0 +1,54 @@ +/* MenuComponentPeer.java -- + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Font; + +public interface MenuComponentPeer +{ + void dispose(); + + /** + * Set the font on this menu component peer. + * + * @param font the new font + * @since 1.5 + */ + void setFont (Font font); +} // interface MenuComponentPeer diff --git a/libjava/classpath/java/awt/peer/MenuItemPeer.java b/libjava/classpath/java/awt/peer/MenuItemPeer.java new file mode 100644 index 000000000..bedbd9f9e --- /dev/null +++ b/libjava/classpath/java/awt/peer/MenuItemPeer.java @@ -0,0 +1,47 @@ +/* MenuItemPeer.java -- Interface for menu item peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface MenuItemPeer extends MenuComponentPeer +{ + void disable(); + void enable(); + void setEnabled(boolean enabled); + void setLabel(String text); +} // interface MenuItemPeer diff --git a/libjava/classpath/java/awt/peer/MenuPeer.java b/libjava/classpath/java/awt/peer/MenuPeer.java new file mode 100644 index 000000000..e1f7a90e2 --- /dev/null +++ b/libjava/classpath/java/awt/peer/MenuPeer.java @@ -0,0 +1,48 @@ +/* MenuPeer.java -- Interface for menu peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.MenuItem; + +public interface MenuPeer extends MenuItemPeer +{ + void addItem (MenuItem item); + void addSeparator (); + void delItem (int index); +} diff --git a/libjava/classpath/java/awt/peer/MouseInfoPeer.java b/libjava/classpath/java/awt/peer/MouseInfoPeer.java new file mode 100644 index 000000000..71d294157 --- /dev/null +++ b/libjava/classpath/java/awt/peer/MouseInfoPeer.java @@ -0,0 +1,61 @@ +/* MouseInfoPeer.java -- peer interface for MouseInfo + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.peer; + +import java.awt.Point; +import java.awt.Window; + +/** + * MouseInfoPeer is the peer interface java.awt.MouseInfo. + * + * @author Sven de Marothy + * @since 1.5 + */ +public interface MouseInfoPeer +{ + /** + * Get the mouse pointer coordinates and store them in p (obviously non-null) + * returns the index of the current screen device of the mouse. + */ + public int fillPointWithCoords(Point p); + + /** + * Returns whether a given Window is under the mouse. + */ + public boolean isWindowUnderMouse(Window w); +} diff --git a/libjava/classpath/java/awt/peer/PanelPeer.java b/libjava/classpath/java/awt/peer/PanelPeer.java new file mode 100644 index 000000000..a72b16cac --- /dev/null +++ b/libjava/classpath/java/awt/peer/PanelPeer.java @@ -0,0 +1,44 @@ +/* PanelPeer.java -- Interface for panel peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface PanelPeer extends ContainerPeer +{ + +} // interface PanelPeer diff --git a/libjava/classpath/java/awt/peer/PopupMenuPeer.java b/libjava/classpath/java/awt/peer/PopupMenuPeer.java new file mode 100644 index 000000000..e747a4de7 --- /dev/null +++ b/libjava/classpath/java/awt/peer/PopupMenuPeer.java @@ -0,0 +1,52 @@ +/* PopupMenuPeer.java -- Interface for popup menu peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Component; +import java.awt.Event; + +public interface PopupMenuPeer extends MenuPeer +{ + /** + * Part of the older API, replaced by event version instead. + */ + void show (Component origin, int x, int y); + + void show (Event e); +} // interface PopupMenuPeer diff --git a/libjava/classpath/java/awt/peer/RobotPeer.java b/libjava/classpath/java/awt/peer/RobotPeer.java new file mode 100644 index 000000000..46c0086dc --- /dev/null +++ b/libjava/classpath/java/awt/peer/RobotPeer.java @@ -0,0 +1,55 @@ +/* RobotPeer.java -- Interface for programatically driving GUI + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Rectangle; + +public interface RobotPeer +{ + void mouseMove (int x, int y); + void mousePress (int buttons); + void mouseRelease (int buttons); + void mouseWheel (int wheelAmt); + void keyPress (int keycode); + void keyRelease (int keycode); + int getRGBPixel (int x, int y); + int[] getRGBPixels (Rectangle screen); + void dispose(); + +} // interface RobotPeer diff --git a/libjava/classpath/java/awt/peer/ScrollPanePeer.java b/libjava/classpath/java/awt/peer/ScrollPanePeer.java new file mode 100644 index 000000000..afbd2f861 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ScrollPanePeer.java @@ -0,0 +1,51 @@ +/* ScrollPanePeer.java -- Interface for scrollable panes + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Adjustable; + +public interface ScrollPanePeer extends ContainerPeer +{ + int getHScrollbarHeight(); + int getVScrollbarWidth(); + void setScrollPosition(int h, int v); + void childResized(int width, int height); + void setUnitIncrement(Adjustable item, int inc); + void setValue(Adjustable item, int value); +} // interface ScollPanePeer diff --git a/libjava/classpath/java/awt/peer/ScrollbarPeer.java b/libjava/classpath/java/awt/peer/ScrollbarPeer.java new file mode 100644 index 000000000..4191a39b7 --- /dev/null +++ b/libjava/classpath/java/awt/peer/ScrollbarPeer.java @@ -0,0 +1,46 @@ +/* ScrollbarPeer.java -- Interface for scrollbar peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +public interface ScrollbarPeer extends ComponentPeer +{ + void setLineIncrement(int inc); + void setPageIncrement(int inc); + void setValues(int value, int visible, int min, int max); +} // interface ScrollbarPeer diff --git a/libjava/classpath/java/awt/peer/TextAreaPeer.java b/libjava/classpath/java/awt/peer/TextAreaPeer.java new file mode 100644 index 000000000..354e46d9c --- /dev/null +++ b/libjava/classpath/java/awt/peer/TextAreaPeer.java @@ -0,0 +1,53 @@ +/* TextAreaPeer.java -- Interface for text area peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Dimension; + +public interface TextAreaPeer extends TextComponentPeer +{ + void insert(String text, int pos); + void insertText(String text, int pos); + Dimension minimumSize(int rows, int cols); + Dimension getMinimumSize(int rows, int cols); + Dimension preferredSize(int rows, int cols); + Dimension getPreferredSize(int rows, int cols); + void replaceRange(String text, int start_pos, int end_pos); + void replaceText(String text, int start_pos, int end_pos); +} // interface TextAreaPeer diff --git a/libjava/classpath/java/awt/peer/TextComponentPeer.java b/libjava/classpath/java/awt/peer/TextComponentPeer.java new file mode 100644 index 000000000..57f0fd2eb --- /dev/null +++ b/libjava/classpath/java/awt/peer/TextComponentPeer.java @@ -0,0 +1,65 @@ +/* TextComponentPeer.java -- Superclass interface for text components + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Rectangle; +import java.awt.im.InputMethodRequests; + +public interface TextComponentPeer extends ComponentPeer +{ + int getSelectionEnd(); + int getSelectionStart(); + String getText(); + void setText(String text); + void select(int start_pos, int end_pos); + void setEditable(boolean editable); + int getCaretPosition(); + void setCaretPosition(int pos); + int getIndexAtPoint(int x, int y); + Rectangle getCharacterBounds(int pos); + long filterEvents(long filter); + + /** + * Retrieve this text component peer's input method requests. + * + * @return the input method requests made by this text component peer + * @since 1.5 + */ + InputMethodRequests getInputMethodRequests(); +} // interface TextComponentPeer diff --git a/libjava/classpath/java/awt/peer/TextFieldPeer.java b/libjava/classpath/java/awt/peer/TextFieldPeer.java new file mode 100644 index 000000000..55c673a2d --- /dev/null +++ b/libjava/classpath/java/awt/peer/TextFieldPeer.java @@ -0,0 +1,51 @@ +/* TextFieldPeer.java -- Interface for text field peers + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Dimension; + +public interface TextFieldPeer extends TextComponentPeer +{ + Dimension minimumSize(int len); + Dimension preferredSize(int len); + Dimension getMinimumSize(int len); + Dimension getPreferredSize(int len); + void setEchoChar(char echo_char); + void setEchoCharacter(char echo_char); +} // interface TextFieldPeer diff --git a/libjava/classpath/java/awt/peer/WindowPeer.java b/libjava/classpath/java/awt/peer/WindowPeer.java new file mode 100644 index 000000000..3ca13a20f --- /dev/null +++ b/libjava/classpath/java/awt/peer/WindowPeer.java @@ -0,0 +1,69 @@ +/* WindowPeer.java -- Interface for window peers + Copyright (C) 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.peer; + +import java.awt.Dialog; + +public interface WindowPeer extends ContainerPeer +{ + void toBack(); + void toFront(); + + /** + * Update the always-on-top status of the Window. + * + * @since 1.5 + */ + void updateAlwaysOnTop(); + + /** + * Request that this window peer be given the window focus. + * + * @return true if the window received focus, false otherwise + * @since 1.5 + */ + boolean requestWindowFocus(); + + void setAlwaysOnTop(boolean alwaysOnTop); + void updateFocusableWindowState(); + void setModalBlocked(Dialog blocker, boolean blocked); + void updateMinimumSize(); + void updateIconImages(); + +} // interface WindowPeer diff --git a/libjava/classpath/java/awt/peer/package.html b/libjava/classpath/java/awt/peer/package.html new file mode 100644 index 000000000..7a7458c17 --- /dev/null +++ b/libjava/classpath/java/awt/peer/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.peer + + +

    Interfaces for using native interface components.

    + + + diff --git a/libjava/classpath/java/awt/print/Book.java b/libjava/classpath/java/awt/print/Book.java new file mode 100644 index 000000000..8b040b2d1 --- /dev/null +++ b/libjava/classpath/java/awt/print/Book.java @@ -0,0 +1,159 @@ +/* Book.java -- A mixed group of pages to print. + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +import java.util.Vector; + +/** + * This class allows documents to be created with different paper types, + * page formatters, and painters. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Book implements Pageable +{ + /** + * Painter objects for the book. + */ + Vector printables = new Vector(); + + /** + * Page formats for the book. + */ + Vector page_formats = new Vector(); + + /** + * Initializes a new instance of Book that is empty. + */ + public Book() + { + } + + /** + * Returns the number of pages in this book. + * + * @return The number of pages in this book. + */ + public int getNumberOfPages() + { + return printables.size(); + } + + /** + * This method returns the PageFormat object for the + * specified page. + * + * @param page_number The number of the page to get information for, where + * page numbers start at 0. + * + * @return The PageFormat object for the specified page. + * + * @exception IndexOutOfBoundsException If the page number is not valid. + */ + public PageFormat getPageFormat(int page_number) + { + return (PageFormat) page_formats.elementAt(page_number); + } + + /** + * This method returns the Printable object for the + * specified page. + * + * @param page_number The number of the page to get information for, where + * page numbers start at 0. + * + * @return The Printable object for the specified page. + * + * @exception IndexOutOfBoundsException If the page number is not valid. + */ + public Printable getPrintable(int page_number) + { + return (Printable) printables.elementAt(page_number); + } + + /** + * This method appends a page to the end of the book. + * + * @param printable The Printable for this page. + * @param page_format The PageFormat for this page. + * + * @exception NullPointerException If either argument is null. + */ + public void append(Printable printable, PageFormat page_format) + { + append(printable, page_format, 1); + } + + /** + * This method appends the specified number of pages to the end of the book. + * Each one will be associated with the specified Printable + * and PageFormat. + * + * @param printable The Printable for this page. + * @param page_format The PageFormat for this page. + * @param num_pages The number of pages to append. + * + * @exception NullPointerException If any argument is null. + */ + public void append(Printable printable, PageFormat page_format, int num_pages) + { + for (int i = 0; i < num_pages; i++) + { + printables.addElement(printable); + page_formats.addElement(page_format); + } + } + + /** + * This method changes the Printable and PageFormat + * for the specified page. The page must already exist or an exception + * will be thrown. + * + * @param page_num The page number to alter. + * @param printable The new Printable for the page. + * @param page_format The new PageFormat for the page. + * + * @throws IndexOutOfBoundsException If the specified page does not exist. + */ + public void setPage(int page_num, Printable printable, PageFormat page_format) + { + printables.setElementAt(printable, page_num); + page_formats.setElementAt(page_format, page_num); + } +} diff --git a/libjava/classpath/java/awt/print/NoPrinterJob.java b/libjava/classpath/java/awt/print/NoPrinterJob.java new file mode 100644 index 000000000..e9659a147 --- /dev/null +++ b/libjava/classpath/java/awt/print/NoPrinterJob.java @@ -0,0 +1,124 @@ +/* NoPrinterJob.java -- Fake PrinterJob that just signals no print service. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * Fake PrinterJob that just signals no print service. This is only + * here so applications can call + * PrintJob.getPrinterJob().getPrinterJob() and check + * that it returns null which indicates no actual + * printing support is available. + */ +class NoPrinterJob extends PrinterJob +{ + public int getCopies() + { + return 0; + } + + public void setCopies(int copies) + { + // Do nothing. + } + + public String getJobName() + { + return "NoPrinterJob"; + } + + public void setJobName(String job_name) + { + // Do nothing. + } + + public String getUserName() + { + return "NoUser"; + } + + public void cancel() + { + // Do nothing. + } + + public boolean isCancelled() + { + return true; + } + + public PageFormat defaultPage(PageFormat page_format) + { + return page_format; + } + + public PageFormat pageDialog(PageFormat page_format) + { + return page_format; + } + + public void print() throws PrinterException + { + throw new PrinterException("No printer"); + } + + public boolean printDialog() + { + return false; + } + + public void setPageable(Pageable pageable) + { + // Do nothing. + } + + public void setPrintable(Printable printable) + { + // Do nothing. + } + + public void setPrintable(Printable printable, PageFormat page_format) + { + // Do nothing. + } + + public PageFormat validatePage(PageFormat page_format) + { + return page_format; + } +} diff --git a/libjava/classpath/java/awt/print/PageFormat.java b/libjava/classpath/java/awt/print/PageFormat.java new file mode 100644 index 000000000..86d8ba210 --- /dev/null +++ b/libjava/classpath/java/awt/print/PageFormat.java @@ -0,0 +1,233 @@ +/* PageFormat.java -- Information about the page format + Copyright (C) 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This class contains information about the desired page format to use for + * printing a particular set of pages. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PageFormat + implements Cloneable +{ + /** + * A constant for a landscaped page orientation. Used by + * getOrientation and setOrientation. + */ + public static final int LANDSCAPE = 0; + + /** + * A constant for a portrait page orientation. Used by + * getOrientation and setOrientation. + */ + public static final int PORTRAIT = 1; + + /** + * A constant for a reversed landscaped page orientation. This is the + * orientation used by Macintosh's for landscape. The origin is in the + * upper right hand corner instead of the upper left. The X and Y axes + * are reversed. Used by getOrientation and + * setOrientation. + */ + public static final int REVERSE_LANDSCAPE = 2; + + // The page orientation + private int orientation; + + // The paper type + private Paper paper; + + /** + * This method creates a default page layout, which will be in portrait + * format. + */ + public PageFormat() + { + this.paper = new Paper(); + this.orientation = PORTRAIT; + } + + /** + * This method returns the width of the page, in 1/72nd's of an inch. The + * "width" measured depends on orientation. + * + * @return The width of the page. + */ + public double getWidth() + { + return paper.getWidth(); + } + + /** + * This method returns the height of the page, in 1/72nd's of an inch. The + * "height" measured depends on the orientation. + * + * @return The height of the page. + */ + public double getHeight() + { + return paper.getHeight(); + } + + /** + * This method returns the X coordinate value of the upper leftmost drawable + * area of the paper. + * + * @return The upper leftmost imageable X coordinate. + */ + public double getImageableX() + { + return paper.getImageableX(); + } + + /** + * This method returns the Y coordinate value of the upper leftmost drawable + * area of the paper. + * + * @return The upper leftmost imageable Y coordinate. + */ + public double getImageableY() + { + return paper.getImageableY(); + } + + /** + * This method returns the imageable width of the paper, in 1/72nd's of an + * inch. + * + * @return The imageable width of the paper. + */ + public double getImageableWidth() + { + return paper.getImageableWidth(); + } + + /** + * This method returns the imageable height of the paper, in 1/72nd's of an + * inch. + * + * @return The imageable height of the paper. + */ + public double getImageableHeight() + { + return paper.getImageableHeight(); + } + + /** + * Returns a copy of the paper object being used for this page + * format. + * + * @return A copy of the Paper object for this format. + */ + public Paper getPaper() + { + return (Paper) paper.clone(); + } + + /** + * Sets the Paper object to be used by this page format. + * + * @param paper The new Paper object for this page format. + */ + public void setPaper(Paper paper) + { + this.paper = paper; + } + + /** + * This method returns the current page orientation. The value returned will + * be one of the page orientation constants from this class. + * + * @return The current page orientation. + */ + public int getOrientation() + { + return orientation; + } + + /** + * This method sets the page orientation for this format to the specified + * value. It must be one of the page orientation constants from this class + * or an exception will be thrown. + * + * @param orientation The new page orientation. + * @exception IllegalArgumentException If the specified page orientation + * value is not one of the constants from this class. + */ + public void setOrientation(int orientation) throws IllegalArgumentException + { + if ((orientation != PORTRAIT) && (orientation != LANDSCAPE) + && (orientation != REVERSE_LANDSCAPE)) + throw new IllegalArgumentException("Bad page orientation value: " + + orientation); + + this.orientation = orientation; + } + + /** + * This method returns a matrix used for transforming user space coordinates + * to page coordinates. The value returned will be six doubles as described + * in java.awt.geom.AffineTransform. + * + * @return The transformation matrix for this page format. + */ + public double[] getMatrix() + { + throw new RuntimeException("Not implemented since I don't know what to do"); + } + + /** + * This method returns a copy of this object. + * + * @return A copy of this object. + */ + public Object clone() + { + try + { + return (super.clone()); + } + catch (CloneNotSupportedException e) + { + return (null); + } + } + +} diff --git a/libjava/classpath/java/awt/print/Pageable.java b/libjava/classpath/java/awt/print/Pageable.java new file mode 100644 index 000000000..58e0885fb --- /dev/null +++ b/libjava/classpath/java/awt/print/Pageable.java @@ -0,0 +1,90 @@ +/* Pageable.java -- Pages to be printed + Copyright (C) 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This interface represents pages that are to be printed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Pageable +{ + /** + * This constant is returned when getNumberOfPages() cannot + * determine the number of pages available for printing. + */ + int UNKNOWN_NUMBER_OF_PAGES = - 1; + + /** + * This method returns the number of pages this object contains, or + * UNKNOWN_NUMBER_OF_PAGES if it cannot determine the number + * of pages to be printed. + * + * @return The number of pages to be printed, or + * UNKNOWN_NUMBER_OF_PAGES if this is unknown. + */ + int getNumberOfPages(); + + /** + * This method returns the PageFormat instance for the + * specified page. Page numbers start at zero. An exception is thrown if the + * requested page does not exist. + * + * @param pageIndex The index of the page to return the + * PageFormat for. + * @return The PageFormat for the requested page. + * @exception IndexOutOfBoundsException If the requested page number does + * not exist. + */ + PageFormat getPageFormat(int pageIndex) throws IndexOutOfBoundsException; + + /** + * This method returns the Printable instance for the specified + * page. Page numbers start at zero. An exception is thrown if the requested + * page does not exist. + * + * @param pageIndex The index of the page to return the + * Printable for. + * @return The Printable for the requested page. + * @exception IndexOutOfBoundsException If the requested page number does + * not exist. + */ + Printable getPrintable(int pageIndex) throws IndexOutOfBoundsException; + +} diff --git a/libjava/classpath/java/awt/print/Paper.java b/libjava/classpath/java/awt/print/Paper.java new file mode 100644 index 000000000..a80da2fea --- /dev/null +++ b/libjava/classpath/java/awt/print/Paper.java @@ -0,0 +1,197 @@ +/* Paper.java -- Information about a paper type. + Copyright (C) 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This class describes a particular type of paper. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Paper + implements Cloneable +{ + // Height of the paper + private double height; + + // Width of the paper + private double width; + + // Upper left imageable X coordinate + private double imageableX; + + // Upper left imageable Y coordinate + private double imageableY; + + // Imageable width of the page + private double imageableWidth; + + // Imageable height of the page + private double imageableHeight; + + /** + * This method creates a letter sized paper with one inch margins + */ + public Paper() + { + width = 8.5 * 72; + height = 11 * 72; + imageableX = 72; + imageableY = 72; + imageableWidth = width - (2 * 72); + imageableHeight = height - (2 * 72); + } + + /** + * This method returns the height of the paper in 1/72nds of an inch. + * + * @return The height of the paper in 1/72nds of an inch. + */ + public double getHeight() + { + return height; + } + + /** + * Returns the width of the paper in 1/72nds of an inch. + * + * @return The width of the paper in 1/72nds of an inch. + */ + public double getWidth() + { + return width; + } + + /** + * This method returns the X coordinate of the upper left hand corner of the + * imageable area of the paper. + * + * @return The X coordinate of the upper left hand corner of the imageable + * area of the paper. + */ + public double getImageableX() + { + return imageableX; + } + + /** + * This method returns the Y coordinate of the upper left hand corner of the + * imageable area of the paper. + * + * @return The Y coordinate of the upper left hand corner of the imageable + * area of the paper. + */ + public double getImageableY() + { + return imageableY; + } + + /** + * Returns the width of the imageable area of the paper. + * + * @return The width of the imageable area of the paper. + */ + public double getImageableWidth() + { + return imageableWidth; + } + + /** + * Returns the height of the imageable area of the paper. + * + * @return The height of the imageable area of the paper. + */ + public double getImageableHeight() + { + return imageableHeight; + } + + /** + * This method sets the size of the paper to the specified width and height, + * which are specified in 1/72nds of an inch. + * + * @param width The width of the paper in 1/72nds of an inch. + * @param height The height of the paper in 1/72nds of an inch. + */ + public void setSize(double width, double height) + { + this.width = width; + this.height = height; + } + + /** + * This method sets the imageable area of the paper by specifying the + * coordinates of the upper left hand corner of that area, and its length + * and height. All values are in 1/72nds of an inch. + * + * @param imageableX The X coordinate of the upper left hand corner of the + * imageable area, in 1/72nds of an inch. + * @param imageableY The Y coordinate of the upper left hand corner of the + * imageable area, in 1/72nds of an inch. + * @param imageableWidth The width of the imageable area of the paper, in + * 1/72nds of an inch. + * @param imageableHeight The heigth of the imageable area of the paper, in + * 1/72nds of an inch. + */ + public void setImageableArea(double imageableX, double imageableY, + double imageableWidth, double imageableHeight) + { + this.imageableX = imageableX; + this.imageableY = imageableY; + this.imageableWidth = imageableWidth; + this.imageableHeight = imageableHeight; + } + + /** + * This method creates a copy of this object. + * + * @return A copy of this object. + */ + public Object clone() + { + try + { + return (super.clone()); + } + catch (CloneNotSupportedException e) + { + return (null); + } + } + +} diff --git a/libjava/classpath/java/awt/print/Printable.java b/libjava/classpath/java/awt/print/Printable.java new file mode 100644 index 000000000..775167e66 --- /dev/null +++ b/libjava/classpath/java/awt/print/Printable.java @@ -0,0 +1,80 @@ +/* Printable.java -- Renders a page to the print device + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.print; + +import java.awt.Graphics; + + +/** + * This interface provides a mechanism for the actual printing of pages to the + * printer. The object implementing this interface performs the page + * rendering. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Printable +{ + /** + * This value is returned by the print() method to indicate + * that the requested page exists and has been printed. + */ + int PAGE_EXISTS = 0; + + /** + * This value is returned by the print() method to indicate + * that the requested page number does not exist. + */ + int NO_SUCH_PAGE = 1; + + /** + * This method prints the specified page to the specified graphics + * context in the specified format. The pages are numbered starting + * from zero. + * + * @param graphics The graphics context to render the pages on. + * @param format The format in which to print the page. + * @param page_number The page number to print, where numbers start at zero. + * + * @return PAGE_EXISTS if the requested page exists and was + * successfully printed, NO_SUCH_PAGE otherwise. + * + * @exception PrinterException If an error occurs during printing. + */ + int print(Graphics graphics, PageFormat format, int page_number) + throws PrinterException; +} diff --git a/libjava/classpath/java/awt/print/PrinterAbortException.java b/libjava/classpath/java/awt/print/PrinterAbortException.java new file mode 100644 index 000000000..458063079 --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterAbortException.java @@ -0,0 +1,71 @@ +/* PrinterAbortException.java -- Indicates the print job was aborted + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This exception is thrown when the print job is aborted, either by the + * user or by the application. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class PrinterAbortException extends PrinterException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 4725169026278854136L; + + /** + * Create a new instance with no detailed error message. + */ + public PrinterAbortException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message the descriptive error message + */ + public PrinterAbortException(String message) + { + super(message); + } +} // class PrinterAbortException diff --git a/libjava/classpath/java/awt/print/PrinterException.java b/libjava/classpath/java/awt/print/PrinterException.java new file mode 100644 index 000000000..c105f549d --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterException.java @@ -0,0 +1,71 @@ +/* PrinterException.java -- generic problem in the printing subsystem + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This is the generic toplevel exception for printing errors. Subclasses + * provide more detailed descriptions of the problem. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class PrinterException extends Exception +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -3757589981158265819L; + + /** + * Create a new instance with no detailed error message. + */ + public PrinterException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message the descriptive error message + */ + public PrinterException(String message) + { + super(message); + } +} // class PrinterException diff --git a/libjava/classpath/java/awt/print/PrinterGraphics.java b/libjava/classpath/java/awt/print/PrinterGraphics.java new file mode 100644 index 000000000..c1f199c65 --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterGraphics.java @@ -0,0 +1,58 @@ +/* PrinterGraphics.java -- Hook to return print job controller. + Copyright (C) 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +/** + * This interface is implemented by the Graphics instance that is + * used for rendering pages. It provides a hook to return the object that is + * controlling the print job. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface PrinterGraphics +{ + /** + * This method returns the instance of PrinterJob that is + * controlling this print job. + * + * @return The PrinterJob that is controlling this print job. + */ + PrinterJob getPrinterJob(); + +} diff --git a/libjava/classpath/java/awt/print/PrinterIOException.java b/libjava/classpath/java/awt/print/PrinterIOException.java new file mode 100644 index 000000000..2089af59e --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterIOException.java @@ -0,0 +1,97 @@ +/* PrinterIOException.java -- The print job encountered an I/O error + Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +import java.io.IOException; + +/** + * This exception is thrown when the print job encounters an I/O problem + * of some kind. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class PrinterIOException extends PrinterException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 5850870712125932846L; + + /** + * The exception that caused this (duplicates Throwable). + * + * @serial the I/O exception that terminated the job + */ + private final IOException mException; + + /** + * Initializes a new instance with the given cause. + * + * @param mException the cause + */ + public PrinterIOException(IOException mException) + { + super(mException == null ? null : mException.toString()); + initCause(mException); + this.mException = mException; + } + + /** + * Gets the underlying IOException that caused this exception. + * This legacy method has been replaced by {@link #getCause()}. + * + * @return the cause + */ + public IOException getIOException() + { + return mException; + } + + /** + * Gets the cause. + * + * @return the cause + */ + public Throwable getCause() + { + return mException; + } +} // class PrinterIOException diff --git a/libjava/classpath/java/awt/print/PrinterJob.java b/libjava/classpath/java/awt/print/PrinterJob.java new file mode 100644 index 000000000..1a4b8c8e2 --- /dev/null +++ b/libjava/classpath/java/awt/print/PrinterJob.java @@ -0,0 +1,300 @@ +/* PrinterJob.java -- This job is the printer control class + Copyright (C) 1999, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.print; + +import gnu.java.awt.print.JavaPrinterJob; + +import java.awt.HeadlessException; +import javax.print.PrintService; +import javax.print.PrintServiceLookup; +import javax.print.DocFlavor; +import javax.print.StreamPrintServiceFactory; +import javax.print.attribute.PrintRequestAttributeSet; + +/** + * This class controls printing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class PrinterJob +{ + // The print service associated with this job + private PrintService printer = null; + + /** + * Creates a new print job. + * + * @return A PrinterJob object for the newly created print job. + */ + public static PrinterJob getPrinterJob() + { + return new JavaPrinterJob(); + } + + /** + * Initializes a new instance of PrinterJob. + */ + public PrinterJob() + { + } + + /** + * Returns the number of copies to be printed. + * + * @return The number of copies to be printed. + */ + public abstract int getCopies(); + + /** + * Sets the number of copies to be printed. + * + * @param copies The number of copies to be printed. + */ + public abstract void setCopies(int copies); + + /** + * Returns the name of the print job. + * + * @return The name of the print job. + */ + public abstract String getJobName(); + + /** + * Sets the name of the print job. + * + * @param job_name The name of the print job. + */ + public abstract void setJobName(String job_name); + + /** + * Returns the printing user name. + * + * @return The printing username. + */ + public abstract String getUserName(); + + /** + * Cancels an in progress print job. + */ + public abstract void cancel(); + + /** + * Tests whether or not this job has been cancelled. + * + * @return true if this job has been cancelled, false + * otherwise. + */ + public abstract boolean isCancelled(); + + /** + * Returns an instance of the default page which will have the default + * paper and orientation. + * + * @return A default instance of PageFormat. + */ + public PageFormat defaultPage() + { + return new PageFormat(); + } + + /** + * Clones the specified PageFormat object then alters the + * clone so that it represents the default page format. + * + * @param page_format The PageFormat to clone. + * + * @return A new default page format. + */ + public abstract PageFormat defaultPage(PageFormat page_format); + + /** + * Displays a dialog box to the user which allows the page format + * attributes to be modified. + * + * @param page_format The PageFormat object to modify. + * + * @return The modified PageFormat. + */ + public abstract PageFormat pageDialog(PageFormat page_format) + throws HeadlessException; + + /** + * @since 1.4 + */ + public PageFormat pageDialog(PrintRequestAttributeSet attributes) + throws HeadlessException + { + // FIXME: Implement this for real. + return pageDialog((PageFormat) null); + } + + /** + * Prints the pages. + */ + public abstract void print () throws PrinterException; + + /** + * Prints the page with given attributes. + */ + public void print (PrintRequestAttributeSet attributes) + throws PrinterException + { + print (); + } + + /** + * Displays a dialog box to the user which allows the print job + * attributes to be modified. + * + * @return false if the user cancels the dialog box, + * true otherwise. + */ + public abstract boolean printDialog() + throws HeadlessException; + + /** + * Displays a dialog box to the user which allows the print job + * attributes to be modified. + * + * @return false if the user cancels the dialog box, + * true otherwise. + */ + public boolean printDialog(PrintRequestAttributeSet attributes) + throws HeadlessException + { + // FIXME: Implement this for real. + return printDialog(); + } + + /** + * This sets the pages that are to be printed. + * + * @param pageable The pages to be printed, which may not be null. + */ + public abstract void setPageable(Pageable pageable); + + /** + * Sets this specified Printable as the one to use for + * rendering the pages on the print device. + * + * @param printable The Printable for the print job. + */ + public abstract void setPrintable(Printable printable); + + /** + * Sets the Printable and the page format for the pages + * to be printed. + * + * @param printable The Printable for the print job. + * @param page_format The PageFormat for the print job. + */ + public abstract void setPrintable(Printable printable, PageFormat page_format); + + /** + * Makes any alterations to the specified PageFormat + * necessary to make it work with the current printer. The alterations + * are made to a clone of the input object, which is then returned. + * + * @param page_format The PageFormat to validate. + * + * @return The validated PageFormat. + */ + public abstract PageFormat validatePage(PageFormat page_format); + + /** + * Find and return 2D image print services. + * + * This is the same as calling PrintServiceLookup.lookupPrintServices() + * with Pageable service-specified DocFlavor. + * @return Array of PrintService objects, could be empty. + * @since 1.4 + */ + public static PrintService[] lookupPrintServices() + { + return PrintServiceLookup.lookupPrintServices + ( + new DocFlavor("application/x-java-jvm-local-objectref", + "java.awt.print.Pageable"), + null); + } + + /** + * Find and return 2D image stream print services. + * + * This is the same as calling + * StreamPrintServiceFactory.lookupStreamPrintServices() + * with Pageable service-specified DocFlavor. + * @param mimeType The output format mime type, or null for any type. + * @return Array of stream print services, could be empty. + * @since 1.4 + */ + public static StreamPrintServiceFactory[] + lookupStreamPrintServices(String mimeType) + { + return StreamPrintServiceFactory.lookupStreamPrintServiceFactories( + DocFlavor.SERVICE_FORMATTED.PAGEABLE, mimeType); + } + + /** + * Return the printer for this job. If print services aren't supported by + * the subclass, returns null. + * + * @return The associated PrintService. + * @since 1.4 + */ + public PrintService getPrintService() + { + return printer; + } + + /** + * Change the printer for this print job to service. Subclasses that + * support setting the print service override this method. Throws + * PrinterException when the class doesn't support setting the printer, + * the service doesn't support Pageable or Printable interfaces for 2D + * print output. + * @param service The new printer to use. + * @throws PrinterException if service is not valid. + */ + public void setPrintService(PrintService service) + throws PrinterException + { + printer = service; + } +} diff --git a/libjava/classpath/java/awt/print/package.html b/libjava/classpath/java/awt/print/package.html new file mode 100644 index 000000000..50abcbfad --- /dev/null +++ b/libjava/classpath/java/awt/print/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.awt.print + + +

    Classes for printer jobs, pages, paper sizes, graphics and formats.

    + + + diff --git a/libjava/classpath/java/beans/AppletInitializer.java b/libjava/classpath/java/beans/AppletInitializer.java new file mode 100644 index 000000000..3bc2534e1 --- /dev/null +++ b/libjava/classpath/java/beans/AppletInitializer.java @@ -0,0 +1,61 @@ +/* java.beans.AppletInitializer + Copyright (C) 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.applet.Applet; +import java.beans.beancontext.BeanContext; + + +/** This interface is a mechanism for the initialization of a Java + * Bean that is also an Applet. It is used by + * Beans.instantiate(). + * + * @author Tom Tromey (tromey@redhat.com) + * @since 1.2 + */ +public interface AppletInitializer +{ + /** Activate the applet. */ + void activate (Applet applet); + + /** This method will be called by Beans.instantiate() + * to associated the new Applet with its AppletContext, AppletStub, + * and Container. + */ + void initialize (Applet applet, BeanContext context); +} diff --git a/libjava/classpath/java/beans/BeanDescriptor.java b/libjava/classpath/java/beans/BeanDescriptor.java new file mode 100644 index 000000000..6795d91b3 --- /dev/null +++ b/libjava/classpath/java/beans/BeanDescriptor.java @@ -0,0 +1,89 @@ +/* java.beans.BeanDescriptor + Copyright (C) 1998, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** BeanDescriptor describes general information about a Bean, plus + ** stores the Bean's Class and it's customizer's Class.

    + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 31 May 1998 + **/ + +public class BeanDescriptor extends FeatureDescriptor { + Class beanClass; + Class customizerClass; + + /** Create a new BeanDescriptor with the given beanClass and + ** no customizer class. + ** @param beanClass the class of the Bean. + **/ + public BeanDescriptor(Class beanClass) { + this(beanClass,null); + } + + /** Create a new BeanDescriptor with the given bean class and + ** customizer class. + ** @param beanClass the class of the Bean. + ** @param customizerClass the class of the Bean's Customizer. + **/ + public BeanDescriptor(Class beanClass, Class customizerClass) { + this.beanClass = beanClass; + this.customizerClass = customizerClass; + + // Set the FeatureDescriptor programmatic name. + String name = beanClass.getName(); + int lastInd = name.lastIndexOf('.'); + if (lastInd != -1) + name = name.substring(lastInd + 1); + + setName(name); + } + + /** Get the Bean's class. **/ + public Class getBeanClass() { + return beanClass; + } + + /** Get the Bean's customizer's class. **/ + public Class getCustomizerClass() { + return customizerClass; + } +} diff --git a/libjava/classpath/java/beans/BeanInfo.java b/libjava/classpath/java/beans/BeanInfo.java new file mode 100644 index 000000000..3c9bf1bcd --- /dev/null +++ b/libjava/classpath/java/beans/BeanInfo.java @@ -0,0 +1,181 @@ +/* java.beans.BeanInfo + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** BeanInfo can be implemented in order to provide explicit information to the Introspector. + ** + ** When you write a BeanInfo class, you implement this interface + ** and provide explicit information by returning a non-null + ** value from the appropriate method. If you wish the + ** Introspector to determine certain information in the normal + ** way, just return null (or in the case of int methods, return + ** -1). There is a class called SimpleBeanInfo which returns + ** null from all methods, which you may extend and only + ** override the methods you wish to override.

    + ** + ** When you have written the class, give it the name + ** <Bean Class Name>BeanInfo and place it in + ** the same package as the Bean, or in the bean info search path + ** (see Introspector for information on search paths).

    + ** + ** A simple note about the way the Introspector interacts with + ** BeanInfo. Introspectors look at a Bean class and determine + ** if there is a BeanInfo class with it. If there is not a + ** BeanInfo class, it will behave as if the BeanInfo class + ** provided was a SimpleBeanInfo class (i.e. it will determine + ** all information automatically).

    If there is a BeanInfo + ** class, then any methods that do *not* return null are + ** regarded as providing definitive information about the class + ** and all of its superclasses for those information types. + ** Even if a parent BeanInfo class explicitly returns that + ** information, it will not be used. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 28 Jul 1998 + **/ + +public interface BeanInfo { + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_COLOR_16x16 = 1; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_COLOR_32x32 = 2; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_MONO_16x16 = 3; + /** Use this as a parameter for the getIcon() command to retrieve a certain type of icon. **/ + int ICON_MONO_32x32 = 4; + + /** Get the general description of this Bean type. + ** @return the BeanDescriptor for the Bean, or null if + ** the BeanDescriptor should be obtained by + ** Introspection. + **/ + BeanDescriptor getBeanDescriptor(); + + /** Get the events this Bean type fires. + ** @return the EventDescriptors representing events this + ** Bean fires. Returns null if the + ** events are to be acquired by Introspection. + **/ + EventSetDescriptor[] getEventSetDescriptors(); + + /** Get the "default" event, basically the one a RAD tool + ** user is most likely to select. + ** @return the index into the getEventSetDescriptors() + ** that the user is most likely to use. Returns + ** -1 if there is no default event. + **/ + int getDefaultEventIndex(); + + /** Get the properties (get/set method pairs) this Bean + ** type supports. + ** @return the PropertyDescriptors representing the + ** properties this Bean type supports. + ** Returns null if the properties + ** are to be obtained by Introspection. + **/ + PropertyDescriptor[] getPropertyDescriptors(); + + /** Get the "default" property, basically the one a RAD + ** tool user is most likely to select. + ** @return the index into the getPropertyDescriptors() + ** that the user is most likely to use. Returns + ** -1 if there is no default event. + **/ + int getDefaultPropertyIndex(); + + /** Get the methods this Bean type supports. + ** @return the MethodDescriptors representing the + ** methods this Bean type supports. Returns + ** null if the methods are to be + ** obtained by Introspection. + **/ + MethodDescriptor[] getMethodDescriptors(); + + /** Get additional BeanInfos representing this Bean. + ** In this version of JavaBeans, this method is used so + ** that space and time can be saved by reading a BeanInfo + ** for each class in the hierarchy (super, super(super), + ** and so on).

    + ** + ** The order of precedence when two pieces of BeanInfo + ** conflict (such as two PropertyDescriptors that have + ** the same name), in order from highest precedence to + ** lowest, is: + **

      + **
    1. This BeanInfo object.
    2. + **
    3. getAdditionalBeanInfo()[getAdditionalBeanInfo().length]
    4. + **
    5. ...
    6. + **
    7. getAdditionalBeanInfo()[1]
    8. + **
    9. getAdditionalBeanInfo()[0]
    10. + **

    + ** + ** Spec Note: It is possible that + ** returning null from this method could + ** stop Introspection in its tracks, but it is unclear + ** from the spec whether this is the case. + ** + ** @return additional BeanInfos representing this Bean. + ** null may be returned (see Spec + ** Note, above). + **/ + BeanInfo[] getAdditionalBeanInfo(); + + /** Get a visual icon for this Bean. + ** A Bean does not have to support icons, and if it does + ** support icons, it does not have to support every single + ** type. Sun recommends that if you only support one + ** type, you support 16x16 color. Sun also notes that you + ** should try to use a type (like GIF) that allows for + ** transparent pixels, so that the background of the RAD + ** tool can show through.

    + ** + ** Spec Note: If you do not support the + ** type of icon that is being asked for, but you do + ** support another type, it is unclear whether you should + ** return the other type or not. I would presume not. + ** + ** @param iconType the type of icon to get (see the + ** ICON_* constants in this class). + ** @return the icon, or null if that type of icon is + ** unsupported by this Bean. + **/ + java.awt.Image getIcon(int iconType); +} diff --git a/libjava/classpath/java/beans/Beans.java b/libjava/classpath/java/beans/Beans.java new file mode 100644 index 000000000..3066be7a5 --- /dev/null +++ b/libjava/classpath/java/beans/Beans.java @@ -0,0 +1,368 @@ +/* java.beans.Beans + Copyright (C) 1998, 1999, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import gnu.java.beans.DummyAppletStub; +import gnu.java.io.ClassLoaderObjectInputStream; + +import java.applet.Applet; +import java.beans.beancontext.BeanContext; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.URL; + +/** + * Beans provides some helper methods that allow the basic + * operations of Bean-ness. + * + * @author John Keiser + * @author Robert Schuster + * + * @since 1.1 + * @status updated to 1.4 + * + */ +public class Beans +{ + static boolean designTime = false; + static boolean guiAvailable = true; + + /** + * Once again, we have a java.beans class with only + * static methods that can be instantiated. When + * will the madness end? :) + */ + public Beans() + { + // Does intentionally nothing here. + } + + /** Creates a bean. + *

    This is a convenience method that calls instantiate(cl, beanName, null, null).

    + * + * @see instantiate(ClassLoader, String, BeanContext, AppletInitializer) + * @param cl ClassLoader to be used or null for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate(ClassLoader cl, String beanName) + throws IOException, ClassNotFoundException + { + return instantiate(cl, beanName, null, null); + } + + /** Creates a bean. + * + *

    This is a convenience method that calls instantiate(cl, beanName, beanContext, null).

    + * + * @see instantiate(ClassLoader, String, BeanContext, AppletInitializer) + * @param cl ClassLoader to be used or null for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @param beanContext Context to which the newly created Bean should be added. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate( + ClassLoader cl, + String beanName, + BeanContext beanContext) + throws IOException, ClassNotFoundException + { + return instantiate(cl, beanName, beanContext, null); + } + + /** Instantiates a bean according to Beans 1.0. + * + *

    In Beans 1.0 the instantiation scheme is as follows:

    + *

    The name should be dot-separated (e.g "place.for.beans.myBean") and indicate either a + * serialized object or a class name. In the first case all dots in the name are replaced with + * slashes ('/') and ".ser" is appended ("place.for.beans.myBean" becomes "place/for/beans/myBean.ser"). + * The bean is then loaded as an application or system resource depending on whether a + * ClassLoader was provided.

    + * + *

    If no such resource exists or if it contains no bean the name is interpreted as a class name of + * which an instance is then created.

    + * + *

    If a BeanContext instance is available the created bean is added to it.

    + * + *

    If the created Bean is an Applet or subclass and an AppletInitializer + * instance is available the applet is initialized and afterwards activated using the initializer. Additionally + * every instantiated Applet bean is initialized using the {@link Applet.init} method. + * Furthermore every applet gets a default AppletStub. The Applet's + * document base is the location of the ".ser" file if it was deserialized or the location of its class + * file if it was instantiated.

    + * + *

    A ClassNotFoundException is not only thrown when a class name was unknown + * but even when the class has public no-argument constructor + * (IllegalAccessException is wrapped) or an exception is thrown while + * invoking such a constructor (causing exception is wrapped).

    + * + * @param cl ClassLoader to be used or null for the system classloader. + * @param beanName Name of a serialized bean or class name. + * @param beanContext Context to which the newly created Bean should be added. + * @param initializer The AppletInitializer which is used for initializing Applet beans. + * @return A newly created bean. + * @throws IOException If access of an IO resource failed. + * @throws ClassNotFoundException If the class name is not known or does not lead to a proper bean class. + */ + public static Object instantiate( + ClassLoader cl, + String beanName, + BeanContext beanContext, + AppletInitializer initializer) + throws IOException, ClassNotFoundException + { + Object bean = null; + URL beanLocation = null; + URL classLocation = null; + + // Converts bean name into a resource name (eg. "a.b.c" -> "a/b/c"). + String resourceName = beanName.replace('.', '/'); + + /* Tries to get an input stream of the Bean, reading it as a system resource + * if no ClassLoader is present or as an application resource if a classloader + * is given. + */ + beanLocation = + (cl == null) + ? ClassLoader.getSystemResource(resourceName + ".ser") + : cl.getResource(resourceName + ".ser"); + + // Reads the serialized Bean from the returned URL. + if (beanLocation != null) + { + // Deserializes the bean instance. + ObjectInputStream ois = + (cl == null) + ? new ObjectInputStream(beanLocation.openStream()) + : new ClassLoaderObjectInputStream( + beanLocation.openStream(), + cl); + + bean = ois.readObject(); + + /* Implementation note: The result of ObjectInputStream.readObject() + * may have been null at this point (its a valid value to deserialize) + * and we explicitly want to try instantiation in such a case + * (this is important for compatibility). + */ + } + + // Instantiates the Bean using reflective instantiation if it has not been created yet. + if (bean == null) + { + // Makes sure that the deserialization was NOT done. + beanLocation = null; + + Class beanClass; + if (cl == null) + { + beanClass = Class.forName(beanName); + classLocation = + ClassLoader.getSystemResource(resourceName + ".class"); + } + else + { + beanClass = cl.loadClass(beanName); + classLocation = cl.getResource(resourceName + ".class"); + } + + // Instantiates and optionally registers the new bean. + try + { + bean = beanClass.newInstance(); + } + catch(Exception e) { + /* Wraps all kinds of Exceptions in a ClassNotFoundException (this behavior + * matches with official >= 1.5, this was different for <=1.4) + */ + throw new ClassNotFoundException(null, e); + } + } + + /* Applet beans are treated in the following way: + * - all AppletS get a default AppletStub + * - all AppletS are initialized using the AppletInitializer instance (if it is available) + * - as every other Bean Applets are added to a BeanContext if one is available + * - each instantiated Applet is initialized using Applet.init() (this is not done for deserialized ones) + * - finally AppletS get activated using the AppletInitializerS activate-Method + * + * The order of operations is important for compatibility. + */ + Applet applet = null; + if (bean instanceof Applet) + { + // Makes a second instanceof call unneccessary (instanceof is expensive). + applet = (Applet) bean; + + /* The AppletStub's code and document base is set as follows: + * The code base is always the URL from where the class data originated + * (without the package name). + * If the Applet was deserialized the document base is the location of + * the serialized instance (usually the ".ser" file) otherwise its the URL + * from where the class data originated (usually the absolute directory + * location of the ".class" file). + */ + applet.setStub( + new DummyAppletStub( + applet + .getClass() + .getProtectionDomain() + .getCodeSource() + .getLocation(), + (beanLocation == null) ? classLocation : beanLocation)); + + // Runs the Applet's initialization using an AppletInitializer. + if (initializer != null) + { + initializer.initialize(applet, beanContext); + } + } + + // Adds the new bean to its BeanContext. + if (beanContext != null) + { + beanContext.add(bean); + } + + if (applet != null) + { + + // Initializes an instantiated (not deserialized) Applet using its own method. + if (beanLocation == null) + { + applet.init(); + } + + // Runs the Applet's activation using an AppletInitializer. + if (initializer != null) + { + initializer.activate(applet); + } + } + + return bean; + } + + /** + * Returns the Bean as a different class type. + * This should be used instead of casting to get a new + * type view of a Bean, because in the future there may + * be new types of Bean, even Beans spanning multiple + * Objects. + * + * @param bean the Bean to cast. + * @param newClass the Class to cast it to. + * + * @return the Bean as a new view, or if the operation + * could not be performed, the Bean itself. + */ + public static Object getInstanceOf(Object bean, Class newClass) + { + return bean; + } + + /** + * Determines whether the Bean can be cast to a different + * class type. + * This should be used instead of instanceof to determine + * a Bean's castability, because in the future there may + * be new types of Bean, even Beans spanning multiple + * Objects. + * + * @param bean the Bean to cast. + * @param newBeanClass the Class to cast it to. + * + * @return whether the Bean can be cast to the class type + * in question. + */ + public static boolean isInstanceOf(Object bean, Class newBeanClass) + { + return newBeanClass.isInstance(bean); + } + + /** + * Returns whether the GUI is available to use. + *

    Defaults to true.

    + * + * @return whether the GUI is available to use. + */ + public static boolean isGuiAvailable() + { + return guiAvailable; + } + + /** + * Returns whether it is design time. Design time means + * we are in a RAD tool. + *

    Defaults to false.

    + * + * @return whether it is design time. + */ + public static boolean isDesignTime() + { + return designTime; + } + + /** + * Sets whether the GUI is available to use. + * + * @param guiAvailable whether the GUI is available to use. + */ + public static void setGuiAvailable(boolean guiAvailable) + throws SecurityException + { + Beans.guiAvailable = guiAvailable; + } + + /** + * Sets whether it is design time. Design time means we + * are in a RAD tool. + * + * @param designTime whether it is design time. + */ + public static void setDesignTime(boolean designTime) + throws SecurityException + { + Beans.designTime = designTime; + } + +} diff --git a/libjava/classpath/java/beans/ConstructorProperties.java b/libjava/classpath/java/beans/ConstructorProperties.java new file mode 100644 index 000000000..4c82c0033 --- /dev/null +++ b/libjava/classpath/java/beans/ConstructorProperties.java @@ -0,0 +1,72 @@ +/* ConstructorProperties.java - Associate constructor params with props + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.CONSTRUCTOR; + +/** + * An annotation used to associate the parameters of a + * constructor with the accessor methods that later provide + * access to these values. For example, the parameters of + * the constructor Person(String name, int age) + * may be linked to the bean's two accessors, getName() + * and getAge() using + * @ConstructorProperties({"name","age"}). + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +@Documented @Retention(RUNTIME) @Target(CONSTRUCTOR) +public @interface ConstructorProperties +{ + + /** + * Contains the name of the accessor methods associated + * with each constructor parameter. + * + * @return the accessor method names corresponding to the + * constructor parameters. + */ + String[] value(); + +} diff --git a/libjava/classpath/java/beans/Customizer.java b/libjava/classpath/java/beans/Customizer.java new file mode 100644 index 000000000..bb6b4e2dd --- /dev/null +++ b/libjava/classpath/java/beans/Customizer.java @@ -0,0 +1,86 @@ +/* java.beans.Customizer + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** You may explicitly provide a Customizer for your Bean + ** class, which allows you complete control of the editing + ** of the Bean.

    + ** + ** A Customizer is meant to be embedded in an RAD tool, + ** and thus must be a descendant of java.awt.Component.

    + ** + ** It must also have a constructor with no arguments. This + ** is the constructor that will be called by the RAD tool to + ** instantiate the Customizer.

    + ** + ** Over its lifetime, an instance of a Customizer will only + ** customize one single Bean. A new instance of the + ** Customizer will be instantiated to edit any other Beans.

    + ** + ** The Customizer is responsible for notifying its + ** PropertyChangeListeners of any changes that are made, + ** according to the rules of PropertyChangeListeners (i.e. + ** notify the clients after the property has + ** changed). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 29 Jul 1998 + ** @see java.beans.BeanDescriptor.getCustomizerClass() + **/ + +public interface Customizer { + /** Set the object to Customize. This will always be a + ** Bean that had a BeanDescriptor indicating this + ** Customizer. + ** @param bean the Bean to customize. + **/ + void setObject(Object bean); + + /** Add a PropertyChangeListener. + ** @param l the PropertyChangeListener to add. + **/ + void addPropertyChangeListener(PropertyChangeListener l); + + /** Remove a PropertyChangeListener. + ** @param l the PropertyChangeListener to remove. + **/ + void removePropertyChangeListener(PropertyChangeListener l); +} diff --git a/libjava/classpath/java/beans/DefaultPersistenceDelegate.java b/libjava/classpath/java/beans/DefaultPersistenceDelegate.java new file mode 100644 index 000000000..3a4d86cca --- /dev/null +++ b/libjava/classpath/java/beans/DefaultPersistenceDelegate.java @@ -0,0 +1,211 @@ +/* DefaultPersistenceDelegate.java + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/**

    DefaultPersistenceDelegate is a {@link PersistenceDelegate} + * implementation that can be used to serialize objects which adhere to the + * Java Beans naming convention.

    + * + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class DefaultPersistenceDelegate extends PersistenceDelegate +{ + + private String[] constructorPropertyNames; + + /** Using this constructor the object to be serialized will be instantiated + * with the default non-argument constructor. + */ + public DefaultPersistenceDelegate() + { + } + + /** This constructor allows to specify which Bean properties appear + * in the constructor. + * + *

    The implementation reads the mentioned properties from the Bean + * instance and applies it in the given order to a corresponding + * constructor.

    + * + * @param constructorPropertyNames The properties the Bean's constructor + * should be given to. + */ + public DefaultPersistenceDelegate(String[] constructorPropertyNames) + { + this.constructorPropertyNames = constructorPropertyNames; + } + + protected boolean mutatesTo(Object oldInstance, Object newInstance) + { + try + { + + return (constructorPropertyNames != null + && constructorPropertyNames.length > 0 + && oldInstance.getClass() + .getDeclaredMethod("equals", + new Class[] { Object.class }) != null) + ? oldInstance.equals(newInstance) + : super.mutatesTo(oldInstance, newInstance); + } + catch (NoSuchMethodException nsme) + { + return super.mutatesTo(oldInstance, newInstance); + } + } + + protected Expression instantiate(Object oldInstance, Encoder out) + { + Object[] args = null; + + try + { + // If there are property names in the array, then we create + // a corresponding argument array and store every + // argument in it. To retrieve an argument object we have + // dig up the right property in the bean class' BeanInfo + // object. + // This is so costly in terms of execution time I better + // not think twice about it ... + if (constructorPropertyNames != null) + { + args = new Object[constructorPropertyNames.length]; + + // Look up the properties of oldInstance's class to find matches for + // the + // names given in the constructor. + PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo( + oldInstance.getClass()).getPropertyDescriptors(); + + for (int i = 0; i < constructorPropertyNames.length; i++) + { + // Scan the property descriptions for a matching name. + for (int j = 0; j < propertyDescs.length; j++) + { + if (propertyDescs[i].getName().equals( + constructorPropertyNames[i])) + { + Method readMethod = propertyDescs[i].getReadMethod(); + + args[i] = readMethod.invoke(oldInstance); + } + } + } + } + + } + catch (IllegalAccessException iae) + { + out.getExceptionListener().exceptionThrown(iae); + } + catch (IllegalArgumentException iarge) + { + out.getExceptionListener().exceptionThrown(iarge); + } + catch (InvocationTargetException ite) + { + out.getExceptionListener().exceptionThrown(ite); + } + catch (IntrospectionException ie) + { + out.getExceptionListener().exceptionThrown(ie); + } + + return new Expression(oldInstance, oldInstance.getClass(), "new", args); + } + + protected void initialize(Class type, Object oldInstance, + Object newInstance, Encoder out) + { + // Calling the supertype's implementation of initialize makes it + // possible that descendants of classes like AbstractHashMap + // or Hashtable are serialized correctly. This mechanism grounds on + // two other facts: + // * Each class which has not registered a special purpose + // PersistenceDelegate is handled by a DefaultPersistenceDelegate + // instance. + // * PersistenceDelegate.initialize() is implemented in a way that it + // calls the initialize method of the superclass' persistence delegate. + super.initialize(type, oldInstance, newInstance, out); + + // Suppresses the writing of property setting statements when this delegate + // is not used for the exact instance type. By doing so the following code + // is called only once per object. + if (type != oldInstance.getClass()) + return; + + try + { + PropertyDescriptor[] propertyDescs = Introspector.getBeanInfo( + oldInstance.getClass()).getPropertyDescriptors(); + + for (int i = 0; i < propertyDescs.length; i++) + { + Method readMethod = propertyDescs[i].getReadMethod(); + Method writeMethod = propertyDescs[i].getWriteMethod(); + + if (readMethod != null && writeMethod != null) + { + Object oldValue = readMethod.invoke(oldInstance); + + if (oldValue != null) + out.writeStatement(new Statement(oldInstance, + writeMethod.getName(), + new Object[] { oldValue })); + } + } + } + catch (IntrospectionException ie) + { + out.getExceptionListener().exceptionThrown(ie); + } + catch (IllegalAccessException iae) + { + out.getExceptionListener().exceptionThrown(iae); + } + catch (InvocationTargetException ite) + { + out.getExceptionListener().exceptionThrown(ite); + } + } +} diff --git a/libjava/classpath/java/beans/DesignMode.java b/libjava/classpath/java/beans/DesignMode.java new file mode 100644 index 000000000..bc79361c1 --- /dev/null +++ b/libjava/classpath/java/beans/DesignMode.java @@ -0,0 +1,95 @@ +/* java.beans.DesignMode + Copyright (C) 1999, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * BeanContextChild implementors implement this to get information + * about whether they are in a design time or runtime environment. + * The reason this is restricted to BeanContextChildren is that + * only things in the BeanContext hierarchy are given this + * information in the first place. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextChild + */ +public interface DesignMode +{ + + /** + * Use this name when firing PropertyChangeEvents from your Bean. + */ + String PROPERTYNAME = "designTime"; + + /** + * The environment will call this method on your + * BeanContextChild when it is registered in a parent + * BeanContext or when behavior needs to switch from + * design time to runtime behavior (or vice versa). + *

    + * + * BeanContexts are required to fire + * PropertyChangeEvents when properties change. + * designTime is a property, and therefore when you + * implement setDesignTime(), you need to fire a + * PropertyChangeEvent with the old value, the new + * value and using PROPERTYNAME as the property name. + * + * @param designTime the new value of design time, + * true if it is design time, + * false if it is runtime. + * + * @fixme I'm frankly not really sure whether it's the case that + * the BeanContext can change the status of the Bean from + * design time to runtime. But it appears that it may be so. + * + * @see java.beans.PropertyChangeEvent + * @see java.beans.beancontext.BeanContext + * @see #PROPERTYNAME + */ + void setDesignTime(boolean designTime); + + /** + * This method should tell whether it is design time or runtime. + * @return true if design time, false if + * runtime. + */ + boolean isDesignTime(); + +} diff --git a/libjava/classpath/java/beans/Encoder.java b/libjava/classpath/java/beans/Encoder.java new file mode 100644 index 000000000..b3d232a31 --- /dev/null +++ b/libjava/classpath/java/beans/Encoder.java @@ -0,0 +1,433 @@ +/* Encoder.java + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.DefaultExceptionListener; +import gnu.java.beans.encoder.ArrayPersistenceDelegate; +import gnu.java.beans.encoder.ClassPersistenceDelegate; +import gnu.java.beans.encoder.CollectionPersistenceDelegate; +import gnu.java.beans.encoder.MapPersistenceDelegate; +import gnu.java.beans.encoder.PrimitivePersistenceDelegate; + +import java.util.AbstractCollection; +import java.util.HashMap; +import java.util.IdentityHashMap; + +/** + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class Encoder +{ + + /** + * An internal DefaultPersistenceDelegate instance that is used for every + * class that does not a have a special special PersistenceDelegate. + */ + private static PersistenceDelegate defaultPersistenceDelegate; + + private static PersistenceDelegate fakePersistenceDelegate; + + /** + * Stores the relation Class->PersistenceDelegate. + */ + private static HashMap delegates = new HashMap(); + + /** + * Stores the relation oldInstance->newInstance + */ + private IdentityHashMap candidates = new IdentityHashMap(); + + private ExceptionListener exceptionListener; + + /** + * A simple number that is used to restrict the access to writeExpression and + * writeStatement. The rule is that both methods should only be used when an + * object is written to the stream (= writeObject). Therefore accessCounter is + * incremented just before the call to writeObject and decremented afterwards. + * Then writeStatement and writeExpression allow execution only if + * accessCounter is bigger than zero. + */ + private int accessCounter = 0; + + public Encoder() + { + setupDefaultPersistenceDelegates(); + + setExceptionListener(null); + } + + /** + * Sets up a bunch of {@link PersistenceDelegate} instances which are needed + * for the basic working of a {@link Encoder}s. + */ + private static void setupDefaultPersistenceDelegates() + { + synchronized (delegates) + { + if (defaultPersistenceDelegate != null) + return; + + delegates.put(Class.class, new ClassPersistenceDelegate()); + + PersistenceDelegate pd = new PrimitivePersistenceDelegate(); + delegates.put(Boolean.class, pd); + delegates.put(Byte.class, pd); + delegates.put(Short.class, pd); + delegates.put(Integer.class, pd); + delegates.put(Long.class, pd); + delegates.put(Float.class, pd); + delegates.put(Double.class, pd); + + delegates.put(Object[].class, new ArrayPersistenceDelegate()); + + pd = new CollectionPersistenceDelegate(); + delegates.put(AbstractCollection.class, pd); + + pd = new MapPersistenceDelegate(); + delegates.put(java.util.AbstractMap.class, pd); + delegates.put(java.util.Hashtable.class, pd); + + defaultPersistenceDelegate = new DefaultPersistenceDelegate(); + delegates.put(Object.class, defaultPersistenceDelegate); + + // Creates a PersistenceDelegate implementation which is + // returned for 'null'. In practice this instance is + // not used in any way and is just here to be compatible + // with the reference implementation which returns a + // similar instance when calling getPersistenceDelegate(null) . + fakePersistenceDelegate = new PersistenceDelegate() + { + protected Expression instantiate(Object o, Encoder e) + { + return null; + } + }; + + } + } + + protected void writeObject(Object o) + { + // 'null' has no PersistenceDelegate and will not + // create an Expression which has to be cloned. + // However subclasses should be aware that writeObject + // may be called with a 'null' argument and should + // write the proper representation of it. + if (o == null) + return; + + PersistenceDelegate pd = getPersistenceDelegate(o.getClass()); + + accessCounter++; + pd.writeObject(o, this); + accessCounter--; + + } + + /** + * Sets the {@link ExceptionListener} instance to be used for reporting + * recorable exceptions in the instantiation and initialization sequence. If + * the argument is null a default instance will be used that + * prints the thrown exception to System.err. + */ + public void setExceptionListener(ExceptionListener listener) + { + exceptionListener = (listener != null) + ? listener : DefaultExceptionListener.INSTANCE; + } + + /** + * Returns the currently active {@link ExceptionListener} instance. + */ + public ExceptionListener getExceptionListener() + { + return exceptionListener; + } + + public PersistenceDelegate getPersistenceDelegate(Class type) + { + // This is not specified but the JDK behaves like this. + if (type == null) + return fakePersistenceDelegate; + + // Treats all array classes in the same way and assigns + // them a shared PersistenceDelegate implementation tailored + // for array instantation and initialization. + if (type.isArray()) + return (PersistenceDelegate) delegates.get(Object[].class); + + PersistenceDelegate pd = (PersistenceDelegate) delegates.get(type); + + return (pd != null) ? pd : defaultPersistenceDelegate; + } + + /** + * Sets the {@link PersistenceDelegate} instance for the given class. + *

    + * Note: Throws a NullPointerException if the argument is + * null. + *

    + *

    + * Note: Silently ignores PersistenceDelegates for Array types and primitive + * wrapper classes. + *

    + *

    + * Note: Although this method is not declared static changes to + * the {@link PersistenceDelegate}s affect all + * {@link Encoder} instances. In this implementation the + * access is thread safe. + *

    + */ + public void setPersistenceDelegate(Class type, + PersistenceDelegate delegate) + { + // If the argument is null this will cause a NullPointerException + // which is expected behavior. + + // This makes custom PDs for array, primitive types and their wrappers + // impossible but this is how the JDK behaves. + if (type.isArray() || type.isPrimitive() || type == Boolean.class + || type == Byte.class || type == Short.class || type == Integer.class + || type == Long.class || type == Float.class || type == Double.class) + return; + + synchronized (delegates) + { + delegates.put(type, delegate); + } + + } + + public Object remove(Object oldInstance) + { + return candidates.remove(oldInstance); + } + + /** + * Returns the replacement object which has been created by the encoder during + * the instantiation sequence or null if the object has not + * been processed yet. + *

    + * Note: The String class acts as an endpoint for the + * inherently recursive algorithm of the {@link Encoder}. Therefore instances + * of String will always be returned by this method. In other + * words the assertion: + * assert (anyEncoder.get(anyString) == anyString) + * + * + *

    Note: If null is requested, the result will + * always be null.

    + */ + public Object get(Object oldInstance) + { + // String instances are handled in a special way. + // No one knows why this is not officially specified + // because this is a rather important design decision. + return (oldInstance == null) ? null : + (oldInstance.getClass() == String.class) ? + oldInstance : candidates.get(oldInstance); + } + + /** + *

    + * Note: If you call this method not from within an object instantiation and + * initialization sequence it will be silently ignored. + *

    + */ + public void writeStatement(Statement stmt) + { + // Silently ignore out of bounds calls. + if (accessCounter <= 0) + return; + + Object target = stmt.getTarget(); + + Object newTarget = get(target); + if (newTarget == null) + { + writeObject(target); + newTarget = get(target); + } + + Object[] args = stmt.getArguments(); + Object[] newArgs = new Object[args.length]; + + for (int i = 0; i < args.length; i++) + { + newArgs[i] = get(args[i]); + if (newArgs[i] == null || isImmutableType(args[i].getClass())) + { + writeObject(args[i]); + newArgs[i] = get(args[i]); + } + } + + Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs); + + try + { + newStmt.execute(); + } + catch (Exception e) + { + exceptionListener.exceptionThrown(e); + } + + } + + /** + *

    + * Note: If you call this method not from within an object instantiation and + * initialization sequence it will be silently ignored. + *

    + */ + public void writeExpression(Expression expr) + { + // Silently ignore out of bounds calls. + if (accessCounter <= 0) + return; + + Object target = expr.getTarget(); + Object value = null; + Object newValue = null; + + try + { + value = expr.getValue(); + } + catch (Exception e) + { + exceptionListener.exceptionThrown(e); + return; + } + + + newValue = get(value); + + if (newValue == null) + { + Object newTarget = get(target); + if (newTarget == null) + { + writeObject(target); + newTarget = get(target); + + // May happen if exception was thrown. + if (newTarget == null) + { + return; + } + } + + Object[] args = expr.getArguments(); + Object[] newArgs = new Object[args.length]; + + for (int i = 0; i < args.length; i++) + { + newArgs[i] = get(args[i]); + if (newArgs[i] == null || isImmutableType(args[i].getClass())) + { + writeObject(args[i]); + newArgs[i] = get(args[i]); + } + } + + Expression newExpr = new Expression(newTarget, expr.getMethodName(), + newArgs); + + // Fakes the result of Class.forName() to make it possible + // to hand such a type to the encoding process. + if (value instanceof Class && ((Class) value).isPrimitive()) + newExpr.setValue(value); + + // Instantiates the new object. + try + { + newValue = newExpr.getValue(); + + candidates.put(value, newValue); + } + catch (Exception e) + { + exceptionListener.exceptionThrown(e); + + return; + } + + writeObject(value); + + } + else if(value.getClass() == String.class || value.getClass() == Class.class) + { + writeObject(value); + } + + } + + /** Returns whether the given class is an immutable + * type which has to be handled differently when serializing it. + * + *

    Immutable objects always have to be instantiated instead of + * modifying an existing instance.

    + * + * @param type The class to test. + * @return Whether the first argument is an immutable type. + */ + boolean isImmutableType(Class type) + { + return type == String.class || type == Class.class + || type == Integer.class || type == Boolean.class + || type == Byte.class || type == Short.class + || type == Long.class || type == Float.class + || type == Double.class; + } + + /** Sets the stream candidate for a given object. + * + * @param oldObject The object given to the encoder. + * @param newObject The object the encoder generated. + */ + void putCandidate(Object oldObject, Object newObject) + { + candidates.put(oldObject, newObject); + } + +} diff --git a/libjava/classpath/java/beans/EventHandler.java b/libjava/classpath/java/beans/EventHandler.java new file mode 100644 index 000000000..967ba8294 --- /dev/null +++ b/libjava/classpath/java/beans/EventHandler.java @@ -0,0 +1,604 @@ +/* java.beans.EventHandler + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + *

    EventHandler forms a bridge between dynamically created listeners and + * arbitrary properties and methods.

    + * + *

    You can use this class to easily create listener implementations for + * some basic interactions between an event source and its target. Using + * the three static methods named create you can create + * these listener implementations.

    + * + *

    See the documentation of each method for usage examples.

    + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Robert Schuster (thebohemian@gmx.net) + * @since 1.4 + */ +public class EventHandler implements InvocationHandler +{ + // The name of the method that will be implemented. If null, any method. + private String listenerMethod; + + // The object to call action on. + private Object target; + + // The name of the method or property setter in target. + private String action; + + // The property to extract from an event passed to listenerMethod. + private String property; + + // The target objects Class. + private Class targetClass; + + // String class doesn't already have a capitalize routine. + private String capitalize(String s) + { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * Creates a new EventHandler instance. + * + *

    Typical creation is done with the create method, not by knewing an + * EventHandler.

    + * + *

    This constructs an EventHandler that will connect the method + * listenerMethodName to target.action, extracting eventPropertyName from + * the first argument of listenerMethodName. and sending it to action.

    + * + *

    Throws a NullPointerException if the target + * argument is null. + * + * @param target Object that will perform the action. + * @param action A property or method of the target. + * @param eventPropertyName A readable property of the inbound event. + * @param listenerMethodName The listener method name triggering the action. + */ + public EventHandler(Object target, String action, String eventPropertyName, + String listenerMethodName) + { + this.target = target; + + // Retrieving the class is done for two reasons: + // 1) The class object is needed very frequently in the invoke() method. + // 2) The constructor should throw a NullPointerException if target is null. + targetClass = target.getClass(); + + this.action = action; // Turn this into a method or do we wait till + // runtime + property = eventPropertyName; + listenerMethod = listenerMethodName; + } + + /** + * Returns the event property name. + */ + public String getEventPropertyName() + { + return property; + } + + /** + * Returns the listener's method name. + */ + public String getListenerMethodName() + { + return listenerMethod; + } + + /** + * Returns the target object. + */ + public Object getTarget() + { + return target; + } + + /** + * Returns the action method name. + */ + public String getAction() + { + return action; + } + + // Fetch a qualified property like a.b.c from object o. The properties can + // be boolean isProp or object getProp properties. + // + // Returns a length 2 array with the first entry containing the value + // extracted from the property, and the second entry contains the class of + // the method return type. + // + // We play this game because if the method returns a native type, the return + // value will be a wrapper. If we then take the type of the wrapper and use + // it to locate the action method that takes the native type, it won't match. + private Object[] getProperty(Object o, String prop) + { + // Isolate the first property name from a.b.c. + int pos; + String rest = null; + if ((pos = prop.indexOf('.')) != -1) + { + rest = prop.substring(pos + 1); + prop = prop.substring(0, pos); + } + + // Find a method named getProp. It could be isProp instead. + Method getter; + try + { + // Look for boolean property getter isProperty + getter = o.getClass().getMethod("is" + capitalize(prop)); + } + catch (NoSuchMethodException nsme1) + { + try { + // Look for regular property getter getProperty + getter = o.getClass().getMethod("get" + capitalize(prop)); + } catch(NoSuchMethodException nsme2) { + try { + // Finally look for a method of the name prop + getter = o.getClass().getMethod(prop); + } catch(NoSuchMethodException nsme3) { + // Ok, give up with an intelligent hint for the user. + throw new RuntimeException("Method not called: Could not find a property or method '" + prop + + "' in " + o.getClass() + " while following the property argument '" + property + "'."); + } + } + } + try { + Object val = getter.invoke(o); + + if (rest != null) + return getProperty(val, rest); + + return new Object[] {val, getter.getReturnType()}; + } catch(InvocationTargetException ite) { + throw new RuntimeException("Method not called: Property or method '" + prop + "' has thrown an exception.", ite); + } catch(IllegalAccessException iae) { + // This cannot happen because we looked up method with Class.getMethod() + // which returns public methods only. + throw (InternalError) new InternalError("Non-public method was invoked.").initCause(iae); + } + } + + /** + * Invokes the EventHandler. + * + *

    This method is normally called by the listener's proxy implementation.

    + * + * @param proxy The listener interface that is implemented using + * the proxy mechanism. + * @param method The method that was called on the proxy instance. + * @param arguments The arguments which where given to the method. + * @throws Throwable NoSuchMethodException is thrown when the EventHandler's + * action method or property cannot be found. + */ + public Object invoke(Object proxy, Method method, Object[] arguments) + { + try { + // The method instance of the target object. We have to find out which + // one we have to invoke. + Method actionMethod = null; + + // Listener methods that weren't specified are ignored. If listenerMethod + // is null, then all listener methods are processed. + if (listenerMethod != null && !method.getName().equals(listenerMethod)) + return null; + + // If a property is defined we definitely need a valid object at + // arguments[0] that can be used to retrieve a value to which the + // property of the target gets set. + if(property != null) { + // Extracts the argument. We will let it fail with a NullPointerException + // the caller used a listener method that has no arguments. + Object event = arguments[0]; + + // Obtains the property XXX propertyType keeps showing up null - why? + // because the object inside getProperty changes, but the ref variable + // can't change this way, dolt! need a better way to get both values out + // - need method and object to do the invoke and get return type + Object v[] = getProperty(event, property); + Object[] args = new Object[] { v[0] }; + + // Changes the class array that controls which method signature we are going + // to look up in the target object. + Class[] argTypes = new Class[] { initClass((Class) v[1]) }; + + // Tries to find a setter method to which we can apply the + while(argTypes[0] != null) { + try + { + // Look for a property setter for action. + actionMethod = targetClass.getMethod("set" + capitalize(action), argTypes); + + return actionMethod.invoke(target, args); + } + catch (NoSuchMethodException e) + { + // If action as property didn't work, try as method later. + } + + argTypes[0] = nextClass(argTypes[0]); + } + + // We could not find a suitable setter method. Now we try again interpreting + // action as the method name itself. + // Since we probably have changed the block local argTypes array + // we need to rebuild it. + argTypes = new Class[] { initClass((Class) v[1]) }; + + // Tries to find a setter method to which we can apply the + while(argTypes[0] != null) { + try + { + actionMethod = targetClass.getMethod(action, argTypes); + + return actionMethod.invoke(target, args); + } + catch (NoSuchMethodException e) + { + } + + argTypes[0] = nextClass(argTypes[0]); + } + + throw new RuntimeException("Method not called: Could not find a public method named '" + + action + "' in target " + targetClass + " which takes a '" + + v[1] + "' argument or a property of this type."); + } + + // If property was null we will search for a no-argument method here. + // Note: The ordering of method lookups is important because we want to prefer no-argument + // calls like the JDK does. This means if we have actionMethod() and actionMethod(Event) we will + // call the first *EVEN* if we have a valid argument for the second method. This is behavior compliant + // to the JDK. + // If actionMethod() is not available but there is a actionMethod(Event) we take this. That makes us + // more specification compliant than the JDK itself because this one will fail in such a case. + try + { + actionMethod = targetClass.getMethod(action); + } + catch(NoSuchMethodException nsme) + { + // Note: If we want to be really strict the specification says that a no-argument method should + // accept an EventObject (or subclass I guess). However since the official implementation is broken + // anyways, it's more flexible without the EventObject restriction and we are compatible on everything + // else this can stay this way. + if(arguments != null && arguments.length >= 1/* && arguments[0] instanceof EventObject*/) { + Class[] targetArgTypes = new Class[] { initClass(arguments[0].getClass()) }; + + while(targetArgTypes[0] != null) { + try + { + // If no property exists we expect the first element of the arguments to be + // an EventObject which is then applied to the target method. + + actionMethod = targetClass.getMethod(action, targetArgTypes); + + return actionMethod.invoke(target, new Object[] { arguments[0] }); + } + catch(NoSuchMethodException nsme2) + { + + } + + targetArgTypes[0] = nextClass(targetArgTypes[0]); + } + + } + } + + // If we do not have a Method instance at this point this means that all our tries + // failed. The JDK throws an ArrayIndexOutOfBoundsException in this case. + if(actionMethod == null) + throw new ArrayIndexOutOfBoundsException(0); + + // Invoke target.action(property) + return actionMethod.invoke(target); + } catch(InvocationTargetException ite) { + throw new RuntimeException(ite.getCause()); + } catch(IllegalAccessException iae) { + // Cannot happen because we always use getMethod() which returns public + // methods only. Otherwise there is something seriously broken in + // GNU Classpath. + throw (InternalError) new InternalError("Non-public method was invoked.").initCause(iae); + } + } + + /** + *

    Returns the primitive type for every wrapper class or the + * class itself if it is no wrapper class.

    + * + *

    This is needed because to be able to find both kinds of methods: + * One that takes a wrapper class as the first argument and one that + * accepts a primitive instead.

    + */ + private Class initClass(Class klass) { + if(klass == Boolean.class) { + return Boolean.TYPE; + } else if(klass == Byte.class) { + return Byte.TYPE; + } else if(klass == Short.class) { + return Short.TYPE; + } else if(klass == Integer.class) { + return Integer.TYPE; + } else if(klass == Long.class) { + return Long.TYPE; + } else if(klass == Float.class) { + return Float.TYPE; + } else if(klass == Double.class) { + return Double.TYPE; + } else { + return klass; + } + } + + /** + * + * + * @param klass + * @return + */ + private Class nextClass(Class klass) { + if(klass == Boolean.TYPE) { + return Boolean.class; + } else if(klass == Byte.TYPE) { + return Byte.class; + } else if(klass == Short.TYPE) { + return Short.class; + } else if(klass == Integer.TYPE) { + return Integer.class; + } else if(klass == Long.TYPE) { + return Long.class; + } else if(klass == Float.TYPE) { + return Float.class; + } else if(klass == Double.TYPE) { + return Double.class; + } else { + return klass.getSuperclass(); + } + } + + /** + *

    Constructs an implementation of listenerInterface + * to dispatch events.

    + * + *

    You can use such an implementation to simply call a public + * no-argument method of an arbitrary target object or to forward + * the first argument of the listener method to the target method.

    + * + *

    Call this method like:

    + * + * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, target, "dispose")); + * + * + *

    to achieve the following behavior:

    + * + * button.addActionListener(new ActionListener() { + * public void actionPerformed(ActionEvent ae) { + * target.dispose(); + * } + * }); + * + * + *

    That means if you need a listener implementation that simply calls a + * a no-argument method on a given instance for each + * method of the listener interface.

    + * + *

    Note: The action is interpreted as a method name. If your target object + * has no no-argument method of the given name the EventHandler tries to find + * a method with the same name but which can accept the first argument of the + * listener method. Usually this will be an event object but any other object + * will be forwarded, too. Keep in mind that using a property name instead of a + * real method here is wrong and will throw an ArrayIndexOutOfBoundsException + * whenever one of the listener methods is called.

    + * + *

    The EventHandler will automatically convert primitives + * to their wrapper class and vice versa. Furthermore it will call + * a target method if it accepts a superclass of the type of the + * first argument of the listener method.

    + * + *

    In case that the method of the target object throws an exception + * it will be wrapped in a RuntimeException and thrown out + * of the listener method.

    + * + *

    In case that the method of the target object cannot be found an + * ArrayIndexOutOfBoundsException will be thrown when the + * listener method is invoked.

    + * + *

    A call to this method is equivalent to: + * create(listenerInterface, target, action, null, null)

    + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target property or method to invoke. + * @return A constructed proxy object. + */ + public static T create(Class listenerInterface, Object target, + String action) + { + return create(listenerInterface, target, action, null, null); + } + + /** + *

    Constructs an implementation of listenerInterface + * to dispatch events.

    + * + *

    Use this method if you want to create an implementation that retrieves + * a property value from the first argument of the listener method + * and applies it to the target's property or method. This first argument + * of the listener is usually an event object but any other object is + * valid, too.

    + * + *

    You can set the value of eventPropertyName to "prop" + * to denote the retrieval of a property named "prop" from the event + * object. In case that no such property exists the EventHandler + * will try to find a method with that name.

    + * + *

    If you set eventPropertyName to a value like this "a.b.c" + * EventHandler will recursively evaluate the properties "a", "b" + * and "c". Again if no property can be found the EventHandler + * tries a method name instead. This allows mixing the names, too: "a.toString" + * will retrieve the property "a" from the event object and will then call + * the method "toString" on it.

    + * + *

    An exception thrown in any of these methods will provoke a + * RuntimeException to be thrown which contains an + * InvocationTargetException containing the triggering exception.

    + * + *

    If you set eventPropertyName to a non-null value the + * action parameter will be interpreted as a property name + * or a method name of the target object.

    + * + *

    Any object retrieved from the event object and applied to the + * target will converted from primitives to their wrapper class or + * vice versa or applied to a method that accepts a superclass + * of the object.

    + * + *

    Examples:

    + *

    The following code:

    + * button.addActionListener( + * new ActionListener() { + * public void actionPerformed(ActionEvent ae) { + * Object o = ae.getSource().getClass().getName(); + * textField.setText((String) o); + * } + * }); + * + * + *

    Can be expressed using the EventHandler like this:

    + *

    + * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "text", "source.class.name"); + * + *

    + * + *

    As said above you can specify the target as a method, too:

    + *

    + * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "source.class.name"); + * + *

    + * + *

    Furthermore you can use method names in the property:

    + *

    + * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "getSource.getClass.getName"); + * + *

    + * + *

    Finally you can mix names:

    + *

    + * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "source.getClass.name"); + * + *

    + * + *

    A call to this method is equivalent to: + * create(listenerInterface, target, action, null, null) + *

    + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target property or method to invoke. + * @param eventPropertyName Name of property to extract from event. + * @return A constructed proxy object. + */ + public static T create(Class listenerInterface, Object target, + String action, String eventPropertyName) + { + return create(listenerInterface, target, action, eventPropertyName, null); + } + + /** + *

    Constructs an implementation of listenerInterface + * to dispatch events.

    + * + *

    Besides the functionality described for {@link create(Class, Object, String)} + * and {@link create(Class, Object, String, String)} this method allows you + * to filter the listener method that should have an effect. Look at these + * method's documentation for more information about the EventHandler's + * usage.

    + * + *

    If you want to call dispose on a JFrame instance + * when the WindowListener.windowClosing() method was invoked use + * the following code:

    + *

    + * + * EventHandler.create(WindowListener.class, jframeInstance, "dispose", null, "windowClosing"); + * + *

    + * + *

    A NullPointerException is thrown if the listenerInterface + * or target argument are null. + * + * @param listenerInterface Listener interface to implement. + * @param target Object to invoke action on. + * @param action Target method name to invoke. + * @param eventPropertyName Name of property to extract from event. + * @param listenerMethodName Listener method to implement. + * @return A constructed proxy object. + */ + public static T create(Class listenerInterface, Object target, + String action, String eventPropertyName, + String listenerMethodName) + { + // Create EventHandler instance + EventHandler eh = new EventHandler(target, action, eventPropertyName, + listenerMethodName); + + // Create proxy object passing in the event handler + Object proxy = Proxy.newProxyInstance(listenerInterface.getClassLoader(), + new Class[] {listenerInterface}, + eh); + + return (T) proxy; + } +} diff --git a/libjava/classpath/java/beans/EventSetDescriptor.java b/libjava/classpath/java/beans/EventSetDescriptor.java new file mode 100644 index 000000000..3f537c743 --- /dev/null +++ b/libjava/classpath/java/beans/EventSetDescriptor.java @@ -0,0 +1,763 @@ +/* java.beans.EventSetDescriptor + Copyright (C) 1998, 2006 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.beans; + +import gnu.java.lang.ClassHelper; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Vector; + +/** + * EventSetDescriptor describes the hookup between an event source class and + * an event listener class. + * + *

    EventSets have several attributes: the listener class, + * the events that can be fired to the listener (methods in the listener + * class), and an add and remove listener method from the event firer's + * class. + *

    + * + *

    + * The methods have these constraints on them: + *

      + *
    • event firing methods: must have void return value. Any + * parameters and exceptions are allowed. May be public, protected or + * package-protected. (Don't ask me why that is, I'm just following the spec. + * The only place it is even mentioned is in the Java Beans white paper, and + * there it is only implied.)
    • + * + *
    • add listener method: must have void return value. Must + * take exactly one argument, of the listener class's type. May fire either + * zero exceptions, or one exception of type + * java.util.TooManyListenersException. + * Must be public.
    • + * + *
    • remove listener method: must have void return value. Must + * take exactly one argument, of the listener class's type. May not fire any + * exceptions. Must be public.
    • + *
    + * + *

    + * A final constraint is that event listener classes must extend from + * EventListener. + *

    + * + *

    + * There are also various design patterns associated with some of the methods + * of construction. Those are explained in more detail in the appropriate + * constructors. + *

    + * + *

    + * Documentation Convention: for proper Internalization of + * Beans inside an RAD tool, sometimes there are two names for a property or + * method: a programmatic, or locale-independent name, which can be used + * anywhere, and a localized, display name, for ease of use. In the + * documentation I will specify different String values as either + * programmatic or localized to make this distinction clear. + * + * @author John Keiser + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.1 + */ + +public class EventSetDescriptor extends FeatureDescriptor +{ + private Method addListenerMethod; + + private Method removeListenerMethod; + + private Class listenerType; + + private MethodDescriptor[] listenerMethodDescriptors; + + private Method[] listenerMethods; + + private Method getListenerMethod; + + private boolean unicast; + + private boolean inDefaultEventSet = true; + + /** + * Creates a new EventSetDescriptor + * This version of the constructor enforces the rules imposed on the methods + * described at the top of this class, as well as searching for: + *

    + * + *
      + *
    1. + * The event-firing method must be non-private with signature void + * <listenerMethodName>(<eventSetName>Event) (where + * <eventSetName> has its first character capitalized + * by the constructor and the Event is a descendant of + * {@link java.util.EventObject}) in class listenerType + * (any exceptions may be thrown). Implementation note: Note that + * there could conceivably be multiple methods with this type of signature + * (example: java.util.MouseEvent vs. + * my.very.own.MouseEvent). In this implementation, all + * methods fitting the description will be put into the + * EventSetDescriptor, even though the spec says only one + * should be chosen (they probably weren't thinking as pathologically as I + * was). I don't like arbitrarily choosing things. If your class has only one + * such signature, as most do, you'll have no problems.
    2. + * + *
    3. The add and remove methods must be public and named void + * add<eventSetName>Listener(<listenerType>) and + * void remove<eventSetName>Listener(<listenerType>) + * in in class eventSourceClass, where + * <eventSetName> will have its first letter capitalized. + * Standard exception rules (see class description) apply.
    4. + *
    + * + * @param eventSourceClass + * the class containing the add/remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). This will + * be used to generate the name of the event object as well as the + * names of the add and remove methods. + * @param listenerType + * the class containing the event firing method. + * @param listenerMethodName + * the name of the event firing method. + * @exception IntrospectionException + * if listenerType is not an EventListener, or if methods are not + * found or are invalid. + */ + public EventSetDescriptor(Class eventSourceClass, String eventSetName, + Class listenerType, String listenerMethodName) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + String[] names = new String[1]; + names[0] = listenerMethodName; + + try + { + eventSetName = Character.toUpperCase(eventSetName.charAt(0)) + + eventSetName.substring(1); + } + catch (StringIndexOutOfBoundsException e) + { + eventSetName = ""; + } + + findMethods(eventSourceClass, listenerType, names, + "add" + eventSetName + "Listener", + "remove" + eventSetName + "Listener", eventSetName + "Event"); + this.listenerType = listenerType; + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** + * Creates a new EventSetDescriptor. + * + *

    This form of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + *

    + * + * @param eventSourceClass + * the class containing the add and remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the event firing methods. + * @param listenerMethodNames + * the names of the even firing methods. + * @param addListenerMethodName + * the name of the add listener method. + * @param removeListenerMethodName + * the name of the remove listener method. + * @exception IntrospectionException + * if listenerType is not an EventListener or if methods are not + * found or are invalid. + */ + public EventSetDescriptor(Class eventSourceClass, String eventSetName, + Class listenerType, String[] listenerMethodNames, + String addListenerMethodName, + String removeListenerMethodName) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + findMethods(eventSourceClass, listenerType, listenerMethodNames, + addListenerMethodName, removeListenerMethodName, null); + this.listenerType = listenerType; + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** + * Creates a new EventSetDescriptor. + * + *

    + * This variant of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + *

    + *

    + * A valid GetListener method is public, flags no exceptions and has one + * argument which is of type Class + * {@link java.awt.Component#getListeners(Class)} is such a method. + *

    + *

    + * Note: The validity of the return value of the GetListener method is not + * checked. + *

    + * + * @param eventSourceClass + * the class containing the add and remove listener methods. + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the event firing methods. + * @param listenerMethodNames + * the names of the even firing methods. + * @param addListenerMethodName + * the name of the add listener method. + * @param removeListenerMethodName + * the name of the remove listener method. + * @param getListenerMethodName + * Name of a method which returns the array of listeners. + * @exception IntrospectionException + * if listenerType is not an EventListener or if methods are not + * found or are invalid. + * @since 1.4 + */ + public EventSetDescriptor(Class eventSourceClass, String eventSetName, + Class listenerType, String[] listenerMethodNames, + String addListenerMethodName, + String removeListenerMethodName, + String getListenerMethodName) + throws IntrospectionException + { + this(eventSourceClass, eventSetName, listenerType, listenerMethodNames, + addListenerMethodName, removeListenerMethodName); + + Method newGetListenerMethod = null; + + try + { + newGetListenerMethod + = eventSourceClass.getMethod(getListenerMethodName, + new Class[] { Class.class }); + } + catch (NoSuchMethodException nsme) + { + throw (IntrospectionException) + new IntrospectionException("No method named " + getListenerMethodName + + " in class " + listenerType + + " which can be used as" + + " getListenerMethod.").initCause(nsme); + } + + // Note: This does not check the return value (which + // should be EventListener[]) but the JDK does not either. + + getListenerMethod = newGetListenerMethod; + + } + + /** + * Creates a new EventSetDescriptor. + * + *

    + * This variant of the constructor allows you to specify the names of the + * methods and adds no new constraints on top of the rules already described + * at the top of the class. + *

    + *

    + * A valid GetListener method is public, flags no exceptions and has one + * argument which is of type Class + * {@link java.awt.Component#getListeners(Class)} is such a method. + *

    + *

    + * Note: The validity of the return value of the GetListener method is not + * checked. + *

    + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethods + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @param getListenerMethod + * The method which returns an array of the listeners. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + * @since 1.4 + */ + public EventSetDescriptor(String eventSetName, Class listenerType, + Method[] listenerMethods, Method addListenerMethod, + Method removeListenerMethod, + Method getListenerMethod) + throws IntrospectionException + { + this(eventSetName, listenerType, listenerMethods, addListenerMethod, + removeListenerMethod); + + // Do no checks if the getListenerMethod is null. + if (getListenerMethod.getParameterTypes().length != 1 + || getListenerMethod.getParameterTypes()[0] != Class.class + || getListenerMethod.getExceptionTypes().length > 0 + || !Modifier.isPublic(getListenerMethod.getModifiers())) + throw new IntrospectionException("GetListener method is invalid."); + + // Note: This does not check the return value (which + // should be EventListener[]) but the JDK does not either. + + this.getListenerMethod = getListenerMethod; + } + + /** + * Creates a new EventSetDescriptor. + * + *

    This form of constructor allows you to explicitly say which methods + * do what, and no reflection is done by the EventSetDescriptor. + * The methods are, however, checked to ensure that they follow the rules + * set forth at the top of the class. + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethods + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + */ + public EventSetDescriptor(String eventSetName, Class listenerType, + Method[] listenerMethods, Method addListenerMethod, + Method removeListenerMethod) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + this.listenerMethods = listenerMethods; + this.addListenerMethod = addListenerMethod; + this.removeListenerMethod = removeListenerMethod; + this.listenerType = listenerType; + checkMethods(); + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** Creates a new EventSetDescriptor. + * + *

    This form of constructor allows you to explicitly say which methods do + * what, and no reflection is done by the EventSetDescriptor. + * The methods are, however, checked to ensure that they follow the rules + * set forth at the top of the class. + * + * @param eventSetName + * the programmatic name of the event set, generally starting with a + * lowercase letter (i.e. fooManChu instead of FooManChu). + * @param listenerType + * the class containing the listenerMethods. + * @param listenerMethodDescriptors + * the event firing methods. + * @param addListenerMethod + * the add listener method. + * @param removeListenerMethod + * the remove listener method. + * @exception IntrospectionException + * if the listenerType is not an EventListener, or any of the + * methods are invalid. + */ + public EventSetDescriptor(String eventSetName, Class listenerType, + MethodDescriptor[] listenerMethodDescriptors, + Method addListenerMethod, + Method removeListenerMethod) + throws IntrospectionException + { + setName(eventSetName); + if (!java.util.EventListener.class.isAssignableFrom(listenerType)) + { + throw new IntrospectionException( + "Listener type is not an EventListener."); + } + + this.listenerMethodDescriptors = listenerMethodDescriptors; + this.listenerMethods = new Method[listenerMethodDescriptors.length]; + for (int i = 0; i < this.listenerMethodDescriptors.length; i++) + { + this.listenerMethods[i] + = this.listenerMethodDescriptors[i].getMethod(); + } + + this.addListenerMethod = addListenerMethod; + this.removeListenerMethod = removeListenerMethod; + this.listenerType = listenerType; + checkMethods(); + checkAddListenerUnicast(); + if (this.removeListenerMethod.getExceptionTypes().length > 0) + { + throw new IntrospectionException( + "Listener remove method throws exceptions."); + } + } + + /** Returns the class that contains the event firing methods. + */ + public Class getListenerType() + { + return listenerType; + } + + /** Returns the event firing methods. + */ + public Method[] getListenerMethods() + { + return listenerMethods; + } + + /** Returns the event firing methods as {@link MethodDescriptor}. + */ + public MethodDescriptor[] getListenerMethodDescriptors() + { + if (listenerMethodDescriptors == null) + { + listenerMethodDescriptors + = new MethodDescriptor[listenerMethods.length]; + + for (int i = 0; i < listenerMethods.length; i++) + { + listenerMethodDescriptors[i] + = new MethodDescriptor(listenerMethods[i]); + } + } + + return listenerMethodDescriptors; + } + + /** Returns the add listener method. + */ + public Method getAddListenerMethod() + { + return addListenerMethod; + } + + /* Returns the remove listener method. + */ + public Method getRemoveListenerMethod() + { + return removeListenerMethod; + } + + /** + * Returns the method that retrieves the listeners or null if + * it does not exist. + */ + public Method getGetListenerMethod() + { + return getListenerMethod; + } + + /** Sets whether or not multiple listeners may be added. + * + * @param unicast + * whether or not multiple listeners may be added. + */ + public void setUnicast(boolean unicast) + { + this.unicast = unicast; + } + + /** Returns whether or not multiple listeners may be added. + * (Defaults to false.) + */ + public boolean isUnicast() + { + return unicast; + } + + /** Sets whether or not this is in the default event set. + * + * @param inDefaultEventSet + * whether this is in the default event set. + */ + public void setInDefaultEventSet(boolean inDefaultEventSet) + { + this.inDefaultEventSet = inDefaultEventSet; + } + + /** Returns whether or not this is in the default event set. + * (Defaults to true.) + */ + public boolean isInDefaultEventSet() + { + return inDefaultEventSet; + } + + private void checkAddListenerUnicast() throws IntrospectionException + { + Class[] addListenerExceptions = this.addListenerMethod.getExceptionTypes(); + if (addListenerExceptions.length > 1) + { + throw new IntrospectionException( + "Listener add method throws too many exceptions."); + } + else if (addListenerExceptions.length == 1 + && !java.util.TooManyListenersException.class + .isAssignableFrom(addListenerExceptions[0])) + { + throw new IntrospectionException( + "Listener add method throws too many exceptions."); + } + } + + private void checkMethods() throws IntrospectionException + { + if (!addListenerMethod.getDeclaringClass() + .isAssignableFrom(removeListenerMethod.getDeclaringClass()) + && !removeListenerMethod.getDeclaringClass() + .isAssignableFrom(addListenerMethod.getDeclaringClass())) + { + throw new IntrospectionException( + "add and remove listener methods do not come from the" + + " same class. This is bad."); + } + if (!addListenerMethod.getReturnType().equals(java.lang.Void.TYPE) + || addListenerMethod.getParameterTypes().length != 1 + || !listenerType.equals(addListenerMethod.getParameterTypes()[0]) + || !Modifier.isPublic(addListenerMethod.getModifiers())) + { + throw new IntrospectionException("Add Listener Method invalid."); + } + if (!removeListenerMethod.getReturnType().equals(java.lang.Void.TYPE) + || removeListenerMethod.getParameterTypes().length != 1 + || !listenerType.equals(removeListenerMethod.getParameterTypes()[0]) + || removeListenerMethod.getExceptionTypes().length > 0 + || !Modifier.isPublic(removeListenerMethod.getModifiers())) + { + throw new IntrospectionException("Remove Listener Method invalid."); + } + + for (int i = 0; i < listenerMethods.length; i++) + { + if (!listenerMethods[i].getReturnType().equals(java.lang.Void.TYPE) + || Modifier.isPrivate(listenerMethods[i].getModifiers())) + { + throw new IntrospectionException("Event Method " + + listenerMethods[i].getName() + + " non-void or private."); + } + if (!listenerMethods[i].getDeclaringClass() + .isAssignableFrom(listenerType)) + { + throw new IntrospectionException("Event Method " + + listenerMethods[i].getName() + + " not from class " + + listenerType.getName()); + } + } + } + + private void findMethods(Class eventSourceClass, Class listenerType, + String listenerMethodNames[], + String addListenerMethodName, + String removeListenerMethodName, + String absurdEventClassCheckName) + throws IntrospectionException + { + + /* Find add listener method and remove listener method. */ + Class[] listenerArgList = new Class[1]; + listenerArgList[0] = listenerType; + try + { + this.addListenerMethod + = eventSourceClass.getMethod(addListenerMethodName, + listenerArgList); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "SecurityException trying to access method " + + addListenerMethodName + "."); + } + catch (NoSuchMethodException E) + { + throw new IntrospectionException("Could not find method " + + addListenerMethodName + "."); + } + + if (this.addListenerMethod == null + || !this.addListenerMethod.getReturnType().equals(java.lang.Void.TYPE)) + { + throw new IntrospectionException( + "Add listener method does not exist, is not public," + + " or is not void."); + } + + try + { + this.removeListenerMethod + = eventSourceClass.getMethod(removeListenerMethodName, + listenerArgList); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "SecurityException trying to access method " + + removeListenerMethodName + "."); + } + catch (NoSuchMethodException E) + { + throw new IntrospectionException("Could not find method " + + removeListenerMethodName + "."); + } + if (this.removeListenerMethod == null + || !this.removeListenerMethod.getReturnType() + .equals(java.lang.Void.TYPE)) + { + throw new IntrospectionException( + "Remove listener method does not exist, is not public," + + " or is not void."); + } + + /* Find the listener methods. */ + Method[] methods; + try + { + methods = ClassHelper.getAllMethods(listenerType); + } + catch (SecurityException E) + { + throw new IntrospectionException( + "Security: You cannot access fields in this class."); + } + + Vector chosenMethods = new Vector(); + boolean[] listenerMethodFound = new boolean[listenerMethodNames.length]; + for (int i = 0; i < methods.length; i++) + { + if (Modifier.isPrivate(methods[i].getModifiers())) + { + continue; + } + Method currentMethod = methods[i]; + Class retval = currentMethod.getReturnType(); + if (retval.equals(java.lang.Void.TYPE)) + { + for (int j = 0; j < listenerMethodNames.length; j++) + { + if (currentMethod.getName().equals(listenerMethodNames[j]) + && (absurdEventClassCheckName == null + || (currentMethod.getParameterTypes().length == 1 + && ((currentMethod.getParameterTypes()[0]) + .getName().equals(absurdEventClassCheckName) + || (currentMethod.getParameterTypes()[0]) + .getName().endsWith("." + absurdEventClassCheckName))))) + { + chosenMethods.addElement(currentMethod); + listenerMethodFound[j] = true; + } + } + } + } + + /* Make sure we found all the methods we were looking for. */ + for (int i = 0; i < listenerMethodFound.length; i++) + { + if (!listenerMethodFound[i]) + { + throw new IntrospectionException("Could not find event method " + + listenerMethodNames[i]); + } + } + + /* Now that we've chosen the listener methods we want, store them. */ + this.listenerMethods = new Method[chosenMethods.size()]; + for (int i = 0; i < chosenMethods.size(); i++) + { + this.listenerMethods[i] = (Method) chosenMethods.elementAt(i); + } + } + +} diff --git a/libjava/classpath/java/beans/ExceptionListener.java b/libjava/classpath/java/beans/ExceptionListener.java new file mode 100644 index 000000000..0eeb8a956 --- /dev/null +++ b/libjava/classpath/java/beans/ExceptionListener.java @@ -0,0 +1,57 @@ +/* ExceptionListener.java -- listen for recoverable internal exceptions + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * This interface allows a class to monitor internal exceptions, to try to + * recover from them. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public interface ExceptionListener +{ + /** + * Fired after an exception occurs. + * + * @param e the trapped exception + */ + void exceptionThrown(Exception e); +} // interface ExceptionListener diff --git a/libjava/classpath/java/beans/Expression.java b/libjava/classpath/java/beans/Expression.java new file mode 100644 index 000000000..d07c28b4a --- /dev/null +++ b/libjava/classpath/java/beans/Expression.java @@ -0,0 +1,138 @@ +/* java.beans.Expression + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +/** + *

    An Expression captures the execution of an object method + * that returns a value.

    + * + *

    It stores an object, the method to call, and the arguments to pass to + * the method.

    + * + *

    While this class can generally be used to describe method calls it is + * part of the XML serialization API.

    + * + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class Expression extends Statement +{ + // This is a placeholder to indicate that value hasn't been set + // yet; + private static final Object UNSET = new Object(); + + // The value to return. This is equal to unset until getValue is called. + private Object value; + + /** + * Constructor Constructs an Expression representing the invocation of + * object.methodName(arg[0], arg[1], ...); However, it will never be executed. + * Instead, value will always be returned. + * + * @param value + * The value to return. + * @param target + * The object to invoke the method on. + * @param methodName + * The object method to invoke. + * @param arguments + * An array of arguments to pass to the method. + */ + public Expression(Object value, Object target, String methodName, + Object[] arguments) + { + super(target, methodName, arguments); + this.value = value; + } + + /** + * Constructor Constructs an Expression representing the invocation of + * object.methodName(arg[0], arg[1], ...); + * + * @param target + * The object to invoke the method on. + * @param methodName + * The object method to invoke. + * @param arguments + * An array of arguments to pass to the method. + */ + public Expression(Object target, String methodName, Object[] arguments) + { + super(target, methodName, arguments); + this.value = UNSET; + } + + /** + * Return the result of executing the method. If the cached value has not yet + * been set, the method is executed in the same way as Statement.execute(), + * except that the value is cached, and then returned. If the value has been + * set, it is returned without executing the method again. + * + * @return the result of executing the method. + * @exception Exception + * if an error occurs + */ + public Object getValue() throws Exception + { + if (value == UNSET) + value = doExecute(); + return value; + } + + /** + * Set the cached value to be returned by getValue() + * + * @param value + * the value to cache and return. + */ + public void setValue(Object value) + { + this.value = value; + } + + /** + * Return a string representation of this expression. + */ + public String toString() + { + String result = super.toString(); + if (value != UNSET) + return value.getClass().getName() + "=" + result; + return result; + } +} diff --git a/libjava/classpath/java/beans/FeatureDescriptor.java b/libjava/classpath/java/beans/FeatureDescriptor.java new file mode 100644 index 000000000..87e4a2fe7 --- /dev/null +++ b/libjava/classpath/java/beans/FeatureDescriptor.java @@ -0,0 +1,232 @@ +/* java.beans.FeatureDescriptor + Copyright (C) 1998, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * FeatureDescriptor is the common superclass for all JavaBeans Descriptor + * classes. JavaBeans descriptors are abstract descriptors of properties, + * events, methods, beans, etc.

    + * + * Documentation Convention: for proper + * Internalization of Beans inside an RAD tool, sometimes there + * are two names for a property or method: a programmatic, or + * locale-independent name, which can be used anywhere, and a + * localized, display name, for ease of use. In the + * documentation I will specify different String values as + * either programmatic or localized to + * make this distinction clear. + * + * @author John Keiser + * @since 1.1 + */ + +public class FeatureDescriptor +{ + String name; + String displayName; + String shortDescription; + boolean expert; + boolean hidden; + boolean preferred; + + Hashtable valueHash; + + /** + * Instantiate this FeatureDescriptor with appropriate default values. + */ + public FeatureDescriptor() + { + valueHash = new Hashtable(); + } + + /** + * Get the programmatic name of this feature. + */ + public String getName() + { + return name; + } + + /** + * Set the programmatic name of this feature. + * + * @param name the new name for this feature. + */ + public void setName(String name) + { + this.name = name; + } + + /** + * Get the localized (display) name of this feature. + * + * @returns The localized display name of this feature or falls + * back to the programmatic name. + */ + public String getDisplayName() + { + return (displayName == null) ? name : displayName; + } + + /** + * Set the localized (display) name of this feature. + * + * @param displayName the new display name for this feature. + */ + public void setDisplayName(String displayName) + { + this.displayName = displayName; + } + + /** + * Get the localized short description for this feature. + * + * @returns A short localized description of this feature or + * what getDisplayName returns in case, that no short description + * is available. + */ + public String getShortDescription() + { + return (shortDescription == null) ? getDisplayName() : shortDescription; + } + + /** + * Set the localized short description for this feature. + * + * @param shortDescription the new short description for this feature. + */ + public void setShortDescription(String shortDescription) + { + this.shortDescription = shortDescription; + } + + /** + * Indicates whether this feature is for expert use only. + * + * @return true if for use by experts only, + * or false if anyone can use it. + */ + public boolean isExpert() + { + return expert; + } + + /** + * Set whether this feature is for expert use only. + * + * @param expert true if for use by experts only, + * or false if anyone can use it. + */ + public void setExpert(boolean expert) + { + this.expert = expert; + } + + /** + * Indicates whether this feature is for use by tools only. + * If it is for use by tools only, then it should not be displayed. + * + * @return true if tools only should use it, + * or false if anyone can see it. + */ + public boolean isHidden() + { + return hidden; + } + + /** + * Set whether this feature is for use by tools only. + * If it is for use by tools only, then it should not be displayed. + * + * @param hidden true if tools only should use it, + * or false if anyone can see it. + */ + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public boolean isPreferred () + { + return preferred; + } + + public void setPreferred (boolean preferred) + { + this.preferred = preferred; + } + + /** + * Get an arbitrary value set with setValue(). + * + * @param name the programmatic name of the key. + * + * @return the value associated with this name, + * or null if there is none. + */ + public Object getValue(String name) + { + return valueHash.get(name); + } + + /** + * Set an arbitrary string-value pair with this feature. + * + * @param name the programmatic name of the key. + * @param value the value to associate with the name. + */ + public void setValue(String name, Object value) + { + valueHash.put(name, value); + } + + /** + * Get a list of the programmatic key names set with setValue(). + * + * @return an Enumerator over all the programmatic key names associated + * with this feature. + */ + public Enumeration attributeNames() + { + return valueHash.keys(); + } +} diff --git a/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java b/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java new file mode 100644 index 000000000..9c46e1fa1 --- /dev/null +++ b/libjava/classpath/java/beans/IndexedPropertyChangeEvent.java @@ -0,0 +1,81 @@ +/* Indexed property change event + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * This is like a PropertyChangeEvent, but also carries with it the + * index of the property which changed. + * @author Tom Tromey (tromey@redhat.com) + * @since 1.5 + */ +public class IndexedPropertyChangeEvent extends PropertyChangeEvent +{ + private static final long serialVersionUID = -320227448495806870L; + + /** + * Index of the item that was changed. + */ + private int index; + + /** + * Create a new IndexedPropertyChangeEvent. + * @param source the Bean containing the property + * @param name the property's name + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @param index the index of the element in the property which changed + * @throws IllegalArgumentException if source is null + */ + public IndexedPropertyChangeEvent(Object source, String name, + Object oldValue, Object newValue, + int index) + { + super(source, name, oldValue, newValue); + this.index = index; + } + + /** + * Return the index of the changed property. + * @return the index + */ + public int getIndex() + { + return index; + } +} diff --git a/libjava/classpath/java/beans/IndexedPropertyDescriptor.java b/libjava/classpath/java/beans/IndexedPropertyDescriptor.java new file mode 100644 index 000000000..b7914133a --- /dev/null +++ b/libjava/classpath/java/beans/IndexedPropertyDescriptor.java @@ -0,0 +1,421 @@ +/* IndexedPropertyDescriptor.java -- + Copyright (C) 1998, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +/** + * IndexedPropertyDescriptor describes information about a JavaBean + * indexed property, by which we mean an array-like property that + * has been exposed via a pair of get and set methods and another + * pair that allows you to get to the property by an index.

    + * + * An example property would have four methods like this:

    + * FooBar[] getFoo()
    + * void setFoo(FooBar[])
    + * FooBar getFoo(int)
    + * void setFoo(int,FooBar)

    + * + * The constraints put on get and set methods are:

    + *

      + *
    1. There must be at least a get(int) or a set(int,...) method. + * Nothing else is required. Spec note:One nice restriction + * would be that if there is a get() there must be a get(int), same + * with set, but that is not in the spec and is fairly harmless.)
    2. + *
    3. A get array method must have signature + * <propertyType>[] <getMethodName>()
    4. + *
    5. A set array method must have signature + * void <setMethodName>(<propertyType>[])
    6. + *
    7. A get index method must have signature + * <propertyType> <getMethodName>(int)
    8. + *
    9. A set index method must have signature + * void <setMethodName>(int,<propertyType>)
    10. + *
    11. All these methods may throw any exception.
    12. + *
    13. All these methods must be public.
    14. + *
    + * + * @author John Keiser + * @since JDK1.1 + */ +public class IndexedPropertyDescriptor extends PropertyDescriptor +{ + private Class indexedPropertyType; + private Method setIndex; + private Method getIndex; + + /** + * Create a new IndexedPropertyDescriptor by introspection. + * This form of constructor creates the PropertyDescriptor by + * looking for getter methods named get<name>() + * and setter methods named + * set<name>() in class + * <beanClass>, where <name> has its + * first letter capitalized by the constructor.

    + * + * Implementation note: If there is a get(int) method, + * then the return type of that method is used to find the + * remaining methods. If there is no get method, then the + * set(int) method is searched for exhaustively and that type + * is used to find the others.

    + * + * Spec note: + * If there is no get(int) method and multiple set(int) methods with + * the same name and the correct parameters (different type of course), + * then an IntrospectionException is thrown. While Sun's spec + * does not state this, it can make Bean behavior different on + * different systems (since method order is not guaranteed) and as + * such, can be treated as a bug in the spec. I am not aware of + * whether Sun's implementation catches this. + * + * @param name the programmatic name of the property, usually + * starting with a lowercase letter (e.g. fooManChu + * instead of FooManChu). + * @param beanClass the class the get and set methods live in. + * + * @exception IntrospectionException if the methods are not found or + * invalid. + */ + public IndexedPropertyDescriptor(String name, Class beanClass) + throws IntrospectionException + { + super(name); + String capitalized; + try + { + capitalized = Character.toUpperCase(name.charAt(0)) + + name.substring(1); + } + catch(StringIndexOutOfBoundsException e) + { + capitalized = ""; + } + findMethods(beanClass, "get" + capitalized, "set" + capitalized, + "get" + capitalized, "set" + capitalized); + } + + /** + * Create a new IndexedPropertyDescriptor by introspection. + * This form of constructor allows you to specify the + * names of the get and set methods to search for.

    + * + * Implementation note: If there is a get(int) method, + * then the return type of that method is used to find the + * remaining methods. If there is no get method, then the + * set(int) method is searched for exhaustively and that type + * is used to find the others.

    + * + * Spec note: + * If there is no get(int) method and multiple set(int) methods with + * the same name and the correct parameters (different type of course), + * then an IntrospectionException is thrown. While Sun's spec + * does not state this, it can make Bean behavior different on + * different systems (since method order is not guaranteed) and as + * such, can be treated as a bug in the spec. I am not aware of + * whether Sun's implementation catches this. + * + * @param name the programmatic name of the property, usually + * starting with a lowercase letter (e.g. fooManChu + * instead of FooManChu). + * @param beanClass the class the get and set methods live in. + * @param getMethodName the name of the get array method. + * @param setMethodName the name of the set array method. + * @param getIndexName the name of the get index method. + * @param setIndexName the name of the set index method. + * + * @exception IntrospectionException if the methods are not found or invalid. + */ + public IndexedPropertyDescriptor(String name, Class beanClass, + String getMethodName, String setMethodName, + String getIndexName, String setIndexName) + throws IntrospectionException + { + super(name); + findMethods(beanClass, getMethodName, setMethodName, getIndexName, + setIndexName); + } + + /** + * Create a new PropertyDescriptor using explicit Methods. + * Note that the methods will be checked for conformance to standard + * Property method rules, as described above at the top of this class. + * + * @param name the programmatic name of the property, usually + * starting with a lowercase letter (e.g. fooManChu + * instead of FooManChu). + * @param getMethod the get array method. + * @param setMethod the set array method. + * @param getIndex the get index method. + * @param setIndex the set index method. + * + * @exception IntrospectionException if the methods are not found or invalid. + */ + public IndexedPropertyDescriptor(String name, Method getMethod, + Method setMethod, Method getIndex, + Method setIndex) + throws IntrospectionException + { + super(name); + if(getMethod != null && getMethod.getParameterTypes().length > 0) + throw new IntrospectionException("get method has parameters"); + if(getMethod != null && setMethod.getParameterTypes().length != 1) + throw new IntrospectionException("set method does not have exactly one parameter"); + if(getMethod != null && setMethod != null) + { + if(!getMethod.getReturnType().equals(setMethod.getParameterTypes()[0])) + { + throw new IntrospectionException("set and get methods do not " + + "share the same type"); + } + if(!getMethod.getDeclaringClass().isAssignableFrom + (setMethod.getDeclaringClass()) + && !setMethod.getDeclaringClass().isAssignableFrom + (getMethod.getDeclaringClass())) + { + throw new IntrospectionException("set and get methods are not in " + + "the same class."); + } + } + + if (getIndex != null + && (getIndex.getParameterTypes().length != 1 + || !(getIndex.getParameterTypes()[0]).equals(java.lang.Integer.TYPE))) + { + throw new IntrospectionException("get index method has wrong " + + "parameters"); + } + if (setIndex != null + && (setIndex.getParameterTypes().length != 2 + || !(setIndex.getParameterTypes()[0]).equals(java.lang.Integer.TYPE))) + { + throw new IntrospectionException("set index method has wrong " + + "parameters"); + } + if (getIndex != null && setIndex != null) + { + if(!getIndex.getReturnType().equals(setIndex.getParameterTypes()[1])) + { + throw new IntrospectionException("set index methods do not share " + + "the same type"); + } + if(!getIndex.getDeclaringClass().isAssignableFrom + (setIndex.getDeclaringClass()) + && !setIndex.getDeclaringClass().isAssignableFrom + (getIndex.getDeclaringClass())) + { + throw new IntrospectionException("get and set index methods are " + + "not in the same class."); + } + } + + if (getIndex != null && getMethod != null + && !getIndex.getDeclaringClass().isAssignableFrom + (getMethod.getDeclaringClass()) + && !getMethod.getDeclaringClass().isAssignableFrom + (getIndex.getDeclaringClass())) + { + throw new IntrospectionException("methods are not in the same class."); + } + + if (getIndex != null && getMethod != null + && !Array.newInstance(getIndex.getReturnType(),0) + .getClass().equals(getMethod.getReturnType())) + { + throw new IntrospectionException("array methods do not match index " + + "methods."); + } + + this.getMethod = getMethod; + this.setMethod = setMethod; + this.getIndex = getIndex; + this.setIndex = setIndex; + this.indexedPropertyType = getIndex != null ? getIndex.getReturnType() + : setIndex.getParameterTypes()[1]; + this.propertyType = getMethod != null ? getMethod.getReturnType() + : (setMethod != null ? setMethod.getParameterTypes()[0] + : Array.newInstance(this.indexedPropertyType,0).getClass()); + } + + public Class getIndexedPropertyType() + { + return indexedPropertyType; + } + + public Method getIndexedReadMethod() + { + return getIndex; + } + + /** + * Sets the method that is used to read an indexed property. + * + * @param m the method to set + */ + public void setIndexedReadMethod(Method m) throws IntrospectionException + { + getIndex = m; + } + + public Method getIndexedWriteMethod() + { + return setIndex; + } + + /** + * Sets the method that is used to write an indexed property. + * + * @param m the method to set + */ + public void setIndexedWriteMethod(Method m) throws IntrospectionException + { + setIndex = m; + } + + private void findMethods(Class beanClass, String getMethodName, + String setMethodName, String getIndexName, + String setIndexName) + throws IntrospectionException + { + try + { + if(getIndexName != null) + { + try + { + Class[] getArgs = new Class[1]; + getArgs[0] = java.lang.Integer.TYPE; + getIndex = beanClass.getMethod(getIndexName,getArgs); + indexedPropertyType = getIndex.getReturnType(); + } + catch(NoSuchMethodException E) + { + } + } + if(getIndex != null) + { + if(setIndexName != null) + { + try + { + Class[] setArgs = new Class[2]; + setArgs[0] = java.lang.Integer.TYPE; + setArgs[1] = indexedPropertyType; + setIndex = beanClass.getMethod(setIndexName,setArgs); + if(!setIndex.getReturnType().equals(java.lang.Void.TYPE)) + { + throw new IntrospectionException(setIndexName + + " has non-void return type"); + } + } + catch(NoSuchMethodException E) + { + } + } + } + else if(setIndexName != null) + { + Method[] m = beanClass.getMethods(); + for(int i=0;i + * + * Don't worry about it too much, though: you can provide + * JavaBeans with as much customized information as you + * want, or as little as you want, using the BeanInfo + * interface (see BeanInfo for details).

    + * + * Order of Operations

    + * + * When you call getBeanInfo(class c), the Introspector + * first searches for BeanInfo class to see if you + * provided any explicit information. It searches for a + * class named <bean class name>BeanInfo in different + * packages, first searching the bean class's package + * and then moving on to search the beanInfoSearchPath.

    + * + * If it does not find a BeanInfo class, it acts as though + * it had found a BeanInfo class returning null from all + * methods (meaning it should discover everything through + * Introspection). If it does, then it takes the + * information it finds in the BeanInfo class to be + * canonical (that is, the information speaks for its + * class as well as all superclasses).

    + * + * When it has introspected the class, calls + * getBeanInfo(c.getSuperclass) and adds that information + * to the information it has, not adding to any information + * it already has that is canonical.

    + * + * Introspection Design Patterns

    + * + * When the Introspector goes in to read the class, it + * follows a well-defined order in order to not leave any + * methods unaccounted for. Its job is to step over all + * of the public methods in a class and determine whether + * they are part of a property, an event, or a method (in + * that order). + * + * + * Properties:

    + * + *

      + *
    1. If there is a public boolean isXXX() + * method, then XXX is a read-only boolean property. + * boolean getXXX() may be supplied in + * addition to this method, although isXXX() is the + * one that will be used in this case and getXXX() + * will be ignored. If there is a + * public void setXXX(boolean) method, + * it is part of this group and makes it a read-write + * property.
    2. + *
    3. If there is a + * public <type> getXXX(int) + * method, then XXX is a read-only indexed property of + * type <type>. If there is a + * public void setXXX(int,<type>) + * method, then it is a read-write indexed property of + * type <type>. There may also be a + * public <type>[] getXXX() and a + * public void setXXX(<type>) + * method as well.
    4. + *
    5. If there is a + * public void setXXX(int,<type>) + * method, then it is a write-only indexed property of + * type <type>. There may also be a + * public <type>[] getXXX() and a + * public void setXXX(<type>) + * method as well.
    6. + *
    7. If there is a + * public <type> getXXX() method, + * then XXX is a read-only property of type + * <type>. If there is a + * public void setXXX(<type>) + * method, then it will be used for the property and + * the property will be considered read-write.
    8. + *
    9. If there is a + * public void setXXX(<type>) + * method, then as long as XXX is not already used as + * the name of a property, XXX is assumed to be a + * write-only property of type <type>.
    10. + *
    11. In all of the above cases, if the setXXX() method + * throws PropertyVetoException, then the + * property in question is assumed to be constrained. + * No properties are ever assumed to be bound + * (Spec Note: this is not in the + * spec, it just makes sense). See PropertyDescriptor + * for a description of bound and constrained + * properties.
    12. + *
    + * + * Events:

    + * + * If there is a pair of methods, + * public void addXXX(<type>) and + * public void removeXXX(<type>), where + * <type> is a descendant of + * java.util.EventListener, then the pair of + * methods imply that this Bean will fire events to + * listeners of type <type>.

    + * + * If the addXXX() method throws + * java.util.TooManyListenersException, then + * the event set is assumed to be unicast. See + * EventSetDescriptor for a discussion of unicast event + * sets.

    + * + * Spec Note: the spec seems to say that + * the listener type's classname must be equal to the XXX + * part of addXXX() and removeXXX(), but that is not the + * case in Sun's implementation, so I am assuming it is + * not the case in general.

    + * + * Methods:

    + * + * Any public methods (including those which were used + * for Properties or Events) are used as Methods. + * + * @author John Keiser + * @since JDK1.1 + * @see java.beans.BeanInfo + */ +public class Introspector { + + public static final int USE_ALL_BEANINFO = 1; + public static final int IGNORE_IMMEDIATE_BEANINFO = 2; + public static final int IGNORE_ALL_BEANINFO = 3; + + static String[] beanInfoSearchPath = {"gnu.java.beans.info"}; + static Hashtable,BeanInfo> beanInfoCache = + new Hashtable,BeanInfo>(); + + private Introspector() {} + + /** + * Get the BeanInfo for class beanClass, + * first by looking for explicit information, next by + * using standard design patterns to determine + * information about the class. + * + * @param beanClass the class to get BeanInfo about. + * @return the BeanInfo object representing the class. + */ + public static BeanInfo getBeanInfo(Class beanClass) + throws IntrospectionException + { + BeanInfo cachedInfo; + synchronized(beanClass) + { + cachedInfo = beanInfoCache.get(beanClass); + if(cachedInfo != null) + { + return cachedInfo; + } + cachedInfo = getBeanInfo(beanClass,null); + beanInfoCache.put(beanClass,cachedInfo); + return cachedInfo; + } + } + + /** + * Returns a {@BeanInfo} instance for the given Bean class where a flag + * controls the usage of explicit BeanInfo class to retrieve that + * information. + * + *

    You have three options:

    + *

    With {@link #USE_ALL_BEANINFO} the result is the same as + * {@link #getBeanInfo(Class)}.

    + * + *

    Calling the method with flag set to + * {@link #IGNORE_IMMEDIATE_BEANINFO} will let it use all + * explicit BeanInfo classes for the beans superclasses + * but not for the bean class itself. Furthermore eventset, + * property and method information is retrieved by introspection + * if the explicit BeanInfos did not provide such data + * (ie. return null on {@link BeanInfo.getMethodDescriptors}, + * {@link BeanInfo.getEventSetDescriptors} and + * {@link BeanInfo.getPropertyDescriptors}.) + *

    + * + *

    When the method is called with flag + * + *

    Note: Any unknown value for flag is interpreted + * as {@link #IGNORE_ALL_BEANINFO}

    . + * + * @param beanClass The class whose BeanInfo should be returned. + * @param flag Controls the usage of explicit BeanInfo classes. + * @return A BeanInfo object describing the class. + * @throws IntrospectionException If something goes wrong while retrieving + * the bean data. + */ + public static BeanInfo getBeanInfo(Class beanClass, int flag) + throws IntrospectionException + { + IntrospectionIncubator ii; + BeanInfoEmbryo infoEmbryo; + + switch(flag) + { + case USE_ALL_BEANINFO: + return getBeanInfo(beanClass); + case IGNORE_IMMEDIATE_BEANINFO: + Class superclass = beanClass.getSuperclass(); + ExplicitInfo explicit = new ExplicitInfo(superclass, null); + + ii = new IntrospectionIncubator(); + if (explicit.explicitEventSetDescriptors != null) + ii.setEventStopClass(superclass); + + if (explicit.explicitMethodDescriptors != null) + ii.setMethodStopClass(superclass); + + if (explicit.explicitPropertyDescriptors != null) + ii.setPropertyStopClass(superclass); + + ii.addMethods(beanClass.getMethods()); + + infoEmbryo = ii.getBeanInfoEmbryo(); + merge(infoEmbryo, explicit); + + infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null)); + + return infoEmbryo.getBeanInfo(); + case IGNORE_ALL_BEANINFO: + default: + ii = new IntrospectionIncubator(); + ii.addMethods(beanClass.getMethods()); + infoEmbryo = ii.getBeanInfoEmbryo(); + infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null)); + + return infoEmbryo.getBeanInfo(); + } + } + + /** + * Flush all of the Introspector's internal caches. + * + * @since 1.2 + */ + public static void flushCaches() + { + beanInfoCache.clear(); + + // Clears all the intermediate ExplicitInfo instances which + // have been created. + // This makes sure we have to retrieve stuff like BeanDescriptors + // again. (Remember that FeatureDescriptor can be modified by the user.) + ExplicitInfo.flushCaches(); + } + + /** + * Flush the Introspector's internal cached information for a given + * class. + * + * @param clz the class to be flushed. + * @throws NullPointerException if clz is null. + * @since 1.2 + */ + public static void flushFromCaches(Class clz) + { + synchronized (clz) + { + beanInfoCache.remove(clz); + } + } + + /** Adds all explicity given bean info data to the introspected + * data. + * + * @param infoEmbryo Bean info data retrieved by introspection. + * @param explicit Bean info data retrieved by BeanInfo classes. + */ + private static void merge(BeanInfoEmbryo infoEmbryo, ExplicitInfo explicit) + { + PropertyDescriptor[] p = explicit.explicitPropertyDescriptors; + if(p!=null) + { + for(int i=0;i -1) + { + infoEmbryo.setDefaultPropertyName(p[explicit.defaultProperty].getName()); + } + } + EventSetDescriptor[] e = explicit.explicitEventSetDescriptors; + if(e!=null) + { + for(int i=0;i -1) + { + infoEmbryo.setDefaultEventName(e[explicit.defaultEvent].getName()); + } + } + MethodDescriptor[] m = explicit.explicitMethodDescriptors; + if(m!=null) + { + for(int i=0;ibeanClass, + * first by looking for explicit information, next by + * using standard design patterns to determine + * information about the class. It crawls up the + * inheritance tree until it hits topClass. + * + * @param beanClass the Bean class. + * @param stopClass the class to stop at. + * @return the BeanInfo object representing the class. + */ + public static BeanInfo getBeanInfo(Class beanClass, Class stopClass) + throws IntrospectionException + { + ExplicitInfo explicit = new ExplicitInfo(beanClass, stopClass); + + IntrospectionIncubator ii = new IntrospectionIncubator(); + ii.setPropertyStopClass(explicit.propertyStopClass); + ii.setEventStopClass(explicit.eventStopClass); + ii.setMethodStopClass(explicit.methodStopClass); + ii.addMethods(beanClass.getMethods()); + + BeanInfoEmbryo currentInfo = ii.getBeanInfoEmbryo(); + + merge(currentInfo, explicit); + + // Sets the info's BeanDescriptor to the one we extracted from the + // explicit BeanInfo instance(s) if they contained one. Otherwise we + // create the BeanDescriptor from scratch. + // Note: We do not create a copy the retrieved BeanDescriptor which will allow + // the user to modify the instance while it is cached. However this is how + // the RI does it. + currentInfo.setBeanDescriptor( + (explicit.explicitBeanDescriptor == null ? + new BeanDescriptor(beanClass, null) : + explicit.explicitBeanDescriptor)); + return currentInfo.getBeanInfo(); + } + + /** + * Get the search path for BeanInfo classes. + * + * @return the BeanInfo search path. + */ + public static String[] getBeanInfoSearchPath() + { + return beanInfoSearchPath; + } + + /** + * Set the search path for BeanInfo classes. + * @param beanInfoSearchPath the new BeanInfo search + * path. + */ + public static void setBeanInfoSearchPath(String[] beanInfoSearchPath) + { + Introspector.beanInfoSearchPath = beanInfoSearchPath; + } + + /** + * A helper method to convert a name to standard Java + * naming conventions: anything with two capitals as the + * first two letters remains the same, otherwise the + * first letter is decapitalized. URL = URL, I = i, + * MyMethod = myMethod. + * + * @param name the name to decapitalize. + * @return the decapitalized name. + */ + public static String decapitalize(String name) + { + try + { + if(!Character.isUpperCase(name.charAt(0))) + { + return name; + } + else + { + try + { + if(Character.isUpperCase(name.charAt(1))) + { + return name; + } + else + { + char[] c = name.toCharArray(); + c[0] = Character.toLowerCase(c[0]); + return new String(c); + } + } + catch(StringIndexOutOfBoundsException E) + { + char[] c = new char[1]; + c[0] = Character.toLowerCase(name.charAt(0)); + return new String(c); + } + } + } + catch(StringIndexOutOfBoundsException E) + { + return name; + } + catch(NullPointerException E) + { + return null; + } + } + + static BeanInfo copyBeanInfo(BeanInfo b) + { + java.awt.Image[] icons = new java.awt.Image[4]; + for(int i=1;i<=4;i++) + { + icons[i-1] = b.getIcon(i); + } + + return new ExplicitBeanInfo(b.getBeanDescriptor(), + b.getAdditionalBeanInfo(), + b.getPropertyDescriptors(), + b.getDefaultPropertyIndex(), + b.getEventSetDescriptors(), + b.getDefaultEventIndex(), + b.getMethodDescriptors(), + icons); + } +} + +class ExplicitInfo +{ + BeanDescriptor explicitBeanDescriptor; + BeanInfo[] explicitBeanInfo; + + PropertyDescriptor[] explicitPropertyDescriptors; + EventSetDescriptor[] explicitEventSetDescriptors; + MethodDescriptor[] explicitMethodDescriptors; + + int defaultProperty; + int defaultEvent; + + java.awt.Image[] im = new java.awt.Image[4]; + + Class propertyStopClass; + Class eventStopClass; + Class methodStopClass; + + static Hashtable explicitBeanInfos = new Hashtable(); + static Vector emptyBeanInfos = new Vector(); + + ExplicitInfo(Class beanClass, Class stopClass) + { + while(beanClass != null && !beanClass.equals(stopClass)) + { + + BeanInfo explicit = findExplicitBeanInfo(beanClass); + + + if(explicit != null) + { + + if(explicitBeanDescriptor == null) + { + explicitBeanDescriptor = explicit.getBeanDescriptor(); + } + + if(explicitBeanInfo == null) + { + explicitBeanInfo = explicit.getAdditionalBeanInfo(); + } + + if(explicitPropertyDescriptors == null) + { + if(explicit.getPropertyDescriptors() != null) + { + explicitPropertyDescriptors = explicit.getPropertyDescriptors(); + defaultProperty = explicit.getDefaultPropertyIndex(); + propertyStopClass = beanClass; + } + } + + if(explicitEventSetDescriptors == null) + { + if(explicit.getEventSetDescriptors() != null) + { + explicitEventSetDescriptors = explicit.getEventSetDescriptors(); + defaultEvent = explicit.getDefaultEventIndex(); + eventStopClass = beanClass; + } + } + + if(explicitMethodDescriptors == null) + { + if(explicit.getMethodDescriptors() != null) + { + explicitMethodDescriptors = explicit.getMethodDescriptors(); + methodStopClass = beanClass; + } + } + + if(im[0] == null && im[1] == null + && im[2] == null && im[3] == null) + { + im[0] = explicit.getIcon(0); + im[1] = explicit.getIcon(1); + im[2] = explicit.getIcon(2); + im[3] = explicit.getIcon(3); + } + } + beanClass = beanClass.getSuperclass(); + } + + if(propertyStopClass == null) + { + propertyStopClass = stopClass; + } + + if(eventStopClass == null) + { + eventStopClass = stopClass; + } + + if(methodStopClass == null) + { + methodStopClass = stopClass; + } + } + + /** Throws away all cached data and makes sure we re-instantiate things + * like BeanDescriptors again. + */ + static void flushCaches() { + explicitBeanInfos.clear(); + emptyBeanInfos.clear(); + } + + static BeanInfo findExplicitBeanInfo(Class beanClass) + { + BeanInfo retval = (BeanInfo)explicitBeanInfos.get(beanClass); + if(retval != null) + { + return retval; + } + else if(emptyBeanInfos.indexOf(beanClass) != -1) + { + return null; + } + else + { + retval = reallyFindExplicitBeanInfo(beanClass); + if(retval != null) + { + explicitBeanInfos.put(beanClass,retval); + } + else + { + emptyBeanInfos.addElement(beanClass); + } + return retval; + } + } + + static BeanInfo reallyFindExplicitBeanInfo(Class beanClass) + { + ClassLoader beanClassLoader = beanClass.getClassLoader(); + BeanInfo beanInfo; + + beanInfo = getBeanInfo(beanClassLoader, beanClass.getName() + "BeanInfo"); + if (beanInfo == null) + { + String newName; + newName = ClassHelper.getTruncatedClassName(beanClass) + "BeanInfo"; + + for(int i = 0; i < Introspector.beanInfoSearchPath.length; i++) + { + if (Introspector.beanInfoSearchPath[i].equals("")) + beanInfo = getBeanInfo(beanClassLoader, newName); + else + beanInfo = getBeanInfo(beanClassLoader, + Introspector.beanInfoSearchPath[i] + "." + + newName); + + // Returns the beanInfo if it exists and the described class matches + // the one we searched. + if (beanInfo != null && beanInfo.getBeanDescriptor() != null && + beanInfo.getBeanDescriptor().getBeanClass() == beanClass) + + return beanInfo; + } + } + + return beanInfo; + } + + /** + * Returns an instance of the given class name when it can be loaded + * through the given class loader, or null otherwise. + */ + private static BeanInfo getBeanInfo(ClassLoader cl, String infoName) + { + try + { + return (BeanInfo) Class.forName(infoName, true, cl).newInstance(); + } + catch (ClassNotFoundException cnfe) + { + return null; + } + catch (IllegalAccessException iae) + { + return null; + } + catch (InstantiationException ie) + { + return null; + } + } + +} diff --git a/libjava/classpath/java/beans/MethodDescriptor.java b/libjava/classpath/java/beans/MethodDescriptor.java new file mode 100644 index 000000000..0185fde81 --- /dev/null +++ b/libjava/classpath/java/beans/MethodDescriptor.java @@ -0,0 +1,87 @@ +/* java.beans.MethodDescriptor + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.lang.reflect.Method; + +/** MethodDescriptor describes information about a JavaBeans method. + ** It's a fairly straightforward class (at least something in this + ** package is straightforward!). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 26 Jul 1998 + **/ +public class MethodDescriptor extends FeatureDescriptor { + private Method m; + private ParameterDescriptor[] parameterDescriptors; + + /** Create a new MethodDescriptor. + ** This method sets the name to the name of the method (Method.getName()). + ** @param m the method it will represent. + **/ + public MethodDescriptor(Method m) { + setName(m.getName()); + this.m = m; + } + + /** Create a new MethodDescriptor. + ** This method sets the name to the name of the method (Method.getName()). + ** @param m the method it will represent. + ** @param parameterDescriptors descriptions of the parameters (especially names). + **/ + public MethodDescriptor(Method m, ParameterDescriptor[] parameterDescriptors) { + setName(m.getName()); + this.m = m; + this.parameterDescriptors = parameterDescriptors; + } + + /** Get the parameter descriptors from this method. + ** Since MethodDescriptor has no way of determining what + ** the parameter names were, this defaults to null. + **/ + public ParameterDescriptor[] getParameterDescriptors() { + return parameterDescriptors; + } + + /** Get the method this MethodDescriptor represents. **/ + public Method getMethod() { + return m; + } +} diff --git a/libjava/classpath/java/beans/ParameterDescriptor.java b/libjava/classpath/java/beans/ParameterDescriptor.java new file mode 100644 index 000000000..148690066 --- /dev/null +++ b/libjava/classpath/java/beans/ParameterDescriptor.java @@ -0,0 +1,52 @@ +/* java.beans.MethodDescriptor + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** ParameterDescriptor represents a single parameter to a method. + ** As it turns out, FeatureDescriptor is sufficient to hold all + ** the information. Use its constructor and methods to set + ** the appropriate values. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 26 Jul 1998 + **/ +public class ParameterDescriptor extends FeatureDescriptor { + +} diff --git a/libjava/classpath/java/beans/PersistenceDelegate.java b/libjava/classpath/java/beans/PersistenceDelegate.java new file mode 100644 index 000000000..d4e777844 --- /dev/null +++ b/libjava/classpath/java/beans/PersistenceDelegate.java @@ -0,0 +1,90 @@ +/* java.beans.PersistenceDelegate + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +/**

    A PersistenceDelegate describes how a another object + * has to constructed and transformed in order to create a complete + * replicate.

    + * + *

    For custom classes you will need to implement + * PersistenceDelegate in a way that is suitable for them. + * To make use of the implementation you have to register it with an + * {@link Encoder} using the {Encoder#setPersistenceDelegate} method.

    + * + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public abstract class PersistenceDelegate +{ + + protected void initialize(Class type, Object oldInstance, + Object newInstance, Encoder out) + { + if (type != Object.class) + { + type = type.getSuperclass(); + + PersistenceDelegate pd = out.getPersistenceDelegate(type); + + pd.initialize(type, oldInstance, newInstance, out); + } + } + + public void writeObject(Object oldInstance, Encoder out) + { + Object streamCandidate = out.get(oldInstance); + + if (mutatesTo(oldInstance, streamCandidate)) + { + initialize(oldInstance.getClass(), oldInstance, streamCandidate, out); + } + else + { + out.remove(oldInstance); + out.writeExpression(instantiate(oldInstance, out)); + } + } + + protected boolean mutatesTo(Object oldInstance, Object newInstance) + { + return (newInstance != null) + && oldInstance.getClass() == newInstance.getClass(); + } + + protected abstract Expression instantiate(Object oldInstance, Encoder out); +} diff --git a/libjava/classpath/java/beans/PropertyChangeEvent.java b/libjava/classpath/java/beans/PropertyChangeEvent.java new file mode 100644 index 000000000..3e3f948e3 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeEvent.java @@ -0,0 +1,189 @@ +/* PropertyChangeEvent.java -- describes a change in a property + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventObject; + +/** + * PropertyChangeEvents are fired in the PropertyChange and VetoableChange + * event classes. They represent the old and new values as well as the + * source Bean. If the old or new value is a primitive type, it must be + * wrapped in the appropriate wrapper type (java.lang.Integer for int, etc., + * etc.). + * + *

    If the old or new values are unknown (although why that would be I do + * not know), they may be null. Also, if the set of properties itself has + * changed, the name should be null, and the old and new values may also be + * null. Right now Sun put in a propagationId, reserved for future use. Read + * the comments on the constructor and on setPropagationId for more + * information. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status udpated to 1.4 + */ +public class PropertyChangeEvent extends EventObject +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7042693688939648123L; + + /** + * The name of the property that changed, may be null. Package visible for + * use by PropertyChangeSupport. + * + * @serial the changed property name + */ + final String propertyName; + + /** + * The new value of the property, may be null. Package visible for use by + * PropertyChangeSupport. + * + * @serial the new property value + */ + final Object newValue; + + /** + * The old value of the property, may be null. Package visible for use by + * PropertyChangeSupport. + * + * @serial the old property value + */ + final Object oldValue; + + /** + * The propagation ID, reserved for future use. May be null. + * + * @see #getPropagationId() + * @serial the Propagation ID + */ + private Object propagationId; + + /** + * Create a new PropertyChangeEvent. Remember that if you received a + * PropertyChangeEvent and are sending a new one, you should also set the + * propagation ID from the old PropertyChangeEvent. + * + * @param source the Bean containing the property + * @param propertyName the property's name + * @param oldVal the old value of the property + * @param newVal the new value of the property + * @throws IllegalArgumentException if source is null + */ + public PropertyChangeEvent(Object source, String propertyName, + Object oldVal, Object newVal) + { + super(source); + this.propertyName = propertyName; + oldValue = oldVal; + newValue = newVal; + } + + /** + * Get the property name. May be null if multiple properties changed. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } + + /** + * Get the property's new value. May be null if multiple properties changed. + * + * @return the property's new value + */ + public Object getNewValue() + { + return newValue; + } + + /** + * Get the property's old value. May be null if multiple properties changed. + * + * @return the property's old value + */ + public Object getOldValue() + { + return oldValue; + } + + /** + * Set the propagation ID. This is a way for the event to be passed from + * hand to hand and retain a little extra state. Right now it is unused, + * but it should be propagated anyway so that future versions of JavaBeans + * can use it, for God knows what. + * + * @param propagationId the propagation ID + * @see #getPropagationId() + */ + public void setPropagationId(Object propagationId) + { + this.propagationId = propagationId; + } + + /** + * Get the propagation ID. Right now, it is not used for anything. + * + * @return the propagation ID + * @see #setPropagationId(Object) + */ + public Object getPropagationId() + { + return propagationId; + } + + /** + * Utility method to rollback a change. + * + * @param event the event to rollback + * @return a new event with old and new swapped + */ + PropertyChangeEvent rollback() + { + PropertyChangeEvent result + = new PropertyChangeEvent(source, propertyName, newValue, oldValue); + result.propagationId = propagationId; + return result; + } +} // class PropertyChangeEvent diff --git a/libjava/classpath/java/beans/PropertyChangeListener.java b/libjava/classpath/java/beans/PropertyChangeListener.java new file mode 100644 index 000000000..a97e6e1c2 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeListener.java @@ -0,0 +1,61 @@ +/* PropertyChangeListener.java -- listen for changes in a bound property + Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListener; + +/** + * PropertyChangeListener allows a class to monitor properties of a Bean for + * changes. A propertyChange() event will only be fired after the + * property has changed. + * + * @author John Keiser + * @see PropertyChangeSupport + * @since 1.1 + * @status updated to 1.4 + */ +public interface PropertyChangeListener extends EventListener +{ + /** + * Fired after a Bean's property has changed. + * + * @param e the change (containing the old and new values) + */ + void propertyChange(PropertyChangeEvent e); +} // interface PropertyChangeListener diff --git a/libjava/classpath/java/beans/PropertyChangeListenerProxy.java b/libjava/classpath/java/beans/PropertyChangeListenerProxy.java new file mode 100644 index 000000000..68a815303 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeListenerProxy.java @@ -0,0 +1,102 @@ +/* PropertyChangeListenerProxy.java -- adds a name to a property listener + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListenerProxy; + +/** + * This class provides an extension to PropertyChangeListener - + * associating a name with the listener. This can be used to filter the + * changes that one is interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status udpated to 1.4 + */ +public class PropertyChangeListenerProxy extends EventListenerProxy + implements PropertyChangeListener +{ + /** + * The name of the property to listen for. Package visible for use by + * PropertyChangeSupport. + */ + final String propertyName; + + /** + * Create a new proxy which filters property change events and only passes + * changes to the named property on to the delegate. A null propertyName + * or listener does not fail now, but may cause a NullPointerException down + * the road. + * + * @param propertyName the property's name to filter on + * @param listener the delegate listener + */ + public PropertyChangeListenerProxy(String propertyName, + PropertyChangeListener listener) + { + super(listener); + this.propertyName = propertyName; + } + + /** + * Forwards the event on to the delegate if the property name matches. + * + * @param event the event to pass on, if it meets the filter + * @throws NullPointerException if the delegate this was created with is null + */ + public void propertyChange(PropertyChangeEvent event) + { + // Note: Sun does not filter, under the assumption that since + // PropertyChangeSupport unwraps proxys, this method should never be + // called by normal use of listeners. + String name = event == null ? null : event.getPropertyName(); + if (name == null ? propertyName == null : name.equals(propertyName)) + ((PropertyChangeListener) getListener()).propertyChange(event); + } + + /** + * Gets the name of the property this proxy is filtering on. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } +} // class PropertyChangeListenerProxy diff --git a/libjava/classpath/java/beans/PropertyChangeSupport.java b/libjava/classpath/java/beans/PropertyChangeSupport.java new file mode 100644 index 000000000..3f3f53d42 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyChangeSupport.java @@ -0,0 +1,545 @@ +/* PropertyChangeSupport.java -- support to manage property change listeners + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Vector; + +/** + * PropertyChangeSupport makes it easy to fire property change events and + * handle listeners. It allows chaining of listeners, as well as filtering + * by property name. In addition, it will serialize only those listeners + * which are serializable, ignoring the others without problem. This class + * is thread-safe. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyChangeSupport implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6401253773779951803L; + + /** + * Maps property names (String) to named listeners (PropertyChangeSupport). + * If this is a child instance, this field will be null. + * + * @serial the map of property names to named listener managers + * @since 1.2 + */ + private Hashtable children; + + /** + * The non-null source object for any generated events. + * + * @serial the event source + */ + private final Object source; + + /** + * A field to compare serialization versions - this class uses version 2. + * + * @serial the serialization format + */ + private static final int propertyChangeSupportSerializedDataVersion = 2; + + /** + * The list of all registered property listeners. If this instance was + * created by user code, this only holds the global listeners (ie. not tied + * to a name), and may be null. If it was created by this class, as a + * helper for named properties, then this vector will be non-null, and this + * instance appears as a value in the children hashtable of + * another instance, so that the listeners are tied to the key of that + * hashtable entry. + */ + private transient Vector listeners; + + /** + * Create a PropertyChangeSupport to work with a specific source bean. + * + * @param source the source bean to use + * @throws NullPointerException if source is null + */ + public PropertyChangeSupport(Object source) + { + this.source = source; + if (source == null) + throw new NullPointerException(); + } + + /** + * Adds a PropertyChangeListener to the list of global listeners. All + * property change events will be sent to this listener. The listener add + * is not unique: that is, n adds with the same listener will + * result in n events being sent to that listener for every + * property change. Adding a null listener is silently ignored. + * This method will unwrap a PropertyChangeListenerProxy, + * registering the underlying delegate to the named property list. + * + * @param l the listener to add + */ + public synchronized void addPropertyChangeListener(PropertyChangeListener l) + { + if (l == null) + return; + + if (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + addPropertyChangeListener(p.propertyName, + (PropertyChangeListener) p.getListener()); + } + else + { + if (listeners == null) + listeners = new Vector(); + listeners.add(l); + } + } + + /** + * Removes a PropertyChangeListener from the list of global listeners. If + * any specific properties are being listened on, they must be deregistered + * by themselves; this will only remove the general listener to all + * properties. If add() has been called multiple times for a + * particular listener, remove() will have to be called the + * same number of times to deregister it. This method will unwrap a + * PropertyChangeListenerProxy, removing the underlying delegate from the + * named property list. + * + * @param l the listener to remove + */ + public synchronized void + removePropertyChangeListener(PropertyChangeListener l) + { + if (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + removePropertyChangeListener(p.propertyName, + (PropertyChangeListener) p.getListener()); + } + else if (listeners != null) + { + listeners.remove(l); + if (listeners.isEmpty()) + listeners = null; + } + } + + /** + * Returns an array of all registered property change listeners. Those that + * were registered under a name will be wrapped in a + * PropertyChangeListenerProxy, so you must check whether the + * listener is an instance of the proxy class in order to see what name the + * real listener is registered under. If there are no registered listeners, + * this returns an empty array. + * + * @return the array of registered listeners + * @see PropertyChangeListenerProxy + * @since 1.4 + */ + public synchronized PropertyChangeListener[] getPropertyChangeListeners() + { + ArrayList list = new ArrayList(); + if (listeners != null) + list.addAll(listeners); + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + Vector v = ((PropertyChangeSupport) e.getValue()).listeners; + int j = v.size(); + while (--j >= 0) + list.add(new PropertyChangeListenerProxy + (name, (PropertyChangeListener) v.get(j))); + } + } + return (PropertyChangeListener[]) + list.toArray(new PropertyChangeListener[list.size()]); + } + + /** + * Adds a PropertyChangeListener listening on the specified property. Events + * will be sent to the listener only if the property name matches. The + * listener add is not unique; that is, n adds on a particular + * property for a particular listener will result in n events + * being sent to that listener when that property is changed. The effect is + * cumulative, too; if you are registered to listen to receive events on + * all property changes, and then you register on a particular property, + * you will receive change events for that property twice. Adding a null + * listener is silently ignored. This method will unwrap a + * PropertyChangeListenerProxy, registering the underlying + * delegate to the named property list if the names match, and discarding + * it otherwise. + * + * @param propertyName the name of the property to listen on + * @param l the listener to add + * @throws NullPointerException if propertyName is null + */ + public synchronized void addPropertyChangeListener(String propertyName, + PropertyChangeListener l) + { + if (l == null) + return; + + while (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (PropertyChangeListener) p.getListener(); + } + PropertyChangeSupport s = null; + if (children == null) + children = new Hashtable(); + else + s = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + { + s = new PropertyChangeSupport(source); + s.listeners = new Vector(); + children.put(propertyName, s); + } + s.listeners.add(l); + } + + /** + * Removes a PropertyChangeListener from listening to a specific property. + * If add() has been called multiple times for a particular + * listener on a property, remove() will have to be called the + * same number of times to deregister it. This method will unwrap a + * PropertyChangeListenerProxy, removing the underlying delegate from the + * named property list if the names match. + * + * @param propertyName the property to stop listening on + * @param l the listener to remove + * @throws NullPointerException if propertyName is null + */ + public synchronized void + removePropertyChangeListener(String propertyName, PropertyChangeListener l) + { + if (children == null) + return; + PropertyChangeSupport s + = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + return; + while (l instanceof PropertyChangeListenerProxy) + { + PropertyChangeListenerProxy p = (PropertyChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (PropertyChangeListener) p.getListener(); + } + s.listeners.remove(l); + if (s.listeners.isEmpty()) + { + children.remove(propertyName); + if (children.isEmpty()) + children = null; + } + } + + /** + * Returns an array of all property change listeners registered under the + * given property name. If there are no registered listeners, or + * propertyName is null, this returns an empty array. + * + * @return the array of registered listeners + * @since 1.4 + */ + public synchronized PropertyChangeListener[] + getPropertyChangeListeners(String propertyName) + { + if (children == null || propertyName == null) + return new PropertyChangeListener[0]; + PropertyChangeSupport s + = (PropertyChangeSupport) children.get(propertyName); + if (s == null) + return new PropertyChangeListener[0]; + return (PropertyChangeListener[]) + s.listeners.toArray(new PropertyChangeListener[s.listeners.size()]); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are non-null + * and equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, + Object oldVal, Object newVal) + { + firePropertyChange(new PropertyChangeEvent(source, propertyName, + oldVal, newVal)); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, int oldVal, int newVal) + { + if (oldVal != newVal) + firePropertyChange(new PropertyChangeEvent(source, propertyName, + Integer.valueOf(oldVal), + Integer.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + */ + public void firePropertyChange(String propertyName, + boolean oldVal, boolean newVal) + { + if (oldVal != newVal) + firePropertyChange(new PropertyChangeEvent(source, propertyName, + Boolean.valueOf(oldVal), + Boolean.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent to all the global listeners, and to all the + * listeners for the specified property name. This does nothing if old and + * new values of the event are equal. + * + * @param event the event to fire + * @throws NullPointerException if event is null + */ + public void firePropertyChange(PropertyChangeEvent event) + { + if (event.oldValue != null && event.oldValue.equals(event.newValue)) + return; + Vector v = listeners; // Be thread-safe. + if (v != null) + { + int i = v.size(); + while (--i >= 0) + ((PropertyChangeListener) v.get(i)).propertyChange(event); + } + Hashtable h = children; // Be thread-safe. + if (h != null && event.propertyName != null) + { + PropertyChangeSupport s + = (PropertyChangeSupport) h.get(event.propertyName); + if (s != null) + { + v = s.listeners; // Be thread-safe. + int i = v == null ? 0 : v.size(); + while (--i >= 0) + ((PropertyChangeListener) v.get(i)).propertyChange(event); + } + } + } + + /** + * Fire an indexed property change event. This will only fire + * an event if the old and new values are not equal and not null. + * @param name the name of the property which changed + * @param index the index of the property which changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String name, int index, + Object oldValue, Object newValue) + { + // Argument checking is done in firePropertyChange(PropertyChangeEvent) . + firePropertyChange(new IndexedPropertyChangeEvent(source, name, + oldValue, newValue, + index)); + } + + /** + * Fire an indexed property change event. This will only fire + * an event if the old and new values are not equal. + * @param name the name of the property which changed + * @param index the index of the property which changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String name, int index, + int oldValue, int newValue) + { + if (oldValue != newValue) + fireIndexedPropertyChange(name, index, Integer.valueOf(oldValue), + Integer.valueOf(newValue)); + } + + /** + * Fire an indexed property change event. This will only fire + * an event if the old and new values are not equal. + * @param name the name of the property which changed + * @param index the index of the property which changed + * @param oldValue the old value of the property + * @param newValue the new value of the property + * @since 1.5 + */ + public void fireIndexedPropertyChange(String name, int index, + boolean oldValue, boolean newValue) + { + if (oldValue != newValue) + fireIndexedPropertyChange(name, index, Boolean.valueOf(oldValue), + Boolean.valueOf(newValue)); + } + + /** + * Tell whether the specified property is being listened on or not. This + * will only return true if there are listeners on all + * properties or if there is a listener specifically on this property. + * + * @param propertyName the property that may be listened on + * @return whether the property is being listened on + */ + public synchronized boolean hasListeners(String propertyName) + { + return listeners != null || (children != null + && children.get(propertyName) != null); + } + + /** + * Saves the state of the object to the stream. + * + * @param s the stream to write to + * @throws IOException if anything goes wrong + * @serialData this writes out a null-terminated list of serializable + * global property change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + if (listeners != null) + { + int i = listeners.size(); + while (--i >= 0) + if (listeners.get(i) instanceof Serializable) + s.writeObject(listeners.get(i)); + } + s.writeObject(null); + } + + /** + * Reads the object back from stream (deserialization). + * + * XXX Since serialization for 1.1 streams was not documented, this may + * not work if propertyChangeSupportSerializedDataVersion is 1. + * + * @param s the stream to read from + * @throws IOException if reading the stream fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads in a null-terminated list of serializable + * global property change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + PropertyChangeListener l = (PropertyChangeListener) s.readObject(); + while (l != null) + { + addPropertyChangeListener(l); + l = (PropertyChangeListener) s.readObject(); + } + // Sun is not as careful with children as we are, and lets some proxys + // in that can never receive events. So, we clean up anything that got + // serialized, to make sure our invariants hold. + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + PropertyChangeSupport pcs = (PropertyChangeSupport) e.getValue(); + if (pcs.listeners == null) + pcs.listeners = new Vector(); + if (pcs.children != null) + pcs.listeners.addAll + (Arrays.asList(pcs.getPropertyChangeListeners(name))); + if (pcs.listeners.size() == 0) + iter.remove(); + else + pcs.children = null; + } + if (children.size() == 0) + children = null; + } + } +} // class PropertyChangeSupport diff --git a/libjava/classpath/java/beans/PropertyDescriptor.java b/libjava/classpath/java/beans/PropertyDescriptor.java new file mode 100644 index 000000000..a74fa7b13 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyDescriptor.java @@ -0,0 +1,665 @@ +/* java.beans.PropertyDescriptor + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.beans; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + ** PropertyDescriptor describes information about a JavaBean property, + ** by which we mean a property that has been exposed via a pair of + ** get and set methods. (There may be no get method, which means + ** the property is write-only, or no set method, which means the + ** the property is read-only.)

    + ** + ** The constraints put on get and set methods are:

    + **

      + **
    1. A get method must have signature + ** <propertyType> <getMethodName>()
    2. + **
    3. A set method must have signature + ** void <setMethodName>(<propertyType>)
    4. + **
    5. Either method type may throw any exception.
    6. + **
    7. Both methods must be public.
    8. + **
    + ** + ** @author John Keiser + ** @author Robert Schuster (thebohemian@gmx.net) + ** @since 1.1 + ** @status updated to 1.4 + **/ +public class PropertyDescriptor extends FeatureDescriptor +{ + Class propertyType; + Method getMethod; + Method setMethod; + + Class propertyEditorClass; + boolean bound; + boolean constrained; + + PropertyDescriptor(String name) + { + setName(name); + } + + /** Create a new PropertyDescriptor by introspection. + ** This form of constructor creates the PropertyDescriptor by + ** looking for a getter method named get<name>() + ** (or, optionally, if the property is boolean, + ** is<name>()) and + ** set<name>() in class + ** <beanClass>, where <name> has its + ** first letter capitalized by the constructor.

    + ** + ** Note that using this constructor the given property must be read- and + ** writeable. If the implementation does not both, a read and a write method, an + ** IntrospectionException is thrown. + ** + ** Implementation note: If there is both are both isXXX and + ** getXXX methods, the former is used in preference to the latter. + ** We do not check that an isXXX method returns a boolean. In both + ** cases, this matches the behaviour of JDK 1.4

    + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param beanClass the class the get and set methods live in. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor(String name, Class beanClass) + throws IntrospectionException + { + setName(name); + if (name.length() == 0) + { + throw new IntrospectionException("empty property name"); + } + String caps = Character.toUpperCase(name.charAt(0)) + name.substring(1); + findMethods(beanClass, "is" + caps, "get" + caps, "set" + caps); + + if (getMethod == null) + { + throw new IntrospectionException( + "Cannot find a is" + caps + " or get" + caps + " method"); + } + + if (setMethod == null) + { + throw new IntrospectionException( + "Cannot find a " + caps + " method"); + } + + // finally check the methods compatibility + propertyType = checkMethods(getMethod, setMethod); + } + + /** Create a new PropertyDescriptor by introspection. + ** This form of constructor allows you to specify the + ** names of the get and set methods to search for.

    + ** + ** Implementation note: If there is a get method (or + ** boolean isXXX() method), then the return type of that method + ** is used to find the set method. If there is no get method, + ** then the set method is searched for exhaustively.

    + ** + ** Spec note: + ** If there is no get method and multiple set methods with + ** the same name and a single parameter (different type of course), + ** then an IntrospectionException is thrown. While Sun's spec + ** does not state this, it can make Bean behavior different on + ** different systems (since method order is not guaranteed) and as + ** such, can be treated as a bug in the spec. I am not aware of + ** whether Sun's implementation catches this. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param beanClass the class the get and set methods live in. + ** @param getMethodName the name of the get method or null if the property is write-only. + ** @param setMethodName the name of the set method or null if the property is read-only. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor( + String name, + Class beanClass, + String getMethodName, + String setMethodName) + throws IntrospectionException + { + setName(name); + findMethods(beanClass, getMethodName, null, setMethodName); + + if (getMethod == null && getMethodName != null) + { + throw new IntrospectionException( + "Cannot find a getter method called " + getMethodName); + } + + if (setMethod == null && setMethodName != null) + { + throw new IntrospectionException( + "Cannot find a setter method called " + setMethodName); + } + + propertyType = checkMethods(getMethod, setMethod); + } + + /** Create a new PropertyDescriptor using explicit Methods. + ** Note that the methods will be checked for conformance to standard + ** Property method rules, as described above at the top of this class. + **
    + ** It is possible to call this method with both Method arguments + ** being null. In such a case the property type is null. + ** + ** @param name the programmatic name of the property, usually + ** starting with a lowercase letter (e.g. fooManChu + ** instead of FooManChu). + ** @param readMethod the read method or null if the property is write-only. + ** @param writeMethod the write method or null if the property is read-only. + ** @exception IntrospectionException if the methods are not found + ** or invalid. + **/ + public PropertyDescriptor( + String name, + Method readMethod, + Method writeMethod) + throws IntrospectionException + { + setName(name); + getMethod = readMethod; + setMethod = writeMethod; + propertyType = checkMethods(getMethod, setMethod); + } + + /** Get the property type. + ** This is the type the get method returns and the set method + ** takes in. + **/ + public Class getPropertyType() + { + return propertyType; + } + + /** Get the get method. Why they call it readMethod here and + ** get everywhere else is beyond me. + **/ + public Method getReadMethod() + { + return getMethod; + } + + /** Sets the read method.
    + * The read method is used to retrieve the value of a property. A legal + * read method must have no arguments. Its return type must not be + * void. If this methods succeeds the property type + * is adjusted to the return type of the read method.
    + *
    + * It is legal to set the read and the write method to null + * or provide method which have been declared in distinct classes. + * + * @param readMethod The new method to be used or null. + * @throws IntrospectionException If the given method is invalid. + * @since 1.2 + */ + public void setReadMethod(Method readMethod) throws IntrospectionException + { + propertyType = checkMethods(readMethod, setMethod); + + getMethod = readMethod; + } + + /** Get the set method. Why they call it writeMethod here and + ** set everywhere else is beyond me. + **/ + public Method getWriteMethod() + { + return setMethod; + } + + /** Sets the write method.
    + * The write method is used to set the value of a property. A legal write method + * must have a single argument which can be assigned to the property. If no + * read method exists the property type changes to the argument type of the + * write method.
    + *
    + * It is legal to set the read and the write method to null + * or provide method which have been declared in distinct classes. + * + * @param writeMethod The new method to be used or null. + * @throws IntrospectionException If the given method is invalid. + * @since 1.2 + */ + public void setWriteMethod(Method writeMethod) + throws IntrospectionException + { + propertyType = checkMethods(getMethod, writeMethod); + + setMethod = writeMethod; + } + + /** Get whether the property is bound. Defaults to false. **/ + public boolean isBound() + { + return bound; + } + + /** Set whether the property is bound. + ** As long as the the bean implements addPropertyChangeListener() and + ** removePropertyChangeListener(), setBound(true) may safely be called.

    + ** If these things are not true, then the behavior of the system + ** will be undefined.

    + ** + ** When a property is bound, its set method is required to fire the + ** PropertyChangeListener.propertyChange()) event + ** after the value has changed. + ** @param bound whether the property is bound or not. + **/ + public void setBound(boolean bound) + { + this.bound = bound; + } + + /** Get whether the property is constrained. Defaults to false. **/ + public boolean isConstrained() + { + return constrained; + } + + /** Set whether the property is constrained. + ** If the set method throws java.beans.PropertyVetoException + ** (or subclass thereof) and the bean implements addVetoableChangeListener() + ** and removeVetoableChangeListener(), then setConstrained(true) may safely + ** be called. Otherwise, the system behavior is undefined. + ** Spec note: given those strict parameters, it would be nice if it + ** got set automatically by detection, but oh well.

    + ** When a property is constrained, its set method is required to:

    + **

      + **
    1. Fire the VetoableChangeListener.vetoableChange() + ** event notifying others of the change and allowing them a chance to + ** say it is a bad thing.
    2. + **
    3. If any of the listeners throws a PropertyVetoException, then + ** it must fire another vetoableChange() event notifying the others + ** of a reversion to the old value (though, of course, the change + ** was never made). Then it rethrows the PropertyVetoException and + ** exits.
    4. + **
    5. If all has gone well to this point, the value may be changed.
    6. + **
    + ** @param constrained whether the property is constrained or not. + **/ + public void setConstrained(boolean constrained) + { + this.constrained = constrained; + } + + /** Get the PropertyEditor class. Defaults to null. **/ + public Class getPropertyEditorClass() + { + return propertyEditorClass; + } + + /** Set the PropertyEditor class. If the class does not implement + ** the PropertyEditor interface, you will likely get an exception + ** late in the game. + ** @param propertyEditorClass the PropertyEditor class for this + ** class to use. + **/ + public void setPropertyEditorClass(Class propertyEditorClass) + { + this.propertyEditorClass = propertyEditorClass; + } + + /** + * Instantiate a property editor using the property editor class. + * If no property editor class has been set, this will return null. + * If the editor class has a public constructor which takes a single + * argument, that will be used and the bean parameter will be passed + * to it. Otherwise, a public no-argument constructor will be used, + * if available. This method will return null if no constructor is + * found or if construction fails for any reason. + * @param bean the argument to the constructor + * @return a new PropertyEditor, or null on error + * @since 1.5 + */ + public PropertyEditor createPropertyEditor(Object bean) + { + if (propertyEditorClass == null) + return null; + Constructor c = findConstructor(propertyEditorClass, + new Class[] { Object.class }); + if (c != null) + return instantiateClass(c, new Object[] { bean }); + c = findConstructor(propertyEditorClass, null); + if (c != null) + return instantiateClass(c, null); + return null; + } + + // Helper method to look up a constructor and return null if it is not + // found. + private Constructor findConstructor(Class k, Class[] argTypes) + { + try + { + return k.getConstructor(argTypes); + } + catch (NoSuchMethodException _) + { + return null; + } + } + + // Helper method to instantiate an object but return null on error. + private PropertyEditor instantiateClass(Constructor c, Object[] args) + { + try + { + return (PropertyEditor) c.newInstance(args); + } + catch (InstantiationException _) + { + return null; + } + catch (InvocationTargetException _) + { + return null; + } + catch (IllegalAccessException _) + { + return null; + } + catch (ClassCastException _) + { + return null; + } + } + + private void findMethods( + Class beanClass, + String getMethodName1, + String getMethodName2, + String setMethodName) + throws IntrospectionException + { + try + { + // Try the first get method name + if (getMethodName1 != null) + { + try + { + getMethod = + beanClass.getMethod(getMethodName1, new Class[0]); + } + catch (NoSuchMethodException e) + {} + } + + // Fall back to the second get method name + if (getMethod == null && getMethodName2 != null) + { + try + { + getMethod = + beanClass.getMethod(getMethodName2, new Class[0]); + } + catch (NoSuchMethodException e) + {} + } + + // Try the set method name + if (setMethodName != null) + { + if (getMethod != null) + { + // If there is a get method, use its return type to help + // select the corresponding set method. + Class propertyType = getMethod.getReturnType(); + if (propertyType == Void.TYPE) + { + String msg = + "The property's read method has return type 'void'"; + throw new IntrospectionException(msg); + } + + Class[] setArgs = new Class[] { propertyType }; + try + { + setMethod = beanClass.getMethod(setMethodName, setArgs); + } + catch (NoSuchMethodException e) + {} + } + else if (getMethodName1 == null && getMethodName2 == null) + { + // If this is a write-only property, choose the first set method + // with the required name, one parameter and return type 'void' + Method[] methods = beanClass.getMethods(); + for (int i = 0; i < methods.length; i++) + { + if (methods[i].getName().equals(setMethodName) + && methods[i].getParameterTypes().length == 1 + && methods[i].getReturnType() == Void.TYPE) + { + setMethod = methods[i]; + break; + } + } + } + } + } + catch (SecurityException e) + { + // FIXME -- shouldn't we just allow SecurityException to propagate? + String msg = + "SecurityException thrown on attempt to access methods."; + throw new IntrospectionException(msg); + } + } + + /** Checks whether the given Method instances are legal read and + * write methods. The following requirements must be met:
    + *
      + *
    • the read method must not have an argument
    • + *
    • the read method must have a non void return type
    • + *
    • the read method may not exist
    • + *
    • the write method must have a single argument
    • + *
    • the property type and the read method's return type must be assignable from the + * write method's argument type
    • + *
    • the write method may not exist
    • + *
    + * While checking the methods a common new property type is calculated. If the method + * succeeds this property type is returned.
    + *
    + * For compatibility this has to be noted:
    + * The two methods are allowed to be defined in two distinct classes and may both be null. + * + * @param readMethod The new read method to check. + * @param writeMethod The new write method to check. + * @return The common property type of the two method. + * @throws IntrospectionException If any of the above requirements are not met. + */ + private Class checkMethods(Method readMethod, Method writeMethod) + throws IntrospectionException + { + Class newPropertyType = propertyType; + + // a valid read method has zero arguments and a non-void return type. + if (readMethod != null) + { + if (readMethod.getParameterTypes().length > 0) + { + throw new IntrospectionException("read method has unexpected parameters"); + } + + newPropertyType = readMethod.getReturnType(); + + if (newPropertyType == Void.TYPE) + { + throw new IntrospectionException("read method return type is void"); + } + } + + // a valid write method has one argument which can be assigned to the property + if (writeMethod != null) + { + if (writeMethod.getParameterTypes().length != 1) + { + String msg = "write method does not have exactly one parameter"; + throw new IntrospectionException(msg); + } + + if (readMethod == null) + { + // changes the property type if there is no read method + newPropertyType = writeMethod.getParameterTypes()[0]; + } + else + { + // checks whether the write method can be assigned to the return type of the read + // method (if this is not the case, the methods are not compatible) + // note: newPropertyType may be null if no methods or method names have been + // delivered in the constructor. + if (newPropertyType != null + && !newPropertyType.isAssignableFrom( + writeMethod.getParameterTypes()[0])) + { + // note: newPropertyType is the same as readMethod.getReturnType() at this point + throw new IntrospectionException("read and write method are not compatible"); + } + + /* note: the check whether both method are defined in related classes makes sense but is not + * done in the JDK. + * I leave this code here in case someone at Sun decides to add that functionality in later versions (rschuster) + if ((!readMethod + .getDeclaringClass() + .isAssignableFrom(writeMethod.getDeclaringClass())) + && (!writeMethod + .getDeclaringClass() + .isAssignableFrom(readMethod.getDeclaringClass()))) + { + String msg = + "set and get methods are not in the same class."; + throw new IntrospectionException(msg); + } + */ + + } + } + + return newPropertyType; + } + + /** + * Return a hash code for this object, conforming to the contract described + * in {@link Object#hashCode()}. + * @return the hash code + * @since 1.5 + */ + public int hashCode() + { + return ((propertyType == null ? 0 : propertyType.hashCode()) + | (propertyEditorClass == null ? 0 : propertyEditorClass.hashCode()) + | (bound ? Boolean.TRUE : Boolean.FALSE).hashCode() + | (constrained ? Boolean.TRUE : Boolean.FALSE).hashCode() + | (getMethod == null ? 0 : getMethod.hashCode()) + | (setMethod == null ? 0 : setMethod.hashCode())); + } + + /** Compares this PropertyDescriptor against the + * given object. + * Two PropertyDescriptors are equals if + *
      + *
    • the read methods are equal
    • + *
    • the write methods are equal
    • + *
    • the property types are equals
    • + *
    • the property editor classes are equal
    • + *
    • the flags (constrained and bound) are equal
    • + *
    + * @return Whether both objects are equal according to the rules given above. + * @since 1.4 + */ + public boolean equals(Object o) + { + if (o instanceof PropertyDescriptor) + { + PropertyDescriptor that = (PropertyDescriptor) o; + + // compares the property types and checks the case where both are null + boolean samePropertyType = + (propertyType == null) + ? that.propertyType == null + : propertyType.equals(that.propertyType); + + // compares the property editor classes and checks the case where both are null + boolean samePropertyEditorClass = + (propertyEditorClass == null) + ? that.propertyEditorClass == null + : propertyEditorClass.equals(that.propertyEditorClass); + + // compares the flags for equality + boolean sameFlags = + bound == that.bound && constrained == that.constrained; + + // compares the read methods and checks the case where both are null + boolean sameReadMethod = + (getMethod == null) + ? that.getMethod == null + : getMethod.equals(that.getMethod); + + boolean sameWriteMethod = + (setMethod == null) + ? that.setMethod == null + : setMethod.equals(that.setMethod); + + return samePropertyType + && sameFlags + && sameReadMethod + && sameWriteMethod + && samePropertyEditorClass; + } + else + { + return false; + } + + } + +} diff --git a/libjava/classpath/java/beans/PropertyEditor.java b/libjava/classpath/java/beans/PropertyEditor.java new file mode 100644 index 000000000..5fba014ea --- /dev/null +++ b/libjava/classpath/java/beans/PropertyEditor.java @@ -0,0 +1,209 @@ +/* java.beans.PropertyEditor + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + ** PropertyEditors are custom GUI editors for specific types of values. + ** + ** A PropertyEditor can be used, for example, if you are editing a type of value + ** that can be more easily represented graphically, such as a Point, or one that + ** can be more easily represented by a list, such as a boolean (true/false).

    + ** + ** A PropertyEditor must be able to display its contents when asked to and + ** be able to allow the user to change its underlying field value. However, it + ** is not the PropertyEditor's responsibility to make the change to the + ** underlying Object; in fact, the PropertyEditor does not even know about the + ** Object it is actually editing--only about the property it is currently + ** editing. When a change is made to the property, the PropertyEditor must + ** simply fire a PropertyChangeEvent and allow the RAD tool to actually set + ** the property in the underlying Bean.

    + ** + ** PropertyEditors should not change the Objects they are given by setValue(). + ** These Objects may or may not be the actual Objects which are properties of + ** the Bean being edited. Instead, PropertyEditors should create a new Object + ** and fire a PropertyChangeEvent with the old and new values.

    + ** + ** PropertyEditors also must support the ability to return a Java + ** initialization string. See the getJavaInitializationString() method for + ** details.

    + ** + ** There are several different ways a PropertyEditor may display and control + ** editing of its value. When multiple types of input and display are + ** given by a single PropertyEditor, the RAD tool may decide which of the call + ** to support. Some RAD tools may even be text-only, so even if you support + ** a graphical set and get, it may choose the text set and get whenever it can. + **

      + **
    1. Every PropertyEditor must support getValue() and setValue(). For + ** setValue(), the component must only support it when the argument is + ** the same type that the PropertyEditor supports.
    2. + **
    3. Every PropertyEditor must support getJavaInitializationString().
    4. + **
    5. You may support painting the value yourself if you wish. To do this, + ** have isPaintable() return true and implement the paintValue() method. + ** This method does not determine in any way how the value is edited; + ** merely how it is displayed.
    6. + **
    7. Let the caller of the PropertyEditor give the user a text input. Do + ** this by returning a non-null String from getAsText(). If you support + ** text input, you *must* support setAsText().
    8. + **
    9. Give the caller a set of possible values, such as "true"/"false", that + ** the user must select from. To do this, return the list of Strings + ** from the getTags() method. The RAD tool may choose to implement the + ** user input any way it wishes, and only guarantees that setAsText() will + ** only be called with one of the Strings returned from getTags().
    10. + **
    11. You may support a whole custom editing control by supporting + ** getCustomEditor(). To do this, return true from supportsCustomEditor() + ** and return a Component that does the job. It is the component's job, + ** or the PropertyEditor's job, to make sure that when the editor changes + ** its value, the PropertyChangeEvent is thrown.
    12. + **
    + ** + ** The PropertyEditor for a particular Bean can be found using the + ** PropertyEditorManager class, which goes through a series of different + ** checks to find the appropriate class.

    + ** + ** A PropertyChangeEvent should be thrown from the PropertyEditor whenever a + ** bound property (a property PropertyDescriptor.isBound() set to true) + ** changes. When this happens, the editor itself should *not* change the value + ** itself, but rather allow the RAD tool to call setValue() or setAsText(). + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 30 June 1998 + ** @see java.beans.PropertyEditorManager + ** @see java.beans.PropertyEditorSupport + **/ + +public interface PropertyEditor { + /** Called by the RAD tool to set the value of this property for the PropertyEditor. + ** If the property type is native, it should be wrapped in the appropriate + ** wrapper type. + ** @param value the value to set this property to. + **/ + void setValue(Object value); + + /** Accessor method to get the current value the PropertyEditor is working with. + ** If the property type is native, it will be wrapped in the appropriate + ** wrapper type. + ** @return the current value of the PropertyEditor. + **/ + Object getValue(); + + + /** Set the value of this property using a String. + ** Whether or not this PropertyEditor is editing a String type, this converts + ** the String into the type of the PropertyEditor. + ** @param text the text to set it to. + ** @exception IllegalArgumentException if the String is in the wrong format or setAsText() is not supported. + **/ + void setAsText(String text) throws IllegalArgumentException; + + /** Get the value of this property in String format. + ** Many times this can simply use Object.toString().

    + ** Return null if you do not support getAsText()/setAsText(). + ** setAsText(getAsText()) should be valid; i.e. the stuff you spit out in + ** getAsText() should be able to go into setAsText(). + ** @return the value of this property in String format. + **/ + String getAsText(); + + /** Get a list of possible Strings which this property type can have. + ** The value of these will be used by the RAD tool to construct some sort + ** of list box or to check text box input, and the resulting String passed + ** to setAsText() should be one of these. Note, however, that like most things + ** with this mammoth, unwieldy interface, this is not guaranteed. Thus, you + ** must check the value in setAsText() anyway. + ** @return the list of possible String values for this property type. + **/ + String[] getTags(); + + + /** The RAD tool calls this to find out whether the PropertyEditor can paint itself. + ** @return true if it can paint itself graphically, false if it cannot. + **/ + boolean isPaintable(); + + /** The RAD tool calls this to paint the actual value of the property. + ** The Graphics context will have the same current font, color, etc. as the + ** parent Container. You may safely change the font, color, etc. and not + ** change them back.

    + ** This method should do a silent no-op if isPaintable() is false. + ** @param g the Graphics context to paint on + ** @param bounds the rectangle you have reserved to work in + **/ + void paintValue(java.awt.Graphics g, java.awt.Rectangle bounds); + + + /** The RAD tool calls this to find out whether the PropertyEditor supports a custom component to edit and display itself. + ** @return true if getCustomEditor() will return a component, false if not. + **/ + boolean supportsCustomEditor(); + + /** The RAD tool calls this to grab the component that can edit this type. + ** The component may be painted anywhere the RAD tool wants to paint it-- + ** even in its own window.

    + ** The component must hook up with the PropertyEditor and, whenever a + ** change to the value is made, fire a PropertyChangeEvent to the source.

    + ** @return the custom editor for this property type. + **/ + java.awt.Component getCustomEditor(); + + + /** Adds a property change listener to this PropertyEditor. + ** @param listener the listener to add + **/ + void addPropertyChangeListener(PropertyChangeListener listener); + + /** Removes a property change listener from this PropertyEditor. + ** @param listener the listener to remove + **/ + void removePropertyChangeListener(PropertyChangeListener listener); + + /** Get a Java language-specific String which could be used to create an Object + ** of the specified type. Every PropertyEditor must support this.

    + ** The reason for this is that while most RAD tools will serialize the Beans + ** and deserialize them at runtime, some RAD tools will generate code that + ** creates the Beans. Examples of Java initialization strings would be:

    + **

      + **
    1. 2
    2. + **
    3. "I am a String"
    4. + **
    5. new MyObject(2, "String", new StringBuffer())
    6. + **
    + ** @return the initialization string for this object in Java. + **/ + String getJavaInitializationString(); +} diff --git a/libjava/classpath/java/beans/PropertyEditorManager.java b/libjava/classpath/java/beans/PropertyEditorManager.java new file mode 100644 index 000000000..253ddafe1 --- /dev/null +++ b/libjava/classpath/java/beans/PropertyEditorManager.java @@ -0,0 +1,216 @@ +/* java.beans.PropertyEditorManager + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.editors.ColorEditor; +import gnu.java.beans.editors.FontEditor; +import gnu.java.beans.editors.NativeBooleanEditor; +import gnu.java.beans.editors.NativeByteEditor; +import gnu.java.beans.editors.NativeDoubleEditor; +import gnu.java.beans.editors.NativeFloatEditor; +import gnu.java.beans.editors.NativeIntEditor; +import gnu.java.beans.editors.NativeLongEditor; +import gnu.java.beans.editors.NativeShortEditor; +import gnu.java.beans.editors.StringEditor; +import gnu.java.lang.ClassHelper; + +import java.awt.Color; +import java.awt.Font; + +/** + * PropertyEditorManager is used to find property editors + * for various types (not necessarily Beans).

    + * + * It first checks to see if the property editor is + * already registered; if it is, that property editor is + * used. Next it takes the type's classname and appends + * "Editor" to it, and searches first in the class's + * package and then in the property editor search path. + * + *

    Default property editors are provided for:

    + * + *
      + *
    1. boolean, byte, short, int, long, float, and double
    2. + *
    3. java.lang.String
    4. + *
    5. java.awt.Color
    6. + *
    7. java.awt.Font
    8. + *
    + * + *

    Spec Suggestion: Perhaps an editor for + * Filename or something like it should be provided. As well + * as char.

    + * + * @author John Keiser + * @since 1.1 + * @version 1.1.0, 29 Jul 1998 + */ + +public class PropertyEditorManager +{ + static java.util.Hashtable,Class> editors = + new java.util.Hashtable,Class>(); + static String[] editorSearchPath = { "gnu.java.beans.editors", + "sun.beans.editors" }; + + static + { + registerEditor(Boolean.TYPE, NativeBooleanEditor.class); + registerEditor(Byte.TYPE, NativeByteEditor.class); + registerEditor(Short.TYPE, NativeShortEditor.class); + registerEditor(Integer.TYPE, NativeIntEditor.class); + registerEditor(Long.TYPE, NativeLongEditor.class); + registerEditor(Float.TYPE, NativeFloatEditor.class); + registerEditor(Double.TYPE, NativeDoubleEditor.class); + registerEditor(String.class, StringEditor.class); + registerEditor(Color.class, ColorEditor.class); + registerEditor(Font.class, FontEditor.class); + } + + /** + * Beats me why this class can be instantiated, but there + * you have it. + */ + public PropertyEditorManager() + { + // Do nothing here + } + + /** + * Register an editor for a class. Replaces old editor + * if there was one registered before. + * + * @param editedClass the class that the property editor + * will edit. + * @param editorClass the PropertyEditor class. + */ + public static void registerEditor(Class editedClass, Class editorClass) + { + editors.put(editedClass, editorClass); + } + + /** + * Returns a new instance of the property editor for the + * specified class. + * + * @param editedClass the class that the property editor + * will edit. + * @return a PropertyEditor instance that can edit the + * specified class. + */ + public static PropertyEditor findEditor(Class editedClass) + { + try + { + Class found = (Class)editors.get(editedClass); + if(found != null) + { + return (PropertyEditor)found.newInstance(); + } + + ClassLoader contextClassLoader + = Thread.currentThread().getContextClassLoader(); + + try + { + found = Class.forName(editedClass.getName()+"Editor", true, + contextClassLoader); + registerEditor(editedClass,found); + return (PropertyEditor)found.newInstance(); + } + catch(ClassNotFoundException E) + { + } + + String appendName + = "." + + ClassHelper.getTruncatedClassName(editedClass) + + "Editor"; + synchronized(editorSearchPath) + { + for(int i=0;i + * + * This class does not do any painting or actual editing. + * For that, you must use or extend it. See the + * PropertyEditor class for better descriptions of what + * the various methods do. + * + * @author John Keiser + * @author Robert Schuster + * @since 1.1 + * @status updated to 1.5 + */ +public class PropertyEditorSupport implements PropertyEditor +{ + Object eventSource; + Object value; + PropertyChangeSupport pSupport; + + /** Call this constructor when you are deriving from + * PropertyEditorSupport. + * + * Using this constructor the event source is this PropertyEditorSupport + * instance itself. + * + * @since 1.5 + * @specnote this was protected prior to 1.5 + */ + public PropertyEditorSupport() + { + eventSource = this; + pSupport = new PropertyChangeSupport(this); + } + + /** Call this constructor when you are using + * PropertyEditorSupport as a helper object. + * + * This constructor throws a NullPointerException when source is null, + * for compatibility reasons with J2SDK 1.5.0 . + * + * @param source The source to use when firing + * property change events. + * @since 1.5 + * @specnote this was protected prior to 1.5 + */ + public PropertyEditorSupport(Object source) + { + // note: constructor rejects source being null for the sake of compatibility + // with official 1.5.0 implementation + if (source == null) + throw new NullPointerException("Event source must not be null."); + + eventSource = source; + pSupport = new PropertyChangeSupport(eventSource); + } + + /** Sets the current value of the property and a property change + * event is fired to all registered PropertyChangeListener instances. + * + * @param newValue The new value for the property. + */ + public void setValue(Object newValue) + { + value = newValue; + + // specification in java.beans.PropertyChangeEvent says + // that without a property name (first argument) the + // new and the old value should always be null + pSupport.firePropertyChange(null, null, null); + } + + /** Gets the current value of the property. + * + * @return the current value of the property. + */ + public Object getValue() + { + return value; + } + + /** Gets whether this object is paintable or not. + * + * @return false + */ + public boolean isPaintable() + { + return false; + } + + /** Paints this object. This class does nothing in + * this method. + */ + public void paintValue(java.awt.Graphics g, java.awt.Rectangle r) + { + } + + /** Gets the Java initialization String for the current + * value of the Object. This class returns gibberish or + * null (though the spec does not say which).

    + * Implementation Note: This class + * returns the string "@$#^" to make sure the code will + * be broken, so that you will know to override it when + * you create your own property editor. + * + * @return the Java initialization string. + */ + public String getJavaInitializationString() + { + return "@$#^"; + } + + /** Gets the value as text. + * In this class, you cannot count on getAsText() doing + * anything useful, although in this implementation I + * do toString(). + * + * @return the value as text. + */ + public String getAsText() + { + return value != null ? value.toString() : "null"; + } + + /** Sets the value as text. + * In this class, you cannot count on setAsText() doing + * anything useful across implementations. + * Implementation Note: In this + * implementation it checks if the String is "null", and + * if it is, sets the value to null, otherwise it throws + * an IllegalArgumentException. + * + * @param s the text to convert to a new value. + * @exception IllegalArgumentException if the text is + * malformed. + */ + public void setAsText(String s) throws IllegalArgumentException + { + if (s.equals("null")) + setValue(null); + else + throw new IllegalArgumentException(); + } + + /** Returns a list of possible choices for the value. + * + * @return null + */ + public String[] getTags() + { + return null; + } + + /** Returns a custom component to edit the value. + * + * @return null in this class. + */ + public java.awt.Component getCustomEditor() + { + return null; + } + + /** Finds out whether this property editor supports a + * custom component to edit its value. + * + * @return false in this class. + */ + public boolean supportsCustomEditor() + { + return false; + } + + /** Adds a property change listener to this property editor. + * + * @param l the listener to add. + */ + public void addPropertyChangeListener(PropertyChangeListener l) + { + pSupport.addPropertyChangeListener(l); + } + + /** Removes a property change listener from this property editor. + * + * @param l the listener to remove. + */ + public void removePropertyChangeListener(PropertyChangeListener l) + { + pSupport.removePropertyChangeListener(l); + } + + /** Notifies people that we've changed, although we don't + * tell them just how. + */ + public void firePropertyChange() + { + pSupport.firePropertyChange(null, null, null); + } + + /** Returns the bean that is used as the source of events. + * + * @return The event source object + * @since 1.5 + */ + public Object getSource() + { + return eventSource; + } + + /** Sets the bean that is used as the source of events + * when property changes occur. + * + * The event source bean is for informational purposes only + * and should not be changed by the PropertyEditor. + * + * @param source + * @since 1.5 + */ + public void setSource(Object source) + { + eventSource = source; + } +} diff --git a/libjava/classpath/java/beans/PropertyVetoException.java b/libjava/classpath/java/beans/PropertyVetoException.java new file mode 100644 index 000000000..1f0399b4b --- /dev/null +++ b/libjava/classpath/java/beans/PropertyVetoException.java @@ -0,0 +1,85 @@ +/* PropertyVetoException.java -- thrown to veto a proposed property change + Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * PropertyVetoException is thrown when a VetoableChangeListener doesn't + * like the proposed change. + * + * @author John Keiser + * @see VetoableChangeListener + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyVetoException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 129596057694162164L; + + /** + * The vetoed change. + * + * @serial the event that was vetoed + */ + private final PropertyChangeEvent evt; + + /** + * Instantiate this exception with the given message and property change. + * + * @param msg the reason for the veto + * @param changeEvent the PropertyChangeEvent that was thrown + */ + public PropertyVetoException(String msg, PropertyChangeEvent changeEvent) + { + super(msg); + evt = changeEvent; + } + + /** + * Get the PropertyChange event that was vetoed. + * + * @return the vetoed change + */ + public PropertyChangeEvent getPropertyChangeEvent() + { + return evt; + } +} diff --git a/libjava/classpath/java/beans/SimpleBeanInfo.java b/libjava/classpath/java/beans/SimpleBeanInfo.java new file mode 100644 index 000000000..d5216caf5 --- /dev/null +++ b/libjava/classpath/java/beans/SimpleBeanInfo.java @@ -0,0 +1,145 @@ +/* java.beans.SimpleBeanInfo + Copyright (C) 1998, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.awt.Image; +import java.awt.Toolkit; +import java.net.URL; + +/** + ** SimpleBeanInfo is a class you may extend to more easily + ** provide select information to the Introspector. It + ** implements all of the methods in BeanInfo by returning + ** null and forces the Introspector to behave exactly as + ** if there were no BeanInfo class at all (Introspecting + ** everything).

    + ** + ** Overriding one or two of these functions + ** to give explicit information on only those things you + ** wish to give explicit information is perfectly safe, + ** and even desirable.

    + ** + ** See the BeanInfo class for information on what the + ** various methods actually do. + ** + ** @author John Keiser + ** @since JDK1.1 + ** @version 1.1.0, 29 Jul 1998 + ** @see java.beans.BeanInfo + **/ + +public class SimpleBeanInfo implements BeanInfo { + /** Force Introspection of the general bean info. + ** @return null. + **/ + public BeanDescriptor getBeanDescriptor() { + return null; + } + + /** Force Introspection of the events this Bean type + ** fires. + ** @return null + **/ + public EventSetDescriptor[] getEventSetDescriptors() { + return null; + } + + /** Say that there is no "default" event set. + ** @return -1. + **/ + public int getDefaultEventIndex() { + return -1; + } + + /** Force Introspection of the Bean properties. + ** @return null. + **/ + public PropertyDescriptor[] getPropertyDescriptors() { + return null; + } + + /** Say that there is no "default" property. + ** @return -1. + **/ + public int getDefaultPropertyIndex() { + return -1; + } + + /** Force Introspection of the Bean's methods. + ** @return null. + **/ + public MethodDescriptor[] getMethodDescriptors() { + return null; + } + + /** Tell the Introspector to go look for other BeanInfo + ** itself. + ** @return null. + **/ + public BeanInfo[] getAdditionalBeanInfo() { + return null; + } + + /** Say that this Bean has no icons. + ** @param iconType the type of icon + ** @return null. + **/ + public Image getIcon(int iconType) { + return null; + } + + /** Helper method to load an image using the Bean class + ** getResource() method on the BeanInfo class (using + ** getClass(), since you'll extend this class to get + ** the BeanInfo). Basically it's assumed that the Bean + ** and its BeanInfo are both loaded by the same + ** ClassLoader, generally a reasonable assumption. + ** @param location the URL relative + ** @return the Image in question (possibly null). + **/ + public Image loadImage(String location) + { + if (location == null) + return null; + URL url = getClass().getResource(location); + if (url == null) + return null; + return Toolkit.getDefaultToolkit().getImage(url); + } +} diff --git a/libjava/classpath/java/beans/Statement.java b/libjava/classpath/java/beans/Statement.java new file mode 100644 index 000000000..d9cd304da --- /dev/null +++ b/libjava/classpath/java/beans/Statement.java @@ -0,0 +1,386 @@ +/* Statement.java + Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.lang.CPStringBuilder; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + *

    A Statement captures the execution of an object method. It stores + * the object, the method to call, and the arguments to the method and + * provides the ability to execute the method on the object, using the + * provided arguments.

    + * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class Statement +{ + private Object target; + private String methodName; + private Object[] arguments; + + /** + * One or the other of these will get a value after execute is + * called once, but not both. + */ + private transient Method method; + private transient Constructor ctor; + + /** + *

    Constructs a statement representing the invocation of + * object.methodName(arg[0], arg[1], ...);

    + * + *

    If the argument array is null it is replaced with an + * array of zero length.

    + * + * @param target The object to invoke the method on. + * @param methodName The object method to invoke. + * @param arguments An array of arguments to pass to the method. + */ + public Statement(Object target, String methodName, Object[] arguments) + { + this.target = target; + this.methodName = methodName; + this.arguments = (arguments != null) ? arguments : new Object[0]; + } + + /** + * Execute the statement. + * + *

    Finds the specified method in the target object and calls it with + * the arguments given in the constructor.

    + * + *

    The most specific method according to the JLS(15.11) is used when + * there are multiple methods with the same name.

    + * + *

    Execute performs some special handling for methods and + * parameters: + *

      + *
    • Static methods can be executed by providing the class as a + * target.
    • + * + *
    • The method name new is reserved to call the constructor + * new() will construct an object and return it. Not useful unless + * an expression :-)
    • + * + *
    • If the target is an array, get and set as defined in + * java.util.List are recognized as valid methods and mapped to the + * methods of the same name in java.lang.reflect.Array.
    • + * + *
    • The native datatype wrappers Boolean, Byte, Character, Double, + * Float, Integer, Long, and Short will map to methods that have + * native datatypes as parameters, in the same way as Method.invoke. + * However, these wrappers also select methods that actually take + * the wrapper type as an argument.
    • + *
    + *

    + * + *

    The Sun spec doesn't deal with overloading between int and + * Integer carefully. If there are two methods, one that takes an + * Integer and the other taking an int, the method chosen is not + * specified, and can depend on the order in which the methods are + * declared in the source file.

    + * + * @throws Exception if an exception occurs while locating or + * invoking the method. + */ + public void execute() throws Exception + { + doExecute(); + } + + private static Class wrappers[] = + { + Boolean.class, Byte.class, Character.class, Double.class, Float.class, + Integer.class, Long.class, Short.class + }; + + private static Class natives[] = + { + Boolean.TYPE, Byte.TYPE, Character.TYPE, Double.TYPE, Float.TYPE, + Integer.TYPE, Long.TYPE, Short.TYPE + }; + + /** Given a wrapper class, return the native class for it. + *

    For example, if c is Integer, + * Integer.TYPE is returned.

    + */ + private Class unwrap(Class c) + { + for (int i = 0; i < wrappers.length; i++) + if (c == wrappers[i]) + return natives[i]; + return null; + } + + /** Returns true if all args can be assigned to + * params, false otherwise. + * + *

    Arrays are guaranteed to be the same length.

    + */ + private boolean compatible(Class[] params, Class[] args) + { + for (int i = 0; i < params.length; i++) + { + // Argument types are derived from argument values. If one of them was + // null then we cannot deduce its type. However null can be assigned to + // any type. + if (args[i] == null) + continue; + + // Treat Integer like int if appropriate + Class nativeType = unwrap(args[i]); + if (nativeType != null && params[i].isPrimitive() + && params[i].isAssignableFrom(nativeType)) + continue; + if (params[i].isAssignableFrom(args[i])) + continue; + + return false; + } + return true; + } + + /** + * Returns true if the method arguments in first are + * more specific than the method arguments in second, i.e. all + * arguments in first can be assigned to those in + * second. + * + *

    A method is more specific if all parameters can also be fed to + * the less specific method, because, e.g. the less specific method + * accepts a base class of the equivalent argument for the more + * specific one.

    + * + * @param first a Class[] value + * @param second a Class[] value + * @return a boolean value + */ + private boolean moreSpecific(Class[] first, Class[] second) + { + for (int j=0; j < first.length; j++) + { + if (second[j].isAssignableFrom(first[j])) + continue; + return false; + } + return true; + } + + final Object doExecute() throws Exception + { + Class klazz = (target instanceof Class) + ? (Class) target : target.getClass(); + Object args[] = (arguments == null) ? new Object[0] : arguments; + Class argTypes[] = new Class[args.length]; + + // Retrieve type or use null if the argument is null. The null argument + // type is later used in compatible(). + for (int i = 0; i < args.length; i++) + argTypes[i] = (args[i] != null) ? args[i].getClass() : null; + + if (target.getClass().isArray()) + { + // FIXME: invoke may have to be used. For now, cast to Number + // and hope for the best. If caller didn't behave, we go boom + // and throw the exception. + if (methodName.equals("get") && argTypes.length == 1) + return Array.get(target, ((Number)args[0]).intValue()); + if (methodName.equals("set") && argTypes.length == 2) + { + Object obj = Array.get(target, ((Number)args[0]).intValue()); + Array.set(target, ((Number)args[0]).intValue(), args[1]); + return obj; + } + throw new NoSuchMethodException("No matching method for statement " + toString()); + } + + // If we already cached the method, just use it. + if (method != null) + return method.invoke(target, args); + else if (ctor != null) + return ctor.newInstance(args); + + // Find a matching method to call. JDK seems to go through all + // this to find the method to call. + + // if method name or length don't match, skip + // Need to go through each arg + // If arg is wrapper - check if method arg is matchable builtin + // or same type or super + // - check that method arg is same or super + + if (methodName.equals("new") && target instanceof Class) + { + Constructor ctors[] = klazz.getConstructors(); + for (int i = 0; i < ctors.length; i++) + { + // Skip methods with wrong number of args. + Class ptypes[] = ctors[i].getParameterTypes(); + + if (ptypes.length != args.length) + continue; + + // Check if method matches + if (!compatible(ptypes, argTypes)) + continue; + + // Use method[i] if it is more specific. + // FIXME: should this check both directions and throw if + // neither is more specific? + if (ctor == null) + { + ctor = ctors[i]; + continue; + } + Class mptypes[] = ctor.getParameterTypes(); + if (moreSpecific(ptypes, mptypes)) + ctor = ctors[i]; + } + if (ctor == null) + throw new InstantiationException("No matching constructor for statement " + toString()); + return ctor.newInstance(args); + } + + Method methods[] = klazz.getMethods(); + + for (int i = 0; i < methods.length; i++) + { + // Skip methods with wrong name or number of args. + if (!methods[i].getName().equals(methodName)) + continue; + Class ptypes[] = methods[i].getParameterTypes(); + if (ptypes.length != args.length) + continue; + + // Check if method matches + if (!compatible(ptypes, argTypes)) + continue; + + // Use method[i] if it is more specific. + // FIXME: should this check both directions and throw if + // neither is more specific? + if (method == null) + { + method = methods[i]; + continue; + } + Class mptypes[] = method.getParameterTypes(); + if (moreSpecific(ptypes, mptypes)) + method = methods[i]; + } + if (method == null) + throw new NoSuchMethodException("No matching method for statement " + toString()); + + // If we were calling Class.forName(String) we intercept and call the + // forName-variant that allows a ClassLoader argument. We take the + // system classloader (aka application classloader) here to make sure + // that application defined classes can be resolved. If we would not + // do that the Class.forName implementation would use the class loader + // of java.beans.Statement which is and cannot resolve application + // defined classes. + if (method.equals( + Class.class.getMethod("forName", new Class[] { String.class }))) + return Class.forName( + (String) args[0], true, ClassLoader.getSystemClassLoader()); + + try { + return method.invoke(target, args); + } catch(IllegalArgumentException iae){ + System.err.println("method: " + method); + + for(int i=0;iStatement
    . + * + * @return A string representation of this Statement. + */ + public String toString() + { + CPStringBuilder result = new CPStringBuilder(); + + String targetName; + if (target != null) + targetName = target.getClass().getSimpleName(); + else + targetName = "null"; + + result.append(targetName); + result.append("."); + result.append(methodName); + result.append("("); + + String sep = ""; + for (int i = 0; i < arguments.length; i++) + { + result.append(sep); + result.append( + ( arguments[i] == null ) ? "null" : + ( arguments[i] instanceof String ) ? "\"" + arguments[i] + "\"" : + arguments[i].getClass().getSimpleName()); + sep = ", "; + } + result.append(");"); + + return result.toString(); + } + +} diff --git a/libjava/classpath/java/beans/TODO b/libjava/classpath/java/beans/TODO new file mode 100644 index 000000000..08e1d25ee --- /dev/null +++ b/libjava/classpath/java/beans/TODO @@ -0,0 +1,4 @@ +- add AppletStub and AppletContext to java.beans.Beans.instantiate(). +- make Introspector more efficient. +- basic Introspection tests are in, but more tests are probably in order. +- 1.2 support (waiting on java.lang.Package, mainly) diff --git a/libjava/classpath/java/beans/VetoableChangeListener.java b/libjava/classpath/java/beans/VetoableChangeListener.java new file mode 100644 index 000000000..5107954b0 --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeListener.java @@ -0,0 +1,73 @@ +/* VetoableChangeListener.java -- listen for a change which can be vetoed + Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListener; + +/** + * VetoableChangeListener allows a class to monitor proposed changes to + * properties of a Bean and, if desired, prevent them from occurring. A + * vetoableChange() event will be fired after the property change has + * been requested, but before it is permanent. If any listener rejects the + * change by throwing the PropertyChangeException, a new vetoableChange() + * event will be fired to all listeners who received a vetoableChange() event + * in the first place, informing them to revert back to the old value. Thus, + * the listener that threw the exception the first time should be prepared + * to rethrow it the second time. The value, of course, never actually changed. + * + *

    Note: This class may not be reliably used to determine + * whether a property has actually changed. Use the PropertyChangeListener + * interface for that instead. + * + * @author John Keiser + * @see java.beans.PropertyChangeListener + * @see java.beans.VetoableChangeSupport + * @since 1.1 + * @status updated to 1.4 + */ +public interface VetoableChangeListener extends EventListener +{ + /** + * Fired before a Bean's property changes. + * + * @param e the change (containing the old and new values) + * @throws PropertyVetoException if the change is vetoed by the listener + */ + void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException; +} // interface VetoableChangeListener diff --git a/libjava/classpath/java/beans/VetoableChangeListenerProxy.java b/libjava/classpath/java/beans/VetoableChangeListenerProxy.java new file mode 100644 index 000000000..56ca5a38c --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeListenerProxy.java @@ -0,0 +1,102 @@ +/* VetoableChangeListenerProxy.java -- adds a name to a vetoable listener + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.util.EventListenerProxy; + +/** + * This class provides an extension to VetoableChangeListener - + * associating a name with the listener. This can be used to filter the + * changes that one is interested in. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status udpated to 1.4 + */ +public class VetoableChangeListenerProxy extends EventListenerProxy + implements VetoableChangeListener +{ + /** + * The name of the property to listen for. Package visible for use by + * VetoableChangeSupport. + */ + final String propertyName; + + /** + * Create a new proxy which filters property change events and only passes + * changes to the named property on to the delegate. + * + * @param propertyName the property's name to filter on + * @param listener the delegate listener + */ + public VetoableChangeListenerProxy(String propertyName, + VetoableChangeListener listener) + { + super(listener); + this.propertyName = propertyName; + } + + /** + * Forwards the event on to the delegate if the property name matches. + * + * @param event the event to pass on, if it meets the filter + * @throws NullPointerException if the delegate this was created with is null + * @throws PropertyVetoException if the change is vetoed by the listener + */ + public void vetoableChange(PropertyChangeEvent event) + throws PropertyVetoException + { + // Note: Sun does not filter, under the assumption that since + // VetoableChangeSupport unwraps proxys, this method should never be + // called by normal use of listeners. + String name = event == null ? null : event.getPropertyName(); + if (name == null ? propertyName == null : name.equals(propertyName)) + ((VetoableChangeListener) getListener()).vetoableChange(event); + } + + /** + * Gets the name of the property this proxy is filtering on. + * + * @return the property name + */ + public String getPropertyName() + { + return propertyName; + } +} // class VetoableChangeListenerProxy diff --git a/libjava/classpath/java/beans/VetoableChangeSupport.java b/libjava/classpath/java/beans/VetoableChangeSupport.java new file mode 100644 index 000000000..698e82d8f --- /dev/null +++ b/libjava/classpath/java/beans/VetoableChangeSupport.java @@ -0,0 +1,532 @@ +/* VetoableChangeSupport.java -- support to manage vetoable change listeners + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006, + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Vector; + +/** + * VetoableChangeSupport makes it easy to fire vetoable change events and + * handle listeners. It allows chaining of listeners, as well as filtering + * by property name. In addition, it will serialize only those listeners + * which are serializable, ignoring the others without problem. This class + * is thread-safe. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class VetoableChangeSupport implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5090210921595982017L; + + /** + * Maps property names (String) to named listeners (VetoableChangeSupport). + * If this is a child instance, this field will be null. + * + * @serial the map of property names to named listener managers + * @since 1.2 + */ + private Hashtable children; + + /** + * The non-null source object for any generated events. + * + * @serial the event source + */ + private final Object source; + + /** + * A field to compare serialization versions - this class uses version 2. + * + * @serial the serialization format + */ + private static final int vetoableChangeSupportSerializedDataVersion = 2; + + /** + * The list of all registered vetoable listeners. If this instance was + * created by user code, this only holds the global listeners (ie. not tied + * to a name), and may be null. If it was created by this class, as a + * helper for named properties, then this vector will be non-null, and this + * instance appears as a value in the children hashtable of + * another instance, so that the listeners are tied to the key of that + * hashtable entry. + */ + private transient Vector listeners; + + /** + * Create a VetoableChangeSupport to work with a specific source bean. + * + * @param source the source bean to use + * @throws NullPointerException if source is null + */ + public VetoableChangeSupport(Object source) + { + this.source = source; + if (source == null) + throw new NullPointerException(); + } + + /** + * Adds a VetoableChangeListener to the list of global listeners. All + * vetoable change events will be sent to this listener. The listener add + * is not unique: that is, n adds with the same listener will + * result in n events being sent to that listener for every + * vetoable change. This method will unwrap a VetoableChangeListenerProxy, + * registering the underlying delegate to the named property list. + * + * @param l the listener to add (null ignored). + */ + public synchronized void addVetoableChangeListener(VetoableChangeListener l) + { + if (l == null) + return; + if (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + addVetoableChangeListener(p.propertyName, + (VetoableChangeListener) p.getListener()); + } + else + { + if (listeners == null) + listeners = new Vector(); + listeners.add(l); + } + } + + /** + * Removes a VetoableChangeListener from the list of global listeners. If + * any specific properties are being listened on, they must be deregistered + * by themselves; this will only remove the general listener to all + * properties. If add() has been called multiple times for a + * particular listener, remove() will have to be called the + * same number of times to deregister it. This method will unwrap a + * VetoableChangeListenerProxy, removing the underlying delegate from the + * named property list. + * + * @param l the listener to remove + */ + public synchronized void + removeVetoableChangeListener(VetoableChangeListener l) + { + if (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + removeVetoableChangeListener(p.propertyName, + (VetoableChangeListener) p.getListener()); + } + else if (listeners != null) + { + listeners.remove(l); + if (listeners.isEmpty()) + listeners = null; + } + } + + /** + * Returns an array of all registered vetoable change listeners. Those that + * were registered under a name will be wrapped in a + * VetoableChangeListenerProxy, so you must check whether the + * listener is an instance of the proxy class in order to see what name the + * real listener is registered under. If there are no registered listeners, + * this returns an empty array. + * + * @return the array of registered listeners + * @see VetoableChangeListenerProxy + * @since 1.4 + */ + public synchronized VetoableChangeListener[] getVetoableChangeListeners() + { + ArrayList list = new ArrayList(); + if (listeners != null) + list.addAll(listeners); + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + Vector v = ((VetoableChangeSupport) e.getValue()).listeners; + int j = v.size(); + while (--j >= 0) + list.add(new VetoableChangeListenerProxy + (name, (VetoableChangeListener) v.get(j))); + } + } + return (VetoableChangeListener[]) + list.toArray(new VetoableChangeListener[list.size()]); + } + + /** + * Adds a VetoableChangeListener listening on the specified property. Events + * will be sent to the listener only if the property name matches. The + * listener add is not unique; that is, n adds on a particular + * property for a particular listener will result in n events + * being sent to that listener when that property is changed. The effect is + * cumulative, too; if you are registered to listen to receive events on + * all vetoable changes, and then you register on a particular property, + * you will receive change events for that property twice. This method + * will unwrap a VetoableChangeListenerProxy, registering the underlying + * delegate to the named property list if the names match, and discarding + * it otherwise. + * + * @param propertyName the name of the property to listen on + * @param l the listener to add + */ + public synchronized void addVetoableChangeListener(String propertyName, + VetoableChangeListener l) + { + if (propertyName == null || l == null) + return; + while (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (VetoableChangeListener) p.getListener(); + } + VetoableChangeSupport s = null; + if (children == null) + children = new Hashtable(); + else + s = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + { + s = new VetoableChangeSupport(source); + s.listeners = new Vector(); + children.put(propertyName, s); + } + s.listeners.add(l); + } + + /** + * Removes a VetoableChangeListener from listening to a specific property. + * If add() has been called multiple times for a particular + * listener on a property, remove() will have to be called the + * same number of times to deregister it. This method will unwrap a + * VetoableChangeListenerProxy, removing the underlying delegate from the + * named property list if the names match. + * + * @param propertyName the property to stop listening on + * @param l the listener to remove + * @throws NullPointerException if propertyName is null + */ + public synchronized void + removeVetoableChangeListener(String propertyName, VetoableChangeListener l) + { + if (children == null) + return; + VetoableChangeSupport s + = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + return; + while (l instanceof VetoableChangeListenerProxy) + { + VetoableChangeListenerProxy p = (VetoableChangeListenerProxy) l; + if (propertyName == null ? p.propertyName != null + : ! propertyName.equals(p.propertyName)) + return; + l = (VetoableChangeListener) p.getListener(); + } + s.listeners.remove(l); + if (s.listeners.isEmpty()) + { + children.remove(propertyName); + if (children.isEmpty()) + children = null; + } + } + + /** + * Returns an array of all vetoable change listeners registered under the + * given property name. If there are no registered listeners, this returns + * an empty array. + * + * @return the array of registered listeners + * @throws NullPointerException if propertyName is null + * @since 1.4 + */ + public synchronized VetoableChangeListener[] + getVetoableChangeListeners(String propertyName) + { + if (children == null) + return new VetoableChangeListener[0]; + VetoableChangeSupport s + = (VetoableChangeSupport) children.get(propertyName); + if (s == null) + return new VetoableChangeListener[0]; + return (VetoableChangeListener[]) + s.listeners.toArray(new VetoableChangeListener[s.listeners.size()]); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are non-null + * and equal. If the change is vetoed, a new event is fired to notify + * listeners about the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, + Object oldVal, Object newVal) + throws PropertyVetoException + { + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + oldVal, newVal)); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * If the change is vetoed, a new event is fired to notify listeners about + * the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, int oldVal, int newVal) + throws PropertyVetoException + { + if (oldVal != newVal) + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + Integer.valueOf(oldVal), + Integer.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent containing the old and new values of the + * property to all the global listeners, and to all the listeners for the + * specified property name. This does nothing if old and new are equal. + * If the change is vetoed, a new event is fired to notify listeners about + * the rollback before the exception is thrown. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value + * @param newVal the new value + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(String propertyName, + boolean oldVal, boolean newVal) + throws PropertyVetoException + { + if (oldVal != newVal) + fireVetoableChange(new PropertyChangeEvent(source, propertyName, + Boolean.valueOf(oldVal), + Boolean.valueOf(newVal))); + } + + /** + * Fire a PropertyChangeEvent to all the global listeners, and to all the + * listeners for the specified property name. This does nothing if old and + * new values of the event are equal. If the change is vetoed, a new event + * is fired to notify listeners about the rollback before the exception is + * thrown. + * + * @param event the event to fire + * @throws NullPointerException if event is null + * @throws PropertyVetoException if the change is vetoed by a listener + */ + public void fireVetoableChange(PropertyChangeEvent event) + throws PropertyVetoException + { + if (event.oldValue != null && event.oldValue.equals(event.newValue)) + return; + Vector v = listeners; // Be thread-safe. + if (v != null) + { + int i = v.size(); + try + { + while (--i >= 0) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + } + catch (PropertyVetoException e) + { + event = event.rollback(); + int limit = i; + i = v.size(); + while (--i >= limit) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + throw e; + } + } + Hashtable h = children; // Be thread-safe. + if (h != null && event.propertyName != null) + { + VetoableChangeSupport s + = (VetoableChangeSupport) h.get(event.propertyName); + if (s != null) + { + Vector v1 = s.listeners; // Be thread-safe. + int i = v1 == null ? 0 : v1.size(); + try + { + while (--i >= 0) + ((VetoableChangeListener) v1.get(i)).vetoableChange(event); + } + catch (PropertyVetoException e) + { + event = event.rollback(); + int limit = i; + i = v.size(); + while (--i >= 0) + ((VetoableChangeListener) v.get(i)).vetoableChange(event); + i = v1.size(); + while (--i >= limit) + ((VetoableChangeListener) v1.get(i)).vetoableChange(event); + throw e; + } + } + } + } + + /** + * Tell whether the specified property is being listened on or not. This + * will only return true if there are listeners on all + * properties or if there is a listener specifically on this property. + * + * @param propertyName the property that may be listened on + * @return whether the property is being listened on + * @throws NullPointerException if propertyName is null + */ + public synchronized boolean hasListeners(String propertyName) + { + return listeners != null || (children != null + && children.get(propertyName) != null); + } + + /** + * Saves the state of the object to the stream. + * + * @param s the stream to write to + * @throws IOException if anything goes wrong + * @serialData this writes out a null-terminated list of serializable + * global vetoable change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + if (listeners != null) + { + int i = listeners.size(); + while (--i >= 0) + if (listeners.get(i) instanceof Serializable) + s.writeObject(listeners.get(i)); + } + s.writeObject(null); + } + + /** + * Reads the object back from stream (deserialization). + * + * XXX Since serialization for 1.1 streams was not documented, this may + * not work if vetoableChangeSupportSerializedDataVersion is 1. + * + * @param s the stream to read from + * @throws IOException if reading the stream fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads in a null-terminated list of serializable + * global vetoable change listeners (the listeners for a named + * property are written out as the global listeners of the + * children, when the children hashtable is saved) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + VetoableChangeListener l = (VetoableChangeListener) s.readObject(); + while (l != null) + { + addVetoableChangeListener(l); + l = (VetoableChangeListener) s.readObject(); + } + // Sun is not as careful with children as we are, and lets some proxys + // in that can never receive events. So, we clean up anything that got + // serialized, to make sure our invariants hold. + if (children != null) + { + int i = children.size(); + Iterator iter = children.entrySet().iterator(); + while (--i >= 0) + { + Entry e = (Entry) iter.next(); + String name = (String) e.getKey(); + VetoableChangeSupport vcs = (VetoableChangeSupport) e.getValue(); + if (vcs.listeners == null) + vcs.listeners = new Vector(); + if (vcs.children != null) + vcs.listeners.addAll + (Arrays.asList(vcs.getVetoableChangeListeners(name))); + if (vcs.listeners.size() == 0) + iter.remove(); + else + vcs.children = null; + } + if (children.size() == 0) + children = null; + } + } +} // class VetoableChangeSupport diff --git a/libjava/classpath/java/beans/Visibility.java b/libjava/classpath/java/beans/Visibility.java new file mode 100644 index 000000000..338060181 --- /dev/null +++ b/libjava/classpath/java/beans/Visibility.java @@ -0,0 +1,85 @@ +/* java.beans.Visibility + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +/** + * Visibility is an interface a Bean may implement so that the environment + * can tell the Bean whether there is a GUI or not, and so that the Bean + * can tell the environment whether it needs one or can run without one. + *

    + * + * Sun decided not to use standard Introspection patterns so that these + * methods did not get included when the Introspector made its sweep on + * the class. + * + * @author John Keiser + * @since JDK1.1 + * @version 1.1.0, 29 Jul 1998 + */ + +public interface Visibility { + /** + * Tells whether the Bean can run without a GUI or not. + * @return false if Bean can run without a GUI, else true. + */ + boolean needsGui(); + + /** + * Tells whether Bean is trying not to use the GUI. + * If needsGui() is true, this method should always return false. + * @return true if definitely not using GUI, otherwise false. + */ + boolean avoidingGui(); + + /** + * Tells the Bean not to use GUI methods. + * If needsGUI() is false, then after this method is called, + * avoidingGui() should return true. + */ + void dontUseGui(); + + /** + * Tells the Bean it may use the GUI. + * The Bean is not required to use the GUI in this case, it is + * merely being permitted to use it. If needsGui() is + * false, avoidingGui() may return true or false after this method + * is called. + */ + void okToUseGui(); +} diff --git a/libjava/classpath/java/beans/XMLDecoder.java b/libjava/classpath/java/beans/XMLDecoder.java new file mode 100644 index 000000000..26896393a --- /dev/null +++ b/libjava/classpath/java/beans/XMLDecoder.java @@ -0,0 +1,307 @@ +/* java.beans.XMLDecoder -- + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.DefaultExceptionListener; +import gnu.java.beans.decoder.PersistenceParser; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * The XMLDecoder reads XML data that is structured according to + * this DTD + * and creates objects according to the content. Usually such data is generated using the + * {@link XMLEncoder} class. + *

    + * An example XML document might look like this: + * + * <java> + * <string>Hello World</string> + * <int>200</int> + * </java> + * + *

    To read the String and the Integer instance the following can be used (assume + * the XML data can be obtained from the InputStream):

    + * + * XMLDecoder decoder = new XMLDecoder(inputStreamContainingXMLData); + * String message = (String) decoder.readObject(); + * Integer number = (Integer) decoder.readObject(); + * + *

    Besides this basic functionality the XMLDecoder has some more features that might come + * handy in certain situations:

    + *

    An owner object can be set using the setOwner method which can then be accessed when + * decoding. This feature is only useful if the XML data is aware of the owner object. Such data may + * look like this (assume that the owner object is a JFrame instance):

    + * + * <java> + * <void method="getOwner"> + * <void method="setVisible"> + * <boolean>true<boolean> + * </void> + * </void> + * </java> + * + * This accesses the JFrame and makes it visible using the setVisible method. + *

    Please note that changing the owner after the having read the first object has no effect, + * because all object have been decoded then.

    + *

    If the XMLDecoder is created with no {@link ExceptionListener} instance a default one + * is used that prints an error message to System.err whenever a recoverable exception + * is thrown. Recovarable exceptions occur when the XML data cannot be interpreted correctly (e.g + * unknown classes or methods, invocation on null, ...). In general be very careful when the + * XMLDecoder provoked such exceptions because the resulting object(s) may be in an + * undesirable state.

    + *

    Note that changing the ExceptionListener instance after readObject has been called + * once has no effect because the decoding is completed then.

    + *

    At last one can provide a specific ClassLoader which is then used when Class + * objects are accessed. See {@link java.lang.Class#forName(String, boolean, ClassLoader)} for details + * on this.

    + *

    Note: If the InputStream instance given to any of the constructors is null + * the resulting XMLDecoder will be silently (without any exception) useless. Each call + * to readObject will return null and never throws an + * ArrayIndexOutOfBoundsException.

    + * + * @author Robert Schuster + * @since 1.4 + * @status updated to 1.5 + */ +public class XMLDecoder +{ + private Object owner; + + private ExceptionListener exceptionListener; + + private InputStream inputStream; + + private boolean isStreamClosed; + + private ClassLoader classLoader; + + private Iterator iterator; + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * Using this constructor no special ClassLoader, a default ExceptionListener + * and no owner object is used. + * + * @param in InputStream to read XML data from. + */ + public XMLDecoder(InputStream in) + { + this(in, null); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * Using this constructor no special ClassLoader and a default ExceptionListener + * is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + */ + public XMLDecoder(InputStream in, Object owner) + { + this(in, owner, null); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * If the ExceptionListener argument is null a default implementation is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + * @param exceptionListener ExceptionListener instance to which exception notifications are send. + */ + public XMLDecoder( + InputStream in, + Object owner, + ExceptionListener exceptionListener) + { + this( + in, + owner, + exceptionListener, + Thread.currentThread().getContextClassLoader()); + } + + /** Creates a XMLDecoder instance that parses the XML data of the given input stream. + * If the ExceptionListener argument is null a default implementation is used. + * + * @param in InputStream to read XML data from. + * @param owner Owner object which can be accessed and modified while parsing. + * @param listener ExceptionListener instance to which exception notifications are send. + * @param cl ClassLoader instance that is used for calls to Class.forName(String, boolean, ClassLoader) + * @since 1.5 + */ + public XMLDecoder( + InputStream in, + Object owner, + ExceptionListener listener, + ClassLoader cl) + { + // initially here was a check for the validity of the InputStream argument but some + // great engineers decided that this API should silently discard this and behave rather + // odd: readObject will always return null ... + inputStream = in; + + setExceptionListener(listener); + + // validity of this object is checked in Class.forName() and therefore may be null + classLoader = cl; + + this.owner = owner; + } + + /** Closes the stream associated with this decoder. This should be done after having read all + * decoded objects. + *

    See the description of the {@link #readObject()} for the effect caused by close.

    + */ + public void close() + { + if (isStreamClosed) + { + return; + } + + try + { + inputStream.close(); + isStreamClosed = true; + } + catch (IOException e) + { + // bad style forced by original API design ... + } + } + + /** Returns the ExceptionListener instance associated with this decoder. + *

    See the description of {@link XMLDecoder} class for more information on the ExceptionListener.

    + * + * @return Current ExceptionListener of the decoder. + */ + public ExceptionListener getExceptionListener() + { + return exceptionListener; + } + + /** Returns the owner object of the decoder. This method is usually called + * from within the parsed XML data. + *

    See the description of {@link XMLDecoder} class for more information on the owner object.

    + * + * @return The owner object of this decoder. + */ + public Object getOwner() + { + return owner; + } + + /** Returns the next available decoded object. + *

    Note that the actual decoding takes place when the method is called for the first time.

    + *

    If the close method was already called a NoSuchElementException + * is thrown.

    + *

    If the InputStream instance used in the constructors was null this method + * will always return null itself.

    + * + * @return The next object in a sequence decoded from XML data. + * @throws ArrayIndexOutOfBoundsException When no more objects are available. + */ + public Object readObject() throws ArrayIndexOutOfBoundsException + { + // note: the RI does it this way ... + if(inputStream == null) { + return null; + } + + // note: the original API documentation says nothing on what to do + // when the stream was closed before readObject is called but it actually + // throws a NoSuchElementException - this behaviour is imitated here + if (isStreamClosed) + { + throw new NoSuchElementException("Cannot read any objects - XMLDecoder was already closed."); + } + + // creates the PersistenceParser (doing the parsing and decoding) and returns its + // Iterator on first invocation + if (iterator == null) + { + iterator = + new PersistenceParser( + inputStream, + exceptionListener, + classLoader, + this) + .iterator(); + } + + // note: done according to the official documentation + if (!iterator.hasNext()) + { + throw new ArrayIndexOutOfBoundsException("No more objects available from this XMLDecoder."); + } + + // returns just the next object if there was no problem + return iterator.next(); + } + + /** Sets the ExceptionListener instance to which notifications of exceptions are send + * while parsing the XML data. + *

    See the description of {@link XMLDecoder} class for more information on the ExceptionListener.

    + * + * @param listener + */ + public void setExceptionListener(ExceptionListener listener) + { + // uses a default implementation when null + if (listener == null) + { + listener = DefaultExceptionListener.INSTANCE; + } + exceptionListener = listener; + } + + /** Sets the owner object which can be accessed from the parsed XML data. + *

    See the description of {@link XMLDecoder} class for more information on the owner object.

    + * + * @param newOwner + */ + public void setOwner(Object newOwner) + { + owner = newOwner; + } + +} diff --git a/libjava/classpath/java/beans/XMLEncoder.java b/libjava/classpath/java/beans/XMLEncoder.java new file mode 100644 index 000000000..40cb6dbfb --- /dev/null +++ b/libjava/classpath/java/beans/XMLEncoder.java @@ -0,0 +1,267 @@ +/* XMLEncoder.java + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.beans; + +import gnu.java.beans.encoder.ScanEngine; + +import java.io.OutputStream; + +/** + * This class uses the {@link PersistenceDelegate} and {@link Encoder} + * infrastructure to generate an XML representation of the objects it + * serializes. + * + * @author Robert Schuster (robertschuster@fsfe.org) + * @since 1.4 + */ +public class XMLEncoder extends Encoder +{ + Object owner; + + Exception exception; + + ScanEngine scanEngine; + + private int accessCounter = 0; + + public XMLEncoder(OutputStream os) + { + scanEngine = new ScanEngine(os); + } + + public void close() + { + if (scanEngine != null) + { + scanEngine.close(); + scanEngine = null; + } + } + + public void flush() + { + scanEngine.flush(); + } + + public void writeExpression(Expression expr) + { + // Implementation note: Why is this method overwritten and nearly exactly + // reimplemented as in Encoder? + // The Encoder class can (and should be) subclassed by users outside of the + // java.beans package. While I have doubts that this is possible from an + // API design point of view I tried to replicate the Encoder's behavior + // in the JDK as exactly as possible. This strictness however made it + // extremely complicated to implement the XMLEncoder's backend. Therefore + // I decided to copy the Encoder's implementation and make all changes + // I needed for a succesfull operation of XMLEncoder. + // + // The same is true for the writeStatement method. + + // Silently ignore out of bounds calls. + if (accessCounter <= 0) + return; + + scanEngine.writeExpression(expr); + + + Object target = expr.getTarget(); + Object value = null; + Object newValue = null; + + try + { + value = expr.getValue(); + } + catch (Exception e) + { + getExceptionListener().exceptionThrown(e); + return; + } + + + newValue = get(value); + + if (newValue == null) + { + Object newTarget = get(target); + if (newTarget == null) + { + writeObject(target); + newTarget = get(target); + + // May happen if exception was thrown. + if (newTarget == null) + { + return; + } + } + + Object[] args = expr.getArguments(); + Object[] newArgs = new Object[args.length]; + + for (int i = 0; i < args.length; i++) + { + newArgs[i] = get(args[i]); + if (newArgs[i] == null || isImmutableType(args[i].getClass())) + { + writeObject(args[i]); + newArgs[i] = get(args[i]); + } + } + + Expression newExpr = new Expression(newTarget, expr.getMethodName(), + newArgs); + + // Fakes the result of Class.forName() to make it possible + // to hand such a type to the encoding process. + if (value instanceof Class && ((Class) value).isPrimitive()) + newExpr.setValue(value); + + // Instantiates the new object. + try + { + newValue = newExpr.getValue(); + + putCandidate(value, newValue); + } + catch (Exception e) + { + getExceptionListener().exceptionThrown(e); + + // In Statement.writeExpression we had no possibility to flags + // an erroneous state to the ScanEngine without behaving different + // to the JDK. + scanEngine.revoke(); + + return; + } + + writeObject(value); + + } + else if(value.getClass() == String.class || value.getClass() == Class.class) + { + writeObject(value); + } + + scanEngine.end(); + } + + public void writeStatement(Statement stmt) + { + // In case of questions have a at the implementation note in + // writeExpression. + + scanEngine.writeStatement(stmt); + + // Silently ignore out of bounds calls. + if (accessCounter <= 0) + return; + + Object target = stmt.getTarget(); + + Object newTarget = get(target); + if (newTarget == null) + { + writeObject(target); + newTarget = get(target); + } + + Object[] args = stmt.getArguments(); + Object[] newArgs = new Object[args.length]; + + for (int i = 0; i < args.length; i++) + { + // Here is the difference to the original writeStatement + // method in Encoder. In case that the object is known or + // not an immutable we put it directly into the ScanEngine + // which will then generate an object reference for it. + newArgs[i] = get(args[i]); + if (newArgs[i] == null || isImmutableType(args[i].getClass())) + { + writeObject(args[i]); + newArgs[i] = get(args[i]); + } + else + scanEngine.writeObject(args[i]); + } + + Statement newStmt = new Statement(newTarget, stmt.getMethodName(), newArgs); + + try + { + newStmt.execute(); + } + catch (Exception e) + { + getExceptionListener().exceptionThrown(e); + + // In Statement.writeStatement we had no possibility to flags + // an erroneous state to the ScanEngine without behaving different + // to the JDK. + scanEngine.revoke(); + return; + } + + scanEngine.end(); + } + + public void writeObject(Object o) + { + accessCounter++; + + scanEngine.writeObject(o); + + if (get(o) == null) + super.writeObject(o); + + accessCounter--; + } + + public void setOwner(Object o) + { + owner = o; + } + + public Object getOwner() + { + return owner; + } + +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContext.java b/libjava/classpath/java/beans/beancontext/BeanContext.java new file mode 100644 index 000000000..803cb36ff --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContext.java @@ -0,0 +1,272 @@ +/* java.beans.beancontext.BeanContext + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.DesignMode; +import java.beans.Visibility; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collection; + +/** + * Acts as a container for sub-beans and as a sub-bean, + * so that an entire hierarchy of beans can be made up of + * BeanContexts. + *

    + * + * Since I can't sprinkle the Collections interface + * documentation with special information for BeanContext + * implementors, I'll have to document special requirements for + * implementors of those functions here. + *

    + * + * add() or addAll(): + *
    + *

      + *
    1. + * May add any Object into the hierarchy as well as a + * BeanContextChild, BeanContext or + * BeanContextProxy object. + * This way, any Bean can be in the hierarchy. + *
    2. + *
    3. + * Must synchronize on BeanContext.globalHierarchyLock. + *
    4. + *
    5. + * Don't add the Object if it's already there (only once + * per BeanContext). + *
    6. + *
    7. + * If it is a BeanContextChild implementor, call + * setBeanContext() on it. If it's a + * BeanContextProxy implementor, call + * getBeanContextProxy().setBeanContext() on it. + * If setBeanContext() vetoes the change, back out + * all changes so far and throw IllegalStateException. + *
    8. + *
    9. + * If it (or its proxy) implements Visibility, call + * dontUseGui() or okToUseGui() on it, + * depending on whether you (the BeanContext) feel like + * allowing it to use the GUI or not. + *
    10. + *
    11. + * If it implements BeanContextChild or + * BeanContextProxy, register yourself (the + * BeanContext) as both a + * PropertyChangeListener and + * VetoableChangeListener on the "beanContext" + * property (it may also add itself on any other properties it wishes + * to). + *
    12. + *
    13. + * If it is a listener or event source that you (the + * BeanContext) are interested in, you may register + * yourself to it or register it to you. + *
    14. + *
    15. + * Fire a java.beans.beancontext.BeanContextMembershipEvent + * before exiting. addAll() should wait until everything + * is done changing before firing the event (or events) so that if a + * failure occurs, the backing-out process can proceed without any + * events being fired at all. + *
    16. + *
    + *

    + * + * remove() or removeAll(): + *
    + *

      + *
    1. + * Must synchronize on BeanContext.globalHierarchyLock. + *
    2. + *
    3. + * If the specified Object is not a child of this + * BeanContext, just exit without performing any actions. + *
    4. + *
    5. + * Remove the Object from your collection of children. + *
    6. + *
    7. + * If it is a BeanContextChild implementor, call + * setBeanContext(null) on it. If it's a + * BeanContextProxy implementor, call + * getBeanContextProxy().setBeanContext(null) on it. + * If setBeanContext() vetoes the change, back out + * all changes so far and throw IllegalStateException. + *
    8. + *
    9. + * If you registered the Object to listen to you or + * registered yourself as a listener on the Object during + * add() or addAll(), undo the registration + * bycalling the appropriate removeListener() method. + *
    10. + *
    11. + * Fire a java.beans.beancontext.BeanContextMembershipEvent + * before exiting. removeAll() should wait until + * everything is done changing before firing the event (or events) so + * that if a failure occurs, the backing-out process can proceed + * without any events being fired at all. + *
    12. + *
    + *

    + * + * addAll(), removeAll(), + * retainAll() and clear() do not need to be + * implemented, but may be if so desired. + *

    + * + * Similarly, Visibility and DesignMode methods + * should propagate changed values to children that implement interfaces + * of the same name. + *

    + * + * A hierarchy of beans is mainly useful so that different sets of beans + * can be established, each with their own set of resources. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContext + extends Collection, BeanContextChild, Visibility, DesignMode { + + /** + * The global lock on changing any BeanContext hierarchy. + * It kinda sucks that there is only one lock, since there can be + * multiple hierarchies. Oh well, I didn't design, I just code. + *

    + * + * Methods that must (or do) synchronize on the global lock: + *
    + *

      + *
    • + * Implementors of BeanContext.add() and addAll() + *
    • + *
    + * @fixme fill in the rest of the methods which use the global lock. + */ + Object globalHierarchyLock = new Object(); + + /** + * Instantiate a Bean using this Bean's ClassLoader + * and this BeanContext as the parent. + *

    + * + * This method exists mainly so that BeanContext + * implementations can perform extra actions on Beans that are + * created within them. + * + * @param beanName the name of the bean to instantiate + * @return the created Bean + * + * @see java.beans.Beans#instantiate(java.lang.ClassLoader,java.lang.String) + * @see java.beans.Beans#instantiate(java.lang.ClassLoader,java.lang.String,java.beans.beancontext.BeanContext) + * @exception IOException if there is an I/O problem during + * instantiation. + * @exception ClassNotFoundException if a serialized Bean's class + * is not found. + */ + Object instantiateChild(String beanName) + throws IOException, + ClassNotFoundException; + + /** + * Get a resource. The BeanContext will typically + * call ClassLoader.getResource(), but may do it any + * way it wants to. This allows a BeanContext to + * have its own set of resources separate from the rest of the + * system. + *

    + * + * Beans should call this method on their parent rather than the + * associated ClassLoader method. + *

    + * + * I am assuming, but am not entirely sure, that if a + * BeanContext cannot find a resource, its + * responsibility is to call the getResource method + * of its parent BeanContext. + * + * @return a URL to the requested resource. + * @param resourceName the name of the resource requested. + * @param requestor a reference to the child requesting the resource. + * @see java.lang.ClassLoader#getResource(java.lang.String) + */ + URL getResource(String resourceName, BeanContextChild requestor); + + /** + * Get a resource as a stream. The BeanContext will + * typically call ClassLoader.getResourceAsStream(), + * but may do it any way it wants to. This allows a + * BeanContext's children to have their own set of + * resources separate from the rest of the system. + *

    + * + * Beans should call this method on their parent rather than the + * associated ClassLoader method. + *

    + * + * I am assuming, but am not entirely sure, that if a + * BeanContext cannot find a resource, its + * responsibility is to call the getResourceAsStream + * method of its parent BeanContext. + * + * @return the requested resource as a stream. + * @param resourceName the name of the resource requested. + * @param requestor a reference to the child requesting the resource. + * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String) + */ + InputStream getResourceAsStream(String resourceName, BeanContextChild requestor); + + /** + * Add a listener on changes to the membership of this + * BeanContext object. + * @param listener the listener to add. + */ + void addBeanContextMembershipListener(BeanContextMembershipListener listener); + + /** + * Remove a listener on changes to the membership of this + * BeanContext object. + * @param listener the listener to remove. + */ + void removeBeanContextMembershipListener(BeanContextMembershipListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChild.java b/libjava/classpath/java/beans/beancontext/BeanContextChild.java new file mode 100644 index 000000000..e2bdcf336 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChild.java @@ -0,0 +1,174 @@ +/* java.beans.beancontext.BeanContextChild + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +/** + * Beans implement this to get information about the execution environment and + * its services and to be placed in the hierarchy. + *

    + * + * The difference between a BeanContext and a + * BeanContextChild, mainly, is that a + * BeanContext may be a parent. + *

    + * + * BeanContextChild instances will be serialized at some + * point in their life, but you need to make sure your bean context does + * not contain a serializable reference (directly or indirectly) to the + * parent BeanContext, to any of the other + * BeanContexts in the tree, or to any resources obtained + * via the BeanContextServices interface. One way to do this + * is to mark any fields that contain such references as + * transient. Another way is to use a custom serializer. + *

    + * + * If you do not do this, when the BeanContext is serialized, + * all the other BeanContexts and other unnecessary things + * will be serialized along with it. + *

    + * + * Before dying, a BeanContextChild should call + * getBeanContext().remove(this) to detach from the + * hierarchy and exit cleanly. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContext + */ + +public interface BeanContextChild { + /** + * Set the parent BeanContext. + *

    + * + * This method is called from BeanContext.add() and + * should not be called directly. + *

    + * + * When this Object is being added to a new BeanContext or moved + * from an old one, a non-null value will be passed in. + *

    + * + * When this Object is being removed from the current + * BeanContext, setBeanContext() will + * receive the parameter null. + *

    + * + * When being removed from the current BeanContext, + * it is the BeanContextChild's responsibility to + * release all services it has obtained. + *

    + * + * This change should generate PropertyChangeEvent + * and VetoableChangeEvents with the property name + * "beanContext". If the change is vetoed, it must re-throw the + * exception and not change anything. In this way, the parent + * BeanContextChild, who has registered himself with + * you, will have a chance to remove this child from its + * collection. + *

    + * + * If the Bean does not wish to change the parent or be removed + * from one, it may throw the PropertyVetoException. + * If you veto a setBeanContext(null) call, then you + * should try your hardest to remedy whatever problem is keeping + * you from being removed from the BeanContext so + * that you can not veto it the next time. + * Otherwise, nasty pathological recursion stuff could occur in + * certain situations. + *

    + * + * If you do veto the change, you must first back out any changes + * you made prior to the veto. Best not to make any such changes + * prior to the veto in the first place. + *

    + * + * This method is called from BeanContext.add() and + * should not be called directly. + * + * @param parent the new parent for the BeanContextChild, + * or null to signify removal from a tree. + * @exception PropertyVetoException if the + * BeanContextChild implementor does not + * wish to have its parent changed. + */ + void setBeanContext(BeanContext parent) + throws PropertyVetoException; + + /** + * Get the parent BeanContext. + * @return the parent BeanContext. + */ + BeanContext getBeanContext(); + + /** + * Add a listener that will be notified when a specific property changes. + * @param prop the name of the property to listen on + * @param listener the listener to listen on the property. + */ + void addPropertyChangeListener(String prop, PropertyChangeListener listener); + + /** + * Remove a listener to a certain property. + * @param prop the name of the property being listened on + * @param listener the listener listening on the property. + */ + void removePropertyChangeListener(String prop, PropertyChangeListener listener); + + /** + * Add a listener that will be notified when a specific property + * change is requested (a PropertyVetoException may be thrown) as + * well as after the change is successfully made. + * + * @param prop the name of the property to listen on + * @param listener the listener to listen on the property. + */ + void addVetoableChangeListener(String prop, VetoableChangeListener listener); + + /** + * Remove a listener to a certain property. + * @param prop the name of the property being listened on + * @param listener the listener listening on the property. + */ + void removeVetoableChangeListener(String prop, VetoableChangeListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java new file mode 100644 index 000000000..cb75d508d --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChildComponentProxy.java @@ -0,0 +1,60 @@ +/* java.beans.beancontext.BeanContextChildComponentProxy + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.awt.Component; + +/** + * Interface for BeanContextChilds which wish to associate an + * AWT component with them. The proxy is provided because the + * addPropertyChangeListener() method would conflict with + * Component if you tried to extend. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextChildComponentProxy { + /** + * Get the Component associated with this BeanContextChild. + * @return the Component associated with this + * BeanContextChild. + */ + Component getComponent(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java new file mode 100644 index 000000000..8cd887d0c --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextChildSupport.java @@ -0,0 +1,381 @@ +/* java.beans.beancontext.BeanContextChildSupport + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.Serializable; + +/** + * Support for creating a BeanContextChild. + * This class contains the most common implementations of the methods in + * the BeanContextChild + * + * @specnote This class is not very well specified. I had to "fill in the + * blanks" in most places with what I thought was reasonable + * behavior. If there are problems, let me know. + * + * @author John Keiser + * @since 1.2 + * @see java.beans.beancontext.BeanContextChild + */ +public class BeanContextChildSupport + implements BeanContextChild, BeanContextServicesListener, Serializable +{ + static final long serialVersionUID = 6328947014421475877L; + + /** + * The peer on which to perform set actions. + * This is here so that this class can be used as a peer. + *

    + * + * When extending this class, this variable will be set to + * this. + */ + public BeanContextChild beanContextChildPeer; + + /** + * The parent BeanContext. + */ + protected transient BeanContext beanContext; + + /** + * If setBeanContext() was vetoed once before, this + * is set to true so that the next time, vetoes will + * be ignored. + */ + protected transient boolean rejectedSetBCOnce; + + /** + * Listeners are registered here and events are fired through here. + */ + protected PropertyChangeSupport pcSupport; + + /** + * Listeners are registered here and events are fired through here. + */ + protected VetoableChangeSupport vcSupport; + + /** + * Create a new BeanContextChildSupport with itself as the peer. + * This is meant to be used when you subclass + * BeanContextChildSupport to create your child. + */ + public BeanContextChildSupport() + { + this (null); + } + + /** + * Create a new BeanContextChildSupport with the specified peer. + * @param peer the peer to use, or null to specify + * this. + */ + public BeanContextChildSupport (BeanContextChild peer) + { + if (peer == null) + { + peer = this; + } + + beanContextChildPeer = peer; + pcSupport = new PropertyChangeSupport (peer); + vcSupport = new VetoableChangeSupport (peer); + } + + /** + * Set the parent BeanContext. + *

    + * + * When this Object is being added to a new BeanContext or moved + * from an old one, a non-null value will be passed in. + *

    + * + * When this Object is being removed from the current + * BeanContext, setBeanContext() will + * receive the parameter null. + *

    + * + * Order of events: + *

      + *
    1. + * If the new BeanContext is the same as the old + * one, nothing happens. + *
    2. + *
    3. + * If the change has not been rejected or vetoed before, call + * validatePendingSetBeanContext(). If this call + * returns false, the change is rejected and a + * PropertyVetoException is thrown. + *
    4. + *
    5. + * If the change has not been rejected or vetoed before, + * VetoableChangeEvents are fired with the name + * "beanContext", using the + * fireVetoableChange() method. If a veto + * occurs, reversion events are fired using the same method, + * the change is rejected, and the veto is rethrown. + *
    6. + *
    7. + * releaseBeanContextResources() is called. + *
    8. + *
    9. + * The change is made. + *
    10. + *
    11. + * PropertyChangeEvents are fired using the + * firePropertyChange() method. + *
    12. + *
    13. + * initializeBeanContextResources() is called. + *
    14. + *
    + *

    + * + * @param newBeanContext the new parent for the + * BeanContextChild, or null to + * signify removal from a tree. + * @exception PropertyVetoException if the + * BeanContextChild implementor does not + * wish to have its parent changed. + */ + public void setBeanContext(BeanContext newBeanContext) + throws PropertyVetoException + { + synchronized (beanContextChildPeer) + { + if (newBeanContext == beanContext) + return; + + if (!rejectedSetBCOnce) + { + if (!validatePendingSetBeanContext (newBeanContext)) + { + rejectedSetBCOnce = true; + throw new PropertyVetoException ("validatePendingSetBeanContext() rejected change", + new PropertyChangeEvent(beanContextChildPeer, "beanContext", beanContext, newBeanContext)); + } + + try + { + fireVetoableChange ("beanContext", beanContext, newBeanContext); + } + catch (PropertyVetoException e) + { + rejectedSetBCOnce = true; + throw e; + } + } + + releaseBeanContextResources (); + + beanContext = newBeanContext; + rejectedSetBCOnce = false; + + firePropertyChange ("beanContext", beanContext, newBeanContext); + + initializeBeanContextResources (); + } + } + + /** + * Get the parent BeanContext. + * @return the parent BeanContext. + */ + public BeanContext getBeanContext() + { + return beanContext; + } + + /** + * Get the peer (or this if there is no peer). + * @return the peer, or this if there is no peer. + */ + public BeanContextChild getBeanContextChildPeer() { + return beanContextChildPeer; + } + + /** + * Determine whether there is a peer. + * This is true iff getBeanContextChildPeer() == this. + * @return whether there is a peer. + */ + public boolean isDelegated() { + return beanContextChildPeer == this; + } + + /** + * Add a listener that will be notified when a specific property changes. + * @param propertyName the name of the property to listen on. + * @param listener the listener to listen on the property. + */ + public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcSupport.addPropertyChangeListener(propertyName, listener); + } + + /** + * Remove a listener to a certain property. + * + * @param propertyName the name of the property being listened on. + * @param listener the listener listening on the property. + */ + public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + pcSupport.removePropertyChangeListener(propertyName, listener); + } + + /** + * Add a listener that will be notified when a specific property + * change is requested (a PropertyVetoException may be thrown) as + * well as after the change is successfully made. + * + * @param propertyName the name of the property to listen on. + * @param listener the listener to listen on the property. + */ + public void addVetoableChangeListener(String propertyName, VetoableChangeListener listener) { + vcSupport.addVetoableChangeListener(propertyName, listener); + } + + /** + * Remove a listener to a certain property. + * + * @param propertyName the name of the property being listened on + * @param listener the listener listening on the property. + */ + public void removeVetoableChangeListener(String propertyName, VetoableChangeListener listener) { + vcSupport.removeVetoableChangeListener(propertyName, listener); + } + + /** + * Fire a property change. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value of the property + * @param newVal the new value of the property + */ + public void firePropertyChange(String propertyName, Object oldVal, Object newVal) { + pcSupport.firePropertyChange(propertyName, oldVal, newVal); + } + + /** + * Fire a vetoable property change. + * + * @param propertyName the name of the property that changed + * @param oldVal the old value of the property + * @param newVal the new value of the property + * @exception PropertyVetoException if the change is vetoed. + */ + public void fireVetoableChange(String propertyName, Object oldVal, Object newVal) + throws PropertyVetoException { + vcSupport.fireVetoableChange(propertyName, oldVal, newVal); + } + + /** + * Called by BeanContextServices.revokeService() to indicate that a service has been revoked. + * If you have a reference to such a service, it should be + * discarded and may no longer function properly. + * getService() will no longer work on the specified + * service class after this event has been fired. + *

    + * + * This method is meant to be overriden. + * BeanContextChildSupport's implementation does + * nothing. + * + * @param event the service revoked event. + * @see java.beans.beancontext.BeanContextServices#revokeService(java.lang.Class,java.beans.beancontext.BeanContextServiceProvider,boolean) + */ + public void serviceRevoked(BeanContextServiceRevokedEvent event) { + } + + /** + * Called by BeanContextServices whenever a service is made available. + *

    + * + * This method is meant to be overriden. + * BeanContextChildSupport's implementation does + * nothing. + * + * @param event the service revoked event, with useful information + * about the new service. + */ + public void serviceAvailable(BeanContextServiceAvailableEvent event) { + } + + /** + * Called by setBeanContext() to determine whether the set should be rejected. + *

    + * + * This method is meant to be overriden. + * BeanContextChildSupport's implementation simply + * returns true. + * + * @param newBeanContext the new parent. + * @return whether to allow the parent to be changed to the new + * value. + */ + public boolean validatePendingSetBeanContext(BeanContext newBeanContext) { + return true; + } + + /** + * Called by setBeanContext() to release resources of a what will soon no longer be the parent. + *

    + * + * This method is meant to be overriden. + * BeanContextChildSupport's implementation does + * nothing. + */ + protected void releaseBeanContextResources() { + } + + /** + * Called by setBeanContext() to grab resources when the parent has been set. + *

    + * + * This method is meant to be overriden. + * BeanContextChildSupport's implementation does + * nothing. + */ + protected void initializeBeanContextResources() { + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java new file mode 100644 index 000000000..962cb5fc9 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextContainerProxy.java @@ -0,0 +1,63 @@ +/* java.beans.beancontext.BeanContextContainerProxy + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.awt.Container; + +/** + * Interface for BeanContexts which wish to associate an + * AWT container with them. The proxy is provided because the + * addPropertyChangeListener() and add() methods + * would conflict with Component and Container + * if you tried to extend. + * + * @specnote It is unclear whether anything besides BeanContexts + * are allowed to implement this interface. + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextContainerProxy { + /** + * Get the Container associated with this BeanContext. + * @return the Container associated with this + * BeanContext. + */ + Container getContainer(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextEvent.java new file mode 100644 index 000000000..959d54e58 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextEvent.java @@ -0,0 +1,110 @@ +/* java.beans.beancontext.BeanContextEvent + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventObject; + +/** + * Generic superclass for events fired by BeanContexts. + * + * @author John Keiser + * @since 1.2 + */ + +public abstract class BeanContextEvent extends EventObject +{ + private static final long serialVersionUID = 7267998073569045052L; + + /** + * The BeanContext that most recently passed this + * event on. + */ + protected BeanContext propagatedFrom; + + /** + * Create a new event, from the specified BeanContext. + * propagatedFrom will be initialized to + * null. + * + * @param source the source of the event. + */ + protected BeanContextEvent(BeanContext source) + { + super(source); + } + + /** + * Get the BeanContext that originated this event. + * @return the originator of this event. + */ + public BeanContext getBeanContext() + { + return (BeanContext)getSource(); + } + + /** + * Get the most recent propagator of this event. + * If this value is null, you have received the event + * straight from the source. + * + * @return the most recent propagator of this event. + */ + public BeanContext getPropagatedFrom() + { + return propagatedFrom; + } + + /** + * Tell whether this event has been propagated. + * @return true iff getPropagatedFrom() != null. + */ + public boolean isPropagated() + { + return propagatedFrom != null; + } + + /** + * Set the most recent propagator of this event. + * @param propagator the most recent propagator of this event. + */ + public void setPropagatedFrom(BeanContext propagator) + { + propagatedFrom = propagator; + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java new file mode 100644 index 000000000..77b1be43e --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextMembershipEvent.java @@ -0,0 +1,114 @@ +/* java.beans.beancontext.BeanContextMembershipEvent + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +/** + * Event fired when children are added to or removed from a BeanContext. + * Whether they were added or removed depends entirely on which method + * of the listener interface was called. + * + * @author John Keiser + * @since 1.2 + * @see java.beans.beancontext.BeanContextMembershipListener + */ +public class BeanContextMembershipEvent extends BeanContextEvent { + private static final long serialVersionUID = 3499346510334590959L; + + /** + * The children that were added or removed. + */ + protected Collection children; + + /** + * Create a new membership event. + * @param context the event source. + * @param children the children added to or removed from the source. + */ + public BeanContextMembershipEvent(BeanContext context, Collection children) { + super(context); + this.children = children; + } + + /** + * Create a new membership event. + * @param context the event source. + * @param children the children added to or removed from the source. + */ + public BeanContextMembershipEvent(BeanContext context, Object[] children) { + super(context); + this.children = Arrays.asList(children); + } + + /** + * The number of children removed or added. + * @return the number of children removed or added. + */ + public int size() { + return children.size(); + } + + /** + * An iterator that will step through all the children. + * @return an iterator over all the children. + */ + public Iterator iterator() { + return children.iterator(); + } + + /** + * An array of the children. + * @return an array of the children. + */ + public Object[] toArray() { + return children.toArray(); + } + + /** + * Tell whether the Object is one of the children added or removed. + * @param child the child to check. + * @return whether the Object is added or removed. + */ + public boolean contains(Object child) { + return children.contains(child); + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java b/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java new file mode 100644 index 000000000..cdb47c811 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextMembershipListener.java @@ -0,0 +1,70 @@ +/* java.beans.beancontext.BeanContextMembershipListener + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventListener; + +/** + * This is the interface to which BeanContextMembershipEvents are sent. + * This happens when children are added to or removed from a + * BeanContext. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextMembershipListener extends EventListener { + /** + * When beans are added to a BeanContext, + * this method is called to fire the event. + * + * @param event the event, including which children were added. + * @see java.beans.beancontext.BeanContext#add(java.lang.Object) + */ + void childrenAdded(BeanContextMembershipEvent event); + + /** + * When beans are removed from a BeanContext, + * this method is called to fire the event. + * + * @param event the event, including which children were removed. + * @see java.beans.beancontext.BeanContext#remove(java.lang.Object) + */ + void childrenRemoved(BeanContextMembershipEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextProxy.java b/libjava/classpath/java/beans/beancontext/BeanContextProxy.java new file mode 100644 index 000000000..53632e86b --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextProxy.java @@ -0,0 +1,65 @@ +/* java.beans.beancontext.BeanContextProxy + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Beans that wish to have a BeanContextChild or BeanContext associated with them + * but do not wish to implement those interfaces directly, can implement this interface. + *

    + * + * Don't shoot yourself in the foot: if you already implement + * BeanContextChild, directly or indirectly, the whole + * workings of this package will be unpredictable because it is + * indeterminate as to whether the BeanContextChild is used + * in preference to its proxy or vice versa. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextProxy { + /** + * Return the BeanContextChild associated with this + * Object. + * + * @return the BeanContextChild associated with this + * Object. + */ + BeanContextChild getBeanContextProxy(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java new file mode 100644 index 000000000..a2bdcdde7 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceAvailableEvent.java @@ -0,0 +1,97 @@ +/* java.beans.beancontext.BeanContextServiceAvailableEvent + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; + +/** + * Event fired when new services become available through a BeanContextServices. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextServicesListener + */ + +public class BeanContextServiceAvailableEvent extends BeanContextEvent { + private static final long serialVersionUID = -5333985775656400778L; + + /** + * The Class representing the service which is now + * available. + */ + protected Class serviceClass; + + /** + * Create a new service available event. + * @param services the BeanContextServices through + * which the service is available. This is also the source + * of the event. + * @param serviceClass the service class that is now available. + */ + public BeanContextServiceAvailableEvent(BeanContextServices services, Class serviceClass) { + super(services); + this.serviceClass = serviceClass; + } + + /** + * Get the current service selectors of the service class. + * This is identical to getSourceAsBeanContextServices().getCurrentServiceSelectors(getServiceClass()) + * @return the current service selectors of the service class. + */ + public Iterator getCurrentServiceSelectors() { + return getSourceAsBeanContextServices().getCurrentServiceSelectors(serviceClass); + } + + /** + * Get the newly available service class. + * @return the service class. + */ + public Class getServiceClass() { + return serviceClass; + } + + /** + * Get the BeanContextServices through which the new service is available. + * @return the BeanContextServices through which the + * new service is available. + */ + public BeanContextServices getSourceAsBeanContextServices() { + return (BeanContextServices)getSource(); + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java new file mode 100644 index 000000000..7475a61bd --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceProvider.java @@ -0,0 +1,138 @@ +/* java.beans.beancontext.BeanContextServiceProvider + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; + +/** + * An actual factory for services. + *

    + * + * It is the BeanContextServiceProvider's responsibility to + * register itself with whatever BeanContextServices object + * it wishes to provide services through using the + * addService() method. + *

    + * + * If for some reason it can no longer provide services for a particular + * class, this class must invoke + * BeanContextServices.revokeService(serviceClass,this,true) + * for all the places it has registered the service. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServiceProvider { + /** + * Get a service. + * Called from BeanContextServices.getService(). + * + *

    If the requested service class is not available, or if this + * BeanContextServiceProvider chooses not honor the + * request for some reason, then this method will return + * null.

    + * + * This method may throw unchecked exceptions, so watch out. + * + * @param services the BeanContextServices that wants + * to get the service. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param requestor the actual requestor of the service. Only + * weak references to this will be retained, and it will + * never be changed, only queried in a read-only manner. + * @param serviceClass the Class of the service being + * requested. + * @param serviceSelector a parameter to customize the service + * returned with. + * @return an instance of serviceClass (such that + * instanceof serviceClass is true), or + * null. + * @see java.beans.beancontext.BeanContextServices#getService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Class,java.lang.Object,java.beans.beancontext.BeanContextServiceRevokedListener) + */ + Object getService(BeanContextServices services, Object requestor, Class serviceClass, Object serviceSelector); + + /** + * Release the service. + *

    + * + * Called by BeanContextServices.releaseService(). + *

    + * + * Most BeanContextServiceProviders won't have to do + * anything here. + * + * @param services the BeanContextServices that wants + * to release the service. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param requestor the original requestor of the service. + * @param service the service to relinquish + * @see java.beans.beancontext.BeanContextServices#releaseService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Object) + */ + void releaseService(BeanContextServices services, Object requestor, Object service); + + /** + * Get a list of valid service selectors for the specified service class. + * This method is called from + * BeanContextServices.getCurrentServiceSelectors(). + *

    + * + * If the specified service class does not have a finite number of + * valid service selectors, it should return null. + * If it takes a general Integer parameter, for + * example, you may as well return null or the poor + * soul who called this method will be iterating all day. + *

    + * + * If it has no valid service selectors, it should still return an empty + * Iterator. + * + * @param services the BeanContextServices that wants + * to get the service selectors. Only weak references to this will + * be retained, and it will never be changed, only queried + * in a read-only manner. + * @param serviceClass the service class to get selectors for. + * @return a list of valid service selectors for the service + * class, or null. + * @see java.beans.beancontext.BeanContextServices#getCurrentServiceSelectors(java.lang.Class) + */ + Iterator getCurrentServiceSelectors(BeanContextServices services, Class serviceClass); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java new file mode 100644 index 000000000..78bfc200d --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceProviderBeanInfo.java @@ -0,0 +1,60 @@ +/* java.beans.beancontext.BeanContextServiceProviderBeanInfo + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.BeanInfo; + +/** + * BeanContextServiceProviders implement this to provide information about all of the services they provide. + *

    + * + * This is apparently so that you can import a bunch of services into a + * RAD tool and it will know about all of them and export them to the + * user in a readable manner. + * + * @author John Keiser + * @since JDK1.2 + */ +public interface BeanContextServiceProviderBeanInfo extends BeanInfo { + /** + * Get BeanInfos for all of the service classes of this BeanInfoServiceProvider. + * @return BeanInfos for all provided service classes. + */ + BeanInfo[] getServicesBeanInfo(); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java new file mode 100644 index 000000000..b4f2fa856 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedEvent.java @@ -0,0 +1,112 @@ +/* java.beans.beancontext.BeanContextServiceRevokedEvent + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Event fired when services are revoked from a BeanContextServices. + * + * @author John Keiser + * @since JDK1.2 + * @see java.beans.beancontext.BeanContextServiceRevokedListener + */ + +public class BeanContextServiceRevokedEvent extends BeanContextEvent { + private static final long serialVersionUID = -1295543154724961754L; + + /** + * The Class representing the service which is now + * available. + */ + protected Class serviceClass; + private boolean invalidateRefs; + + /** + * Create a new service revoked event. + * @param services the BeanContextServices through + * which the service was available. This is also the source + * of the event. + * @param serviceClass the service class that is now revoked. + * @param revokeNow whether the revocation is immediate for all + * classes or just a suggestion. + */ + public BeanContextServiceRevokedEvent(BeanContextServices services, Class serviceClass, boolean revokeNow) { + super(services); + this.serviceClass = serviceClass; + invalidateRefs = revokeNow; + } + + /** + * Get the revoked service class. + * @return the service class. + */ + public Class getServiceClass() { + return serviceClass; + } + + /** + * Tell whether the revoked service class is the same as the specified class. + * Identical to getServiceClass().equals(c). + * @param c the class to compare. + * @return whether the clases are equal. + */ + public boolean isServiceClass(Class c) { + return serviceClass.equals(c); + } + + /** + * Get the BeanContextServices through which the service was available. + * @return the BeanContextServices through which the + * service was available. + */ + public BeanContextServices getSourceAsBeanContextServices() { + return (BeanContextServices)getSource(); + } + + /** + * Tell whether current instances of the revoked service are usable or not. + * This is determined by whether the service was revoked + * immediately. + * + * @return whether current instances of the revoked service are + * usable. + */ + public boolean isCurrentServiceInvalidNow() { + return invalidateRefs; + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java new file mode 100644 index 000000000..11b83dd65 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServiceRevokedListener.java @@ -0,0 +1,62 @@ +/* java.beans.beancontext.BeanContextServiceRevokedListener + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.EventListener; + +/** + * Listens for service revoke events. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServiceRevokedListener extends EventListener { + /** + * Called by BeanContextServices.revokeService() to indicate that a service has been revoked. + * If you have a reference to such a service, it should be + * discarded and may no longer function properly. + * getService() will no longer work on the specified + * service class after this event has been fired. + * + * @param event the service revoked event. + * @see java.beans.beancontext.BeanContextServices#revokeService(java.lang.Class,java.beans.beancontext.BeanContextServiceProvider,boolean) + */ + void serviceRevoked(BeanContextServiceRevokedEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServices.java b/libjava/classpath/java/beans/beancontext/BeanContextServices.java new file mode 100644 index 000000000..787618ad5 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServices.java @@ -0,0 +1,216 @@ +/* java.beans.beancontext.BeanContextServices + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.util.Iterator; +import java.util.TooManyListenersException; + +/** + * Allows a BeanContext to provide services to its children. + * + * @specnote it is unclear whether a BeanContextServices + * should delegate unhandled requests to parents. I assume so. + * @author John Keiser + * @since 1.2 + */ + +public interface BeanContextServices + extends BeanContext, BeanContextServicesListener +{ + /** + * Register a service to make it available to others. + * This class may refuse to add the service based on whatever + * information it can gather, including whether the service + * provider is trusted. + * + * @param serviceClass the service class. + * @param provider the factory that will actually provide the service. + * @return whether the service was added or not. + */ + boolean addService (Class serviceClass, + BeanContextServiceProvider provider); + + /** + * Make it so that no one else can use this service. + *

    + * + * If revokeNow is false, the only + * effect of this method is to make all subsequent calls to + * getService() on this service class fail. + *

    + * + * If it is true, a message is also sent out to all + * listeners on the service and all references to it are released. + * + * @param serviceClass the service class to revoke. + * @param provider the service provider providing the service class. + * @param revokeNow whether to release all current references to + * the service. + */ + void revokeService (Class serviceClass, + BeanContextServiceProvider provider, + boolean revokeNow); + + /** + * Release your copy of this service. + *

    + * + * If all copies of the service's class have been relinquished by + * the requestor, the BeanContextServiceRevokedListener + * previously registered by getService() will be + * unregistered. + * + * @param requestorChild the original BeanContextChild + * requesting the service. + * @param requestor the original requestor of the service. + * @param service the service to relinquish + * @see #getService(java.beans.beancontext.BeanContextChild,java.lang.Object,java.lang.Class,java.lang.Object,java.beans.beancontext.BeanContextServiceRevokedListener) + */ + void releaseService (BeanContextChild requestorChild, Object requestor, + Object service); + + /** + * Get a service from this BeanContextServices. + *

    + * + * The specified listener will be registered to receive a + * revocation notice for the specified serviceClass. One + * notification per service class per requestor object will be + * sent. + *

    + * + * The listener will be unregistered when all services that were + * obtained by that requestor for that service class are released. + *

    + * + * If the requested service class is not available, or if this + * BeanContextServices object chooses not honor the + * request because the service class has been revoked or for some + * other reason, then this method will return null. + *

    + * + * This method may throw unchecked exceptions, so watch out. + * + * @specnote it is not specified what happens when two subsequent + * calls are made to getService() with the + * same requestor object and service class but different + * listeners. Which listener is to be notified? + * + * @param requestorChild the BeanContextChild + * associated with the requestor. Typically this will be + * the same as the requestor itself, but since any + * Object, even one outside the hierarchy, may + * make a request, this parameter is necessary. Only weak + * references to this will be retained, and it will never + * be changed, only queried in a read-only manner. + * @param requestor the actual requestor of the service. Only + * weak references to this will be retained, and it will + * never be changed, only queried in a read-only manner. + * @param serviceClass the Class of the service being + * requested. + * @param serviceSelector a parameter to customize the service + * returned with. + * @param listener a listener that will be notified if the service + * being requested is revoked. + * @return an instance of serviceClass (such that + * instanceof serviceClass is true), or + * null. + */ + Object getService (BeanContextChild requestorChild, Object requestor, + Class serviceClass, Object serviceSelector, + BeanContextServiceRevokedListener listener) + throws TooManyListenersException; + + /** + * Get a list of all service classes supported. + *

    + * + * This method must synchronize on + * BeanContext.globalHierarchyLock. + * + * @return a list of all service classes supported. + * @see java.beans.beancontext.BeanContext#globalHierarchyLock + */ + Iterator getCurrentServiceClasses (); + + /** + * Get a list of valid service selectors for the specified service class. + *

    + * + * If the specified service class does not have a finite number of + * valid service selectors, it should return null. + * If it takes a general Integer parameter, for + * example, you may as well return null or the poor + * soul who called this method will be iterating all day. + *

    + * + * If it has no valid service selectors, it should still return an empty + * Iterator. + * + * @param serviceClass the service class to get selectors for. + * @return a list of valid service selectors for the service + * class, or null. + */ + Iterator getCurrentServiceSelectors (Class serviceClass); + + /** + * Tell whether the specified service class is available. + * Iff getService() could return a non-null value for the + * specified service, this method will return true. + * + * @param serviceClass the service class to check on. + * @return whether the specified service class is available. + */ + boolean hasService (Class serviceClass); + + /** + * Add a listener on all adds and removes of services. + * @param listener the listener to add. + */ + void addBeanContextServicesListener (BeanContextServicesListener listener); + + /** + * Remove a listener on all adds and removes of services. + * @specnote it is not certain whether this should remove this + * listener if it was specified in + * getService(). + * @param listener the listener to add. + */ + void removeBeanContextServicesListener (BeanContextServicesListener listener); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java b/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java new file mode 100644 index 000000000..2081080dd --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServicesListener.java @@ -0,0 +1,56 @@ +/* java.beans.beancontext.BeanContextServicesListener + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +/** + * Listens for service add and revoke events. + * + * @author John Keiser + * @since JDK1.2 + */ + +public interface BeanContextServicesListener extends BeanContextServiceRevokedListener { + /** + * Called by BeanContextServices whenever a service is made available. + * + * @param event the service revoked event, with useful information + * about the new service. + */ + void serviceAvailable(BeanContextServiceAvailableEvent event); +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java new file mode 100644 index 000000000..30b4a6fbd --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextServicesSupport.java @@ -0,0 +1,896 @@ +/* BeanContextServicesSupport.java -- + Copyright (C) 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.TooManyListenersException; + +/** + * This is a helper class for implementing a bean context which + * supplies services. It is intended to be used either by + * subclassing or by calling methods of this implementation + * from another. + * + * @author Michael Koch + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.2 + */ +public class BeanContextServicesSupport + extends BeanContextSupport + implements BeanContextServices +{ + private static final long serialVersionUID = -8494482757288719206L; + + protected class BCSSChild + extends BeanContextSupport.BCSChild + { + private static final long serialVersionUID = -3263851306889194873L; + + BCSSChild(Object targetChild, Object peer) + { + super(targetChild, peer); + } + } + + protected class BCSSProxyServiceProvider + implements BeanContextServiceProvider, + BeanContextServiceRevokedListener + { + private static final long serialVersionUID = 7078212910685744490L; + + private BeanContextServiceProvider provider; + + BCSSProxyServiceProvider(BeanContextServiceProvider p) + { + provider = p; + } + + public Iterator getCurrentServiceSelectors (BeanContextServices bcs, + Class serviceClass) + { + return provider.getCurrentServiceSelectors(bcs, serviceClass); + } + + public Object getService (BeanContextServices bcs, + Object requestor, + Class serviceClass, + Object serviceSelector) + { + return provider.getService(bcs, requestor, serviceClass, + serviceSelector); + } + + public void releaseService (BeanContextServices bcs, + Object requestor, + Object service) + { + provider.releaseService(bcs, requestor, service); + } + + public void serviceRevoked (BeanContextServiceRevokedEvent bcsre) + { + if (provider instanceof BeanContextServiceRevokedListener) + ((BeanContextServiceRevokedListener) provider).serviceRevoked(bcsre); + } + } + + protected static class BCSSServiceProvider + implements Serializable + { + private static final long serialVersionUID = 861278251667444782L; + + protected BeanContextServiceProvider serviceProvider; + + private Class serviceClass; + + private BCSSServiceProvider(Class serviceClass, + BeanContextServiceProvider provider) + { + this.serviceClass = serviceClass; + serviceProvider = provider; + } + + protected BeanContextServiceProvider getServiceProvider() + { + return serviceProvider; + } + + private Class getServiceClass() + { + return serviceClass; + } + + } + + /** + * Represents a request for a service. This is + * a common superclass used by the classes which maintain + * the listener-requestor and service-requestor relationships. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static abstract class Request + { + private Object requestor; + + public Request(Object requestor) + { + this.requestor = requestor; + } + + public boolean equals(Object obj) + { + if (obj instanceof Request) + { + Request req = (Request) obj; + return req.getRequestor().equals(requestor); + } + return false; + } + + public Object getRequestor() + { + return requestor; + } + + } + + /** + * Represents a relationship between a service requestor + * and a revocation listener. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static class ServiceRequest + extends Request + { + + private BeanContextServiceRevokedListener listener; + + public ServiceRequest(Object requestor, + BeanContextServiceRevokedListener listener) + { + super(requestor); + this.listener = listener; + } + + public boolean equals(Object obj) + { + if (obj instanceof ServiceRequest) + { + ServiceRequest sr = (ServiceRequest) obj; + return (super.equals(obj) && + sr.getListener().equals(listener)); + } + return false; + } + + public BeanContextServiceRevokedListener getListener() + { + return listener; + } + } + + /** + * Represents a relationship between a service requestor + * and a service instance. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static class ServiceLease + extends Request + { + + private Object service; + + public ServiceLease(Object requestor, Object service) + { + super(requestor); + this.service = service; + } + + public boolean equals(Object obj) + { + if (obj instanceof ServiceLease) + { + ServiceLease sl = (ServiceLease) obj; + return (super.equals(obj) && + sl.getService().equals(service)); + } + return false; + } + + public Object getService() + { + return service; + } + } + + /** + * A collection of listeners who receive availability + * and revocation notifications. + */ + protected transient ArrayList bcsListeners; + + protected transient BCSSProxyServiceProvider proxy; + + /** + * The number of serializable service providers. + */ + protected transient int serializable; + + /** + * A map of registered services, linking the service + * class to its associated {@link BCSSServiceProvider}. + */ + protected transient HashMap services; + + /** + * A map of children to a list of services they + * have obtained. + */ + private transient HashMap serviceUsers; + + /** + * A map of services to {@link ServiceRequest}s. + */ + private transient HashMap serviceRequests; + + /** + * A map of {@link ServiceLease}s to providers. + */ + private transient HashMap serviceLeases; + + /** + * Construct a {@link BeanContextServicesSupport} instance. + */ + public BeanContextServicesSupport () + { + super(); + } + + /** + * Construct a {@link BeanContextServicesSupport} instance. + * + * @param peer the bean context services peer (null permitted). + */ + public BeanContextServicesSupport (BeanContextServices peer) + { + super(peer); + } + + /** + * Construct a {@link BeanContextServicesSupport} instance. + * + * @param peer the bean context peer (null permitted). + * @param locale the locale (null permitted, equivalent to + * the default locale). + */ + public BeanContextServicesSupport(BeanContextServices peer, Locale locale) + { + super(peer, locale); + } + + /** + * Construct a {@link BeanContextServicesSupport} instance. + * + * @param peer the bean context peer (null permitted). + * @param locale the locale (null permitted, equivalent to + * the default locale). + * @param dtime a flag indicating whether or not the bean context is in + * design time mode. + */ + public BeanContextServicesSupport(BeanContextServices peer, Locale locale, + boolean dtime) + { + super(peer, locale, dtime); + } + + /** + * Construct a {@link BeanContextServicesSupport} instance. + * + * @param peer the bean context peer (null permitted). + * @param locale the locale (null permitted, equivalent to + * the default locale). + * @param dtime a flag indicating whether or not the bean context is in + * design time mode. + * @param visible initial value of the okToUseGui flag. + */ + public BeanContextServicesSupport(BeanContextServices peer, Locale locale, + boolean dtime, boolean visible) + { + super(peer, locale, dtime, visible); + } + + /** + * Adds a new listener for service availability and + * revocation events. + * + * @param listener the listener to add. + */ + public void addBeanContextServicesListener + (BeanContextServicesListener listener) + { + synchronized (bcsListeners) + { + if (! bcsListeners.contains(listener)) + bcsListeners.add(listener); + } + } + + /** + * Registers a new service from the specified service provider. + * The service is internally associated with the service provider + * and a BeanContextServiceAvailableEvent is fired. If + * the service is already registered, then this method instead + * returns false. This is equivalent to calling + * addService(serviceClass, bcsp, true). + * + * @param serviceClass the class of the service to be registered. + * @param bcsp the provider of the given service. + * @return true if the service was registered successfully. + * @see #addService(Class, BeanContextServiceProvider, boolean) + */ + public boolean addService (Class serviceClass, + BeanContextServiceProvider bcsp) + { + return addService(serviceClass, bcsp, true); + } + + /** + * Registers a new service from the specified service provider. + * The service is internally associated with the service provider + * and (if fireEvent is true) a + * BeanContextServiceAvailableEvent is fired. If + * the service is already registered, then this method instead + * returns false. + * + * @param serviceClass the class of the service to be registered. + * @param bcsp the provider of the given service. + * @param fireEvent true if a service availability event should + * be fired. + * @return true if the service was registered successfully. + */ + protected boolean addService (Class serviceClass, + BeanContextServiceProvider bcsp, + boolean fireEvent) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + if (services.containsKey(serviceClass)) + return false; + services.put(serviceClass, + createBCSSServiceProvider(serviceClass, bcsp)); + if (bcsp instanceof Serializable) + ++serializable; + if (fireEvent) + fireServiceAdded(serviceClass); + return true; + } + } + } + + /** + * Deserializes any service providers which are serializable. This + * method is called by the readObject method of + * {@link BeanContextSupport} prior to deserialization of the children. + * Subclasses may envelope its behaviour in order to read further + * serialized data to the stream. + * + * @param ois the stream from which data is being deserialized. + * @throws IOException if an I/O error occurs. + * @throws ClassNotFoundException if the class of a deserialized object + * can not be found. + */ + protected void bcsPreDeserializationHook (ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + serializable = ois.readInt(); + for (int a = 0; a < serializable; ++a) + { + BCSSServiceProvider bcsssp = (BCSSServiceProvider) ois.readObject(); + addService(bcsssp.getServiceClass(), bcsssp.getServiceProvider()); + } + } + + /** + * Serializes any service providers which are serializable. This + * method is called by the writeObject method of + * {@link BeanContextSupport} prior to serialization of the children. + * Subclasses may envelope its behaviour in order to add further + * serialized data to the stream. + * + * @param oos the stream to which data is being serialized. + * @throws IOException if an I/O error occurs. + */ + protected void bcsPreSerializationHook (ObjectOutputStream oos) + throws IOException + { + oos.writeInt(serializable); + synchronized (services) + { + Iterator i = services.values().iterator(); + while (i.hasNext()) + { + BCSSServiceProvider bcsssp = (BCSSServiceProvider) i.next(); + if (bcsssp.getServiceProvider() instanceof Serializable) + oos.writeObject(bcsssp); + } + } + } + + /** + * Revokes any services used by a child that has just been removed. + * The superclass ({@link BeanContextSupport}) calls this method + * when a child has just been successfully removed. Subclasses can + * extend this method in order to perform additional operations + * on child removal. + * + * @param child the child being removed. + * @param bcsc the support object for the child. + */ + protected void childJustRemovedHook (Object child, + BeanContextSupport.BCSChild bcsc) + { + if (child instanceof BeanContextChild) + { + BeanContextChild bcchild = (BeanContextChild) child; + Iterator childServices = ((List) serviceUsers.get(bcchild)).iterator(); + while (childServices.hasNext()) + releaseService(bcchild, this, childServices.next()); + serviceUsers.remove(bcchild); + } + } + + /** + * Overrides the {@link BeanContextSupport#createBCSChild} method + * so as to use a {@link BCSSChild} instead. + * + * @param targetChild the child to create the child for. + * @param peer the peer which relates to the child if a proxy is used. + * @return a new instance of {@link BCSSChild}. + */ + protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, + Object peer) + { + return new BCSSChild(targetChild, peer); + } + + /** + * Provides a hook so that subclasses can replace the + * {@link BCSSServiceProvider} class, used to store registered + * service providers, with a subclass without replacing the + * {@link #addService(Class, BeanContextServiceProvider)} method. + * + * @param sc the class of service being registered. + * @param bcsp the provider of the service. + * @return a instance of {@link BCSSServiceProvider} wrapping the provider. + */ + protected BeanContextServicesSupport.BCSSServiceProvider + createBCSSServiceProvider (Class sc, BeanContextServiceProvider bcsp) + { + return new BCSSServiceProvider(sc, bcsp); + } + + /** + * Sends a BeanContextServiceAvailableEvent to all + * registered listeners. + * + * @param bcssae the event to send. + */ + protected final void fireServiceAdded (BeanContextServiceAvailableEvent bcssae) + { + synchronized (bcsListeners) + { + int size = bcsListeners.size(); + for (int i = 0; i < size; ++i) + { + BeanContextServicesListener bcsl + = (BeanContextServicesListener) bcsListeners.get(i); + bcsl.serviceAvailable(bcssae); + } + } + } + + /** + * Sends a BeanContextServiceAvailableEvent to all + * registered listeners. + * + * @param serviceClass the service that is now available. + * @see #fireServiceAdded(BeanContextServiceAvailableEvent) + */ + protected final void fireServiceAdded (Class serviceClass) + { + fireServiceAdded(new BeanContextServiceAvailableEvent(this, + serviceClass)); + } + + /** + * Sends a BeanContextServiceRevokedEvent to all + * registered listeners. + * + * @param event the event to send. + */ + protected final void fireServiceRevoked(BeanContextServiceRevokedEvent event) + { + synchronized (bcsListeners) + { + int size = bcsListeners.size(); + for (int i = 0; i < size; ++i) + { + BeanContextServicesListener bcsl + = (BeanContextServicesListener) bcsListeners.get(i); + bcsl.serviceRevoked(event); + } + List requests = (List) serviceRequests.get(event.getServiceClass()); + if (requests != null) + { + Iterator i = requests.iterator(); + while (i.hasNext()) + { + ServiceRequest r = (ServiceRequest) i.next(); + r.getListener().serviceRevoked(event); + } + } + } + } + + /** + * Sends a BeanContextServiceRevokedEvent to all + * registered listeners. + * + * @param serviceClass the service that has been revoked. + * @see #fireServiceRevoked(BeanContextServiceRevokedEvent) + */ + protected final void fireServiceRevoked (Class serviceClass, + boolean revokeNow) + { + fireServiceRevoked(new BeanContextServiceRevokedEvent(this, serviceClass, + revokeNow)); + } + + /** + * Returns the services peer given at construction time, + * or null if no peer was given. + * + * @return the {@link BeanContextServices} peer. + */ + public BeanContextServices getBeanContextServicesPeer () + { + return (BeanContextServices) beanContextChildPeer; + } + + /** + * Returns child as an instance of + * {@link BeanContextServicesListener}, or null if + * child does not implement that interface. + * + * @param child the child (null permitted). + * + * @return The child cast to {@link BeanContextServicesListener}. + */ + protected static final BeanContextServicesListener + getChildBeanContextServicesListener(Object child) + { + if (child instanceof BeanContextServicesListener) + return (BeanContextServicesListener) child; + else + return null; + } + + /** + * Returns an iterator over the currently available + * services. + * + * @return an iterator over the currently available services. + */ + public Iterator getCurrentServiceClasses () + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + return services.keySet().iterator(); + } + } + } + + /** + * Returns an iterator over the service selectors of the service + * provider for the given service. The iterator is actually + * obtained by calling the + * {@link BeanContextServiceProvider#getCurrentServiceSelectors} + * of the provider itself. If the specified service is not available, + * null is returned. + * + * @param serviceClass the service whose provider's selectors should + * be iterated over. + * @return an {@link Iterator} over the service selectors of the + * provider of the given service. + */ + public Iterator getCurrentServiceSelectors (Class serviceClass) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + BeanContextServiceProvider bcsp + = ((BCSSServiceProvider) + services.get(serviceClass)).getServiceProvider(); + if (bcsp == null) + return null; + else + return bcsp.getCurrentServiceSelectors(this, serviceClass); + } + } + } + + /** + * Retrieves the specified service. If a provider for the service + * is registered in this context, then the request is passed on to + * the provider and the service returned. Otherwise, the request + * is delegated to a parent {@link BeanContextServices}, if possible. + * If the service can not be found at all, then null + * is returned. + * + * @param child the child obtaining the reference. + * @param requestor the requestor of the service, which may be the + * child itself. + * @param serviceClass the service being requested. + * @param serviceSelector an additional service-dependent parameter + * (may be null if not appropriate). + * @param bcsrl a listener used to notify the requestor that the service + * has since been revoked. + * @return a reference to the service requested, or null. + * @throws TooManyListenersException according to Sun's documentation. + */ + public Object getService (BeanContextChild child, Object requestor, + Class serviceClass, Object serviceSelector, + BeanContextServiceRevokedListener bcsrl) + throws TooManyListenersException + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + Object service; + BeanContextServiceProvider provider = ((BCSSServiceProvider) + services.get(serviceClass)).getServiceProvider(); + if (provider != null) + { + service = provider.getService(this, requestor, serviceClass, + serviceSelector); + List childServices = (List) serviceUsers.get(child); + if (childServices == null) + { + childServices = new ArrayList(); + serviceUsers.put(child, childServices); + } + childServices.add(serviceClass); + } + else + { + BeanContextServices peer = getBeanContextServicesPeer(); + if (peer != null) + service = peer.getService(child, requestor, serviceClass, + serviceSelector, bcsrl); + else + service = null; + } + if (service != null) + { + ServiceRequest request = new ServiceRequest(requestor, bcsrl); + Set requests = (Set) serviceRequests.get(serviceClass); + if (requests == null) + { + requests = new HashSet(); + serviceRequests.put(serviceClass, requests); + } + requests.add(request); + ServiceLease lease = new ServiceLease(requestor, service); + serviceLeases.put(lease, provider); + } + return service; + } + } + } + + /** + * Returns true if the specified service is available. + * + * @param serviceClass the service to check for. + * @return true if the service is available. + */ + public boolean hasService (Class serviceClass) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + return services.containsKey(serviceClass); + } + } + } + + public void initialize () + { + super.initialize(); + + bcsListeners = new ArrayList(); + services = new HashMap(); + serviceUsers = new HashMap(); + serviceRequests = new HashMap(); + serviceLeases = new HashMap(); + } + + /** + * Subclasses may override this method to allocate resources + * from the nesting bean context. + */ + protected void initializeBeanContextResources() + { + /* Purposefully left empty */ + } + + /** + * Relinquishes any resources obtained from the parent context. + * Specifically, those services obtained from the parent are revoked. + * Subclasses may override this method to deallocate resources + * from the nesting bean context. + */ + protected void releaseBeanContextResources() + { + /* Purposefully left empty */ + } + + /** + * Releases the reference to a service held by a + * {@link BeanContextChild} (or an arbitrary object associated + * with it). It simply calls the appropriate method on the + * underlying provider. + * + * @param child the child who holds the reference. + * @param requestor the object that requested the reference. + * @param service the service being released. + */ + public void releaseService (BeanContextChild child, Object requestor, + Object service) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + ServiceLease lease = new ServiceLease(requestor, service); + BeanContextServiceProvider provider = (BeanContextServiceProvider) + serviceLeases.get(lease); + if (provider != null) + provider.releaseService(this, requestor, service); + else + { + BeanContextServices peer = getBeanContextServicesPeer(); + if (peer != null) + peer.releaseService(child, requestor, service); + } + serviceLeases.remove(lease); + } + } + } + + public void removeBeanContextServicesListener + (BeanContextServicesListener listener) + { + synchronized (bcsListeners) + { + bcsListeners.remove(listener); + } + } + + /** + * Revokes the given service. A {@link BeanContextServiceRevokedEvent} is + * emitted to all registered {@link BeanContextServiceRevokedListener}s + * and {@link BeanContextServiceListener}s. If revokeCurrentServicesNow + * is true, termination of the service is immediate. Otherwise, prior + * acquisitions of the service by requestors remain valid. + * + * @param serviceClass the service to revoke. + * @param bcsp the provider of the revoked service. + * @param revokeCurrentServicesNow true if this is an exceptional circumstance + * where service should be immediately revoked. + */ + public void revokeService (Class serviceClass, BeanContextServiceProvider bcsp, + boolean revokeCurrentServicesNow) + { + synchronized (globalHierarchyLock) + { + synchronized (services) + { + fireServiceRevoked(serviceClass, revokeCurrentServicesNow); + services.remove(serviceClass); + if (bcsp instanceof Serializable) + --serializable; + } + } + } + + public void serviceAvailable (BeanContextServiceAvailableEvent bcssae) + { + synchronized (services) + { + Class klass = bcssae.getServiceClass(); + if (services.containsKey(klass)) + return; + Iterator it = bcsChildren(); + while (it.hasNext()) + { + Object obj = it.next(); + if (obj instanceof BeanContextServices) + ((BeanContextServices) obj).serviceAvailable(bcssae); + } + } + } + + public void serviceRevoked (BeanContextServiceRevokedEvent bcssre) + { + synchronized (services) + { + Class klass = bcssre.getServiceClass(); + if (services.containsKey(klass)) + return; + Iterator it = bcsChildren(); + while (it.hasNext()) + { + Object obj = it.next(); + if (obj instanceof BeanContextServices) + ((BeanContextServices) obj).serviceRevoked(bcssre); + } + } + } +} diff --git a/libjava/classpath/java/beans/beancontext/BeanContextSupport.java b/libjava/classpath/java/beans/beancontext/BeanContextSupport.java new file mode 100644 index 000000000..fdae387d4 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/BeanContextSupport.java @@ -0,0 +1,1079 @@ +/* BeanContextSupport.java -- + Copyright (C) 2003, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.beans.beancontext; + +import java.beans.Beans; +import java.beans.DesignMode; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.beans.Visibility; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +/** + * This is a helper class for implementing a bean context. It is + * intended to be used either by subclassing or by calling methods + * of this implementation from another. + * + * @author Michael Koch + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.2 + */ +public class BeanContextSupport extends BeanContextChildSupport + implements BeanContext, Serializable, PropertyChangeListener, + VetoableChangeListener +{ + private static final long serialVersionUID = -4879613978649577204L; + + /** + * Deserializes a stored bean context. Hook methods are provided to allow + * subclasses to perform their own deserialization after the default + * deserialization but prior to the deserialization of the children. Note that + * {@link #readChildren(ObjectInputStream)} is only called if there + * is no distinct peer. If there is, the peer is expected to call + * the method instead. + * + * @param s the stream to deserialize. + * @throws ClassNotFoundException if the class of an object being deserialized + * could not be found. + * @throws IOException if an I/O error occurs. + */ + private void readObject (ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + bcsPreDeserializationHook(s); + BeanContext peer = getBeanContextPeer(); + if (peer == null || peer == this) + readChildren(s); + } + + /** + * Serializes a bean context. Hook methods are provided to allow + * subclasses to perform their own serialization after the default + * serialization but prior to serialization of the children. Note that + * {@link #writeChildren(ObjectOutputStream)} is only called if there + * is no distinct peer. If there is, the peer is expected to call + * the method instead. + * + * @param s the stream to serialize. + * @throws ClassNotFoundException if the class of an object being deserialized + * could not be found. + * @throws IOException if an I/O error occurs. + */ + private void writeObject (ObjectOutputStream s) + throws ClassNotFoundException, IOException + { + serializing = true; + s.defaultWriteObject(); + bcsPreSerializationHook(s); + BeanContext peer = getBeanContextPeer(); + if (peer == null || peer == this) + writeChildren(s); + serializing = false; + } + + protected class BCSChild implements Serializable + { + private static final long serialVersionUID = -5815286101609939109L; + + private Object targetChild; + private Object peer; + + BCSChild(Object targetChild, Object peer) + { + this.targetChild = targetChild; + this.peer = peer; + } + + private Object getTargetChild() + { + return targetChild; + } + + } + + protected static final class BCSIterator implements Iterator + { + private Iterator child; + + BCSIterator(Iterator child) + { + this.child = child; + } + + public boolean hasNext () + { + return child.hasNext(); + } + + public Object next () + { + return child.next(); + } + + public void remove () + { + // This must be a noop remove operation. + } + } + + protected transient ArrayList bcmListeners; + + protected transient HashMap children; + + protected transient boolean designTime; + + protected transient Locale locale; + + protected transient boolean okToUseGui; + + private transient boolean serializing; + + /** + * Construct a BeanContextSupport instance. + */ + public BeanContextSupport () + { + this (null, null, false, true); + } + + /** + * Construct a BeanContextSupport instance. + * + * @param peer the bean context peer (null permitted). + */ + public BeanContextSupport(BeanContext peer) + { + this (peer, null, false, true); + } + + /** + * Construct a BeanContextSupport instance. + * + * @param peer the bean context peer (null permitted). + * @param locale the locale (null permitted, equivalent to + * the default locale). + */ + public BeanContextSupport (BeanContext peer, Locale locale) + { + this (peer, locale, false, true); + } + + /** + * Construct a BeanContextSupport instance. + * + * @param peer the bean context peer (null permitted). + * @param locale the locale (null permitted, equivalent to + * the default locale). + * @param dtime a flag indicating whether or not the bean context is in + * design time mode. + */ + public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime) + { + this (peer, locale, dtime, true); + } + + /** + * Construct a BeanContextSupport instance. + * + * @param peer the bean context peer (null permitted). + * @param locale the locale (null permitted, equivalent to + * the default locale). + * @param dtime a flag indicating whether or not the bean context is in + * design time mode. + * @param visible initial value of the okToUseGui flag. + */ + public BeanContextSupport (BeanContext peer, Locale locale, boolean dtime, + boolean visible) + { + super(peer); + + this.locale = locale == null ? Locale.getDefault() : locale; + designTime = dtime; + okToUseGui = visible; + + initialize (); + } + + /** + *

    + * Add a child to the bean context. A child can be a simple + * Object, a BeanContextChild + * or another BeanContext. + *

    + *

    + * The children of a BeanContext form a set. As + * a result, this method returns false if the given + * object is already a child of this context. + *

    + *

    + * If the child is a BeanContextChild, or a proxy + * for such a child, the setBeanContext() method + * is invoked on the child. If this operation is vetoed by the + * child, via throwing a PropertyVetoException, + * then the current completion state of the add() + * operation is rolled back and a IllegalStateException + * is thrown. If the BeanContextChild is successfully + * added, then the context registers with its + * PropertyChangeListener and + * VetoableChangeListener for "beanContext" events. + *

    + *

    + * If the child implements java.beans.Visibility, + * then its ability to use a GUI is set based on that of + * this context. + *

    + *

    + * A BeanContextMembershipEvent is fired when the + * child is successfully added to the bean context. + *

    + *

    + * This method is synchronized over the global hierarchy lock. + *

    + * + * @param targetChild the child to add. + * @return false if the child has already been added. + * @throws IllegalArgumentException if the child is null. + * @throws IllegalStateException if the child vetos the setting + * of its context. + */ + public boolean add(Object targetChild) + { + synchronized (globalHierarchyLock) + { + if (targetChild == null) + throw new IllegalArgumentException(); + + BCSChild child; + synchronized (children) + { + if (children.containsKey(targetChild) + || ! validatePendingAdd(targetChild)) + return false; + child = createBCSChild(targetChild, beanContextChildPeer); + children.put(targetChild, child); + } + synchronized (targetChild) + { + BeanContextChild bcChild = null; + if (targetChild instanceof BeanContextChild) + bcChild = (BeanContextChild) targetChild; + if (targetChild instanceof BeanContextProxy) + bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy(); + if (bcChild != null) + try + { + bcChild.setBeanContext(this); + bcChild.addVetoableChangeListener("beanContext", this); + bcChild.addPropertyChangeListener("beanContext", this); + } + catch (PropertyVetoException e) + { + synchronized (children) + { + children.remove(targetChild); + } + throw new IllegalStateException("The child refused to " + + "associate itself with " + + "this context.", e); + } + if (targetChild instanceof Visibility) + { + Visibility visibleChild = (Visibility) targetChild; + if (okToUseGui) + visibleChild.okToUseGui(); + else + visibleChild.dontUseGui(); + } + childJustAddedHook(targetChild, child); + } + fireChildrenAdded(new BeanContextMembershipEvent(this, + new Object[]{ targetChild })); + return true; + } + } + + public boolean addAll (Collection c) + { + // Intentionally throws an exception. + throw new UnsupportedOperationException(); + } + + public void addBeanContextMembershipListener + (BeanContextMembershipListener listener) + { + synchronized (bcmListeners) + { + if (! bcmListeners.contains(listener)) + bcmListeners.add(listener); + } + } + + /** + * Returns true if this bean needs a GUI + * but is being prevented from using one. + * + * @return true if needsGui() + * is true but the bean has been + * told not to use it. + */ + public boolean avoidingGui() + { + return needsGui() && (!okToUseGui); + } + + protected Iterator bcsChildren () + { + synchronized (children) + { + return new BCSIterator(children.values().iterator()); + } + } + + /** + * Subclasses may use this method to perform their own deserialization + * after the default deserialization process has taken place, but + * prior to the deserialization of the children. It should not + * be used to replace the implementation of readObject + * in the subclass. + * + * @param ois the input stream. + * @throws ClassNotFoundException if the class of an object being deserialized + * could not be found. + * @throws IOException if an I/O error occurs. + */ + protected void bcsPreDeserializationHook (ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + /* Purposefully left empty */ + } + + /** + * Subclasses may use this method to perform their own serialization + * after the default serialization process has taken place, but + * prior to the serialization of the children. It should not + * be used to replace the implementation of writeObject + * in the subclass. + * + * @param oos the output stream. + * @throws IOException if an I/O error occurs. + */ + protected void bcsPreSerializationHook (ObjectOutputStream oos) + throws IOException + { + /* Purposefully left empty */ + } + + /** + * Called when a child is deserialized. + * + * @param child the deserialized child. + * @param bcsc the deserialized context wrapper for the child. + */ + protected void childDeserializedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + // Do nothing in the base class. + } + + protected void childJustAddedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + // Do nothing in the base class. + } + + protected void childJustRemovedHook (Object child, BeanContextSupport.BCSChild bcsc) + { + // Do nothing in the base class. + } + + protected static final boolean classEquals (Class first, Class second) + { + // Lame function! + return (first == second || first.getName().equals(second.getName())); + } + + public void clear () + { + // This is the right thing to do. + // The JDK docs are really bad here. + throw new UnsupportedOperationException(); + } + + public boolean contains (Object o) + { + synchronized (children) + { + return children.containsKey(o); + } + } + + public boolean containsAll (Collection c) + { + synchronized (children) + { + Iterator it = c.iterator(); + while (it.hasNext()) + if (! children.containsKey(it.next())) + return false; + } + return true; + } + + public boolean containsKey (Object o) + { + synchronized (children) + { + return children.containsKey(o); + } + } + + protected final Object[] copyChildren () + { + synchronized (children) + { + return children.keySet().toArray(); + } + } + + protected BeanContextSupport.BCSChild createBCSChild (Object targetChild, Object peer) + { + return new BCSChild(targetChild, peer); + } + + /** + * Deserializes objects (written by {@link #serialize(ObjectOutputStream, + * Collection)}) and adds them to the specified collection. + * + * @param ois the input stream (null not permitted). + * @param coll the collection to add the objects to (null not + * permitted). + * + * @throws ClassNotFoundException + * @throws IOException + * + * @see #serialize(ObjectOutputStream, Collection) + */ + protected final void deserialize (ObjectInputStream ois, Collection coll) + throws ClassNotFoundException, IOException + { + int itemCount = ois.readInt(); + for (int i = 0; i < itemCount; i++) + coll.add(ois.readObject()); + } + + /** + * Informs this bean that is should not make + * use of the GUI. + */ + public void dontUseGui() + { + okToUseGui = false; + } + + protected final void fireChildrenAdded (BeanContextMembershipEvent bcme) + { + synchronized (bcmListeners) + { + Iterator it = bcmListeners.iterator(); + while (it.hasNext()) + { + BeanContextMembershipListener l + = (BeanContextMembershipListener) it.next(); + l.childrenAdded(bcme); + } + } + } + + protected final void fireChildrenRemoved (BeanContextMembershipEvent bcme) + { + synchronized (bcmListeners) + { + Iterator it = bcmListeners.iterator(); + while (it.hasNext()) + { + BeanContextMembershipListener l + = (BeanContextMembershipListener) it.next(); + l.childrenRemoved(bcme); + } + } + } + + /** + * Returns the bean context peer. + * + * @return The bean context peer. + * + * @see BeanContextChildSupport#beanContextChildPeer + */ + public BeanContext getBeanContextPeer() + { + return (BeanContext) beanContextChildPeer; + } + + /** + * Returns the {@link BeanContextChild} implementation for the given child. + * + * @param child the child (null permitted). + * + * @return The bean context child. + * + * @throws IllegalArgumentException if child implements both + * the {@link BeanContextChild} and {@link BeanContextProxy} interfaces. + */ + protected static final BeanContextChild getChildBeanContextChild(Object child) + { + if (child == null) + return null; + if (child instanceof BeanContextChild && child instanceof BeanContextProxy) + throw new IllegalArgumentException("Child cannot implement " + + "BeanContextChild and BeanContextProxy simultaneously."); + if (child instanceof BeanContextChild) + return (BeanContextChild) child; + if (child instanceof BeanContextProxy) + return ((BeanContextProxy) child).getBeanContextProxy(); + return null; + } + + /** + * Returns child as an instance of + * {@link BeanContextMembershipListener}, or null if + * child does not implement that interface. + * + * @param child the child (null permitted). + * + * @return The child cast to {@link BeanContextMembershipListener}. + */ + protected static final BeanContextMembershipListener + getChildBeanContextMembershipListener(Object child) + { + if (child instanceof BeanContextMembershipListener) + return (BeanContextMembershipListener) child; + else + return null; + } + + /** + * Returns child as an instance of + * {@link PropertyChangeListener}, or null if child + * does not implement that interface. + * + * @param child the child (null permitted). + * + * @return The child cast to {@link PropertyChangeListener}. + */ + protected static final PropertyChangeListener getChildPropertyChangeListener( + Object child) + { + if (child instanceof PropertyChangeListener) + return (PropertyChangeListener) child; + else + return null; + } + + /** + * Returns child as an instance of {@link Serializable}, or + * null if child does not implement that + * interface. + * + * @param child the child (null permitted). + * + * @return The child cast to {@link Serializable}. + */ + protected static final Serializable getChildSerializable(Object child) + { + if (child instanceof Serializable) + return (Serializable) child; + else + return null; + } + + /** + * Returns child as an instance of + * {@link VetoableChangeListener}, or null if child + * does not implement that interface. + * + * @param child the child (null permitted). + * + * @return The child cast to {@link VetoableChangeListener}. + */ + protected static final VetoableChangeListener getChildVetoableChangeListener( + Object child) + { + if (child instanceof VetoableChangeListener) + return (VetoableChangeListener) child; + else + return null; + } + + /** + * Returns child as an instance of {@link Visibility}, or + * null if child does not implement that interface. + * + * @param child the child (null permitted). + * + * @return The child cast to {@link Visibility}. + */ + protected static final Visibility getChildVisibility(Object child) + { + if (child instanceof Visibility) + return (Visibility) child; + else + return null; + } + + public Locale getLocale () + { + return locale; + } + + public URL getResource (String name, BeanContextChild bcc) + { + if (! contains(bcc)) + throw new IllegalArgumentException("argument not a child"); + ClassLoader loader = bcc.getClass().getClassLoader(); + return (loader == null ? ClassLoader.getSystemResource(name) + : loader.getResource(name)); + } + + public InputStream getResourceAsStream (String name, BeanContextChild bcc) + { + if (! contains(bcc)) + throw new IllegalArgumentException("argument not a child"); + ClassLoader loader = bcc.getClass().getClassLoader(); + return (loader == null ? ClassLoader.getSystemResourceAsStream(name) + : loader.getResourceAsStream(name)); + } + + protected void initialize () + { + bcmListeners = new ArrayList(); + children = new HashMap(); + } + + /** + * This is a convenience method for instantiating a bean inside this + * context. It delegates to the appropriate method in + * java.beans.Beans using the context's classloader. + * + * @param beanName the name of the class of bean to instantiate. + * @throws IOException if an I/O error occurs in loading the class. + * @throws ClassNotFoundException if the class, beanName, + * can not be found. + */ + public Object instantiateChild (String beanName) + throws IOException, ClassNotFoundException + { + return Beans.instantiate(getClass().getClassLoader(), beanName, this); + } + + /** + * Returns true if the BeanContext is in + * design time mode, and false if it is in runtime mode. + * + * @return A boolean. + * + * @see #setDesignTime(boolean) + */ + public boolean isDesignTime() + { + return designTime; + } + + /** + * Returns true if this bean context has no children. + * + * @return true if there are no children. + */ + public boolean isEmpty () + { + synchronized (children) + { + return children.isEmpty(); + } + } + + /** + * Returns true if the bean context is in the process + * of being serialized. + * + * @return true if the context is being serialized. + */ + public boolean isSerializing() + { + return serializing; + } + + public Iterator iterator () + { + synchronized (children) + { + return children.keySet().iterator(); + } + } + + /** + * Returns false as this bean does not a + * GUI for its operation. + * + * @return false + */ + public boolean needsGui() + { + return false; + } + + /** + * Informs this bean that it is okay to make use of + * the GUI. + */ + public void okToUseGui () + { + okToUseGui = true; + } + + /** + * Subclasses may use this method to catch property changes + * arising from the children of this context. At present, + * we just listen for the beans being assigned to a different + * context and remove them from here if such an event occurs. + * + * @param pce the property change event. + */ + public void propertyChange (PropertyChangeEvent pce) + { + if (pce.getNewValue() != this) + remove(pce.getSource(), false); + } + + /** + * Deserializes the children using the + * {@link #deserialize(ObjectInputStream, Collection} method + * and then calls {@link childDeserializedHook(Object, BCSChild)} + * for each child deserialized. + * + * @param ois the input stream. + * @throws IOException if an I/O error occurs. + */ + public final void readChildren (ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + List temp = new ArrayList(); + deserialize(ois, temp); + Iterator i = temp.iterator(); + synchronized (globalHierarchyLock) + { + synchronized (children) + { + while (i.hasNext()) + { + BCSChild bcs = (BCSChild) i.next(); + childDeserializedHook(bcs.getTargetChild(), bcs); + children.put(bcs.getTargetChild(), bcs); + } + } + } + } + + /** + * Remove the specified child from the context. This is + * the same as calling remove(Object,boolean) + * with a request for the setBeanContext() method + * of the child to be called (i.e. the second argument is true). + * + * @param targetChild the child to remove. + */ + public boolean remove (Object targetChild) + { + return remove(targetChild, true); + } + + /** + *

    + * Removes a child from the bean context. A child can be a simple + * Object, a BeanContextChild + * or another BeanContext. If the given child is not + * a child of this context, this method returns false. + *

    + *

    + * If the child is a BeanContextChild, or a proxy + * for such a child, the setBeanContext() method + * is invoked on the child (if specified). If this operation is vetoed + * by the child, via throwing a PropertyVetoException, + * then the current completion state of the remove() + * operation is rolled back and a IllegalStateException + * is thrown. If the BeanContextChild is successfully + * removed, then the context deregisters with its + * PropertyChangeListener and + * VetoableChangeListener for "beanContext" events. + *

    + *

    + * A BeanContextMembershipEvent is fired when the + * child is successfully removed from the bean context. + *

    + *

    + * This method is synchronized over the global hierarchy lock. + *

    + * + * @param targetChild the child to remove. + * @param callChildSetBC true if the setBeanContext() + * method of the child should be called. + * @return false if the child doesn't exist. + * @throws IllegalArgumentException if the child is null. + * @throws IllegalStateException if the child vetos the setting + * of its context. + */ + protected boolean remove (Object targetChild, boolean callChildSetBC) + { + synchronized (globalHierarchyLock) + { + if (targetChild == null) + throw new IllegalArgumentException(); + + BCSChild child; + synchronized (children) + { + if (!children.containsKey(targetChild) + || !validatePendingRemove(targetChild)) + return false; + child = (BCSChild) children.remove(targetChild); + } + synchronized (targetChild) + { + BeanContextChild bcChild = null; + if (targetChild instanceof BeanContextChild) + bcChild = (BeanContextChild) targetChild; + if (targetChild instanceof BeanContextProxy) + bcChild = ((BeanContextProxy) targetChild).getBeanContextProxy(); + if (bcChild != null) + try + { + if (callChildSetBC) + bcChild.setBeanContext(null); + bcChild.removeVetoableChangeListener("beanContext", this); + bcChild.removePropertyChangeListener("beanContext", this); + } + catch (PropertyVetoException e) + { + synchronized (children) + { + children.put(targetChild, child); + } + throw new IllegalStateException("The child refused to " + + "disassociate itself with " + + "this context.", e); + } + childJustRemovedHook(targetChild, child); + } + fireChildrenRemoved(new BeanContextMembershipEvent(this, + new Object[]{ targetChild })); + return true; + } + } + + public boolean removeAll (Collection c) + { + // Intentionally throws an exception. + throw new UnsupportedOperationException(); + } + + public void removeBeanContextMembershipListener (BeanContextMembershipListener bcml) + { + synchronized (bcmListeners) + { + bcmListeners.remove(bcml); + } + } + + public boolean retainAll (Collection c) + { + // Intentionally throws an exception. + throw new UnsupportedOperationException(); + } + + /** + * Writes the items in the collection to the specified output stream. Items + * in the collection that are not instances of {@link Serializable} + * (this includes null) are simply ignored. + * + * @param oos the output stream (null not permitted). + * @param coll the collection (null not permitted). + * + * @throws IOException + * + * @see #deserialize(ObjectInputStream, Collection) + */ + protected final void serialize(ObjectOutputStream oos, Collection coll) + throws IOException + { + Object[] items = coll.toArray(); + int itemCount = 0; + for (int i = 0; i < items.length; i++) + { + if (items[i] instanceof Serializable) + itemCount++; + } + oos.writeInt(itemCount); + for (int i = 0; i < items.length; i++) + { + if (items[i] instanceof Serializable) + oos.writeObject(items[i]); + } + } + + /** + * Sets the flag that indicates whether or not the + * BeanContext is in design mode. If the flag changes + * value, a {@link PropertyChangeEvent} (with the property name 'designMode') + * is sent to registered listeners. Note that the property name used here + * does NOT match the specification in the {@link DesignMode} interface, we + * match the reference implementation instead - see bug parade entry 4295174. + * + * @param dtime the new value for the flag. + * + * @see #isDesignTime() + */ + public void setDesignTime(boolean dtime) + { + boolean save = designTime; + designTime = dtime; + // note that we use the same property name as Sun's implementation, + // even though this is a known bug: see bug parade entry 4295174 + firePropertyChange("designMode", Boolean.valueOf(save), + Boolean.valueOf(dtime)); + } + + public void setLocale (Locale newLocale) + throws PropertyVetoException + { + if (newLocale == null || locale == newLocale) + return; + fireVetoableChange("locale", locale, newLocale); + Locale oldLocale = locale; + locale = newLocale; + firePropertyChange("locale", oldLocale, newLocale); + } + + public int size () + { + synchronized (children) + { + return children.size(); + } + } + + /** + * Returns an array containing the children of this BeanContext. + * + * @return An array containing the children. + */ + public Object[] toArray() + { + synchronized (children) + { + return children.keySet().toArray(); + } + } + + /** + * Populates, then returns, the supplied array with the children of this + * BeanContext. If the array is too short to hold the + * children, a new array is allocated and returned. If the array is too + * long, it is padded with null items at the end. + * + * @param array an array to populate (null not permitted). + */ + public Object[] toArray(Object[] array) + { + synchronized (children) + { + return children.keySet().toArray(array); + } + } + + protected boolean validatePendingAdd (Object targetChild) + { + return true; + } + + protected boolean validatePendingRemove (Object targetChild) + { + return true; + } + + /** + * Subclasses may use this method to veto changes arising + * from the children of this context. + * + * @param pce the vetoable property change event fired. + */ + public void vetoableChange (PropertyChangeEvent pce) + throws PropertyVetoException + { + /* Purposefully left empty */ + } + + /** + * Serializes the children using the + * {@link #serialize(ObjectOutputStream, Collection} method. + * + * @param oos the output stream. + * @throws IOException if an I/O error occurs. + */ + public final void writeChildren (ObjectOutputStream oos) + throws IOException + { + synchronized (children) + { + serialize(oos, children.values()); + } + } + +} diff --git a/libjava/classpath/java/beans/beancontext/package.html b/libjava/classpath/java/beans/beancontext/package.html new file mode 100644 index 000000000..55e3bd723 --- /dev/null +++ b/libjava/classpath/java/beans/beancontext/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.beans.beancontext + + +

    Containers and execution environments for beans.

    + + + diff --git a/libjava/classpath/java/beans/package.html b/libjava/classpath/java/beans/package.html new file mode 100644 index 000000000..5ca800037 --- /dev/null +++ b/libjava/classpath/java/beans/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.beans + + +

    Listeners and descriptors for managing beans.

    + + + diff --git a/libjava/classpath/java/io/BufferedInputStream.java b/libjava/classpath/java/io/BufferedInputStream.java new file mode 100644 index 000000000..7013b0948 --- /dev/null +++ b/libjava/classpath/java/io/BufferedInputStream.java @@ -0,0 +1,379 @@ +/* BufferedInputStream.java -- An input stream that implements buffering + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of FilterInputStream buffers input from an + * underlying implementation to provide a possibly more efficient read + * mechanism. It maintains the buffer and buffer state in instance + * variables that are available to subclasses. The default buffer size + * of 2048 bytes can be overridden by the creator of the stream. + *

    + * This class also implements mark/reset functionality. It is capable + * of remembering any number of input bytes, to the limits of + * system memory or the size of Integer.MAX_VALUE + *

    + * Please note that this class does not properly handle character + * encodings. Consider using the BufferedReader class which + * does. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @author Jeroen Frijters (jeroen@frijters.net) + */ +public class BufferedInputStream extends FilterInputStream +{ + + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 2048; + + /** + * The buffer used for storing data from the underlying stream. + */ + protected byte[] buf; + + /** + * The number of valid bytes currently in the buffer. It is also the index + * of the buffer position one byte past the end of the valid data. + */ + protected int count; + + /** + * The index of the next character that will by read from the buffer. + * When pos == count, the buffer is empty. + */ + protected int pos; + + /** + * The value of pos when the mark() method was + * called. + * This is set to -1 if there is no mark set. + */ + protected int markpos = -1; + + /** + * This is the maximum number of bytes than can be read after a + * call to mark() before the mark can be discarded. + * After this may bytes are read, the reset() method + * may not be called successfully. + */ + protected int marklimit; + + /** + * This is the initial buffer size. When the buffer is grown because + * of marking requirements, it will be grown by bufferSize increments. + * The underlying stream will be read in chunks of bufferSize. + */ + private final int bufferSize; + + /** + * This method initializes a new BufferedInputStream that will + * read from the specified subordinate stream with a default buffer size + * of 2048 bytes + * + * @param in The subordinate stream to read from + */ + public BufferedInputStream(InputStream in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new BufferedInputStream that will + * read from the specified subordinate stream with a buffer size that + * is specified by the caller. + * + * @param in The subordinate stream to read from + * @param size The buffer size to use + * + * @exception IllegalArgumentException when size is smaller then 1 + */ + public BufferedInputStream(InputStream in, int size) + { + super(in); + if (size <= 0) + throw new IllegalArgumentException(); + buf = new byte[size]; + // initialize pos & count to bufferSize, to prevent refill from + // allocating a new buffer (if the caller starts out by calling mark()). + pos = count = bufferSize = size; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + *

    + * The number of available bytes will be the number of read ahead bytes + * stored in the internal buffer plus the number of available bytes in + * the underlying stream. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public synchronized int available() throws IOException + { + return count - pos + super.available(); + } + + /** + * This method closes the underlying input stream and frees any + * resources associated with it. Sets buf to null. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + // Free up the array memory. + buf = null; + pos = count = 0; + markpos = -1; + super.close(); + } + + /** + * This method marks a position in the input to which the stream can be + * "reset" by calling the reset() method. The parameter + * readlimit is the number of bytes that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if mark() is called with a read limit of 10, then + * when 11 bytes of data are read from the stream before the + * reset() method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + *

    + * Note that the number of bytes that can be remembered by this method + * can be greater than the size of the internal read buffer. It is also + * not dependent on the subordinate stream supporting mark/reset + * functionality. + * + * @param readlimit The number of bytes that can be read before the mark + * becomes invalid + */ + public synchronized void mark(int readlimit) + { + marklimit = readlimit; + markpos = pos; + } + + /** + * This method returns true to indicate that this class + * supports mark/reset functionality. + * + * @return true to indicate that mark/reset functionality is + * supported + * + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + *

    + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public synchronized int read() throws IOException + { + if (pos >= count && !refill()) + return -1; // EOF + + return buf[pos++] & 0xFF; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index off + * into the buffer and attempts to read len bytes. This method + * can return before reading the number of bytes requested, but it will try + * to read the requested number of bytes by repeatedly calling the underlying + * stream as long as available() for this stream continues to return a + * non-zero value (or until the requested number of bytes have been read). + * The actual number of bytes read is returned as an int. A -1 is returned + * to indicate the end of the stream. + *

    + * This method will block until some data can be read. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + * @exception IndexOutOfBoundsException when off or + * len are negative, or when off + len + * is larger then the size of b, + */ + public synchronized int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || b.length - off < len) + throw new IndexOutOfBoundsException(); + + if (len == 0) + return 0; + + if (pos >= count && !refill()) + return -1; // No bytes were read before EOF. + + int totalBytesRead = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, totalBytesRead); + pos += totalBytesRead; + off += totalBytesRead; + len -= totalBytesRead; + + while (len > 0 && super.available() > 0 && refill()) + { + int remain = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, remain); + pos += remain; + off += remain; + len -= remain; + totalBytesRead += remain; + } + + return totalBytesRead; + } + + /** + * This method resets a stream to the point where the mark() + * method was called. Any bytes that were read after the mark point was + * set will be re-read during subsequent reads. + *

    + * This method will throw an IOException if the number of bytes read from + * the stream since the call to mark() exceeds the mark limit + * passed when establishing the mark. + * + * @exception IOException If mark() was never called or more + * then marklimit bytes were read since the last + * call to mark() + */ + public synchronized void reset() throws IOException + { + if (markpos == -1) + throw new IOException(buf == null ? "Stream closed." : "Invalid mark."); + + pos = markpos; + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public synchronized long skip(long n) throws IOException + { + if (buf == null) + throw new IOException("Stream closed."); + + final long origN = n; + + while (n > 0L) + { + if (pos >= count && !refill()) + break; + + int numread = (int) Math.min((long) (count - pos), n); + pos += numread; + n -= numread; + } + + return origN - n; + } + + /** + * Called to refill the buffer (when count is equal to pos). + * + * @return true when at least one additional byte was read + * into buf, false otherwise (at EOF). + */ + private boolean refill() throws IOException + { + if (buf == null) + throw new IOException("Stream closed."); + + if (markpos == -1 || count - markpos >= marklimit) + { + markpos = -1; + pos = count = 0; + } + else + { + byte[] newbuf = buf; + if (markpos < bufferSize) + { + newbuf = new byte[count - markpos + bufferSize]; + } + System.arraycopy(buf, markpos, newbuf, 0, count - markpos); + buf = newbuf; + count -= markpos; + pos -= markpos; + markpos = 0; + } + + int numread = super.read(buf, count, bufferSize); + + if (numread <= 0) // EOF + return false; + + count += numread; + return true; + } +} diff --git a/libjava/classpath/java/io/BufferedOutputStream.java b/libjava/classpath/java/io/BufferedOutputStream.java new file mode 100644 index 000000000..e6027cad6 --- /dev/null +++ b/libjava/classpath/java/io/BufferedOutputStream.java @@ -0,0 +1,191 @@ +/* BufferedOutputStream.java -- Buffer output into large blocks before writing + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class accumulates bytes written in a buffer instead of immediately + * writing the data to the underlying output sink. The bytes are instead + * as one large block when the buffer is filled, or when the stream is + * closed or explicitly flushed. This mode operation can provide a more + * efficient mechanism for writing versus doing numerous small unbuffered + * writes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class BufferedOutputStream extends FilterOutputStream +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 512; + + /** + * This is the internal byte array used for buffering output before + * writing it. + */ + protected byte[] buf; + + /** + * This is the number of bytes that are currently in the buffer and + * are waiting to be written to the underlying stream. It always points to + * the index into the buffer where the next byte of data will be stored + */ + protected int count; + + /** + * This method initializes a new BufferedOutputStream instance + * that will write to the specified subordinate OutputStream + * and which will use a default buffer size of 512 bytes. + * + * @param out The underlying OutputStream to write data to + */ + public BufferedOutputStream(OutputStream out) + { + this(out, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new BufferedOutputStream instance + * that will write to the specified subordinate OutputStream + * and which will use the specified buffer size + * + * @param out The underlying OutputStream to write data to + * @param size The size of the internal buffer + */ + public BufferedOutputStream(OutputStream out, int size) + { + super(out); + + buf = new byte[size]; + } + + /** + * This method causes any currently buffered bytes to be immediately + * written to the underlying output stream. + * + * @exception IOException If an error occurs + */ + public synchronized void flush() throws IOException + { + if (count == 0) + return; + + out.write(buf, 0, count); + count = 0; + out.flush(); + } + + /** + * This method flushes any remaining buffered bytes then closes the + * underlying output stream. Any further attempts to write to this stream + * may throw an exception + * + public synchronized void close() throws IOException + { + flush(); + out.close(); + } + */ + + /** + * This method runs when the object is garbage collected. It is + * responsible for ensuring that all buffered bytes are written and + * for closing the underlying stream. + * + * @exception IOException If an error occurs (ignored by the Java runtime) + * + protected void finalize() throws IOException + { + close(); + } + */ + + /** + * This method writes a single byte of data. This will be written to the + * buffer instead of the underlying data source. However, if the buffer + * is filled as a result of this write request, it will be flushed to the + * underlying output stream. + * + * @param b The byte of data to be written, passed as an int + * + * @exception IOException If an error occurs + */ + public synchronized void write(int b) throws IOException + { + if (count == buf.length) + flush(); + + buf[count] = (byte)(b & 0xFF); + ++count; + } + + /** + * This method writes len bytes from the byte array + * buf starting at position offset in the buffer. + * These bytes will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param buf The array of bytes to write. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs + */ + public synchronized void write(byte[] buf, int offset, int len) + throws IOException + { + // Buffer can hold everything. Note that the case where LEN < 0 + // is automatically handled by the downstream write. + if (len < (this.buf.length - count)) + { + System.arraycopy(buf, offset, this.buf, count, len); + count += len; + } + else + { + // The write was too big. So flush the buffer and write the new + // bytes directly to the underlying stream, per the JDK 1.2 + // docs. + flush(); + out.write (buf, offset, len); + } + } + +} // class BufferedOutputStream diff --git a/libjava/classpath/java/io/BufferedReader.java b/libjava/classpath/java/io/BufferedReader.java new file mode 100644 index 000000000..868fa3a94 --- /dev/null +++ b/libjava/classpath/java/io/BufferedReader.java @@ -0,0 +1,575 @@ +/* BufferedReader.java + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.lang.CPStringBuilder; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of FilterReader buffers input from an + * underlying implementation to provide a possibly more efficient read + * mechanism. It maintains the buffer and buffer state in instance + * variables that are available to subclasses. The default buffer size + * of 8192 chars can be overridden by the creator of the stream. + *

    + * This class also implements mark/reset functionality. It is capable + * of remembering any number of input chars, to the limits of + * system memory or the size of Integer.MAX_VALUE + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class BufferedReader extends Reader +{ + Reader in; + char[] buffer; + /* Index of current read position. Must be >= 0 and <= limit. */ + /* There is a special case where pos may be equal to limit+1; this + * is used as an indicator that a readLine was done with a '\r' was + * the very last char in the buffer. Since we don't want to read-ahead + * and potentially block, we set pos this way to indicate the situation + * and deal with it later. Doing it this way rather than having a + * separate boolean field to indicate the condition has the advantage + * that it is self-clearing on things like mark/reset. + */ + int pos; + /* Limit of valid data in buffer. Must be >= pos and <= buffer.length. */ + /* This can be < pos in the one special case described above. */ + int limit; + + /* The value -1 means there is no mark, or the mark has been invalidated. + Otherwise, markPos is the index in the buffer of the marked position. + Must be >= 0 and <= pos. + Note we do not explicitly store the read-limit. + The implicit read-limit is (buffer.length - markPos), which is + guaranteed to be >= the read-limit requested in the call to mark. */ + int markPos = -1; + + // The JCL book specifies the default buffer size as 8K characters. + // This is package-private because it is used by LineNumberReader. + static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * Create a new BufferedReader that will read from the + * specified subordinate stream with a default buffer size of 8192 chars. + * + * @param in The subordinate stream to read from + */ + public BufferedReader(Reader in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * Create a new BufferedReader that will read from the + * specified subordinate stream with a buffer size that is specified by the + * caller. + * + * @param in The subordinate stream to read from + * @param size The buffer size to use + * + * @exception IllegalArgumentException if size <= 0 + */ + public BufferedReader(Reader in, int size) + { + super(in.lock); + if (size <= 0) + throw new IllegalArgumentException("Illegal buffer size: " + size); + this.in = in; + buffer = new char[size]; + } + + /** + * This method closes the underlying stream and frees any associated + * resources. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + if (in != null) + in.close(); + in = null; + buffer = null; + } + } + + /** + * Returns true to indicate that this class supports mark/reset + * functionality. + * + * @return true + */ + public boolean markSupported() + { + return true; + } + + /** + * Mark a position in the input to which the stream can be + * "reset" by calling the reset() method. The parameter + * readLimit is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if mark() is called with a read limit of 10, then + * when 11 chars of data are read from the stream before the + * reset() method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + *

    + * Note that the number of chars that can be remembered by this method + * can be greater than the size of the internal read buffer. It is also + * not dependent on the subordinate stream supporting mark/reset + * functionality. + * + * @param readLimit The number of chars that can be read before the mark + * becomes invalid + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException if readLimit is negative. + */ + public void mark(int readLimit) throws IOException + { + if (readLimit < 0) + throw new IllegalArgumentException("Read-ahead limit is negative"); + + synchronized (lock) + { + checkStatus(); + // In this method we need to be aware of the special case where + // pos + 1 == limit. This indicates that a '\r' was the last char + // in the buffer during a readLine. We'll want to maintain that + // condition after we shift things around and if a larger buffer is + // needed to track readLimit, we'll have to make it one element + // larger to ensure we don't invalidate the mark too early, if the + // char following the '\r' is NOT a '\n'. This is ok because, per + // the spec, we are not required to invalidate when passing readLimit. + // + // Note that if 'pos > limit', then doing 'limit -= pos' will cause + // limit to be negative. This is the only way limit will be < 0. + + if (pos + readLimit > limit) + { + char[] old_buffer = buffer; + int extraBuffSpace = 0; + if (pos > limit) + extraBuffSpace = 1; + if (readLimit + extraBuffSpace > limit) + buffer = new char[readLimit + extraBuffSpace]; + limit -= pos; + if (limit >= 0) + { + System.arraycopy(old_buffer, pos, buffer, 0, limit); + pos = 0; + } + } + + if (limit < 0) + { + // Maintain the relationship of 'pos > limit'. + pos = 1; + limit = markPos = 0; + } + else + markPos = pos; + // Now pos + readLimit <= buffer.length. thus if we need to read + // beyond buffer.length, then we are allowed to invalidate markPos. + } + } + + /** + * Reset the stream to the point where the mark() method + * was called. Any chars that were read after the mark point was set will + * be re-read during subsequent reads. + *

    + * This method will throw an IOException if the number of chars read from + * the stream since the call to mark() exceeds the mark limit + * passed when establishing the mark. + * + * @exception IOException If an error occurs; + */ + public void reset() throws IOException + { + synchronized (lock) + { + checkStatus(); + if (markPos < 0) + throw new IOException("mark never set or invalidated"); + + // Need to handle the extremely unlikely case where a readLine was + // done with a '\r' as the last char in the buffer; which was then + // immediately followed by a mark and a reset with NO intervening + // read of any sort. In that case, setting pos to markPos would + // lose that info and a subsequent read would thus not skip a '\n' + // (if one exists). The value of limit in this rare case is zero. + // We can assume that if limit is zero for other reasons, then + // pos is already set to zero and doesn't need to be readjusted. + if (limit > 0) + pos = markPos; + } + } + + /** + * This method determines whether or not a stream is ready to be read. If + * this method returns false then this stream could (but is + * not guaranteed to) block on the next read attempt. + * + * @return true if this stream is ready to be read, + * false otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + synchronized (lock) + { + checkStatus(); + return pos < limit || in.ready(); + } + } + + /** + * This method read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index + * offset into + * the buffer and attempts to read len chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicate the + * end of the stream. + *

    + * This method will block until some data can be read. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param count The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + * @exception IndexOutOfBoundsException If offset and count are not + * valid regarding buf. + */ + public int read(char[] buf, int offset, int count) throws IOException + { + if (offset < 0 || offset + count > buf.length || count < 0) + throw new IndexOutOfBoundsException(); + + synchronized (lock) + { + checkStatus(); + // Once again, we need to handle the special case of a readLine + // that has a '\r' at the end of the buffer. In this case, we'll + // need to skip a '\n' if it is the next char to be read. + // This special case is indicated by 'pos > limit'. + boolean retAtEndOfBuffer = false; + + int avail = limit - pos; + if (count > avail) + { + if (avail > 0) + count = avail; + else // pos >= limit + { + if (limit == buffer.length) + markPos = -1; // read too far - invalidate the mark. + if (pos > limit) + { + // Set a boolean and make pos == limit to simplify things. + retAtEndOfBuffer = true; + --pos; + } + if (markPos < 0) + { + // Optimization: can read directly into buf. + if (count >= buffer.length && !retAtEndOfBuffer) + return in.read(buf, offset, count); + pos = limit = 0; + } + avail = in.read(buffer, limit, buffer.length - limit); + if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n') + { + --avail; + limit++; + } + if (avail < count) + { + if (avail <= 0) + return avail; + count = avail; + } + limit += avail; + } + } + System.arraycopy(buffer, pos, buf, offset, count); + pos += count; + return count; + } + } + + /* Read more data into the buffer. Update pos and limit appropriately. + Assumes pos==limit initially. May invalidate the mark if read too much. + Return number of chars read (never 0), or -1 on eof. */ + private int fill() throws IOException + { + checkStatus(); + // Handle the special case of a readLine that has a '\r' at the end of + // the buffer. In this case, we'll need to skip a '\n' if it is the + // next char to be read. This special case is indicated by 'pos > limit'. + boolean retAtEndOfBuffer = false; + if (pos > limit) + { + retAtEndOfBuffer = true; + --pos; + } + + if (markPos >= 0 && limit == buffer.length) + markPos = -1; + if (markPos < 0) + pos = limit = 0; + int count = in.read(buffer, limit, buffer.length - limit); + if (count > 0) + limit += count; + + if (retAtEndOfBuffer && buffer[pos] == '\n') + { + --count; + // If the mark was set to the location of the \n, then we + // must change it to fully pretend that the \n does not + // exist. + if (markPos == pos) + ++markPos; + ++pos; + } + + return count; + } + + public int read() throws IOException + { + synchronized (lock) + { + checkStatus(); + if (pos >= limit && fill () <= 0) + return -1; + return buffer[pos++]; + } + } + + /* Return the end of the line starting at this.pos and ending at limit. + * The index returns is *before* any line terminators, or limit + * if no line terminators were found. + */ + private int lineEnd(int limit) + { + int i = pos; + for (; i < limit; i++) + { + char ch = buffer[i]; + if (ch == '\n' || ch == '\r') + break; + } + return i; + } + + /** + * This method reads a single line of text from the input stream, returning + * it as a String. A line is terminated by "\n", a "\r", or + * an "\r\n" sequence. The system dependent line separator is not used. + * The line termination characters are not returned in the resulting + * String. + * + * @return The line of text read, or null if end of stream. + * + * @exception IOException If an error occurs + */ + public String readLine() throws IOException + { + checkStatus(); + // Handle the special case where a previous readLine (with no intervening + // reads/skips) had a '\r' at the end of the buffer. + // In this case, we'll need to skip a '\n' if it's the next char to be read. + // This special case is indicated by 'pos > limit'. + if (pos > limit) + { + int ch = read(); + if (ch < 0) + return null; + if (ch != '\n') + --pos; + } + int i = lineEnd(limit); + if (i < limit) + { + String str = String.valueOf(buffer, pos, i - pos); + pos = i + 1; + // If the last char in the buffer is a '\r', we must remember + // to check if the next char to be read after the buffer is refilled + // is a '\n'. If so, skip it. To indicate this condition, we set pos + // to be limit + 1, which normally is never possible. + if (buffer[i] == '\r') + if (pos == limit || buffer[pos] == '\n') + pos++; + return str; + } + CPStringBuilder sbuf = new CPStringBuilder(200); + sbuf.append(buffer, pos, i - pos); + pos = i; + // We only want to return null when no characters were read before + // EOF. So we must keep track of this separately. Otherwise we + // would treat an empty `sbuf' as an EOF condition, which is wrong + // when there is just a newline. + boolean eof = false; + for (;;) + { + // readLine should block. So we must not return until a -1 is reached. + if (pos >= limit) + { + // here count == 0 isn't sufficient to give a failure. + int count = fill(); + if (count < 0) + { + eof = true; + break; + } + continue; + } + int ch = buffer[pos++]; + if (ch == '\n' || ch == '\r') + { + // Check here if a '\r' was the last char in the buffer; if so, + // mark it as in the comment above to indicate future reads + // should skip a newline that is the next char read after + // refilling the buffer. + if (ch == '\r') + if (pos == limit || buffer[pos] == '\n') + pos++; + break; + } + i = lineEnd(limit); + sbuf.append(buffer, pos - 1, i - (pos - 1)); + pos = i; + } + return (sbuf.length() == 0 && eof) ? null : sbuf.toString(); + } + + /** + * This method skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + *

    + * This method first discards chars in the buffer, then calls the + * skip method on the underlying stream to skip the + * remaining chars. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs. + * @exception IllegalArgumentException If count is negative. + */ + public long skip(long count) throws IOException + { + synchronized (lock) + { + checkStatus(); + if (count < 0) + throw new IllegalArgumentException("skip value is negative"); + if (count == 0) + return 0; + // Yet again, we need to handle the special case of a readLine + // that has a '\r' at the end of the buffer. In this case, we need + // to ignore a '\n' if it is the next char to be read. + // This special case is indicated by 'pos > limit' (i.e. avail < 0). + // To simplify things, if we're dealing with the special case for + // readLine, just read the next char (since the fill method will + // skip the '\n' for us). By doing this, we'll have to back up pos. + // That's easier than trying to keep track of whether we've skipped + // one element or not. + if (pos > limit) + { + if (read() < 0) + return 0; + else + --pos; + } + + int avail = limit - pos; + + if (count < avail) + { + pos += count; + return count; + } + + pos = limit; + long todo = count - avail; + if (todo > buffer.length) + { + markPos = -1; + todo -= in.skip(todo); + } + else + { + while (todo > 0) + { + avail = fill(); + if (avail <= 0) + break; + if (avail > todo) + avail = (int) todo; + pos += avail; + todo -= avail; + } + } + return count - todo; + } + } + + private void checkStatus() throws IOException + { + if (in == null) + throw new IOException("Stream closed"); + } +} diff --git a/libjava/classpath/java/io/BufferedWriter.java b/libjava/classpath/java/io/BufferedWriter.java new file mode 100644 index 000000000..ae6751c43 --- /dev/null +++ b/libjava/classpath/java/io/BufferedWriter.java @@ -0,0 +1,262 @@ +/* BufferedWriter.java -- Buffer output into large blocks before writing + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class accumulates chars written in a buffer instead of immediately + * writing the data to the underlying output sink. The chars are instead + * as one large block when the buffer is filled, or when the stream is + * closed or explicitly flushed. This mode operation can provide a more + * efficient mechanism for writing versus doing numerous small unbuffered + * writes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 25, 1998 + */ +public class BufferedWriter extends Writer +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 8192; + + /** + * This is the underlying Writer to which this object + * sends its output. + */ + private Writer out; + + /** + * This is the internal char array used for buffering output before + * writing it. + */ + char[] buffer; + + /** + * This is the number of chars that are currently in the buffer and + * are waiting to be written to the underlying stream. It always points to + * the index into the buffer where the next char of data will be stored + */ + int count; + + /** + * This method initializes a new BufferedWriter instance + * that will write to the specified subordinate Writer + * and which will use a default buffer size of 8192 chars. + * + * @param out The underlying Writer to write data to + */ + public BufferedWriter (Writer out) + { + this (out, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new BufferedWriter instance + * that will write to the specified subordinate Writer + * and which will use the specified buffer size + * + * @param out The underlying Writer to write data to + * @param size The size of the internal buffer + */ + public BufferedWriter (Writer out, int size) + { + super(out.lock); + this.out = out; + this.buffer = new char[size]; + this.count = 0; + } + + /** + * This method flushes any remaining buffered chars then closes the + * underlying output stream. Any further attempts to write to this stream + * may throw an exception + * + * @exception IOException If an error occurs. + */ + public void close () throws IOException + { + synchronized (lock) + { + // It is safe to call localFlush even if the stream is already + // closed. + localFlush (); + out.close(); + buffer = null; + } + } + + /** + * This method causes any currently buffered chars to be immediately + * written to the underlying output stream. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + localFlush (); + out.flush(); + } + } + + /** + * This method writes out a system depedent line separator sequence. The + * actual value written is detemined from the

    line.separator + * system property. + * + * @exception IOException If an error occurs + */ + public void newLine () throws IOException + { + write (System.getProperty("line.separator")); + } + + /** + * This method writes a single char of data. This will be written to the + * buffer instead of the underlying data source. However, if the buffer + * is filled as a result of this write request, it will be flushed to the + * underlying output stream. + * + * @param oneChar The char of data to be written, passed as an int + * + * @exception IOException If an error occurs + */ + public void write (int oneChar) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + buffer[count++] = (char) oneChar; + if (count == buffer.length) + localFlush (); + } + } + + /** + * This method writes len chars from the char array + * buf starting at position offset in the buffer. + * These chars will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param buf The array of chars to write. + * @param offset The index into the char array to start writing from. + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (char[] buf, int offset, int len) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + + // Bypass buffering if there is too much incoming data. + if (count + len > buffer.length) + { + localFlush (); + out.write(buf, offset, len); + } + else + { + System.arraycopy(buf, offset, buffer, count, len); + count += len; + if (count == buffer.length) + localFlush (); + } + } + } + + /** + * This method writes len chars from the String + * str starting at position offset in the string. + * These chars will be written to the internal buffer. However, if this + * write operation fills the buffer, the buffer will be flushed to the + * underlying output stream. + * + * @param str The String to write. + * @param offset The index into the string to start writing from. + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (String str, int offset, int len) throws IOException + { + synchronized (lock) + { + if (buffer == null) + throw new IOException ("Stream closed"); + + if (count + len > buffer.length) + { + localFlush (); + out.write(str, offset, len); + } + else + { + str.getChars(offset, offset + len, buffer, count); + count += len; + if (count == buffer.length) + localFlush (); + } + } + } + + // This should only be called with the lock held. + private void localFlush () throws IOException + { + if (count > 0) + { + out.write(buffer, 0, count); + count = 0; + } + } +} diff --git a/libjava/classpath/java/io/ByteArrayInputStream.java b/libjava/classpath/java/io/ByteArrayInputStream.java new file mode 100644 index 000000000..f46ee3c0f --- /dev/null +++ b/libjava/classpath/java/io/ByteArrayInputStream.java @@ -0,0 +1,251 @@ +/* ByteArrayInputStream.java -- Read an array as a stream + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class permits an array of bytes to be read as an input stream. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class ByteArrayInputStream extends InputStream +{ + /** + * The array that contains the data supplied during read operations + */ + protected byte[] buf; + + /** + * The array index of the next byte to be read from the buffer + * buf + */ + protected int pos; + + /** + * The currently marked position in the stream. This defaults to 0, so a + * reset operation on the stream resets it to read from array index 0 in + * the buffer - even if the stream was initially created with an offset + * greater than 0 + */ + protected int mark; + + /** + * This indicates the maximum number of bytes that can be read from this + * stream. It is the array index of the position after the last valid + * byte in the buffer buf + */ + protected int count; + + /** + * Create a new ByteArrayInputStream that will read bytes from the passed + * in byte array. This stream will read from the beginning to the end + * of the array. It is identical to calling an overloaded constructor + * as ByteArrayInputStream(buf, 0, buf.length). + *

    + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * bytes supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The byte array buffer this stream will read from. + */ + public ByteArrayInputStream(byte[] buffer) + { + this(buffer, 0, buffer.length); + } + + /** + * Create a new ByteArrayInputStream that will read bytes from the + * passed in byte array. This stream will read from position + * offset in the array for a length of + * length bytes past offset. If the + * stream is reset to a position before offset then + * more than length bytes can be read from the stream. + * The length value should be viewed as the array index + * one greater than the last position in the buffer to read. + *

    + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * bytes supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The byte array buffer this stream will read from. + * @param offset The index into the buffer to start reading bytes from + * @param length The number of bytes to read from the buffer + */ + public ByteArrayInputStream(byte[] buffer, int offset, int length) + { + if (offset < 0 || length < 0 || offset > buffer.length) + throw new IllegalArgumentException(); + + buf = buffer; + + count = offset + length; + if (count > buf.length) + count = buf.length; + + pos = offset; + mark = pos; + } + + /** + * This method returns the number of bytes available to be read from this + * stream. The value returned will be equal to count - pos. + * + * @return The number of bytes that can be read from this stream + * before blocking, which is all of them + */ + public synchronized int available() + { + return count - pos; + } + + /** + * This method sets the mark position in this stream to the current + * position. Note that the readlimit parameter in this + * method does nothing as this stream is always capable of + * remembering all the bytes int it. + *

    + * Note that in this class the mark position is set by default to + * position 0 in the stream. This is in constrast to some other + * stream types where there is no default mark position. + * + * @param readLimit The number of bytes this stream must remember. + * This parameter is ignored. + */ + public synchronized void mark(int readLimit) + { + // readLimit is ignored per Java Class Lib. book, p.220. + mark = pos; + } + + /** + * This method overrides the markSupported method in + * InputStream in order to return true - + * indicating that this stream class supports mark/reset + * functionality. + * + * @return true to indicate that this class supports + * mark/reset. + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads one byte from the stream. The pos + * counter is advanced to the next byte to be read. The byte read is + * returned as an int in the range of 0-255. If the stream position + * is already at the end of the buffer, no byte is read and a -1 is + * returned in order to indicate the end of the stream. + * + * @return The byte read, or -1 if end of stream + */ + public synchronized int read() + { + if (pos < count) + return ((int) buf[pos++]) & 0xFF; + return -1; + } + + /** + * This method reads bytes from the stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * offset into the buffer and attempts to read + * len bytes. This method can return before reading + * the number of bytes requested if the end of the stream is + * encountered first. The actual number of bytes read is returned. + * If no bytes can be read because the stream is already at the end + * of stream position, a -1 is returned. + *

    + * This method does not block. + * + * @param buffer The array into which the bytes read should be stored. + * @param offset The offset into the array to start storing bytes + * @param length The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + */ + public synchronized int read(byte[] buffer, int offset, int length) + { + if (pos >= count) + return -1; + + int numBytes = Math.min(count - pos, length); + System.arraycopy(buf, pos, buffer, offset, numBytes); + pos += numBytes; + return numBytes; + } + + /** + * This method sets the read position in the stream to the mark + * point by setting the pos variable equal to the + * mark variable. Since a mark can be set anywhere in + * the array, the mark/reset methods int this class can be used to + * provide random search capabilities for this type of stream. + */ + public synchronized void reset() + { + pos = mark; + } + + /** + * This method attempts to skip the requested number of bytes in the + * input stream. It does this by advancing the pos + * value by the specified number of bytes. It this would exceed the + * length of the buffer, then only enough bytes are skipped to + * position the stream at the end of the buffer. The actual number + * of bytes skipped is returned. + * + * @param num The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + */ + public synchronized long skip(long num) + { + // Even though the var numBytes is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numBytes might as well just be a long. + long numBytes = Math.min((long) (count - pos), num < 0 ? 0L : num); + pos += numBytes; + return numBytes; + } +} diff --git a/libjava/classpath/java/io/ByteArrayOutputStream.java b/libjava/classpath/java/io/ByteArrayOutputStream.java new file mode 100644 index 000000000..9c1a86b7f --- /dev/null +++ b/libjava/classpath/java/io/ByteArrayOutputStream.java @@ -0,0 +1,284 @@ +/* BufferedReader.java + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class allows data to be written to a byte array buffer and + * and then retrieved by an application. The internal byte array + * buffer is dynamically resized to hold all the data written. Please + * be aware that writing large amounts to data to this stream will + * cause large amounts of memory to be allocated. + *

    + * The size of the internal buffer defaults to 32 and it is resized + * by doubling the size of the buffer. This default size can be + * overridden by using the + * gnu.java.io.ByteArrayOutputStream.initialBufferSize + * property. + *

    + * There is a constructor that specified the initial buffer size and + * that is the preferred way to set that value because it it portable + * across all Java class library implementations. + *

    + * Note that this class also has methods that convert the byte array + * buffer to a String using either the system default or an + * application specified character encoding. Thus it can handle + * multibyte character encodings. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 24, 1998 + */ +public class ByteArrayOutputStream extends OutputStream +{ + /** + * This method initializes a new ByteArrayOutputStream + * with the default buffer size of 32 bytes. If a different initial + * buffer size is desired, see the constructor + * ByteArrayOutputStream(int size). For applications + * where the source code is not available, the default buffer size + * can be set using the system property + * gnu.java.io.ByteArrayOutputStream.initialBufferSize + */ + public ByteArrayOutputStream () + { + this (initial_buffer_size); + } + + /** + * This method initializes a new ByteArrayOutputStream with + * a specified initial buffer size. + * + * @param size The initial buffer size in bytes + */ + public ByteArrayOutputStream (int size) + { + buf = new byte[size]; + count = 0; + } + + /** + * This method discards all of the bytes that have been written to + * the internal buffer so far by setting the count + * variable to 0. The internal buffer remains at its currently + * allocated size. + */ + public synchronized void reset () + { + count = 0; + } + + /** + * This method returns the number of bytes that have been written to + * the buffer so far. This is the same as the value of the protected + * count variable. If the reset method is + * called, then this value is reset as well. Note that this method does + * not return the length of the internal buffer, but only the number + * of bytes that have been written to it. + * + * @return The number of bytes in the internal buffer + * + * @see #reset() + */ + public int size () + { + return count; + } + + /** + * This method returns a byte array containing the bytes that have been + * written to this stream so far. This array is a copy of the valid + * bytes in the internal buffer and its length is equal to the number of + * valid bytes, not necessarily to the the length of the current + * internal buffer. Note that since this method allocates a new array, + * it should be used with caution when the internal buffer is very large. + */ + public synchronized byte[] toByteArray () + { + byte[] ret = new byte[count]; + System.arraycopy(buf, 0, ret, 0, count); + return ret; + } + + /** + * Returns the bytes in the internal array as a String. The + * bytes in the buffer are converted to characters using the system default + * encoding. There is an overloaded toString() method that + * allows an application specified character encoding to be used. + * + * @return A String containing the data written to this + * stream so far + */ + public String toString () + { + return new String (buf, 0, count); + } + + /** + * Returns the bytes in the internal array as a String. The + * bytes in the buffer are converted to characters using the specified + * encoding. + * + * @param enc The name of the character encoding to use + * + * @return A String containing the data written to this + * stream so far + * + * @exception UnsupportedEncodingException If the named encoding is + * not available + */ + public String toString (String enc) throws UnsupportedEncodingException + { + return new String (buf, 0, count, enc); + } + + /** + * This method returns the bytes in the internal array as a + * String. It uses each byte in the array as the low + * order eight bits of the Unicode character value and the passed in + * parameter as the high eight bits. + *

    + * This method does not convert bytes to characters in the proper way and + * so is deprecated in favor of the other overloaded toString + * methods which use a true character encoding. + * + * @param hibyte The high eight bits to use for each character in + * the String + * + * @return A String containing the data written to this + * stream so far + * + * @deprecated + */ + public String toString (int hibyte) + { + return new String (buf, hibyte, 0, count); + } + + // Resize buffer to accommodate new bytes. + private void resize (int add) + { + if (count + add > buf.length) + { + int newlen = buf.length * 2; + if (count + add > newlen) + newlen = count + add; + byte[] newbuf = new byte[newlen]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + } + + /** + * This method writes the writes the specified byte into the internal + * buffer. + * + * @param oneByte The byte to be read passed as an int + */ + public synchronized void write (int oneByte) + { + resize (1); + buf[count++] = (byte) oneByte; + } + + /** + * This method writes len bytes from the passed in array + * buf starting at index offset into the + * internal buffer. + * + * @param buffer The byte array to write data from + * @param offset The index into the buffer to start writing data from + * @param add The number of bytes to write + */ + public synchronized void write (byte[] buffer, int offset, int add) + { + // If ADD < 0 then arraycopy will throw the appropriate error for + // us. + if (add >= 0) + resize (add); + System.arraycopy(buffer, offset, buf, count, add); + count += add; + } + + /** + * This method writes all the bytes that have been written to this stream + * from the internal buffer to the specified OutputStream. + * + * @param out The OutputStream to write to + * + * @exception IOException If an error occurs + */ + public synchronized void writeTo (OutputStream out) throws IOException + { + out.write(buf, 0, count); + } + + /** + * The internal buffer where the data written is stored + */ + protected byte[] buf; + + /** + * The number of bytes that have been written to the buffer + */ + protected int count; + + /** + * The default initial buffer size. Specified by the JCL. + */ + private static final int DEFAULT_INITIAL_BUFFER_SIZE = 32; + + // The default buffer size which can be overridden by the user. + private static final int initial_buffer_size; + + static + { + int r + = Integer.getInteger ("gnu.java.io.ByteArrayOutputStream.initialBufferSize", + DEFAULT_INITIAL_BUFFER_SIZE).intValue (); + if (r <= 0) + r = DEFAULT_INITIAL_BUFFER_SIZE; + initial_buffer_size = r; + } +} diff --git a/libjava/classpath/java/io/CharArrayReader.java b/libjava/classpath/java/io/CharArrayReader.java new file mode 100644 index 000000000..8405f48f3 --- /dev/null +++ b/libjava/classpath/java/io/CharArrayReader.java @@ -0,0 +1,305 @@ +/* CharArrayReader.java -- Read an array of characters as a stream + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class permits an array of chars to be read as an input stream. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class CharArrayReader extends Reader +{ + /** + * The array that contains the data supplied during read operations + */ + protected char[] buf; + + /** + * The array index of the next char to be read from the buffer + * buf + */ + protected int pos; + + /** + * The currently marked position in the stream. This defaults to 0, so a + * reset operation on the stream resets it to read from array index 0 in + * the buffer - even if the stream was initially created with an offset + * greater than 0 + */ + protected int markedPos; + + /** + * This indicates the maximum number of chars that can be read from this + * stream. It is the array index of the position after the last valid + * char in the buffer buf + */ + protected int count; + + /** + * Create a new CharArrayReader that will read chars from the passed + * in char array. This stream will read from the beginning to the end + * of the array. It is identical to calling an overloaded constructor + * as CharArrayReader(buf, 0, buf.length). + *

    + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * chars supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The char array buffer this stream will read from. + */ + public CharArrayReader(char[] buffer) + { + this(buffer, 0, buffer.length); + } + + /** + * Create a new CharArrayReader that will read chars from the passed + * in char array. This stream will read from position + * offset in the array for a length of + * length chars past offset. If the + * stream is reset to a position before offset then + * more than length chars can be read from the stream. + * The length value should be viewed as the array index + * one greater than the last position in the buffer to read. + *

    + * Note that this array is not copied. If its contents are changed + * while this stream is being read, those changes will be reflected in the + * chars supplied to the reader. Please use caution in changing the + * contents of the buffer while this stream is open. + * + * @param buffer The char array buffer this stream will read from. + * @param offset The index into the buffer to start reading chars from + * @param length The number of chars to read from the buffer + */ + public CharArrayReader(char[] buffer, int offset, int length) + { + super(); + if (offset < 0 || length < 0 || offset > buffer.length) + throw new IllegalArgumentException(); + + buf = buffer; + + count = offset + length; + if (count > buf.length) + count = buf.length; + + pos = offset; + markedPos = pos; + } + + /** + * This method closes the stream. + */ + public void close() + { + synchronized (lock) + { + buf = null; + } + } + + /** + * This method sets the mark position in this stream to the current + * position. Note that the readlimit parameter in this + * method does nothing as this stream is always capable of + * remembering all the chars int it. + *

    + * Note that in this class the mark position is set by default to + * position 0 in the stream. This is in constrast to some other + * stream types where there is no default mark position. + * + * @param readAheadLimit The number of chars this stream must + * remember. This parameter is ignored. + * + * @exception IOException If an error occurs + */ + public void mark(int readAheadLimit) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + // readAheadLimit is ignored per Java Class Lib. book, p. 318. + markedPos = pos; + } + } + + /** + * This method overrides the markSupported method in + * Reader in order to return true - + * indicating that this stream class supports mark/reset + * functionality. + * + * @return true to indicate that this class supports + * mark/reset. + */ + public boolean markSupported() + { + return true; + } + + /** + * This method reads one char from the stream. The pos + * counter is advanced to the next char to be read. The char read + * is returned as an int in the range of 0-65535. If the stream + * position is already at the end of the buffer, no char is read and + * a -1 is returned in order to indicate the end of the stream. + * + * @return The char read, or -1 if end of stream + */ + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + if (pos < 0) + throw new ArrayIndexOutOfBoundsException(pos); + + if (pos < count) + return ((int) buf[pos++]) & 0xFFFF; + return -1; + } + } + + /** + * This method reads chars from the stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * offset into the buffer and attempts to read + * len chars. This method can return before reading + * the number of chars requested if the end of the stream is + * encountered first. The actual number of chars read is returned. + * If no chars can be read because the stream is already at the end + * of stream position, a -1 is returned. + *

    + * This method does not block. + * + * @param b The array into which the chars read should be stored. + * @param off The offset into the array to start storing chars + * @param len The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + */ + public int read(char[] b, int off, int len) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + /* Don't need to check pos value, arraycopy will check it. */ + if (off < 0 || len < 0 || off + len > b.length) + throw new IndexOutOfBoundsException(); + + if (pos >= count) + return -1; + + int numChars = Math.min(count - pos, len); + System.arraycopy(buf, pos, b, off, numChars); + pos += numChars; + return numChars; + } + } + + /** + * Return true if more characters are available to be read. + * + * @return true to indicate that this stream is ready + * to be read. + * + * @specnote The JDK 1.3 API docs are wrong here. This method will + * return false if there are no more characters available. + */ + public boolean ready() throws IOException + { + if (buf == null) + throw new IOException("Stream closed"); + + return (pos < count); + } + + /** + * This method sets the read position in the stream to the mark + * point by setting the pos variable equal to the + * mark variable. Since a mark can be set anywhere in + * the array, the mark/reset methods int this class can be used to + * provide random search capabilities for this type of stream. + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + pos = markedPos; + } + } + + /** + * This method attempts to skip the requested number of chars in the + * input stream. It does this by advancing the pos value by the + * specified number of chars. It this would exceed the length of the + * buffer, then only enough chars are skipped to position the stream at + * the end of the buffer. The actual number of chars skipped is returned. + * + * @param n The requested number of chars to skip + * + * @return The actual number of chars skipped. + */ + public long skip(long n) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // Even though the var numChars is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numChars might as well just be a long. + long numChars = Math.min((long) (count - pos), n < 0 ? 0L : n); + pos += numChars; + return numChars; + } + } +} diff --git a/libjava/classpath/java/io/CharArrayWriter.java b/libjava/classpath/java/io/CharArrayWriter.java new file mode 100644 index 000000000..dea727aa1 --- /dev/null +++ b/libjava/classpath/java/io/CharArrayWriter.java @@ -0,0 +1,352 @@ +/* CharArrayWriter.java -- Write chars to a buffer + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class allows data to be written to a char array buffer and + * and then retrieved by an application. The internal char array + * buffer is dynamically resized to hold all the data written. Please + * be aware that writing large amounts to data to this stream will + * cause large amounts of memory to be allocated. + *

    + * The size of the internal buffer defaults to 32 and it is resized + * in increments of 1024 chars. This behavior can be over-ridden by using the + * following two properties: + *

    + *

      + *
    • gnu.java.io.CharArrayWriter.initialBufferSize
    • + *
    • gnu.java.io.CharArrayWriter.bufferIncrementSize
    • + *
    + *

    + * There is a constructor that specified the initial buffer size and + * that is the preferred way to set that value because it it portable + * across all Java class library implementations. + *

    + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class CharArrayWriter extends Writer +{ + /** + * The default initial buffer size + */ + private static final int DEFAULT_INITIAL_BUFFER_SIZE = 32; + + /** + * This method initializes a new CharArrayWriter with + * the default buffer size of 32 chars. If a different initial + * buffer size is desired, see the constructor + * CharArrayWriter(int size). + */ + public CharArrayWriter () + { + this (DEFAULT_INITIAL_BUFFER_SIZE); + } + + /** + * This method initializes a new CharArrayWriter with + * a specified initial buffer size. + * + * @param size The initial buffer size in chars + */ + public CharArrayWriter (int size) + { + super (); + buf = new char[size]; + } + + /** + * Closes the stream. This method is guaranteed not to free the contents + * of the internal buffer, which can still be retrieved. + */ + public void close () + { + } + + /** + * This method flushes all buffered chars to the stream. + */ + public void flush () + { + } + + /** + * This method discards all of the chars that have been written to the + * internal buffer so far by setting the count variable to + * 0. The internal buffer remains at its currently allocated size. + */ + public void reset () + { + synchronized (lock) + { + count = 0; + } + } + + /** + * This method returns the number of chars that have been written to + * the buffer so far. This is the same as the value of the protected + * count variable. If the reset method is + * called, then this value is reset as well. Note that this method does + * not return the length of the internal buffer, but only the number + * of chars that have been written to it. + * + * @return The number of chars in the internal buffer + * + * @see #reset() + */ + public int size () + { + return count; + } + + /** + * This method returns a char array containing the chars that have been + * written to this stream so far. This array is a copy of the valid + * chars in the internal buffer and its length is equal to the number of + * valid chars, not necessarily to the the length of the current + * internal buffer. Note that since this method allocates a new array, + * it should be used with caution when the internal buffer is very large. + */ + public char[] toCharArray () + { + synchronized (lock) + { + char[] nc = new char[count]; + System.arraycopy(buf, 0, nc, 0, count); + return nc; + } + } + + /** + * Returns the chars in the internal array as a String. The + * chars in the buffer are converted to characters using the system default + * encoding. There is an overloaded toString() method that + * allows an application specified character encoding to be used. + * + * @return A String containing the data written to this + * stream so far + */ + public String toString () + { + synchronized (lock) + { + return new String (buf, 0, count); + } + } + + /** + * This method writes the writes the specified char into the internal + * buffer. + * + * @param oneChar The char to be read passed as an int + */ + public void write (int oneChar) + { + synchronized (lock) + { + resize (1); + buf[count++] = (char) oneChar; + } + } + + /** + * This method writes len chars from the passed in array + * buf starting at index offset into that buffer + * + * @param buffer The char array to write data from + * @param offset The index into the buffer to start writing data from + * @param len The number of chars to write + */ + public void write (char[] buffer, int offset, int len) + { + synchronized (lock) + { + if (len >= 0) + resize (len); + System.arraycopy(buffer, offset, buf, count, len); + count += len; + } + } + + /** + * This method writes len chars from the passed in + * String buf starting at index + * offset into the internal buffer. + * + * @param str The String to write data from + * @param offset The index into the string to start writing data from + * @param len The number of chars to write + */ + public void write (String str, int offset, int len) + { + synchronized (lock) + { + if (len >= 0) + resize (len); + str.getChars(offset, offset + len, buf, count); + count += len; + } + } + + /** + * This method writes all the chars that have been written to this stream + * from the internal buffer to the specified Writer. + * + * @param out The Writer to write to + * + * @exception IOException If an error occurs + */ + public void writeTo (Writer out) throws IOException + { + synchronized (lock) + { + out.write(buf, 0, count); + } + } + + /** + * Appends the Unicode character, c, to the output stream + * underlying this writer. This is equivalent to write(c). + * + * @param c the character to append. + * @return a reference to this object. + * @since 1.5 + */ + public CharArrayWriter append(char c) + { + write(c); + return this; + } + + /** + * Appends the specified sequence of Unicode characters to the + * output stream underlying this writer. This is equivalent to + * appending the results of calling toString() on the + * character sequence. As a result, the entire sequence may not be + * appended, as it depends on the implementation of + * toString() provided by the + * CharSequence. For example, if the character + * sequence is wrapped around an input buffer, the results will + * depend on the current position and length of that buffer. + * + * @param cs the character sequence to append. If seq is null, + * then the string "null" (the string representation of null) + * is appended. + * @return a reference to this object. + * @since 1.5 + */ + public CharArrayWriter append(CharSequence cs) + { + try + { + write(cs == null ? "null" : cs.toString()); + } + catch (IOException _) + { + // Can't happen. + } + return this; + } + + /** + * Appends the specified subsequence of Unicode characters to the + * output stream underlying this writer, starting and ending at the + * specified positions within the sequence. The behaviour of this + * method matches the behaviour of writing the result of + * append(seq.subSequence(start,end)) when the sequence + * is not null. + * + * @param cs the character sequence to append. If seq is null, + * then the string "null" (the string representation of null) + * is appended. + * @param start the index of the first Unicode character to use from + * the sequence. + * @param end the index of the last Unicode character to use from the + * sequence. + * @return a reference to this object. + * @throws IndexOutOfBoundsException if either of the indices are negative, + * the start index occurs after the end index, or the end index is + * beyond the end of the sequence. + * @since 1.5 + */ + public CharArrayWriter append(CharSequence cs, int start, int end) + { + try + { + write(cs == null ? "null" : cs.subSequence(start, end).toString()); + } + catch (IOException _) + { + // Can't happen. + } + return this; + } + + /** + * This private method makes the buffer bigger when we run out of room + * by allocating a larger buffer and copying the valid chars from the + * old array into it. This is obviously slow and should be avoided by + * application programmers by setting their initial buffer size big + * enough to hold everything if possible. + */ + private void resize (int len) + { + if (count + len >= buf.length) + { + int newlen = buf.length * 2; + if (count + len > newlen) + newlen = count + len; + char[] newbuf = new char[newlen]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + } + + /** + * The internal buffer where the data written is stored + */ + protected char[] buf; + + /** + * The number of chars that have been written to the buffer + */ + protected int count; +} diff --git a/libjava/classpath/java/io/CharConversionException.java b/libjava/classpath/java/io/CharConversionException.java new file mode 100644 index 000000000..a7a608429 --- /dev/null +++ b/libjava/classpath/java/io/CharConversionException.java @@ -0,0 +1,73 @@ +/* CharConversionException.java -- Character conversion exceptions + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown to indicate that a problem occurred with + * an attempted character conversion. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class CharConversionException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8680016352018427031L; + + /** + * Create an exception without a descriptive error message. + */ + public CharConversionException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public CharConversionException(String message) + { + super(message); + } +} // class CharConversionException diff --git a/libjava/classpath/java/io/Closeable.java b/libjava/classpath/java/io/Closeable.java new file mode 100644 index 000000000..b8523d79e --- /dev/null +++ b/libjava/classpath/java/io/Closeable.java @@ -0,0 +1,63 @@ +/* Closeable.java -- Closeable object + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * A Closeable class represents a stream of + * data, which can be closed when it is no longer needed. + * Closing a stream allows the resources it uses to be + * freed for an alternate use. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Closeable +{ + + /** + * Closes the stream represented by this class, thus freeing + * system resources. In that case that the stream is already + * in the closed state, this method has no effect. + * + * @throws IOException if an I/O error occurs in closing. + */ + void close() + throws IOException; + +} diff --git a/libjava/classpath/java/io/DataInput.java b/libjava/classpath/java/io/DataInput.java new file mode 100644 index 000000000..a713319cf --- /dev/null +++ b/libjava/classpath/java/io/DataInput.java @@ -0,0 +1,456 @@ +/* DataInput.java -- Interface for reading data from a stream + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. */ + +/** + * This interface is implemented by classes that can data from streams + * into Java primitive types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface DataInput +{ + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is false. If the byte is non-zero, then + * the value returned is true. + *

    + * This method can read a boolean written by an object + * implementing the writeBoolean() method in the + * DataOutput interface. + * + * @return The boolean value read + * + * @exception EOFException If end of file is reached before + * reading the boolean + * @exception IOException If any other error occurs + * + * @see DataOutput#writeBoolean + */ + boolean readBoolean() throws EOFException, IOException; + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + *

    + * This method can read a byte written by an object + * implementing the + * writeByte() method in the DataOutput interface. + *

    + * @return The byte value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + byte readByte() throws EOFException, IOException; + + /** + * This method reads 8 unsigned bits into a Java int value from + * the stream. The value returned is in the range of 0 to 255. + *

    + * This method can read an unsigned byte written by an object + * implementing the + * writeByte() method in the DataOutput + * interface. + * + * @return The unsigned bytes value read as a Java int. + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + int readUnsignedByte() throws EOFException, IOException; + + /** + * This method reads a Java char value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java char. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 and byte2 represent the + * first and second byte read from the stream respectively, they will be + * transformed to a char in the following manner: + *

    + * (char)((byte1 << 8) + byte2) + *

    + * This method can read a char written by an object implementing + * the + * writeChar() method in the DataOutput interface. + * + * @return The char value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput#writeChar + */ + char readChar() throws EOFException, IOException; + + /** + * This method reads a signed 16-bit value into a Java in from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java short. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 and byte2 represent the + * first and second byte read from the stream respectively, they will be + * transformed to a short in the following manner: + *

    + * (short)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF)) + *

    + * The value returned is in the range of -32768 to 32767. + *

    + * This method can read a short written by an object + * implementing + * the writeShort() method in the DataOutput + * interface. + * + * @return The short value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + short readShort() throws EOFException, IOException; + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java int. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 and byte2 represent the + * first and second byte read from the stream respectively, they will be + * transformed to an int in the following manner: + *

    + * (int)(((byte1 0xFF) << 8) + (byte2 & 0xFF)) + *

    + * The value returned is in the range of 0 to 65535. + *

    + * This method can read an unsigned short written by an object implementing + * the writeShort() method in the + * DataOutput + * interface. + * + * @return The unsigned short value read as a Java int. + * + * @exception EOFException If end of file is reached before reading + * the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + int readUnsignedShort() throws EOFException, IOException; + + /** + * This method reads a Java int value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java int. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 through byte4 represent + * the first four bytes read from the stream, they will be + * transformed to an int in the following manner: + *

    + * (int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF)<< 8) + (byte4 & 0xFF))) + *

    + * The value returned is in the range of -2147483648 to 2147483647. + *

    + * This method can read an int written by an object + * implementing the writeInt() method in the + * DataOutput interface. + * + * @return The int value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput#writeInt + */ + int readInt() throws EOFException, IOException; + + /** + * This method reads a Java long value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java long. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 through byte8 represent + * the first eight bytes read from the stream, they will be + * transformed to an long in the following manner: + *

    + * (long)(((byte1 & 0xFF) << 56) + ((byte2 & 0xFF) << 48) + + * ((byte3 & 0xFF) << 40) + ((byte4 & 0xFF) << 32) + + * ((byte5 & 0xFF) << 24) + ((byte6 & 0xFF) << 16) + + * ((byte7 & 0xFF) << 8) + (byte8 & 0xFF))) + * + *

    + * The value returned is in the range of -9223372036854775808 to + * 9223372036854775807. + *

    + * This method can read an long written by an object + * implementing the writeLong() method in the + * DataOutput interface. + * + * @return The long value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput#writeLong + */ + long readLong() throws EOFException, IOException; + + /** + * This method reads a Java float value from an input stream. It operates + * by first reading an int value from the stream by calling the + * readInt() method in this interface, then converts that + * int to a float using the + * intBitsToFloat method in the class + * java.lang.Float. + *

    + * This method can read a float written by an object + * implementing + * the writeFloat() method in the DataOutput + * interface. + * + * @return The float value read + * + * @exception EOFException If end of file is reached before reading the + * float + * @exception IOException If any other error occurs + * + * @see DataOutput#writeFloat + * @see java.lang.Float#intBitsToFloat + */ + float readFloat() throws EOFException, IOException; + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a long value from the stream by calling the + * readLong() method in this interface, then converts that + * long to a double using the + * longBitsToDouble method in the class + * java.lang.Double. + *

    + * This method can read a double written by an object + * implementing the writeDouble() method in the + * DataOutput interface. + * + * @return The double value read + * + * @exception EOFException If end of file is reached before reading the + * double + * @exception IOException If any other error occurs + * + * @see DataOutput#writeDouble + * @see java.lang.Double#longBitsToDouble + */ + double readDouble() throws EOFException, IOException; + + /** + * This method reads the next line of text data from an input stream. + * It operates by reading bytes and converting those bytes to + * char + * values by treating the byte read as the low eight bits of the + * char and using 0 as the high eight bits. Because of this, + * it does not support the full 16-bit Unicode character set. + *

    + * The reading of bytes ends when either the end of file or a line terminator + * is encountered. The bytes read are then returned as a + * String. + * A line terminator is a byte sequence consisting of either + * \r, \n or \r\n. These termination + * charaters are discarded and are not returned as part of the string. + * A line is also terminated by an end of file condition. + *

    + * + * @return The line read as a String + * + * @exception IOException If an error occurs + */ + String readLine() throws IOException; + + /** + * This method reads a String from an input stream that is + * encoded in a modified UTF-8 format. This format has a leading two byte + * sequence that contains the remaining number of bytes to read. + * This two byte + * sequence is read using the readUnsignedShort() method of this + * interface. + * + * After the number of remaining bytes have been determined, these bytes + * are read an transformed into char values. These + * char values are encoded in the stream using either a one, + * two, or three byte format. + * The particular format in use can be determined by examining the first + * byte read. + *

    + * If the first byte has a high order bit of 0, then + * that character consists on only one byte. This character value consists + * of seven bits that are at positions 0 through 6 of the byte. As an + * example, if byte1 is the byte read from the stream, it would + * be converted to a char like so: + *

    + * (char)byte1 + *

    + * If the first byte has 110 as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + *

    + * As an example, if byte1 and byte2 are the first + * two bytes read respectively, and the high order bits of them match the + * patterns which indicate a two byte character encoding, then they would be + * converted to a Java char like so: + *

    + * (char)(((byte1 & 0x1F) << 6) + (byte2 & 0x3F)) + *

    + * If the first byte has a 1110 as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have 10 as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + *

    + * As an example, if byte1, byte2, and + * byte3 are the three bytes read, and the high order bits of + * them match the patterns which indicate a three byte character encoding, + * then they would be converted to a Java char like so: + * + * + * (char)(((byte1 & 0x0F) << 12) + ((byte2 & 0x3F) + (byte3 & 0x3F)) + * + * + * Note that all characters are encoded in the method that requires the + * fewest number of bytes with the exception of the character with the + * value of \<llll>u0000 which is encoded as two bytes. + * This is a modification of the UTF standard used to prevent C language + * style NUL values from appearing in the byte stream. + *

    + * This method can read data that was written by an object implementing the + * writeUTF() method in DataOutput. + * + * @return The String read + * + * @exception EOFException If end of file is reached before reading the + * String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput#writeUTF + */ + String readUTF() throws EOFException, UTFDataFormatException, IOException; + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer. Note also that zero length buffers are permitted. + * In this case, the method will return immediately without reading any + * bytes from the stream. + * + * @param buf The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + void readFully(byte[] buf) throws EOFException, IOException; + + /** + * This method reads raw bytes into the passed array buf + * starting + * offset bytes into the buffer. The number of bytes read + * will be + * exactly len. Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read len bytes. Note also that zero length + * buffers are permitted. In this case, the method will return immediately + * without reading any bytes from the stream. + * + * @param buf The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param len The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + void readFully(byte[] buf, int offset, int len) + throws EOFException, IOException; + + /** + * This method skips and discards the specified number of bytes in an + * input stream. Note that this method may skip less than the requested + * number of bytes. The actual number of bytes skipped is returned. + * No bytes are skipped if a negative number is passed to this method. + * + * @param numBytes The number of bytes to skip + * + * @return The number of bytes actually skipped, which will always be + * numBytes + * + * @exception EOFException If end of file is reached before all bytes can be + * skipped + * @exception IOException If any other error occurs + */ + int skipBytes(int numBytes) throws EOFException, IOException; + +} // interface DataInput diff --git a/libjava/classpath/java/io/DataInputStream.java b/libjava/classpath/java/io/DataInputStream.java new file mode 100644 index 000000000..518205b24 --- /dev/null +++ b/libjava/classpath/java/io/DataInputStream.java @@ -0,0 +1,785 @@ +/* DataInputStream.java -- FilteredInputStream that implements DataInput + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2008 + Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +import gnu.java.lang.CPStringBuilder; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This subclass of FilteredInputStream implements the + * DataInput interface that provides method for reading primitive + * Java data types from a stream. + * + * @see DataInput + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date October 20, 1998. + */ +public class DataInputStream extends FilterInputStream implements DataInput +{ + // Byte buffer, used to make primitive read calls more efficient. + byte[] buf = new byte [8]; + + /** + * This constructor initializes a new DataInputStream + * to read from the specified subordinate stream. + * + * @param in The subordinate InputStream to read from + */ + public DataInputStream (InputStream in) + { + super (in); + } + + /** + * This method reads bytes from the underlying stream into the specified + * byte array buffer. It will attempt to fill the buffer completely, but + * may return a short count if there is insufficient data remaining to be + * read to fill the buffer. + * + * @param b The buffer into which bytes will be read. + * + * @return The actual number of bytes read, or -1 if end of stream reached + * before reading any bytes. + * + * @exception IOException If an error occurs. + */ + public final int read (byte[] b) throws IOException + { + return in.read (b, 0, b.length); + } + + /** + * This method reads bytes from the underlying stream into the specified + * byte array buffer. It will attempt to read len bytes and + * will start storing them at position off into the buffer. + * This method can return a short count if there is insufficient data + * remaining to be read to complete the desired read length. + * + * @param b The buffer into which bytes will be read. + * @param off The offset into the buffer to start storing bytes. + * @param len The requested number of bytes to read. + * + * @return The actual number of bytes read, or -1 if end of stream reached + * before reading any bytes. + * + * @exception IOException If an error occurs. + */ + public final int read (byte[] b, int off, int len) throws IOException + { + return in.read (b, off, len); + } + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is false. If the byte is non-zero, then + * the value returned is true. + *

    + * This method can read a boolean written by an object + * implementing the writeBoolean() method in the + * DataOutput interface. + * + * @return The boolean value read + * + * @exception EOFException If end of file is reached before reading + * the boolean + * @exception IOException If any other error occurs + * + * @see DataOutput#writeBoolean + */ + public final boolean readBoolean () throws IOException + { + return convertToBoolean (in.read ()); + } + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + *

    + * This method can read a byte written by an object + * implementing the writeByte() method in the + * DataOutput interface. + * + * @return The byte value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + public final byte readByte () throws IOException + { + return convertToByte (in.read ()); + } + + /** + * This method reads a Java char value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java char. The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 and byte2 + * represent the first and second byte read from the stream + * respectively, they will be transformed to a char in + * the following manner: + *

    + * (char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF) + *

    + * This method can read a char written by an object + * implementing the writeChar() method in the + * DataOutput interface. + * + * @return The char value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput#writeChar + */ + public final char readChar () throws IOException + { + readFully (buf, 0, 2); + return convertToChar (buf); + } + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a long value from the stream by calling the + * readLong() method in this interface, then converts + * that long to a double using the + * longBitsToDouble method in the class + * java.lang.Double + *

    + * This method can read a double written by an object + * implementing the writeDouble() method in the + * DataOutput interface. + * + * @return The double value read + * + * @exception EOFException If end of file is reached before reading + * the double + * @exception IOException If any other error occurs + * + * @see DataOutput#writeDouble + * @see java.lang.Double#longBitsToDouble + */ + public final double readDouble () throws IOException + { + return Double.longBitsToDouble (readLong ()); + } + + /** + * This method reads a Java float value from an input stream. It + * operates by first reading an int value from the + * stream by calling the readInt() method in this + * interface, then converts that int to a + * float using the intBitsToFloat method + * in the class java.lang.Float + *

    + * This method can read a float written by an object + * implementing the writeFloat() method in the + * DataOutput interface. + * + * @return The float value read + * + * @exception EOFException If end of file is reached before reading the float + * @exception IOException If any other error occurs + * + * @see DataOutput#writeFloat + * @see java.lang.Float#intBitsToFloat + */ + public final float readFloat () throws IOException + { + return Float.intBitsToFloat (readInt ()); + } + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer. Note also that zero length buffers are permitted. + * In this case, the method will return immediately without reading any + * bytes from the stream. + * + * @param b The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] b) throws IOException + { + readFully (b, 0, b.length); + } + + /** + * This method reads raw bytes into the passed array buf + * starting + * offset bytes into the buffer. The number of bytes read + * will be + * exactly len. Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read len bytes. Note also that zero length + * buffers are permitted. In this case, the method will return immediately + * without reading any bytes from the stream. + * + * @param buf The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param len The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buf, int offset, int len) throws IOException + { + if (len < 0) + throw new IndexOutOfBoundsException("Negative length: " + len); + + while (len > 0) + { + // in.read will block until some data is available. + int numread = in.read (buf, offset, len); + if (numread < 0) + throw new EOFException (); + len -= numread; + offset += numread; + } + } + + /** + * This method reads a Java int value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java int. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 through byte4 represent + * the first four bytes read from the stream, they will be + * transformed to an int in the following manner: + *

    + * (int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF)<< 8) + (byte4 & 0xFF))) + *

    + * The value returned is in the range of -2147483648 to 2147483647. + *

    + * This method can read an int written by an object + * implementing the writeInt() method in the + * DataOutput interface. + * + * @return The int value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput#writeInt + */ + public final int readInt () throws IOException + { + readFully (buf, 0, 4); + return convertToInt (buf); + } + + /** + * This method reads the next line of text data from an input + * stream. It operates by reading bytes and converting those bytes + * to char values by treating the byte read as the low + * eight bits of the char and using 0 as the high eight + * bits. Because of this, it does not support the full 16-bit + * Unicode character set. + *

    + * The reading of bytes ends when either the end of file or a line + * terminator is encountered. The bytes read are then returned as a + * String A line terminator is a byte sequence + * consisting of either \r, \n or + * \r\n. These termination charaters are discarded and + * are not returned as part of the string. + *

    + * This method can read data that was written by an object implementing the + * writeLine() method in DataOutput. + * + * @return The line read as a String + * + * @exception IOException If an error occurs + * + * @see DataOutput + * + * @deprecated + */ + public final String readLine() throws IOException + { + CPStringBuilder strb = new CPStringBuilder(); + + while (true) + { + int c = in.read(); + if (c == -1) // got an EOF + return strb.length() > 0 ? strb.toString() : null; + if (c == '\r') + { + int next_c = in.read(); + if (next_c != '\n' && next_c != -1) + { + if (!(in instanceof PushbackInputStream)) + in = new PushbackInputStream(in); + ((PushbackInputStream) in).unread(next_c); + } + break; + } + if (c == '\n') + break; + strb.append((char) c); + } + + return strb.length() > 0 ? strb.toString() : ""; + } + + /** + * This method reads a Java long value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java long. The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 through byte8 represent + * the first eight bytes read from the stream, they will be + * transformed to an long in the following manner: + *

    + * (long)(((byte1 & 0xFF) << 56) + ((byte2 & 0xFF) << 48) + + * ((byte3 & 0xFF) << 40) + ((byte4 & 0xFF) << 32) + + * ((byte5 & 0xFF) << 24) + ((byte6 & 0xFF) << 16) + + * ((byte7 & 0xFF) << 8) + (byte8 & 0xFF))) + * + *

    + * The value returned is in the range of -9223372036854775808 to + * 9223372036854775807. + *

    + * This method can read an long written by an object + * implementing the writeLong() method in the + * DataOutput interface. + * + * @return The long value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput#writeLong + */ + public final long readLong () throws IOException + { + readFully (buf, 0, 8); + return convertToLong (buf); + } + + /** + * This method reads a signed 16-bit value into a Java in from the + * stream. It operates by reading two bytes from the stream and + * converting them to a single 16-bit Java short. The + * two bytes are stored most significant byte first (i.e., "big + * endian") regardless of the native host byte ordering. + *

    + * As an example, if byte1 and byte2 + * represent the first and second byte read from the stream + * respectively, they will be transformed to a short. in + * the following manner: + *

    + * (short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF)) + *

    + * The value returned is in the range of -32768 to 32767. + *

    + * This method can read a short written by an object + * implementing the writeShort() method in the + * DataOutput interface. + * + * @return The short value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + public final short readShort () throws IOException + { + readFully (buf, 0, 2); + return convertToShort (buf); + } + + /** + * This method reads 8 unsigned bits into a Java int + * value from the stream. The value returned is in the range of 0 to + * 255. + *

    + * This method can read an unsigned byte written by an object + * implementing the writeUnsignedByte() method in the + * DataOutput interface. + * + * @return The unsigned bytes value read as a Java int. + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeByte + */ + public final int readUnsignedByte () throws IOException + { + return convertToUnsignedByte (in.read ()); + } + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java int The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 and byte2 + * represent the first and second byte read from the stream + * respectively, they will be transformed to an int in + * the following manner: + *

    + * (int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF)) + *

    + * The value returned is in the range of 0 to 65535. + *

    + * This method can read an unsigned short written by an object + * implementing the writeUnsignedShort() method in the + * DataOutput interface. + * + * @return The unsigned short value read as a Java int + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput#writeShort + */ + public final int readUnsignedShort () throws IOException + { + readFully (buf, 0, 2); + return convertToUnsignedShort (buf); + } + + /** + * This method reads a String from an input stream that + * is encoded in a modified UTF-8 format. This format has a leading + * two byte sequence that contains the remaining number of bytes to + * read. This two byte sequence is read using the + * readUnsignedShort() method of this interface. + *

    + * After the number of remaining bytes have been determined, these + * bytes are read an transformed into char values. + * These char values are encoded in the stream using + * either a one, two, or three byte format. The particular format + * in use can be determined by examining the first byte read. + *

    + * If the first byte has a high order bit of 0, then that character + * consists on only one byte. This character value consists of + * seven bits that are at positions 0 through 6 of the byte. As an + * example, if byte1 is the byte read from the stream, + * it would be converted to a char like so: + *

    + * (char)byte1 + *

    + * If the first byte has 110 as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + *

    + * As an example, if byte1 and byte2 are + * the first two bytes read respectively, and the high order bits of + * them match the patterns which indicate a two byte character + * encoding, then they would be converted to a Java + * char like so: + *

    + * (char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F)) + *

    + * If the first byte has a 1110 as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have 10 as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + *

    + * As an example, if byte1 byte2 and + * byte3 are the three bytes read, and the high order + * bits of them match the patterns which indicate a three byte + * character encoding, then they would be converted to a Java + * char like so: + *

    + * (char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | + * (byte3 & 0x3F)) + *

    + * Note that all characters are encoded in the method that requires + * the fewest number of bytes with the exception of the character + * with the value of \u0000 which is encoded as two + * bytes. This is a modification of the UTF standard used to + * prevent C language style NUL values from appearing + * in the byte stream. + *

    + * This method can read data that was written by an object implementing the + * writeUTF() method in DataOutput + * + * @return The String read + * + * @exception EOFException If end of file is reached before reading + * the String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput#writeUTF + */ + public final String readUTF () throws IOException + { + return readUTF (this); + } + + /** + * This method reads a String encoded in UTF-8 format from the + * specified DataInput source. + * + * @param in The DataInput source to read from + * + * @return The String read from the source + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + public static final String readUTF(DataInput in) throws IOException + { + final int UTFlen = in.readUnsignedShort (); + + return readUTF(in, UTFlen); + } + + /** + * This method is similar to readUTF, but the + * UTF-8 byte length is in 64 bits. + * This method is not public. It is used by ObjectInputStream. + * + * @return The String read + * + * @exception EOFException If end of file is reached before reading + * the String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput#writeUTFLong + */ + final String readUTFLong () throws IOException + { + long l = readLong (); + if (l > Integer.MAX_VALUE) + throw new IOException("The string length > Integer.MAX_VALUE"); + final int UTFlen = (int)l; + return readUTF (this, UTFlen); + } + + /** + * This method performs the main task of readUTF and + * readUTFLong. + * + * @param in The DataInput source to read from + * + * @param len The UTF-8 byte length of the String to be read + * + * @return The String read from the source + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + private static final String readUTF(DataInput in, int len) throws IOException + { + byte[] buf = new byte [len]; + + // This blocks until the entire string is available rather than + // doing partial processing on the bytes that are available and then + // blocking. An advantage of the latter is that Exceptions + // could be thrown earlier. The former is a bit cleaner. + in.readFully (buf, 0, len); + + return convertFromUTF (buf); + } + + /** + * This method attempts to skip and discard the specified number of bytes + * in the input stream. It may actually skip fewer bytes than requested. + * This method will not skip any bytes if passed a negative number of bytes + * to skip. + * + * @param n The requested number of bytes to skip. + * + * @return The requested number of bytes to skip. + * + * @exception IOException If an error occurs. + * @specnote The JDK docs claim that this returns the number of bytes + * actually skipped. The JCL claims that this method can throw an + * EOFException. Neither of these appear to be true in the JDK 1.3's + * implementation. This tries to implement the actual JDK behaviour. + */ + public final int skipBytes (int n) throws IOException + { + if (n <= 0) + return 0; + try + { + return (int) in.skip (n); + } + catch (EOFException x) + { + // do nothing. + } + return n; + } + + static boolean convertToBoolean (int b) throws EOFException + { + if (b < 0) + throw new EOFException (); + + return (b != 0); + } + + static byte convertToByte (int i) throws EOFException + { + if (i < 0) + throw new EOFException (); + + return (byte) i; + } + + static int convertToUnsignedByte (int i) throws EOFException + { + if (i < 0) + throw new EOFException (); + + return (i & 0xFF); + } + + static char convertToChar (byte[] buf) + { + return (char) ((buf [0] << 8) + | (buf [1] & 0xff)); + } + + static short convertToShort (byte[] buf) + { + return (short) ((buf [0] << 8) + | (buf [1] & 0xff)); + } + + static int convertToUnsignedShort (byte[] buf) + { + return (((buf [0] & 0xff) << 8) + | (buf [1] & 0xff)); + } + + static int convertToInt (byte[] buf) + { + return (((buf [0] & 0xff) << 24) + | ((buf [1] & 0xff) << 16) + | ((buf [2] & 0xff) << 8) + | (buf [3] & 0xff)); + } + + static long convertToLong (byte[] buf) + { + return (((long)(buf [0] & 0xff) << 56) | + ((long)(buf [1] & 0xff) << 48) | + ((long)(buf [2] & 0xff) << 40) | + ((long)(buf [3] & 0xff) << 32) | + ((long)(buf [4] & 0xff) << 24) | + ((long)(buf [5] & 0xff) << 16) | + ((long)(buf [6] & 0xff) << 8) | + ((long)(buf [7] & 0xff))); + } + + // FIXME: This method should be re-thought. I suspect we have multiple + // UTF-8 decoders floating around. We should use the standard charset + // converters, maybe and adding a direct call into one of the new + // NIO converters for a super-fast UTF8 decode. + static String convertFromUTF (byte[] buf) + throws EOFException, UTFDataFormatException + { + // Give StringBuffer an initial estimated size to avoid + // enlarge buffer frequently + CPStringBuilder strbuf = new CPStringBuilder (buf.length / 2 + 2); + + for (int i = 0; i < buf.length; ) + { + if ((buf [i] & 0x80) == 0) // bit pattern 0xxxxxxx + strbuf.append ((char) (buf [i++] & 0xFF)); + else if ((buf [i] & 0xE0) == 0xC0) // bit pattern 110xxxxx + { + if (i + 1 >= buf.length + || (buf [i + 1] & 0xC0) != 0x80) + throw new UTFDataFormatException (); + + strbuf.append((char) (((buf [i++] & 0x1F) << 6) + | (buf [i++] & 0x3F))); + } + else if ((buf [i] & 0xF0) == 0xE0) // bit pattern 1110xxxx + { + if (i + 2 >= buf.length + || (buf [i + 1] & 0xC0) != 0x80 + || (buf [i + 2] & 0xC0) != 0x80) + throw new UTFDataFormatException (); + + strbuf.append ((char) (((buf [i++] & 0x0F) << 12) + | ((buf [i++] & 0x3F) << 6) + | (buf [i++] & 0x3F))); + } + else // must be ((buf [i] & 0xF0) == 0xF0 || (buf [i] & 0xC0) == 0x80) + throw new UTFDataFormatException (); // bit patterns 1111xxxx or + // 10xxxxxx + } + + return strbuf.toString (); + } +} diff --git a/libjava/classpath/java/io/DataOutput.java b/libjava/classpath/java/io/DataOutput.java new file mode 100644 index 000000000..6eee4b702 --- /dev/null +++ b/libjava/classpath/java/io/DataOutput.java @@ -0,0 +1,325 @@ +/* DataOutput.java -- Interface for writing data from a stream + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This interface is implemented by classes that can wrte data to streams + * from Java primitive types. This data can subsequently be read back + * by classes implementing the DataInput interface. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * + * @see DataInput + */ +public interface DataOutput +{ + /** + * This method writes a Java boolean value to an output stream. If + * value is true, a byte with the value of + * 1 will be written, otherwise a byte with the value of 0 will be + * written. + * + * The value written can be read using the readBoolean + * method in DataInput. + * + * @param value The boolean value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readBoolean + */ + void writeBoolean(boolean value) throws IOException; + + /** + * This method writes a Java byte value to an output stream. The + * byte to be written will be in the lowest 8 bits of the + * int value passed. + * + * The value written can be read using the readByte or + * readUnsignedByte methods in DataInput. + * + * @param value The int value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readByte + * @see DataInput#readUnsignedByte + */ + void writeByte(int value) throws IOException; + + /** + * This method writes a Java char value to an output stream. The + * char to be written will be in the lowest 16 bits of the int + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + *

    + * byte0 = (byte)((value & 0xFF00) >> 8);
    + * byte1 = (byte)(value & 0x00FF);
    + *

    + * + * The value written can be read using the readChar + * method in DataInput. + * + * @param value The char value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readChar + */ + void writeChar(int value) throws IOException; + + /** + * This method writes a Java short value to an output stream. The + * char to be written will be in the lowest 16 bits of the int + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + *

    + * byte0 = (byte)((value & 0xFF00) >> 8);
    + * byte1 = (byte)(value & 0x00FF);
    + *

    + * + * The value written can be read using the readShort and + * readUnsignedShort methods in DataInput. + * + * @param value The int value to write as a 16-bit value + * + * @exception IOException If an error occurs + * + * @see DataInput#readShort + * @see DataInput#readUnsignedShort + */ + void writeShort(int value) throws IOException; + + /** + * This method writes a Java int value to an output stream. The 4 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + *

    + * byte0 = (byte)((value & 0xFF000000) >> 24);
    + * byte1 = (byte)((value & 0x00FF0000) >> 16);
    + * byte2 = (byte)((value & 0x0000FF00) >> 8);
    + * byte3 = (byte)(value & 0x000000FF);
    + *

    + * + * The value written can be read using the readInt + * method in DataInput. + * + * @param value The int value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readInt + */ + void writeInt(int value) throws IOException; + + /** + * This method writes a Java long value to an output stream. The 8 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + *

    + * byte0 = (byte)((value & 0xFF00000000000000L) >> 56);
    + * byte1 = (byte)((value & 0x00FF000000000000L) >> 48);
    + * byte2 = (byte)((value & 0x0000FF0000000000L) >> 40);
    + * byte3 = (byte)((value & 0x000000FF00000000L) >> 32);
    + * byte4 = (byte)((value & 0x00000000FF000000L) >> 24);
    + * byte5 = (byte)((value & 0x0000000000FF0000L) >> 16);
    + * byte6 = (byte)((value & 0x000000000000FF00L) >> 8);
    + * byte7 = (byte)(value & 0x00000000000000FFL);
    + *

    + * + * The value written can be read using the readLong + * method in DataInput. + * + * @param value The long value to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readLong + */ + void writeLong(long value) throws IOException; + + /** + * This method writes a Java float value to the stream. This + * value is written by first calling the method + * Float.floatToIntBits + * to retrieve an int representing the floating point number, + * then writing this int value to the stream exactly the same + * as the writeInt() method does. + * + * The value written can be read using the readFloat + * method in DataInput. + * + * @param value The float value to write + * + * @exception IOException If an error occurs + * + * @see #writeInt + * @see DataInput#readFloat + * @see Float#floatToIntBits + */ + void writeFloat(float value) throws IOException; + + /** + * This method writes a Java double value to the stream. This + * value is written by first calling the method + * Double.doubleToLongBits + * to retrieve an long representing the floating point number, + * then writing this long value to the stream exactly the same + * as the writeLong() method does. + * + * The value written can be read using the readDouble + * method in DataInput. + * + * @param value The double value to write + * + * @exception IOException If any other error occurs + * + * @see #writeLong + * @see DataInput#readDouble + * @see Double#doubleToLongBits + */ + void writeDouble(double value) throws IOException; + + /** + * This method writes all the bytes in a String out to the + * stream. One byte is written for each character in the + * String. + * The high eight bits of each character are discarded, thus this + * method is inappropriate for completely representing Unicode characters. + * + * @param value The String to write + * + * @exception IOException If an error occurs + */ + void writeBytes(String value) throws IOException; + + /** + * This method writes all the characters of a String to an + * output stream as an array of char's. Each character + * is written using the method specified in the writeChar + * method. + * + * @param value The String to write + * + * @exception IOException If an error occurs + * + * @see #writeChar(int) + */ + void writeChars(String value) throws IOException; + + /** + * This method writes a Java String to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. This is written in the form of a Java + * short value in the same manner used by the + * writeShort method. Note that this is the number of + * bytes in the + * encoded String not the String length. Next + * come the encoded characters. Each character in the String + * is encoded as either one, two or three bytes. For characters in the + * range of \u0001 to \u007F, one byte is used. + * The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of \u0080 to \u007FF, two bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * \u0000. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * The value written can be read using the readUTF + * method in DataInput. + * + * @param value The String to write + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + void writeUTF(String value) throws IOException; + + /** + * This method writes an 8-bit value (passed into the method as a Java + * int) to an output stream. The low 8 bits of the + * passed value are written. + * + * @param value The byte to write to the output stream + * + * @exception IOException If an error occurs + */ + void write(int value) throws IOException; + + /** + * This method writes the raw byte array passed in to the output stream. + * + * @param buf The byte array to write + * + * @exception IOException If an error occurs + */ + void write(byte[] buf) throws IOException; + + /** + * This method writes raw bytes from the passed array buf + * starting + * offset bytes into the buffer. The number of bytes + * written will be exactly len. + * + * @param buf The buffer from which to write the data + * @param offset The offset into the buffer to start writing data from + * @param len The number of bytes to write from the buffer to the output + * stream + * + * @exception IOException If any other error occurs + */ + void write(byte[] buf, int offset, int len) throws IOException; + +} // interface DataOutput diff --git a/libjava/classpath/java/io/DataOutputStream.java b/libjava/classpath/java/io/DataOutputStream.java new file mode 100644 index 000000000..4837795c6 --- /dev/null +++ b/libjava/classpath/java/io/DataOutputStream.java @@ -0,0 +1,539 @@ +/* DataOutputStream.java -- Writes primitive Java datatypes to streams + Copyright (C) 1998, 2001, 2003, 2005, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class provides a mechanism for writing primitive Java datatypes + * to an OutputStream in a portable way. Data written to + * a stream using this class can be read back in using the + * DataInputStream class on any platform. + * + * @see DataInputStream + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class DataOutputStream extends FilterOutputStream implements DataOutput +{ + /** + * This is the total number of bytes that have been written to the + * stream by this object instance. + */ + protected int written; + + /** + * Utf8 byte buffer, used by writeUTF() + */ + private byte[] buf; + + /** + * This method initializes an instance of DataOutputStream to + * write its data to the specified underlying OutputStream + * + * @param out The subordinate OutputStream to which this + * object will write + */ + public DataOutputStream (OutputStream out) + { + super (out); + written = 0; + } + + /** + * This method flushes any unwritten bytes to the underlying stream. + * + * @exception IOException If an error occurs. + */ + public void flush () throws IOException + { + out.flush(); + } + + /** + * This method returns the total number of bytes that have been written to + * the underlying output stream so far. This is the value of the + * written instance variable + * + * @return The number of bytes written to the stream. + */ + public final int size () + { + return written; + } + + /** + * This method writes the specified byte (passed as an int) + * to the underlying output stream. + * + * @param value The byte to write, passed as an int. + * + * @exception IOException If an error occurs. + */ + public synchronized void write (int value) throws IOException + { + out.write (value); + ++written; + } + + /** + * This method writes len bytes from the specified byte array + * buf starting at position offset into the + * buffer to the underlying output stream. + * + * @param buf The byte array to write from. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs. + */ + public synchronized void write (byte[] buf, int offset, int len) + throws IOException + { + out.write(buf, offset, len); + written += len; + } + + /** + * This method writes a Java boolean value to an output stream. If + * value is true, a byte with the value of + * 1 will be written, otherwise a byte with the value of 0 will be + * written. + * + * The value written can be read using the readBoolean + * method in DataInput. + * + * @param value The boolean value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readBoolean + */ + public final void writeBoolean (boolean value) throws IOException + { + write (value ? 1 : 0); + } + + /** + * This method writes a Java byte value to an output stream. The + * byte to be written will be in the lowest 8 bits of the + * int value passed. + * + * The value written can be read using the readByte or + * readUnsignedByte methods in DataInput. + * + * @param value The byte to write to the stream, passed as + * the low eight bits of an int. + * + * @exception IOException If an error occurs + * + * @see DataInput#readByte + * @see DataInput#readUnsignedByte + */ + public final void writeByte (int value) throws IOException + { + write (value & 0xff); + } + + /** + * This method writes a Java short value to an output stream. The + * char to be written will be in the lowest 16 bits of the int + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + *

    + * byte0 = (byte)((value & 0xFF00) >> 8);
    + * byte1 = (byte)(value & 0x00FF);
    + *

    + * + * The value written can be read using the readShort and + * readUnsignedShort methods in DataInput. + * + * @param value The short value to write to the stream, + * passed as an int. + * + * @exception IOException If an error occurs + * + * @see DataInput#readShort + * @see DataInput#readUnsignedShort + */ + public final synchronized void writeShort (int value) throws IOException + { + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java char value to an output stream. The + * char to be written will be in the lowest 16 bits of the int + * value passed. These bytes will be written "big endian". That is, + * with the high byte written first in the following manner: + *

    + * byte0 = (byte)((value & 0xFF00) >> 8);
    + * byte1 = (byte)(value & 0x00FF);
    + *

    + * + * The value written can be read using the readChar + * method in DataInput. + * + * @param value The char value to write, + * passed as an int. + * + * @exception IOException If an error occurs + * + * @see DataInput#readChar + */ + public final synchronized void writeChar (int value) throws IOException + { + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java int value to an output stream. The 4 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + *

    + * byte0 = (byte)((value & 0xFF000000) >> 24);
    + * byte1 = (byte)((value & 0x00FF0000) >> 16);
    + * byte2 = (byte)((value & 0x0000FF00) >> 8);
    + * byte3 = (byte)(value & 0x000000FF);
    + *

    + * + * The value written can be read using the readInt + * method in DataInput. + * + * @param value The int value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readInt + */ + public final synchronized void writeInt (int value) throws IOException + { + write ((byte) (0xff & (value >> 24))); + write ((byte) (0xff & (value >> 16))); + write ((byte) (0xff & (value >> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java long value to an output stream. The 8 bytes + * of the passed value will be written "big endian". That is, with + * the high byte written first in the following manner: + *

    + * byte0 = (byte)((value & 0xFF00000000000000L) >> 56);
    + * byte1 = (byte)((value & 0x00FF000000000000L) >> 48);
    + * byte2 = (byte)((value & 0x0000FF0000000000L) >> 40);
    + * byte3 = (byte)((value & 0x000000FF00000000L) >> 32);
    + * byte4 = (byte)((value & 0x00000000FF000000L) >> 24);
    + * byte5 = (byte)((value & 0x0000000000FF0000L) >> 16);
    + * byte6 = (byte)((value & 0x000000000000FF00L) >> 8);
    + * byte7 = (byte)(value & 0x00000000000000FFL);
    + *

    + * + * The value written can be read using the readLong + * method in DataInput. + * + * @param value The long value to write to the stream + * + * @exception IOException If an error occurs + * + * @see DataInput#readLong + */ + public final synchronized void writeLong (long value) throws IOException + { + write ((byte) (0xff & (value >> 56))); + write ((byte) (0xff & (value>> 48))); + write ((byte) (0xff & (value>> 40))); + write ((byte) (0xff & (value>> 32))); + write ((byte) (0xff & (value>> 24))); + write ((byte) (0xff & (value>> 16))); + write ((byte) (0xff & (value>> 8))); + write ((byte) (0xff & value)); + } + + /** + * This method writes a Java float value to the stream. This + * value is written by first calling the method + * Float.floatToIntBits + * to retrieve an int representing the floating point number, + * then writing this int value to the stream exactly the same + * as the writeInt() method does. + * + * The value written can be read using the readFloat + * method in DataInput. + * + * @param value The float value to write to the stream + * + * @exception IOException If an error occurs + * + * @see #writeInt(int) + * @see DataInput#readFloat + * @see Float#floatToIntBits + */ + public final void writeFloat (float value) throws IOException + { + writeInt (Float.floatToIntBits (value)); + } + + /** + * This method writes a Java double value to the stream. This + * value is written by first calling the method + * Double.doubleToLongBits + * to retrieve an long representing the floating point number, + * then writing this long value to the stream exactly the same + * as the writeLong() method does. + * + * The value written can be read using the readDouble + * method in DataInput. + * + * @param value The double value to write to the stream + * + * @exception IOException If an error occurs + * + * @see #writeLong(long) + * @see DataInput#readDouble + * @see Double#doubleToLongBits + */ + public final void writeDouble (double value) throws IOException + { + writeLong (Double.doubleToLongBits (value)); + } + + /** + * This method writes all the bytes in a String out to the + * stream. One byte is written for each character in the + * String. + * The high eight bits of each character are discarded, thus this + * method is inappropriate for completely representing Unicode characters. + * + * @param value The String to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBytes (String value) throws IOException + { + int len = value.length(); + for (int i = 0; i < len; ++i) + writeByte (value.charAt(i)); + } + + /** + * This method writes all the characters of a String to an + * output stream as an array of char's. Each character + * is written using the method specified in the writeChar + * method. + * + * @param value The String to write to the stream + * + * @exception IOException If an error occurs + * + * @see #writeChar(char) + */ + public final void writeChars (String value) throws IOException + { + int len = value.length(); + for (int i = 0; i < len; ++i) + writeChar (value.charAt(i)); + } + + /** + * Calculate the length, in bytes, of a String in Utf8 format. + * This method is package-private so that ObjectOutputStream + * may use it. The return type is long so that a long string whose + * Utf8 byte count is 64 bit long may be handled. + * + * @param value The String to measure + * @param start String index at which to begin count + * @param sum Starting Utf8 byte count + * + */ + long getUTFlength(String value, int start, long sum) + { + int len = value.length(); + + for (int i = start; i < len; ++i) + { + char c = value.charAt(i); + if (c >= '\u0001' && c <= '\u007f') + sum += 1; + else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff')) + sum += 2; + else + sum += 3; + } + + return sum; + } + + /** + * This method writes a Java String to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. Note that this is the number of bytes in the + * encoded String not the String length. Next + * come the encoded characters. Each character in the String + * is encoded as either one, two or three bytes. For characters in the + * range of \u0001 to <\u007F>, one byte is used. The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of \u0080 to \u007FF, two bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * \u0000. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * The value written can be read using the readUTF + * method in DataInput. + * + * @param value The String to write to the output in UTF format + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + public final synchronized void writeUTF(String value) throws IOException + { + long l = getUTFlength(value, 0, 0); + if (l > 65535) + throw new UTFDataFormatException (); + writeUTFShort(value, (int)l); + } + + /** + * This method performs the main task of writeUTF. + * This method is package-private because ObjectOutputStream uses it. + * + * @param value The String to write to the output in UTF format + * + * @param bytelen The UTF-8 byte length of the String. When + * this method is called, the expected byte length must have been calculated + * by getUTFlength. + * + * @exception IOException If an error occurs + * + * @see DataInput#readUTF + */ + final synchronized void writeUTFShort(String value, int bytelen) + throws IOException + { + writeShort(bytelen); + writeUTFBytes(value); + } + + /** + * This method is similar to writeUTF, but it writes the + * UTF-8 byte length in 64 bits. + * This method is not public but ObjectOutputStream uses it. + * + * @param value The String to write to the output in UTF format + * + * @param bytelen The UTF-8 byte length of the String. When + * this method is called, the expected byte length must have been calculated + * by getUTFlength. + * + * @exception IOException If an error occurs + * + */ + final synchronized void writeUTFLong(String value, long bytelen) + throws IOException + { + writeLong(bytelen); + writeUTFBytes(value); + } + + /** + * This method performes the main task of writeUTF and + * WriteUTFLong, which is to write the UTF-8 byte + * sequence to the output. + * + * @param value The String to write to the output in UTF format + * + * @exception IOException If an error occurs + * + */ + private final synchronized void writeUTFBytes(String value) + throws IOException + { + int len = value.length(); + int i = 0; + int pos = 0; + + if (buf == null) + buf = new byte[512]; + + do + { + while (i < len && pos < buf.length - 3) + { + char c = value.charAt(i++); + if (c >= '\u0001' && c <= '\u007f') + buf[pos++] = (byte) c; + else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07ff')) + { + buf[pos++] = (byte) (0xc0 | (0x1f & (c >> 6))); + buf[pos++] = (byte) (0x80 | (0x3f & c)); + } + else + { + // JSL says the first byte should be or'd with 0xc0, but + // that is a typo. Unicode says 0xe0, and that is what is + // consistent with DataInputStream. + buf[pos++] = (byte) (0xe0 | (0x0f & (c >> 12))); + buf[pos++] = (byte) (0x80 | (0x3f & (c >> 6))); + buf[pos++] = (byte) (0x80 | (0x3f & c)); + } + } + write(buf, 0, pos); + pos = 0; + } + while (i < len); + } + +} // class DataOutputStream diff --git a/libjava/classpath/java/io/DeleteFileHelper.java b/libjava/classpath/java/io/DeleteFileHelper.java new file mode 100644 index 000000000..a2b96bb41 --- /dev/null +++ b/libjava/classpath/java/io/DeleteFileHelper.java @@ -0,0 +1,106 @@ +/* DeleteFileHelper.java -- Helper class to delete files on VM exit + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; + +/** + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Jeroen Frijters (jeroen@sumatra.nl) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +final class DeleteFileHelper extends Thread +{ + private static ArrayList filesToDelete; + + static synchronized void add(File file) + { + if (filesToDelete == null) + { + filesToDelete = new ArrayList(); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + try + { + Runtime.getRuntime().addShutdownHook(new DeleteFileHelper()); + } + catch (IllegalStateException e) + { + // Shutdown is already in progress, so we can't + // register ours. + } + + return null; + } + }); + } + + filesToDelete.add(file); + } + + private static synchronized void deleteFiles() + { + for (File file : filesToDelete) + { + try + { + file.delete(); + } + catch (Exception e) + { + // Do nothing here. + } + } + } + + // Package-private to avoid a trampoline constructor. + DeleteFileHelper() + { + } + + public void run() + { + deleteFiles(); + } +} diff --git a/libjava/classpath/java/io/EOFException.java b/libjava/classpath/java/io/EOFException.java new file mode 100644 index 000000000..0d90a3f0f --- /dev/null +++ b/libjava/classpath/java/io/EOFException.java @@ -0,0 +1,76 @@ +/* EOFException.java -- unexpected end of file exception + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when the end of the file or stream was + * encountered unexpectedly. This is not the normal way that an EOF + * condition is reported; such as a special value like -1 being returned. + * However, certain types of streams expecting certain data in a certain + * format might reach EOF before reading their expected data pattern and + * thus throw this exception. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class EOFException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6433858223774886977L; + + /** + * Create an exception without a descriptive error message. + */ + public EOFException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public EOFException(String message) + { + super(message); + } +} // class EOFException diff --git a/libjava/classpath/java/io/Externalizable.java b/libjava/classpath/java/io/Externalizable.java new file mode 100644 index 000000000..7399dab3b --- /dev/null +++ b/libjava/classpath/java/io/Externalizable.java @@ -0,0 +1,107 @@ +/* Externalizable.java -- Interface for saving and restoring object data + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface provides a way that classes can completely control how + * the data of their object instances are written and read to and from + * streams. It has two methods which are used to write the data to a stream + * and to read the data from a stream. The read method must read the data + * in exactly the way it was written by the write method. + *

    + * Note that classes which implement this interface must take into account + * that all superclass data must also be written to the stream as well. + * The class implementing this interface must figure out how to make that + * happen. + *

    + * This interface can be used to provide object persistence. When an + * object is to be stored externally, the writeExternal method is + * called to save state. When the object is restored, an instance is + * created using the default no-argument constructor and the + * readExternal method is used to restore the state. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Externalizable extends Serializable +{ + /** + * This method restores an object's state by reading in the instance data + * for the object from the passed in stream. Note that this stream is not + * a subclass of InputStream, but rather is a class that + * implements + * the ObjectInput interface. That interface provides a + * mechanism for + * reading in Java data types from a stream. + *

    + * Note that this method must be compatible with writeExternal. + * It must read back the exact same types that were written by that + * method in the exact order they were written. + *

    + * If this method needs to read back an object instance, then the class + * for that object must be found and loaded. If that operation fails, + * then this method throws a ClassNotFoundException + * + * @param in An ObjectInput instance for reading in the object + * state + * + * @exception ClassNotFoundException If the class of an object being + * restored cannot be found + * @exception IOException If any other error occurs + */ + void readExternal(ObjectInput in) + throws ClassNotFoundException, IOException; + + /** + * This method is responsible for writing the instance data of an object + * to the passed in stream. Note that this stream is not a subclass of + * OutputStream, but rather is a class that implements the + * ObjectOutput interface. That interface provides a + * number of methods + * for writing Java data values to a stream. + *

    + * Not that the implementation of this method must be coordinated with + * the implementation of readExternal. + * + * @param out An ObjectOutput instance for writing the + * object state + * + * @exception IOException If an error occurs + */ + void writeExternal(ObjectOutput out) throws IOException; +} diff --git a/libjava/classpath/java/io/File.java b/libjava/classpath/java/io/File.java new file mode 100644 index 000000000..4f670e147 --- /dev/null +++ b/libjava/classpath/java/io/File.java @@ -0,0 +1,1605 @@ +/* File.java -- Class representing a file on disk + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.CPStringBuilder; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.3. + */ + +/** + * This class represents a file or directory on a local disk. It provides + * facilities for dealing with a variety of systems that use various + * types of path separators ("/" versus "\", for example). It also + * contains method useful for creating and deleting files and directories. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class File implements Serializable, Comparable +{ + private static final long serialVersionUID = 301077366599181567L; + + /** + * This is the path separator string for the current host. This field + * contains the value of the file.separator system property. + * An example separator string would be "/" on the GNU system. + */ + public static final String separator = SystemProperties.getProperty("file.separator"); + private static final String dupSeparator = separator + separator; + + /** + * This is the first character of the file separator string. On many + * hosts (for example, on the GNU system), this represents the entire + * separator string. The complete separator string is obtained from the + * file.separatorsystem property. + */ + public static final char separatorChar = separator.charAt(0); + + /** + * This is the string that is used to separate the host name from the + * path name in paths that include the host name. It is the value of + * the path.separator system property. + */ + public static final String pathSeparator + = SystemProperties.getProperty("path.separator"); + + /** + * This is the first character of the string used to separate the host name + * from the path name in paths that include a host. The separator string + * is taken from the path.separator system property. + */ + public static final char pathSeparatorChar = pathSeparator.charAt(0); + + /** + * This is the path to the file set when the object is created. It + * may be an absolute or relative path name. + */ + private String path; + + + /** + * The time (millisecond), when the last temporary file was created. + */ + private static long last_tmp; + + /** + * The number of files, created during the current millisecond. + */ + private static int n_created; + + /** + * This method tests whether or not the current thread is allowed to + * to read the file pointed to by this object. This will be true if and + * and only if 1) the file exists and 2) the SecurityManager + * (if any) allows access to the file via it's checkRead + * method 3) the file is readable. + * + * @return true if reading is allowed, + * false otherwise + * + * @exception SecurityException If the SecurityManager + * does not allow access to the file + */ + public boolean canRead() + { + // Test for existence. This also does the SecurityManager check + if (!exists()) + return false; + + return VMFile.canRead(path); + } + + /** + * This method test whether or not the current thread is allowed to + * write to this object. This will be true if and only if 1) The + * SecurityManager (if any) allows write access to the + * file and 2) The file exists and 3) The file is writable. To determine + * whether or not a non-existent file can be created, check the parent + * directory for write access. + * + * @return true if writing is allowed, false + * otherwise + * + * @exception SecurityException If the SecurityManager + * does not allow access to the file + */ + public boolean canWrite() + { + // First do a SecurityCheck before doing anything else. + checkWrite(); + + // Test for existence. This is required by the spec + if (! VMFile.exists(path)) + return false; + + if (VMFile.isDirectory(path)) + return VMFile.canWriteDirectory(path); + else + return VMFile.canWrite(path); + } + + /** + * This method tests whether or not the current thread is allowed to + * to execute the file pointed to by this object. This will be true if and + * and only if 1) the file exists and 2) the SecurityManager + * (if any) allows access to the file via it's checkExec + * method 3) the file is executable. + * + * @return true if execution is allowed, + * false otherwise + * + * @exception SecurityException If the SecurityManager + * does not allow access to the file + */ + public boolean canExecute() + { + if (!VMFile.exists(path)) + return false; + + checkExec(); + + return VMFile.canExecute(path); + } + + /** + * This method creates a new file of zero length with the same name as + * the path of this File object if an only if that file + * does not already exist. + *

    + * A SecurityManager.checkWrite check is done prior + * to performing this action. + * + * @return true if the file was created, false if + * the file alread existed. + * + * @exception IOException If an I/O error occurs + * @exception SecurityException If the SecurityManager will + * not allow this operation to be performed. + * + * @since 1.2 + */ + public boolean createNewFile() throws IOException + { + checkWrite(); + return VMFile.create(path); + } + /** + * This method deletes the file represented by this object. If this file + * is a directory, it must be empty in order for the delete to succeed. + * + * @return true if the file was deleted, false + * otherwise + * + * @exception SecurityException If deleting of the file is not allowed + */ + public synchronized boolean delete() + { + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkDelete(path); + + return VMFile.delete(path); + } + + /** + * This method tests two File objects for equality by + * comparing the path of the specified File against the path + * of this object. The two objects are equal if an only if 1) The + * argument is not null 2) The argument is a File object and + * 3) The path of the Fileargument is equal to the path + * of this object. + *

    + * The paths of the files are determined by calling the + * getPath() + * method on each object. + * + * @return true if the two objects are equal, + * false otherwise. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof File)) + return false; + + File other = (File) obj; + + if (VMFile.IS_CASE_SENSITIVE) + return path.equals(other.path); + else + return path.equalsIgnoreCase(other.path); + } + + /** + * This method tests whether or not the file represented by the object + * actually exists on the filesystem. + * + * @return true if the file exists, falseotherwise. + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean exists() + { + checkRead(); + return VMFile.exists(path); + } + + /** + * This method initializes a new File object to represent + * a file with the specified path. + * + * @param name The path name of the file + */ + public File(String name) + { + path = normalizePath (name); + } + + // Remove duplicate and redundant separator characters. + private String normalizePath(String p) + { + // On Windows, convert any '/' to '\'. This appears to be the same logic + // that Sun's Win32 Java performs. + if (separatorChar == '\\') + { + p = p.replace ('/', '\\'); + // We have to special case the "\c:" prefix. + if (p.length() > 2 && p.charAt(0) == '\\' && + ((p.charAt(1) >= 'a' && p.charAt(1) <= 'z') || + (p.charAt(1) >= 'A' && p.charAt(1) <= 'Z')) && + p.charAt(2) == ':') + p = p.substring(1); + } + + int dupIndex = p.indexOf(dupSeparator); + int plen = p.length(); + + // Special case: permit Windows UNC path prefix. + if (dupSeparator.equals("\\\\") && dupIndex == 0) + dupIndex = p.indexOf(dupSeparator, 1); + + if (dupIndex == -1) + { + // Ignore trailing separator (though on Windows "a:\", for + // example, is a valid and minimal path). + if (plen > 1 && p.charAt (plen - 1) == separatorChar) + { + if (! (separatorChar == '\\' && ((plen == 3 && p.charAt(1) == ':') + || (plen == 2 && p.charAt(0) == separatorChar)))) + return p.substring (0, plen - 1); + } + else + return p; + } + + CPStringBuilder newpath = new CPStringBuilder(plen); + int last = 0; + while (dupIndex != -1) + { + newpath.append(p.substring(last, dupIndex)); + // Ignore the duplicate path characters. + while (p.charAt(dupIndex) == separatorChar) + { + dupIndex++; + if (dupIndex == plen) + { + if ((separatorChar == '\\' + && newpath.length() == 2 + && newpath.charAt(1) == ':') + || (separatorChar != '\\' && newpath.length() == 0)) + { + newpath.append(separatorChar); + } + return newpath.toString(); + } + } + newpath.append(separatorChar); + last = dupIndex; + dupIndex = p.indexOf(dupSeparator, last); + } + + // Again, ignore possible trailing separator (except special cases + // like "a:\" on Windows). + int end; + if (plen > 1 && p.charAt (plen - 1) == separatorChar) + { + if (separatorChar == '\\' + && ((plen == 3 && p.charAt(1) == ':') + || (plen == 2 && p.charAt(0) == separatorChar))) + end = plen; + else + end = plen - 1; + } + else + end = plen; + newpath.append(p.substring(last, end)); + + return newpath.toString(); + } + + /** + * This method initializes a new File object to represent + * a file in the specified named directory. The path name to the file + * will be the directory name plus the separator string plus the file + * name. If the directory path name ends in the separator string, another + * separator string will still be appended. + * + * @param dirPath The path to the directory the file resides in + * @param name The name of the file + */ + public File(String dirPath, String name) + { + if (name == null) + throw new NullPointerException(); + if (dirPath != null) + { + if (dirPath.length() > 0) + { + // Try to be smart about the number of separator characters. + if (dirPath.charAt(dirPath.length() - 1) == separatorChar + || name.length() == 0) + path = normalizePath(dirPath + name); + else + path = normalizePath(dirPath + separatorChar + name); + } + else + { + // If dirPath is empty, use a system dependant + // default prefix. + // Note that the leading separators in name have + // to be chopped off, to prevent them forming + // a UNC prefix on Windows. + if (separatorChar == '\\' /* TODO use ON_WINDOWS */) + { + int skip = 0; + while(name.length() > skip + && (name.charAt(skip) == separatorChar + || name.charAt(skip) == '/')) + { + skip++; + } + name = name.substring(skip); + } + path = normalizePath(separatorChar + name); + } + } + else + path = normalizePath(name); + } + + /** + * This method initializes a new File object to represent + * a file in the specified directory. If the directory + * argument is null, the file is assumed to be in the + * current directory as specified by the user.dir system + * property + * + * @param directory The directory this file resides in + * @param name The name of the file + */ + public File(File directory, String name) + { + this (directory == null ? null : directory.path, name); + } + + /** + * This method initializes a new File object to represent + * a file corresponding to the specified file: protocol URI. + * + * @param uri The URI + * @throws IllegalArgumentException if the URI is not hierarchical + */ + public File(URI uri) + { + if (uri == null) + throw new NullPointerException("uri is null"); + + if (!uri.getScheme().equals("file")) + throw new IllegalArgumentException("invalid uri protocol"); + + String name = uri.getPath(); + if (name == null) + throw new IllegalArgumentException("URI \"" + uri + + "\" is not hierarchical"); + path = normalizePath(name); + } + + /** + * This method returns the path of this file as an absolute path name. + * If the path name is already absolute, then it is returned. Otherwise + * the value returned is the current directory plus the separatory + * string plus the path of the file. The current directory is determined + * from the user.dir system property. + * + * @return The absolute path of this file + */ + public String getAbsolutePath() + { + if (isAbsolute()) + return path; + else + return VMFile.getAbsolutePath(path); + } + + /** + * This method returns a File object representing the + * absolute path of this object. + * + * @return A File with the absolute path of the object. + * + * @since 1.2 + */ + public File getAbsoluteFile() + { + return new File(getAbsolutePath()); + } + + /** + * This method returns a canonical representation of the pathname of + * this file. The actual form of the canonical representation is + * system-dependent. On the GNU system, conversion to canonical + * form involves the removal of redundant separators, references to + * "." and "..", and symbolic links. + *

    + * Note that this method, unlike the other methods which return path + * names, can throw an IOException. This is because native method + * might be required in order to resolve the canonical path + * + * @exception IOException If an error occurs + */ + public String getCanonicalPath() throws IOException + { + // On Windows, getAbsolutePath might end up calling us, so we + // have to special case that call to avoid infinite recursion. + if (separatorChar == '\\' && path.length() == 2 && + ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || + (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && + path.charAt(1) == ':') + { + return VMFile.toCanonicalForm(path); + } + // Call getAbsolutePath first to make sure that we do the + // current directory handling, because the native code + // may have a different idea of the current directory. + return VMFile.toCanonicalForm(getAbsolutePath()); + } + + /** + * This method returns a File object representing the + * canonical path of this object. + * + * @return A File instance representing the canonical path of + * this object. + * + * @exception IOException If an error occurs. + * + * @since 1.2 + */ + public File getCanonicalFile() throws IOException + { + return new File(getCanonicalPath()); + } + + /** + * This method returns the name of the file. This is everything in the + * complete path of the file after the last instance of the separator + * string. + * + * @return The file name + */ + public String getName() + { + return VMFile.getName(path); + } + + /** + * This method returns a String the represents this file's + * parent. null is returned if the file has no parent. The + * parent is determined via a simple operation which removes the name + * after the last file separator character, as determined by the platform. + * + * @return The parent directory of this file + */ + public String getParent() + { + String prefix = null; + int nameSeqIndex = 0; + + if (path.equals("")) + return null; + + // The "prefix", if present, is the leading "/" on UNIX and + // either the drive specifier (e.g. "C:") or the leading "\\" + // of a UNC network path on Windows. + if (separatorChar == '/' && path.charAt (0) == '/') + { + prefix = "/"; + nameSeqIndex = 1; + } + else if (separatorChar == '\\' && path.length() > 1) + { + if ((path.charAt (0) == '\\' && path.charAt (1) == '\\') + || (((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') + || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z')) + && path.charAt (1) == ':')) + { + prefix = path.substring (0, 2); + nameSeqIndex = 2; + } + } + + // According to the JDK docs, the returned parent path is the + // portion of the name sequence before the last separator + // character, if found, prefixed by the prefix, otherwise null. + if (nameSeqIndex < path.length()) + { + String nameSeq = path.substring (nameSeqIndex, path.length()); + int last = nameSeq.lastIndexOf (separatorChar); + if (last == -1) + return prefix; + else if (last == (nameSeq.length() - 1)) + // Note: The path would not have a trailing separator + // except for cases like "C:\" on Windows (see + // normalizePath( )), where Sun's JRE 1.4 returns null. + return null; + else if (last == 0) + last++; + + if (prefix != null) + return prefix + nameSeq.substring (0, last); + else + return nameSeq.substring (0, last); + } + else + // Sun's JRE 1.4 returns null if the prefix is the only + // component of the path - so "/" gives null on UNIX and + // "C:", "\\", etc. return null on Windows. + return null; + } + + /** + * This method returns a File object representing the parent + * file of this one. + * + * @return a File for the parent of this object. + * null + * will be returned if this object does not have a parent. + * + * @since 1.2 + */ + public File getParentFile() + { + String parent = getParent(); + return parent != null ? new File(parent) : null; + } + + /** + * Returns the path name that represents this file. May be a relative + * or an absolute path name + * + * @return The pathname of this file + */ + public String getPath() + { + return path; + } + + /** + * This method returns a hash code representing this file. It is the + * hash code of the path of this file (as returned by getPath()) + * exclusived or-ed with the value 1234321. + * + * @return The hash code for this object + */ + public int hashCode() + { + if (VMFile.IS_CASE_SENSITIVE) + return path.hashCode() ^ 1234321; + else + return path.toLowerCase().hashCode() ^ 1234321; + } + + /** + * This method returns true if this object represents an absolute file + * path and false if it does not. The definition of an absolute path varies + * by system. As an example, on GNU systems, a path is absolute if it starts + * with a "/". + * + * @return true if this object represents an absolute + * file name, false otherwise. + */ + public boolean isAbsolute() + { + return VMFile.isAbsolute(path); + } + + /** + * This method tests whether or not the file represented by this object + * is a directory. In order for this method to return true, + * the file represented by this object must exist and be a directory. + * + * @return true if this file is a directory, false + * otherwise + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean isDirectory() + { + checkRead(); + return VMFile.isDirectory(path); + } + + /** + * This method tests whether or not the file represented by this object + * is a "plain" file. A file is a plain file if and only if it 1) Exists, + * 2) Is not a directory or other type of special file. + * + * @return true if this is a plain file, false + * otherwise + * + * @exception SecurityException If reading of the file is not permitted + */ + public boolean isFile() + { + checkRead(); + return VMFile.isFile(path); + } + + /** + * This method tests whether or not this file represents a "hidden" file. + * On GNU systems, a file is hidden if its name begins with a "." + * character. Files with these names are traditionally not shown with + * directory listing tools. + * + * @return true if the file is hidden, false + * otherwise. + * + * @since 1.2 + */ + public boolean isHidden() + { + return VMFile.isHidden(path); + } + + /** + * This method returns the last modification time of this file. The + * time value returned is an abstract value that should not be interpreted + * as a specified time value. It is only useful for comparing to other + * such time values returned on the same system. In that case, the larger + * value indicates a more recent modification time. + *

    + * If the file does not exist, then a value of 0 is returned. + * + * @return The last modification time of the file + * + * @exception SecurityException If reading of the file is not permitted + */ + public long lastModified() + { + checkRead(); + return VMFile.lastModified(path); + } + + /** + * This method returns the length of the file represented by this object, + * or 0 if the specified file does not exist. + * + * @return The length of the file + * + * @exception SecurityException If reading of the file is not permitted + */ + public long length() + { + checkRead(); + return VMFile.length(path); + } + + /** + * This method returns a array of String's representing the + * list of files is then directory represented by this object. If this + * object represents a non-directory file or a non-existent file, then + * null is returned. The list of files will not contain + * any names such as "." or ".." which indicate the current or parent + * directory. Also, the names are not guaranteed to be sorted. + *

    + * In this form of the list() method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The FilenameFilter specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + *

    + * A SecurityManager check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @param filter An object which will identify files to exclude from + * the directory listing. + * + * @return An array of files in the directory, or null + * if this object does not represent a valid directory. + * + * @exception SecurityException If read access is not allowed to the + * directory by the SecurityManager + */ + public String[] list(FilenameFilter filter) + { + checkRead(); + + if (!exists() || !isDirectory()) + return null; + + // Get the list of files + String files[] = VMFile.list(path); + + // Check if an error occured in listInternal(). + // This is an unreadable directory, pretend there is nothing inside. + if (files == null) + return new String[0]; + + if (filter == null) + return files; + + // Apply the filter + int count = 0; + for (int i = 0; i < files.length; i++) + { + if (filter.accept(this, files[i])) + ++count; + else + files[i] = null; + } + + String[] retfiles = new String[count]; + count = 0; + for (int i = 0; i < files.length; i++) + if (files[i] != null) + retfiles[count++] = files[i]; + + return retfiles; + } + + /** + * This method returns a array of String's representing the + * list of files is then directory represented by this object. If this + * object represents a non-directory file or a non-existent file, then + * null is returned. The list of files will not contain + * any names such as "." or ".." which indicate the current or parent + * directory. Also, the names are not guaranteed to be sorted. + *

    + * A SecurityManager check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of files in the directory, or null if + * this object does not represent a valid directory. + * + * @exception SecurityException If read access is not allowed to the + * directory by the SecurityManager + */ + public String[] list() + { + return list(null); + } + + /** + * This method returns an array of File objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, null is returned. + * Each of the returned File object is constructed with this + * object as its parent. + *

    + * A SecurityManager check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of File objects for this directory. + * + * @exception SecurityException If the SecurityManager denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles() + { + return listFiles((FilenameFilter) null); + } + + /** + * This method returns an array of File objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, null is returned. + * Each of the returned File object is constructed with this + * object as its parent. + *

    + * In this form of the listFiles() method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The FilenameFilter specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + *

    + * A SecurityManager check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of File objects for this directory. + * + * @exception SecurityException If the SecurityManager denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles(FilenameFilter filter) + { + String[] filelist = list(filter); + + if (filelist == null) + return null; + + File[] fobjlist = new File [filelist.length]; + + for (int i = 0; i < filelist.length; i++) + fobjlist [i] = new File(this, filelist [i]); + + return fobjlist; + } + + /** + * This method returns an array of File objects representing + * all the files in the directory represented by this object. If this + * object does not represent a directory, null is returned. + * Each of the returned File object is constructed with this + * object as its parent. + *

    + * In this form of the listFiles() method, a filter is specified + * that allows the caller to control which files are returned in the + * list. The FileFilter specified is called for each + * file returned to determine whether or not that file should be included + * in the list. + *

    + * A SecurityManager check is made prior to reading the + * directory. If read access to the directory is denied, an exception + * will be thrown. + * + * @return An array of File objects for this directory. + * + * @exception SecurityException If the SecurityManager denies + * access to this directory. + * + * @since 1.2 + */ + public File[] listFiles(FileFilter filter) + { + File[] fobjlist = listFiles((FilenameFilter) null); + + if (fobjlist == null) + return null; + + if (filter == null) + return fobjlist; + + int count = 0; + for (int i = 0; i < fobjlist.length; i++) + if (filter.accept(fobjlist[i]) == true) + ++count; + + File[] final_list = new File[count]; + count = 0; + for (int i = 0; i < fobjlist.length; i++) + if (filter.accept(fobjlist[i]) == true) + { + final_list[count] = fobjlist[i]; + ++count; + } + + return final_list; + } + + /** + * This method returns a String that is the path name of the + * file as returned by getPath. + * + * @return A String representation of this file + */ + public String toString() + { + return path; + } + + /** + * @return A URI for this object. + */ + public URI toURI() + { + String abspath = getAbsolutePath(); + + if (isDirectory() || path.equals("")) + abspath = abspath + separatorChar; + + if (separatorChar == '\\') + abspath = separatorChar + abspath; + + try + { + return new URI("file", null, null, -1, + abspath.replace(separatorChar, '/'), + null, null); + } + catch (URISyntaxException use) + { + // Can't happen. + throw (InternalError) new InternalError("Unconvertible file: " + + this).initCause(use); + } + } + + /** + * This method returns a URL with the file: + * protocol that represents this file. The exact form of this URL is + * system dependent. + * + * @return A URL for this object. + * + * @exception MalformedURLException If the URL cannot be created + * successfully. + */ + public URL toURL() throws MalformedURLException + { + return VMFile.toURL(this); + } + + + /** + * This method creates a directory for the path represented by this object. + * + * @return true if the directory was created, + * false otherwise + * + * @exception SecurityException If write access is not allowed to this file + */ + public boolean mkdir() + { + checkWrite(); + return VMFile.mkdir(path); + } + + /** + * This method creates a directory for the path represented by this file. + * It will also create any intervening parent directories if necessary. + * + * @return true if the directory was created, + * false otherwise + * + * @exception SecurityException If write access is not allowed to this file + */ + public boolean mkdirs() + { + String parent = getParent(); + if (parent == null) + { + return mkdir(); + } + + File f = new File(parent); + if (!f.exists()) + { + boolean rc = f.mkdirs(); + if (rc == false) + return false; + } + + return mkdir(); + } + + /** + * This method creates a temporary file in the specified directory. If + * the directory name is null, then this method uses the system temporary + * directory. The files created are guaranteed not to currently exist and + * the same file name will never be used twice in the same virtual + * machine instance. + * The system temporary directory is determined by examinging the + * java.io.tmpdir system property. + *

    + * The prefix parameter is a sequence of at least three + * characters that are used as the start of the generated filename. The + * suffix parameter is a sequence of characters that is used + * to terminate the file name. This parameter may be null + * and if it is, the suffix defaults to ".tmp". + *

    + * If a SecurityManager exists, then its checkWrite + * method is used to verify that this operation is permitted. + * + * @param prefix The character prefix to use in generating the path name. + * @param suffix The character suffix to use in generating the path name. + * @param directory The directory to create the file in, or + * null for the default temporary directory + * + * @exception IllegalArgumentException If the patterns is not valid + * @exception SecurityException If there is no permission to perform + * this operation + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public static synchronized File createTempFile(String prefix, String suffix, + File directory) + throws IOException + { + // Grab the system temp directory if necessary + if (directory == null) + { + String dirname = System.getProperty("java.io.tmpdir"); + if (dirname == null) + throw new IOException("Cannot determine system temporary directory"); + + directory = new File(dirname); + if (! VMFile.exists(directory.path)) + throw new IOException("System temporary directory " + + directory.getName() + " does not exist."); + if (! VMFile.isDirectory(directory.path)) + throw new IOException("System temporary directory " + + directory.getName() + + " is not really a directory."); + } + + // Check if prefix is at least 3 characters long + if (prefix.length() < 3) + throw new IllegalArgumentException("Prefix too short: " + prefix); + + // Set default value of suffix + if (suffix == null) + suffix = ".tmp"; + + // Now identify a file name and make sure it doesn't exist. + File file; + if (!VMFile.IS_DOS_8_3) + { + do + { + long now = System.currentTimeMillis(); + if (now > last_tmp) + { + // The last temporary file was created more than 1 ms ago. + last_tmp = now; + n_created = 0; + } + else + n_created++; + + String name = Long.toHexString(now); + if (n_created > 0) + name += '_'+Integer.toHexString(n_created); + String filename = prefix + name + suffix; + file = new File(directory, filename); + } + while (VMFile.exists(file.path)); + } + else + { + // make sure prefix is not longer than 7 characters + if (prefix.length() >= 8) + throw new IllegalArgumentException("Prefix too long: " + prefix + "(valid length 3..7)"); + + long mask = 0x000000ffffFFFFL >> (prefix.length() * 4); + do + { + int n = (int) (System.currentTimeMillis() & mask); + String filename = prefix + java.lang.Integer.toHexString(n) + suffix; + file = new File(directory, filename); + } + while (VMFile.exists(file.path)); + } + + // Verify that we are allowed to create this file + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkWrite(file.getAbsolutePath()); + + // Now create the file and return our file object + // XXX - FIXME race condition. + VMFile.create(file.getAbsolutePath()); + return file; + } + + /** + * This method sets the owner's read permission for the File represented by + * this object. + * + * It is the same as calling setReadable(readable, true). + * + * @param readable true to set read permission, + * false to unset the read permission. + * @return true if the file permissions are changed, + * false otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setReadable(boolean, boolean) + * @since 1.6 + */ + public boolean setReadable(boolean readable) + { + return setReadable(readable, true); + } + + /** + * This method sets the read permissions for the File represented by + * this object. + * + * If ownerOnly is set to true then only the + * read permission bit for the owner of the file is changed. + * + * If ownerOnly is set to false, the file + * permissions are changed so that the file can be read by everyone. + * + * On unix like systems this sets the user, group + * and other read bits and is equal to call + * chmod a+r on the file. + * + * @param readable true to set read permission, + * false to unset the read permission. + * @param ownerOnly true to set read permission + * for owner only, false for all. + * @return true if the file permissions are changed, + * false otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setReadable(boolean) + * @since 1.6 + */ + public boolean setReadable(boolean readable, boolean ownerOnly) + { + checkWrite(); + return VMFile.setReadable(path, readable, ownerOnly); + } + + /** + * This method sets the owner's write permission for the File represented by + * this object. + * + * It is the same as calling setWritable(readable, true). + * + * @param writable true to set write permission, + * false to unset write permission. + * @return true if the file permissions are changed, + * false otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setWritable(boolean, boolean) + * @since 1.6 + */ + public boolean setWritable(boolean writable) + { + return setWritable(writable, true); + } + + /** + * This method sets the write permissions for the File represented by + * this object. + * + * If ownerOnly is set to true then only the + * write permission bit for the owner of the file is changed. + * + * If ownerOnly is set to false, the file + * permissions are changed so that the file can be written by everyone. + * + * On unix like systems this set the user, group + * and other write bits and is equal to call + * chmod a+w on the file. + * + * @param writable true to set write permission, + * false to unset write permission. + * @param ownerOnly true to set write permission + * for owner only, false for all. + * @return true if the file permissions are changed, + * false otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setWritable(boolean) + * @since 1.6 + */ + public boolean setWritable(boolean writable, boolean ownerOnly) + { + checkWrite(); + return VMFile.setWritable(path, writable, ownerOnly); + } + + /** + * This method sets the owner's execute permission for the File represented + * by this object. + * + * It is the same as calling setExecutable(readable, true). + * + * @param executable true to set execute permission, + * false to unset execute permission. + * @return true if the file permissions are changed, + * false otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setExecutable(boolean, boolean) + * @since 1.6 + */ + public boolean setExecutable(boolean executable) + { + return setExecutable(executable, true); + } + + /** + * This method sets the execute permissions for the File represented by + * this object. + * + * If ownerOnly is set to true then only the + * execute permission bit for the owner of the file is changed. + * + * If ownerOnly is set to false, the file + * permissions are changed so that the file can be executed by everyone. + * + * On unix like systems this set the user, group + * and other write bits and is equal to call + * chmod a+x on the file. + * + * @param executable true to set write permission, + * false to unset write permission. + * @param ownerOnly true to set write permission + * for owner only, false for all. + * @return true if the file permissions are changed, + * false otherwise. + * @exception SecurityException If write access of the file is not permitted. + * @see #setExecutable(boolean) + * @since 1.6 + */ + public boolean setExecutable(boolean executable, boolean ownerOnly) + { + checkWrite(); + return VMFile.setExecutable(path, executable, ownerOnly); + } + + /** + * Get the total space for the partition pointed by this file path, in bytes. + * + * @return the total number of bytes in this partition. + * @since 1.6 + */ + public long getTotalSpace() + { + // check security manager. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new RuntimePermission("getFileSystemAttributes")); + checkRead(); + + return VMFile.getTotalSpace(path); + } + + /** + * Get the free space in the partition pointed by this file path, in bytes. + * + * @return the number of free bytes in this partition. + * @since 1.6 + */ + public long getFreeSpace() + { + // check security manager. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new RuntimePermission("getFileSystemAttributes")); + checkRead(); + + return VMFile.getFreeSpace(path); + } + + /** + * Get the usable space in the partition pointed by this file path, in bytes. + * This is not necessarily the same as the number returned by + * {@link #getFreeSpace()}. + * + * Implementation note: Unlike the RI, on Linux and UNIX + * like systems this methods take into account the reserved space for the + * "root" user. This means that the returned results will be a little + * different if a normal user or root perform the query. + * + * Also, the bytes returned should be interpreted as an hint, and may be + * different at each call of this method or even right after the method + * returns. + * + * @return the number of usable bytes in this partition. + * @since 1.6 + */ + public long getUsableSpace() + { + // check security manager. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new RuntimePermission("getFileSystemAttributes")); + checkRead(); + + // root users can use the reserved extra space + String user = System.getProperty("user.name"); + if (user != null && user.equals("root")) + return VMFile.getFreeSpace(path); + + return VMFile.getUsableSpace(path); + } + + /** + * This method sets the file represented by this object to be read only. + * A read only file or directory cannot be modified. Please note that + * GNU systems allow read only files to be deleted if the directory it + * is contained in is writable. + * + * @return true if the operation succeeded, false + * otherwise. + * + * @exception SecurityException If the SecurityManager does + * not allow this operation. + * + * @since 1.2 + */ + public boolean setReadOnly() + { + // Do a security check before trying to do anything else. + checkWrite(); + + // Test for existence. + if (! VMFile.exists(path)) + return false; + + return VMFile.setReadOnly(path); + } + + /** + * This method returns an array of filesystem roots. Some operating systems + * have volume oriented filesystem. This method provides a mechanism for + * determining which volumes exist. GNU systems use a single hierarchical + * filesystem, so will have only one "/" filesystem root. + * + * @return An array of File objects for each filesystem root + * available. + * + * @since 1.2 + */ + public static File[] listRoots() + { + File[] roots = VMFile.listRoots(); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + // Only return roots to which the security manager permits read access. + int count = roots.length; + for (int i = 0; i < roots.length; i++) + { + try + { + s.checkRead (roots[i].path); + } + catch (SecurityException sx) + { + roots[i] = null; + count--; + } + } + if (count != roots.length) + { + File[] newRoots = new File[count]; + int k = 0; + for (int i = 0; i < roots.length; i++) + { + if (roots[i] != null) + newRoots[k++] = roots[i]; + } + roots = newRoots; + } + } + return roots; + } + + /** + * This method creates a temporary file in the system temporary directory. + * The files created are guaranteed not to currently exist and the same file + * name will never be used twice in the same virtual machine instance. The + * system temporary directory is determined by examinging the + * java.io.tmpdir system property. + *

    + * The prefix parameter is a sequence of at least three + * characters that are used as the start of the generated filename. The + * suffix parameter is a sequence of characters that is used + * to terminate the file name. This parameter may be null + * and if it is, the suffix defaults to ".tmp". + *

    + * If a SecurityManager exists, then its checkWrite + * method is used to verify that this operation is permitted. + *

    + * This method is identical to calling + * createTempFile(prefix, suffix, null). + * + * @param prefix The character prefix to use in generating the path name. + * @param suffix The character suffix to use in generating the path name. + * + * @exception IllegalArgumentException If the prefix or suffix are not valid. + * @exception SecurityException If there is no permission to perform + * this operation + * @exception IOException If an error occurs + */ + public static File createTempFile(String prefix, String suffix) + throws IOException + { + return createTempFile(prefix, suffix, null); + } + + /** + * This method compares the specified File to this one + * to test for equality. It does this by comparing the canonical path names + * of the files. + *

    + * The canonical paths of the files are determined by calling the + * getCanonicalPath method on each object. + *

    + * This method returns a 0 if the specified Object is equal + * to this one, a negative value if it is less than this one + * a positive value if it is greater than this one. + * + * @return An integer as described above + * + * @since 1.2 + */ + public int compareTo(File other) + { + if (VMFile.IS_CASE_SENSITIVE) + return path.compareTo (other.path); + else + return path.compareToIgnoreCase (other.path); + } + + /** + * This method renames the file represented by this object to the path + * of the file represented by the argument File. + * + * @param dest The File object representing the target name + * + * @return true if the rename succeeds, false + * otherwise. + * + * @exception SecurityException If write access is not allowed to the + * file by the SecurityMananger. + */ + public synchronized boolean renameTo(File dest) + { + checkWrite(); + dest.checkWrite(); + // Call our native rename method + return VMFile.renameTo(path, dest.path); + } + + /** + * This method sets the modification time on the file to the specified + * value. This is specified as the number of seconds since midnight + * on January 1, 1970 GMT. + * + * @param time The desired modification time. + * + * @return true if the operation succeeded, false + * otherwise. + * + * @exception IllegalArgumentException If the specified time is negative. + * @exception SecurityException If the SecurityManager will + * not allow this operation. + * + * @since 1.2 + */ + public boolean setLastModified(long time) + { + if (time < 0) + throw new IllegalArgumentException("Negative modification time: " + time); + + checkWrite(); + return VMFile.setLastModified(path, time); + } + + private void checkWrite() + { + // Check the SecurityManager + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkWrite(path); + } + + private void checkRead() + { + // Check the SecurityManager + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkRead(path); + } + + private void checkExec() + { + // Check the SecurityManager + SecurityManager s = System.getSecurityManager(); + + if (s != null) + s.checkExec(path); + } + + /** + * Calling this method requests that the file represented by this object + * be deleted when the virtual machine exits. Note that this request cannot + * be cancelled. Also, it will only be carried out if the virtual machine + * exits normally. + * + * @exception SecurityException If deleting of the file is not allowed + * + * @since 1.2 + */ + public void deleteOnExit() + { + // Check the SecurityManager + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkDelete(path); + + DeleteFileHelper.add(this); + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + oos.defaultWriteObject(); + oos.writeChar(separatorChar); + } + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException + { + ois.defaultReadObject(); + + // If the file was from an OS with a different dir separator, + // fixup the path to use the separator on this OS. + char oldSeparatorChar = ois.readChar(); + + if (oldSeparatorChar != separatorChar) + path = path.replace(oldSeparatorChar, separatorChar); + } + +} // class File diff --git a/libjava/classpath/java/io/FileDescriptor.java b/libjava/classpath/java/io/FileDescriptor.java new file mode 100644 index 000000000..ce58a9fb0 --- /dev/null +++ b/libjava/classpath/java/io/FileDescriptor.java @@ -0,0 +1,140 @@ +/* FileDescriptor.java -- Opaque file handle class + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.FileChannelImpl; + +import java.nio.channels.ByteChannel; +import java.nio.channels.FileChannel; + +/** + * This class represents an opaque file handle as a Java class. It should + * be used only to pass to other methods that expect an object of this + * type. No system specific information can be obtained from this object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date September 24, 1998 + */ +public final class FileDescriptor +{ + /** + * A FileDescriptor representing the system standard input + * stream. This will usually be accessed through the + * System.invariable. + */ + public static final FileDescriptor in + = new FileDescriptor (FileChannelImpl.in); + + /** + * A FileDescriptor representing the system standard output + * stream. This will usually be accessed through the + * System.outvariable. + */ + public static final FileDescriptor out + = new FileDescriptor (FileChannelImpl.out); + + /** + * A FileDescriptor representing the system standard error + * stream. This will usually be accessed through the + * System.errvariable. + */ + public static final FileDescriptor err + = new FileDescriptor (FileChannelImpl.err); + + final ByteChannel channel; + + /** + * This method is used to initialize an invalid FileDescriptor object. + */ + public FileDescriptor() + { + channel = null; + } + + /** + * This method is used to initialize a FileDescriptor object. + */ + FileDescriptor(ByteChannel channel) + { + this.channel = channel; + } + + + /** + * This method forces all data that has not yet been physically written to + * the underlying storage medium associated with this + * FileDescriptor + * to be written out. This method will not return until all data has + * been fully written to the underlying device. If the device does not + * support this functionality or if an error occurs, then an exception + * will be thrown. + */ + public void sync () throws SyncFailedException + { + if (channel instanceof FileChannel) + { + try + { + ((FileChannel) channel).force(true); + } + catch (IOException ex) + { + if (ex instanceof SyncFailedException) + throw (SyncFailedException) ex; + else + throw new SyncFailedException(ex.toString()); + } + } + } + + /** + * This methods tests whether or not this object represents a valid open + * native file handle. + * + * @return true if this object represents a valid + * native file handle, false otherwise + */ + public boolean valid () + { + ByteChannel c = channel; + return (c != null) && (c.isOpen()); + } +} diff --git a/libjava/classpath/java/io/FileFilter.java b/libjava/classpath/java/io/FileFilter.java new file mode 100644 index 000000000..087c5e9f0 --- /dev/null +++ b/libjava/classpath/java/io/FileFilter.java @@ -0,0 +1,65 @@ +/* FileFilter.java -- Filter a list of pathnames + Copyright (C) 1998,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface has one method which is used for filtering pathnames + * returned in a pathname listing. It is currently used by the + * File.listFiles(FileFilter) method. + *

    + * The method in this interface determines if a particular pathname should + * or should not be included in the pathname listing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see File#listFiles(java.io.FileFilter) + */ +public interface FileFilter +{ + /** + * This method determines whether or not a given pathname should be included + * in a pathname listing. + * + * @param pathname The pathname to test + * + * @return true if the path should be included in the list, + * false otherwise. + */ + boolean accept(File pathname); +} diff --git a/libjava/classpath/java/io/FileInputStream.java b/libjava/classpath/java/io/FileInputStream.java new file mode 100644 index 000000000..0e71caf37 --- /dev/null +++ b/libjava/classpath/java/io/FileInputStream.java @@ -0,0 +1,322 @@ +/* FileInputStream.java -- An input stream that reads from disk files. + Copyright (C) 1998, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.FileChannelImpl; + +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This class is a stream that reads its bytes from a file. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class FileInputStream extends InputStream +{ + /** + * This is the native file handle for the file this stream is reading from + */ + private FileDescriptor fd; + + private FileChannelImpl ch; + + /** + * This method initializes a FileInputStream to read from the + * specified named file. A security check is first made to determine + * whether or not access to this file is allowed. This is done by + * calling the checkRead() method of the + * SecurityManager + * (if one exists) with the name of this file. An exception is thrown + * if reading is not allowed. If the file does not exist, an exception + * is also thrown. + * + * @param name The name of the file this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + * @exception FileNotFoundException If the file does not exist + * or if it is a directory + */ + public FileInputStream(String name) throws FileNotFoundException + { + this(new File(name)); + } + + /** + * This method initializes a FileInputStream to read from the + * specified File object. A security check is first + * made to determine + * whether or not access to this file is allowed. This is done by + * calling the checkRead() method of the + * SecurityManager + * (if one exists) with the name of this file. An exception is thrown + * if reading is not allowed. If the file does not exist, an exception + * is also thrown. + * + * @param file The File object this stream should read from + * + * @exception SecurityException If read access to the file is not allowed + * @exception FileNotFoundException If the file does not exist + * or if it is a directory. + */ + public FileInputStream(File file) throws FileNotFoundException + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkRead(file.getPath()); + + try + { + ch = FileChannelImpl.create(file, FileChannelImpl.READ); + } + catch (FileNotFoundException fnfe) + { + throw fnfe; + } + catch (IOException ioe) + { + FileNotFoundException fnfe = new FileNotFoundException(file.getPath()); + fnfe.initCause(ioe); + throw fnfe; + } + } + + /** + * This method initializes a FileInputStream to read from the + * specified FileDescriptor object. A security + * check is first made to + * determine whether or not access to this file is allowed. This is done by + * calling the checkRead() method of the + * SecurityManager + * (if one exists) with the specified FileDescriptor + * An exception is + * thrown if reading is not allowed. + * + * @param fdObj The FileDescriptor object this stream + * should read from + * + * @exception SecurityException If read access to the file is not allowed + */ + public FileInputStream(FileDescriptor fdObj) + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkRead(fdObj); + + fd = fdObj; + ch = (FileChannelImpl) fdObj.channel; + } + + FileInputStream(FileChannelImpl ch) + { + this.ch = ch; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + *

    + * This method returns the number of unread bytes remaining in the file if + * the descriptor being read from is an actual file. If this method is + * reading from a ''special'' file such a the standard input, this method + * will return the appropriate value for the stream being read. + *

    + * Be aware that reads on plain files that do not reside locally might + * possibly block even if this method says they should not. For example, + * a remote server might crash, preventing an NFS mounted file from being + * read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return ch.available(); + } + + /** + * This method closes the stream. Any futher attempts to read from the + * stream will likely generate an IOException since the underlying file + * will be closed. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + ch.close(); + } + + protected void finalize() throws IOException + { + // We don't actually need this, but we include it because it is + // mentioned in the JCL. + } + + /** + * This method returns a FileDescriptor object representing the + * underlying native file handle of the file this stream is reading + * from + * + * @return A FileDescriptor for this stream + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD() throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + *

    + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return ch.read(); + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the stream. + *

    + * This method will block until some data can be read. + *

    + * This method operates by calling an overloaded read method like so: + * read(buf, 0, buf.length) + * + * @param buf The buffer into which the bytes read will be stored. + * + * @return The number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] buf) throws IOException + { + return read(buf, 0, buf.length); + } + + /** + * This method read bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index + * offset into + * the buffer and attempts to read len bytes. This method can + * return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returned to indicate the + * end of the stream. + *

    + * This method will block until some data can be read. + * + * @param buf The array into which the bytes read should be stored + * @param offset The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] buf, int offset, int len) throws IOException + { + if (offset < 0 + || len < 0 + || offset + len > buf.length) + throw new ArrayIndexOutOfBoundsException(); + + return ch.read(ByteBuffer.wrap(buf, offset, len)); + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + *

    + * @param numBytes The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public synchronized long skip (long numBytes) throws IOException + { + if (numBytes < 0) + throw new IllegalArgumentException ("Can't skip negative bytes: " + + numBytes); + + if (numBytes == 0) + return 0; + + long oldPos = ch.position (); + ch.position(oldPos + numBytes); + return ch.position() - oldPos; + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public synchronized FileChannel getChannel () + { + return ch; + } + +} // class FileInputStream diff --git a/libjava/classpath/java/io/FileNotFoundException.java b/libjava/classpath/java/io/FileNotFoundException.java new file mode 100644 index 000000000..3c11e2960 --- /dev/null +++ b/libjava/classpath/java/io/FileNotFoundException.java @@ -0,0 +1,73 @@ +/* FileNotFoundException.java -- the requested file could not be found + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when an attempt is made to access a file that + * does not exist, or is inaccessible for some other reason (such as writing + * a read-only file). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class FileNotFoundException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -897856973823710492L; + + /** + * Create an exception without a descriptive error message. + */ + public FileNotFoundException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public FileNotFoundException(String message) + { + super(message); + } +} // class FileNotFoundException diff --git a/libjava/classpath/java/io/FileOutputStream.java b/libjava/classpath/java/io/FileOutputStream.java new file mode 100644 index 000000000..6915822ea --- /dev/null +++ b/libjava/classpath/java/io/FileOutputStream.java @@ -0,0 +1,309 @@ +/* FileOutputStream.java -- Writes to a file on disk. + Copyright (C) 1998, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.FileChannelImpl; + +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This classes allows a stream of data to be written to a disk file or + * any open FileDescriptor. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FileOutputStream extends OutputStream +{ + private FileDescriptor fd; + + private final FileChannelImpl ch; + + /** + * This method initializes a FileOutputStream object to write + * to the named file. The file is created if it does not exist, and + * the bytes written are written starting at the beginning of the file if + * the append argument is false or at the end + * of the file if the append argument is true. + *

    + * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param path The name of the file this stream should write to + * @param append true to append bytes to the end of the file, + * or false to write bytes to the beginning + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (String path, boolean append) + throws SecurityException, FileNotFoundException + { + this (new File(path), append); + } + + /** + * This method initializes a FileOutputStream object to write + * to the named file. The file is created if it does not exist, and + * the bytes written are written starting at the beginning of the file. + *

    + * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param path The name of the file this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (String path) + throws SecurityException, FileNotFoundException + { + this (path, false); + } + + /** + * This method initializes a FileOutputStream object to write + * to the specified File object. The file is created if it + * does not exist, and the bytes written are written starting at the + * beginning of the file. + *

    + * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param file The File object this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (File file) + throws SecurityException, FileNotFoundException + { + this (file, false); + } + + /** + * This method initializes a FileOutputStream object to write + * to the specified File object. The file is created if it + * does not exist, and the bytes written are written starting at the + * beginning of the file if the append parameter is + * false. Otherwise bytes are written at the end of the + * file. + *

    + * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (if + * one exists) with the name of the file to be opened. An exception is + * thrown if writing is not allowed. + * + * @param file The File object this stream should write to + * @param append true to append bytes to the end of the file, + * or false to write bytes to the beginning + * + * @exception SecurityException If write access to the file is not allowed + * @exception FileNotFoundException If a non-security error occurs + */ + public FileOutputStream (File file, boolean append) + throws FileNotFoundException + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkWrite(file.getPath()); + + try + { + ch = FileChannelImpl.create(file, (append + ? FileChannelImpl.WRITE + | FileChannelImpl.APPEND + : FileChannelImpl.WRITE)); + } + catch (FileNotFoundException fnfe) + { + throw fnfe; + } + catch (IOException ioe) + { + FileNotFoundException fnfe = new FileNotFoundException(file.getPath()); + fnfe.initCause(ioe); + throw fnfe; + } + } + + /** + * This method initializes a FileOutputStream object to write + * to the file represented by the specified FileDescriptor + * object. This method does not create any underlying disk file or + * reposition the file pointer of the given descriptor. It assumes that + * this descriptor is ready for writing as is. + *

    + * Before opening a file, a security check is performed by calling the + * checkWrite method of the SecurityManager (if + * one exists) with the specified FileDescriptor as an argument. + * An exception is thrown if writing is not allowed. + * + * @param fdObj The FileDescriptor this stream should write to + * + * @exception SecurityException If write access to the file is not allowed + */ + public FileOutputStream (FileDescriptor fdObj) + throws SecurityException + { + // Hmm, no other exception but this one to throw, but if the descriptor + // isn't valid, we surely don't have "permission" to write to it. + if (!fdObj.valid()) + throw new SecurityException("Invalid FileDescriptor"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkWrite(fdObj); + + fd = fdObj; + ch = (FileChannelImpl) fdObj.channel; + } + + FileOutputStream(FileChannelImpl ch) + { + this.ch = ch; + } + + protected void finalize () throws IOException + { + // We don't actually need this, but we include it because it is + // mentioned in the JCL. + } + + /** + * This method returns a FileDescriptor object representing + * the file that is currently being written to + * + * @return A FileDescriptor object for this stream + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD () throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method writes a single byte of data to the file. + * + * @param b The byte of data to write, passed as an int + * + * @exception IOException If an error occurs + */ + public void write (int b) throws IOException + { + ch.write (b); + } + + /** + * This method writes all the bytes in the specified array to the + * file. + * + * @param buf The array of bytes to write to the file + * + * @exception IOException If an error occurs + */ + public void write (byte[] buf) + throws IOException + { + write (buf, 0, buf.length); + } + + /** + * This method writes len bytes from the byte array + * buf to the file starting at index offset. + * + * @param buf The array of bytes to write to the file + * @param offset The offset into the array to start writing bytes from + * @param len The number of bytes to write to the file + * + * @exception IOException If an error occurs + */ + public void write (byte[] buf, int offset, int len) + throws IOException + { + if (offset < 0 + || len < 0 + || offset + len > buf.length) + throw new ArrayIndexOutOfBoundsException (); + + ch.write(ByteBuffer.wrap(buf, offset, len)); + } + + /** + * This method closes the underlying file. Any further attempts to + * write to this stream will likely generate an exception since the + * file is closed. + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + ch.close(); + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public synchronized FileChannel getChannel() + { + return ch; + } + +} // class FileOutputStream diff --git a/libjava/classpath/java/io/FilePermission.java b/libjava/classpath/java/io/FilePermission.java new file mode 100644 index 000000000..5aa10b3c0 --- /dev/null +++ b/libjava/classpath/java/io/FilePermission.java @@ -0,0 +1,293 @@ +/* FilePermission.java -- + Copyright (C) 1998, 2000, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.security.Permission; + +public final class FilePermission extends Permission implements Serializable +{ + private static final long serialVersionUID = 7930732926638008763L; + + private static final String ALL_FILES = "<>"; + + private boolean readPerm = false; + private boolean writePerm = false; + private boolean executePerm = false; + private boolean deletePerm = false; + private final String actionsString; + + // Checks and caches the actions + private void checkPerms() throws IllegalArgumentException + { + String action; + int i = actionsString.indexOf(','); + int startI = 0; + while (i != -1) + { + action = actionsString.substring(startI, i).trim().toLowerCase(); + if (action.equals("read")) + readPerm = true; + else if (action.equals("write")) + writePerm = true; + else if (action.equals("execute")) + executePerm = true; + else if (action.equals("delete")) + deletePerm = true; + else + throw new IllegalArgumentException("Unknown action: " + action); + + startI = i + 1; + i = actionsString.indexOf(',', startI); + } + + action = actionsString.substring(startI).trim().toLowerCase(); + if (action.equals("read")) + readPerm = true; + else if (action.equals("write")) + writePerm = true; + else if (action.equals("execute")) + executePerm = true; + else if (action.equals("delete")) + deletePerm = true; + else + throw new IllegalArgumentException("Unknown action: " + action); + } + + /** + * Create a new FilePermission. + * + * @param pathExpression an expression specifying the paths this + * permission represents. + * @param actionsString a comma-separated list of the actions this + * permission represents. The actions must be "read", "write", + * "execute" and/or "delete". + */ + public FilePermission(String pathExpression, String actionsString) + { + // FIXME: what to do when the file string is malformed? + super(pathExpression); + if (pathExpression == null) + throw new NullPointerException("pathExpression"); + if (actionsString == null) + throw new IllegalArgumentException("actionsString"); + this.actionsString = actionsString; + checkPerms(); + } + + /** + * Get the actions this FilePermission supports. + * @return the String representing the actions this FilePermission supports. + */ + public String getActions() + { + return actionsString; + } + + /** + * Get the hash code for this Object.

    + * FilePermission's hash code is calculated as the exclusive or of the + * target + * String's hash code and the action String's hash code. + * @specnote Sun did not specify how to calculate the hash code; + * I made this up. + * @return the hash code for this Object. + */ + public int hashCode() + { + return getName().hashCode() ^ actionsString.hashCode(); + } + + /** + * Check two FilePermissions for semantic equality. + * Two FilePermissions are exactly equivalent if they have identical path + * expressions and have exactly the same access permissions. + * @param o the Object to compare to. + * @return whether the Objects are semantically equivalent. + */ + public boolean equals(Object o) + { + if (! (o instanceof FilePermission)) + return false; + FilePermission p = (FilePermission) o; + + String f1 = getName(); + String f2 = p.getName(); + + // Compare names, taking into account if they refer to a directory + // and one has a separator and the other does not. + if (f1.length() > 0 && f1.charAt(f1.length() - 1) == File.separatorChar) + { + if (f2.length() > 0 + && f2.charAt(f2.length() - 1) == File.separatorChar) + { + if (! f2.equals(f1)) + return false; + } + else + { + if (! f2.equals(f1.substring(0, f1.length() - 1))) + return false; + } + } + else + { + if (f2.length() > 0 + && f2.charAt(f2.length() - 1) == File.separatorChar) + { + if (! f1.equals(f2.substring(0, f2.length() - 1))) + return false; + } + else + { + if (! f1.equals(f2)) + return false; + } + } + return (readPerm == p.readPerm + && writePerm == p.writePerm + && executePerm == p.executePerm + && deletePerm == p.deletePerm); + } + + /** + * Check to see if this permission implies another. + * Permission A implies permission B if these things are all true: + *

      + *
    1. A and B are both FilePermissions.
    2. + *
    3. All possible files in B are included in A + * (possibly more are in A).
    4. + *
    5. All actions B supports, A also supports.
    6. + *
    + * @param p the Permission to compare against. + * @return whether this Permission implies p + */ + public boolean implies(Permission p) + { + if (! (p instanceof FilePermission)) + return false; + + String f1 = getName(); + + if (f1.equals(ALL_FILES)) + return true; + + FilePermission fp = (FilePermission) p; + String f2 = fp.getName(); + + if (f2.equals(ALL_FILES)) + return false; + + try + { + f1 = new File(f1).getCanonicalPath(); + f2 = new File(f2).getCanonicalPath(); + } + catch (IOException ioe) + { + return false; + } + + String sub1; + + switch (f1.charAt(f1.length() - 1)) + { + case '*': + sub1 = f1.substring(0, f1.length() - 1); // chop off "*" + if (f2.length() <= sub1.length()) + { + // If it's smaller, there is no way it could be part of + // this directory. If it's the same (or length - 1), it + // could be the same directory but specifies access to + // the directory rather than the files in it. + return false; + } + else if (f2.charAt(sub1.length() - 1) == File.separatorChar) + { + // Make sure the part before the "/" is the same. + if (! f2.substring(0, sub1.length()).equals(sub1)) + return false; + // Make sure there are no subdirectories specified + // underneath this one. + if (f2.substring(sub1.length() + 1).indexOf(File.separatorChar) + != -1) + return false; + } + else + { + // Obviously not equal: f2 is either not a directory or + // is not the same directory (its name continues further + // than we want). + return false; + } + break; + case '-': + // Chop off "/-". + sub1 = f1.substring(0, f1.length() - 2); + if (f2.length() < sub1.length()) + { + // If it's smaller, there is no way it could be part of + // this directory. + return false; + } + else if (f2.length() > sub1.length() + && f2.charAt(sub1.length()) != File.separatorChar) + return false; + else if (! f2.substring(0, sub1.length()).equals(sub1)) + return false; + break; + + default: + if (!f1.equals(f2)) + return false; + break; + } + + if (fp.readPerm && ! readPerm) + return false; + if (fp.writePerm && ! writePerm) + return false; + if (fp.executePerm && ! executePerm) + return false; + if (fp.deletePerm && ! deletePerm) + return false; + + return true; + } +} diff --git a/libjava/classpath/java/io/FileReader.java b/libjava/classpath/java/io/FileReader.java new file mode 100644 index 000000000..05e14c376 --- /dev/null +++ b/libjava/classpath/java/io/FileReader.java @@ -0,0 +1,91 @@ +/* FileReader.java -- Convenience class for reading characters from a file + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class provides a convenient way to set up a Reader + * to read from a file. It opens the specified file for reading and creates + * the InputStreamReader to read from the + * resulting FileInputStream. This class can only be used + * to read from files using the default character encoding. Use + * InputStreamReader directly to use a non-default encoding. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class FileReader extends InputStreamReader +{ + /** + * This method initializes a FileReader instance to read from + * the specified File object. + * + * @param file The File object representing the file to read from + * + * @exception FileNotFoundException If the file is not found or some other + * error occurs + */ + public FileReader(File file) throws FileNotFoundException + { + super(new FileInputStream(file)); + } + + /** + * This method initializes a FileReader instance to read from + * this specified FileDescriptor object. + * + * @param fd The FileDescriptor to read from. + */ + public FileReader(FileDescriptor fd) + { + super(new FileInputStream(fd)); + } + + /** + * This method initializes a FileReader instance to read from + * the specified named file. + * + * @param name The name of the file to read from + * + * @exception FileNotFoundException If the file is not found or some other + * error occurs + */ + public FileReader(String name) throws FileNotFoundException + { + super(new FileInputStream(name)); + } +} // class FileReader diff --git a/libjava/classpath/java/io/FileWriter.java b/libjava/classpath/java/io/FileWriter.java new file mode 100644 index 000000000..95d5eea3b --- /dev/null +++ b/libjava/classpath/java/io/FileWriter.java @@ -0,0 +1,137 @@ +/* FileWriter.java -- Convenience class for writing to files. + Copyright (C) 1998, 1999, 2001, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This is a convenience class for writing to files. It creates an + * FileOutputStream and initializes an + * OutputStreamWriter to write to it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FileWriter extends OutputStreamWriter +{ + /** + * This method initializes a new FileWriter object to write + * to the specified File object. + * + * @param file The File object to write to. + * + * @throws SecurityException If writing to this file is forbidden by the + * SecurityManager. + * @throws IOException If any other error occurs + */ + public FileWriter(File file) throws SecurityException, IOException + { + super(new FileOutputStream(file)); + } + + /** + * This method initializes a new FileWriter object to write + * to the specified File object. + * + * @param file The File object to write to. + * @param append true to start adding data at the end of the + * file, false otherwise. + * + * @throws SecurityException If writing to this file is forbidden by the + * SecurityManager. + * @throws IOException If any other error occurs + */ + public FileWriter(File file, boolean append) throws IOException + { + super(new FileOutputStream(file, append)); + } + + /** + * This method initializes a new FileWriter object to write + * to the specified FileDescriptor object. + * + * @param fd The FileDescriptor object to write to + * + * @throws SecurityException If writing to this file is forbidden by the + * SecurityManager. + */ + public FileWriter(FileDescriptor fd) throws SecurityException + { + super(new FileOutputStream(fd)); + } + + /** + * This method intializes a new FileWriter object to + * write to the + * specified named file. + * + * @param name The name of the file to write to + * + * @throws SecurityException If writing to this file is forbidden by the + * SecurityManager. + * @throws IOException If any other error occurs + */ + public FileWriter(String name) throws IOException + { + super(new FileOutputStream(name)); + } + + /** + * This method intializes a new FileWriter object to + * write to the + * specified named file. This form of the constructor allows the caller + * to determine whether data should be written starting at the beginning or + * the end of the file. + * + * @param name The name of the file to write to + * @param append true to start adding data at the end of the + * file, false otherwise. + * + * @throws SecurityException If writing to this file is forbidden by the + * SecurityManager. + * @throws IOException If any other error occurs + */ + public FileWriter(String name, boolean append) throws IOException + { + super(new FileOutputStream(name, append)); + } +} diff --git a/libjava/classpath/java/io/FilenameFilter.java b/libjava/classpath/java/io/FilenameFilter.java new file mode 100644 index 000000000..5a9045f52 --- /dev/null +++ b/libjava/classpath/java/io/FilenameFilter.java @@ -0,0 +1,75 @@ +/* FilenameFilter.java -- Filter a list of filenames + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to 1.1. + */ + +/** + * This interface has one method which is used for filtering filenames + * returned in a directory listing. It is currently used by the + * File.list(FilenameFilter) method and by the filename + * dialog in AWT. + *

    + * The method in this interface determines if a particular file should + * or should not be included in the file listing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * + * @see File#listFiles(java.io.FilenameFilter) + * @see java.awt.FileDialog#setFilenameFilter(java.io.FilenameFilter) + */ +public interface FilenameFilter +{ + /** + * This method determines whether or not a given file should be included + * in a directory listing. + * + * @param dir The File instance for the directory being read + * @param name The name of the file to test + * + * @return true if the file should be included in the list, + * false otherwise. + */ + boolean accept(File dir, String name); + +} // interface FilenameFilter diff --git a/libjava/classpath/java/io/FilterInputStream.java b/libjava/classpath/java/io/FilterInputStream.java new file mode 100644 index 000000000..c9d50e535 --- /dev/null +++ b/libjava/classpath/java/io/FilterInputStream.java @@ -0,0 +1,203 @@ +/* FilterInputStream.java -- Base class for classes that filter input + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This is the common superclass of all standard classes that filter + * input. It acts as a layer on top of an underlying InputStream + * and simply redirects calls made to it to the subordinate InputStream + * instead. Subclasses of this class perform additional filtering + * functions in addition to simply redirecting the call. + *

    + * This class is not abstract. However, since it only redirects calls + * to a subordinate InputStream without adding any functionality + * on top of it, this class should not be used directly. Instead, various + * subclasses of this class should be used. This is enforced with a + * protected constructor. Do not try to hack around it. + *

    + * When creating a subclass of FilterInputStream, override the + * appropriate methods to implement the desired filtering. However, note + * that the read(byte[]) method does not need to be overridden + * as this class redirects calls to that method to + * read(byte[], int, int) instead of to the subordinate + * InputStream read(byte[]) method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class FilterInputStream extends InputStream +{ + /** + * This is the subordinate InputStream to which method calls + * are redirected + */ + protected InputStream in; + + /** + * Create a FilterInputStream with the specified subordinate + * InputStream. + * + * @param in The subordinate InputStream + */ + protected FilterInputStream(InputStream in) + { + this.in = in; + } + + /** + * Calls the in.mark(int) method. + * + * @param readlimit The parameter passed to in.mark(int) + */ + public void mark(int readlimit) + { + in.mark(readlimit); + } + + /** + * Calls the in.markSupported() method. + * + * @return true if mark/reset is supported, false + * otherwise + */ + public boolean markSupported() + { + return in.markSupported(); + } + + /** + * Calls the in.reset() method. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + } + + /** + * Calls the in.available() method. + * + * @return The value returned from in.available() + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return in.available(); + } + + /** + * Calls the in.skip(long) method + * + * @param numBytes The requested number of bytes to skip. + * + * @return The value returned from in.skip(long) + * + * @exception IOException If an error occurs + */ + public long skip(long numBytes) throws IOException + { + return in.skip(numBytes); + } + + /** + * Calls the in.read() method + * + * @return The value returned from in.read() + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return in.read(); + } + + /** + * Calls the read(byte[], int, int) overloaded method. + * Note that + * this method does not redirect its call directly to a corresponding + * method in in. This allows subclasses to override only the + * three argument version of read. + * + * @param buf The buffer to read bytes into + * + * @return The value retured from in.read(byte[], int, int) + * + * @exception IOException If an error occurs + */ + public int read(byte[] buf) throws IOException + { + return read(buf, 0, buf.length); + } + + /** + * Calls the in.read(byte[], int, int) method. + * + * @param buf The buffer to read bytes into + * @param offset The index into the buffer to start storing bytes + * @param len The maximum number of bytes to read. + * + * @return The value retured from in.read(byte[], int, int) + * + * @exception IOException If an error occurs + */ + public int read(byte[] buf, int offset, int len) throws IOException + { + return in.read(buf, offset, len); + } + + /** + * This method closes the input stream by closing the input stream that + * this object is filtering. Future attempts to access this stream may + * throw an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + in.close(); + } +} diff --git a/libjava/classpath/java/io/FilterOutputStream.java b/libjava/classpath/java/io/FilterOutputStream.java new file mode 100644 index 000000000..401e5478c --- /dev/null +++ b/libjava/classpath/java/io/FilterOutputStream.java @@ -0,0 +1,149 @@ +/* FilterOutputStream.java -- Parent class for output streams that filter + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class is the common superclass of output stream classes that + * filter the output they write. These classes typically transform the + * data in some way prior to writing it out to another underlying + * OutputStream. This class simply overrides all the + * methods in OutputStream to redirect them to the + * underlying stream. Subclasses provide actual filtering. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class FilterOutputStream extends OutputStream +{ + /** + * This is the subordinate OutputStream that this class + * redirects its method calls to. + */ + protected OutputStream out; + + /** + * This method initializes an instance of FilterOutputStream + * to write to the specified subordinate OutputStream. + * + * @param out The OutputStream to write to + */ + public FilterOutputStream(OutputStream out) + { + this.out = out; + } + + /** + * This method closes the underlying OutputStream. Any + * further attempts to write to this stream may throw an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + flush(); + out.close(); + } + + /** + * This method attempt to flush all buffered output to be written to the + * underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush() throws IOException + { + out.flush(); + } + + /** + * This method writes a single byte of output to the underlying + * OutputStream. + * + * @param b The byte to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + out.write(b); + } + + /** + * This method writes all the bytes in the specified array to the underlying + * OutputStream. It does this by calling the three parameter + * version of this method - write(byte[], int, int) in this + * class instead of writing to the underlying OutputStream + * directly. This allows most subclasses to avoid overriding this method. + * + * @param buf The byte array to write bytes from + * + * @exception IOException If an error occurs + */ + public void write(byte[] buf) throws IOException + { + // Don't do checking here, per Java Lang Spec. + write(buf, 0, buf.length); + } + + /** + * This method calls the write(int) method len + * times for all bytes from the array buf starting at index + * offset. Subclasses should overwrite this method to get a + * more efficient implementation. + * + * @param buf The byte array to write bytes from + * @param offset The index into the array to start writing bytes from + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write(byte[] buf, int offset, int len) throws IOException + { + // Don't do checking here, per Java Lang Spec. + for (int i=0; i < len; i++) + write(buf[offset + i]); + + } + +} // class FilterOutputStream diff --git a/libjava/classpath/java/io/FilterReader.java b/libjava/classpath/java/io/FilterReader.java new file mode 100644 index 000000000..0e57e21c5 --- /dev/null +++ b/libjava/classpath/java/io/FilterReader.java @@ -0,0 +1,184 @@ +/* FilterReader.java -- Base class for char stream classes that filter input + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This is the common superclass of all standard classes that filter + * input. It acts as a layer on top of an underlying Reader + * and simply redirects calls made to it to the subordinate Reader + * instead. Subclasses of this class perform additional filtering + * functions in addition to simply redirecting the call. + *

    + * When creating a subclass of FilterReader, override the + * appropriate methods to implement the desired filtering. However, note + * that the read(char[]) method does not need to be overridden + * as this class redirects calls to that method to + * read(yte[], int, int) instead of to the subordinate + * Reader} read(yte[]) method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class FilterReader extends Reader +{ + /** + * This is the subordinate Reader to which method calls + * are redirected + */ + protected Reader in; + + /** + * Create a FilterReader with the specified subordinate + * Reader. + * The lock of the new FilterReader will be set + * to in.lock. + * + * @param in The subordinate Reader + */ + protected FilterReader(Reader in) + { + super(in.lock); + this.in = in; + } + + /** + * Calls the in.mark(int) method. + * + * @param readlimit The parameter passed to in.mark(int) + * + * @exception IOException If an error occurs + */ + public void mark(int readlimit) throws IOException + { + in.mark(readlimit); + } + + /** + * Calls the in.markSupported() method. + * + * @return true if mark/reset is supported, + * false otherwise + */ + public boolean markSupported() + { + return(in.markSupported()); + } + + /** + * Calls the in.reset() method. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + } + + /** + * Calls the in.read() method. + * + * @return The value returned from in.available() + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + return(in.ready()); + } + + /** + * Calls the in.skip(long) method + * + * @param num_chars The requested number of chars to skip. + * + * @return The value returned from in.skip(long) + * + * @exception IOException If an error occurs + */ + public long skip(long num_chars) throws IOException + { + return(in.skip(num_chars)); + } + + /** + * Calls the in.read() method + * + * @return The value returned from in.read() + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + return(in.read()); + } + + /** + * Calls the in.read(char[], int, int) method. + * + * @param buf The buffer to read chars into + * @param offset The index into the buffer to start storing chars + * @param len The maximum number of chars to read. + * + * @return The value retured from in.read(char[], int, int) + * + * @exception IOException If an error occurs + */ + public int read(char[] buf, int offset, int len) throws IOException + { + return(in.read(buf, offset, len)); + } + + /** + * This method closes the stream by calling the close() method + * of the underlying stream. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + in.close(); + } + +} // class FilterReader diff --git a/libjava/classpath/java/io/FilterWriter.java b/libjava/classpath/java/io/FilterWriter.java new file mode 100644 index 000000000..382dec06a --- /dev/null +++ b/libjava/classpath/java/io/FilterWriter.java @@ -0,0 +1,146 @@ +/* FilterWriter.java -- Parent class for output streams that filter + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Complete to version 1.1. + */ + +/** + * This class is the common superclass of output character stream classes + * that filter the output they write. These classes typically transform the + * data in some way prior to writing it out to another underlying + * Writer. This class simply overrides all the + * methods in Writer to redirect them to the + * underlying stream. Subclasses provide actual filtering. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public abstract class FilterWriter extends Writer +{ + /** + * This is the subordinate Writer that this class + * redirects its method calls to. + */ + protected Writer out; + + /** + * This method initializes an instance of FilterWriter + * to write to the specified subordinate Writer. + * The given Writer will be used as lock for + * the newly created FilterWriter. + * + * @param out The Writer to write to + */ + protected FilterWriter(Writer out) + { + super(out.lock); + this.out = out; + } + + /** + * This method closes the underlying Writer. Any + * further attempts to write to this stream may throw an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + out.close(); + } + + /** + * This method attempt to flush all buffered output to be written to the + * underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush() throws IOException + { + out.flush(); + } + + /** + * This method writes a single char of output to the underlying + * Writer. + * + * @param b The char to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + out.write(b); + } + + /** + * This method writes len chars from the array buf + * starting at index offset to the underlying + * Writer. + * + * @param buf The char array to write chars from + * @param offset The index into the array to start writing chars from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(char[] buf, int offset, int len) throws IOException + { + out.write(buf, offset, len); + } + + /** + * This method writes len chars from the String + * starting at position offset. + * + * @param str The String that is to be written + * @param offset The character offset into the String + * to start writing from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(String str, int offset, int len) throws IOException + { + out.write(str, offset, len); + } + +} // class FilterWriter diff --git a/libjava/classpath/java/io/Flushable.java b/libjava/classpath/java/io/Flushable.java new file mode 100644 index 000000000..e9718d60a --- /dev/null +++ b/libjava/classpath/java/io/Flushable.java @@ -0,0 +1,62 @@ +/* Flushable.java -- Flushable object + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * A Flushable class represents a stream of + * data, for which internally buffered data can be `flushed'. + * Flushing such a stream causes the buffered data to be + * written to the stream. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Flushable +{ + + /** + * Flushes the stream represented by this class, + * so that any buffered data is written to the stream. + * + * @throws IOException if an I/O error occurs in flushing. + */ + void flush() + throws IOException; + +} diff --git a/libjava/classpath/java/io/IOException.java b/libjava/classpath/java/io/IOException.java new file mode 100644 index 000000000..cf3ad1946 --- /dev/null +++ b/libjava/classpath/java/io/IOException.java @@ -0,0 +1,74 @@ +/* IOException.java -- Generic input/output exception + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown to indicate an I/O problem of some sort + * occurred. Since this is a fairly generic exception, often a subclass + * of IOException will actually be thrown in order to provide a more + * detailed indication of what happened. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class IOException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 7818375828146090155L; + + /** + * Create an exception without a descriptive error message. + */ + public IOException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public IOException(String message) + { + super(message); + } +} // class IOException diff --git a/libjava/classpath/java/io/InputStream.java b/libjava/classpath/java/io/InputStream.java new file mode 100644 index 000000000..58b3dbd19 --- /dev/null +++ b/libjava/classpath/java/io/InputStream.java @@ -0,0 +1,270 @@ +/* InputStream.java -- Base class for input + Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This abstract class forms the base of the hierarchy of classes that read + * input as a stream of bytes. It provides a common set of methods for + * reading bytes from streams. Subclasses implement and extend these + * methods to read bytes from a particular input source such as a file + * or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class InputStream implements Closeable +{ + /** + * Default, no-arg, public constructor + */ + public InputStream() + { + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + *

    + * This method always returns 0 in this class + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + return 0; + } + + /** + * This method closes the stream. Any futher attempts to read from the + * stream may generate an IOException + *

    + * This method does nothing in this class, but subclasses may override + * this method in order to provide additional functionality. + * + * @exception IOException If an error occurs, which can only happen + * in a subclass + */ + public void close() throws IOException + { + // Do nothing + } + + /** + * This method marks a position in the input to which the stream can + * be "reset" by calling the reset() method. The + * parameter @code{readlimit} is the number of bytes that can be read + * from the stream after setting the mark before the mark becomes + * invalid. For example, if mark() is called with a + * read limit of 10, then when 11 bytes of data are read from the + * stream before the reset() method is called, then the + * mark is invalid and the stream object instance is not required to + * remember the mark. + *

    + * This method does nothing in this class, but subclasses may override it + * to provide mark/reset functionality. + * + * @param readLimit The number of bytes that can be read before the + * mark becomes invalid + */ + public void mark(int readLimit) + { + // Do nothing + } + + /** + * This method returns a boolean that indicates whether the mark/reset + * methods are supported in this class. Those methods can be used to + * remember a specific point in the stream and reset the stream to that + * point. + *

    + * This method always returns false in this class, but + * subclasses can override this method to return true + * if they support mark/reset functionality. + * + * @return true if mark/reset functionality is + * supported, false otherwise + */ + public boolean markSupported() + { + return false; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. + *

    + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public abstract int read() throws IOException; + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the stream. + *

    + * This method will block until some data can be read. + *

    + * This method operates by calling an overloaded read method like so: + * read(b, 0, b.length) + * + * @param b The buffer into which the bytes read will be stored. + * + * @return The number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b) throws IOException + { + return read(b, 0, b.length); + } + + /** + * This method read bytes from a stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * off into the buffer and attempts to read + * len bytes. This method can return before reading the + * number of bytes requested. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the + * stream. + *

    + * This method will block until some data can be read. + *

    + * This method operates by calling the single byte read() method + * in a loop until the desired number of bytes are read. The read loop + * stops short if the end of the stream is encountered or if an IOException + * is encountered on any read operation except the first. If the first + * attempt to read a bytes fails, the IOException is allowed to propagate + * upward. And subsequent IOException is caught and treated identically + * to an end of stream condition. Subclasses can (and should if possible) + * override this method to provide a more efficient implementation. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || b.length - off < len) + throw new IndexOutOfBoundsException(); + + int i, ch; + + for (i = 0; i < len; ++i) + try + { + if ((ch = read()) < 0) + return i == 0 ? -1 : i; // EOF + b[off + i] = (byte) ch; + } + catch (IOException ex) + { + // Only reading the first byte should cause an IOException. + if (i == 0) + throw ex; + return i; + } + + return i; + } + + /** + * This method resets a stream to the point where the + * mark() method was called. Any bytes that were read + * after the mark point was set will be re-read during subsequent + * reads. + *

    + * This method always throws an IOException in this class, but subclasses + * can override this method if they provide mark/reset functionality. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("mark/reset not supported"); + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + *

    + * This method reads and discards bytes into a byte array until the + * specified number of bytes were skipped or until either the end of stream + * is reached or a read attempt returns a short count. Subclasses can + * override this metho to provide a more efficient implementation where + * one exists. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long n) throws IOException + { + // Throw away n bytes by reading them into a temp byte[]. + // Limit the temp array to 2Kb so we don't grab too much memory. + final int buflen = n > 2048 ? 2048 : (int) n; + byte[] tmpbuf = new byte[buflen]; + final long origN = n; + + while (n > 0L) + { + int numread = read(tmpbuf, 0, n > buflen ? buflen : (int) n); + if (numread <= 0) + break; + n -= numread; + } + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/InputStreamReader.java b/libjava/classpath/java/io/InputStreamReader.java new file mode 100644 index 000000000..51925a8c5 --- /dev/null +++ b/libjava/classpath/java/io/InputStreamReader.java @@ -0,0 +1,509 @@ +/* InputStreamReader.java -- Reader than transforms bytes to chars + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.SystemProperties; +import gnu.java.nio.charset.EncodingHelper; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +/** + * This class reads characters from a byte input stream. The characters + * read are converted from bytes in the underlying stream by a + * decoding layer. The decoding layer transforms bytes to chars according + * to an encoding standard. There are many available encodings to choose + * from. The desired encoding can either be specified by name, or if no + * encoding is selected, the system default encoding will be used. The + * system default encoding name is determined from the system property + * file.encoding. The only encodings that are guaranteed to + * be availalbe are "8859_1" (the Latin-1 character set) and "UTF8". + * Unforunately, Java does not provide a mechanism for listing the + * ecodings that are supported in a given implementation. + *

    + * Here is a list of standard encoding names that may be available: + *

    + *

      + *
    • 8859_1 (ISO-8859-1/Latin-1)
    • + *
    • 8859_2 (ISO-8859-2/Latin-2)
    • + *
    • 8859_3 (ISO-8859-3/Latin-3)
    • + *
    • 8859_4 (ISO-8859-4/Latin-4)
    • + *
    • 8859_5 (ISO-8859-5/Latin-5)
    • + *
    • 8859_6 (ISO-8859-6/Latin-6)
    • + *
    • 8859_7 (ISO-8859-7/Latin-7)
    • + *
    • 8859_8 (ISO-8859-8/Latin-8)
    • + *
    • 8859_9 (ISO-8859-9/Latin-9)
    • + *
    • ASCII (7-bit ASCII)
    • + *
    • UTF8 (UCS Transformation Format-8)
    • + *
    • More later
    • + *
    + *

    + * It is recommended that applications do not use + * InputStreamReader's + * directly. Rather, for efficiency purposes, an object of this class + * should be wrapped by a BufferedReader. + *

    + * Due to a deficiency the Java class library design, there is no standard + * way for an application to install its own byte-character encoding. + * + * @see BufferedReader + * @see InputStream + * + * @author Robert Schuster + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @date April 22, 1998. + */ +public class InputStreamReader extends Reader +{ + /** + * The input stream. + */ + private InputStream in; + + /** + * The charset decoder. + */ + private CharsetDecoder decoder; + + /** + * End of stream reached. + */ + private boolean isDone = false; + + /** + * Need this. + */ + private float maxBytesPerChar; + + /** + * Buffer holding surplus loaded bytes (if any) + */ + private ByteBuffer byteBuffer; + + /** + * java.io canonical name of the encoding. + */ + private String encoding; + + /** + * We might decode to a 2-char UTF-16 surrogate, which won't fit in the + * output buffer. In this case we need to save the surrogate char. + */ + private char savedSurrogate; + 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 InputStreamReader + * to read from the specified stream using the default encoding. + * + * @param in The InputStream to read from + */ + public InputStreamReader(InputStream in) + { + if (in == null) + throw new NullPointerException(); + this.in = in; + try + { + encoding = SystemProperties.getProperty("file.encoding"); + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding)) + { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + return; + } + Charset cs = EncodingHelper.getCharset(encoding); + decoder = cs.newDecoder(); + encoding = EncodingHelper.getOldCanonical(cs.name()); + try { + maxBytesPerChar = cs.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + } catch(RuntimeException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } catch(UnsupportedEncodingException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } + } + + /** + * This method initializes a new instance of InputStreamReader + * to read from the specified stream using a caller supplied character + * encoding scheme. Note that due to a deficiency in the Java language + * design, there is no way to determine which encodings are supported. + * + * @param in The InputStream to read from + * @param encoding_name The name of the encoding scheme to use + * + * @exception UnsupportedEncodingException If the encoding scheme + * requested is not available. + */ + public InputStreamReader(InputStream in, String encoding_name) + throws UnsupportedEncodingException + { + if (in == null + || encoding_name == null) + throw new NullPointerException(); + + this.in = in; + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding_name)) + { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + return; + } + try { + Charset cs = EncodingHelper.getCharset(encoding_name); + try { + maxBytesPerChar = cs.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + decoder = cs.newDecoder(); + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + + // The encoding should be the old name, if such exists. + encoding = EncodingHelper.getOldCanonical(cs.name()); + } catch(RuntimeException e) { + encoding = "ISO8859_1"; + maxBytesPerChar = 1f; + decoder = null; + } + } + + /** + * Creates an InputStreamReader that uses a decoder of the given + * charset to decode the bytes in the InputStream into + * characters. + * + * @since 1.4 + */ + public InputStreamReader(InputStream in, Charset charset) { + if (in == null) + throw new NullPointerException(); + this.in = in; + decoder = charset.newDecoder(); + + try { + maxBytesPerChar = charset.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + encoding = EncodingHelper.getOldCanonical(charset.name()); + } + + /** + * Creates an InputStreamReader that uses the given charset decoder + * to decode the bytes in the InputStream into characters. + * + * @since 1.4 + */ + public InputStreamReader(InputStream in, CharsetDecoder decoder) { + if (in == null) + throw new NullPointerException(); + this.in = in; + this.decoder = decoder; + + Charset charset = decoder.charset(); + try { + if (charset == null) + maxBytesPerChar = 1f; + else + maxBytesPerChar = charset.newEncoder().maxBytesPerChar(); + } catch(UnsupportedOperationException _){ + maxBytesPerChar = 1f; + } + + decoder.onMalformedInput(CodingErrorAction.REPLACE); + decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + decoder.reset(); + if (charset == null) + encoding = "US-ASCII"; + else + encoding = EncodingHelper.getOldCanonical(decoder.charset().name()); + } + + /** + * This method closes this stream, as well as the underlying + * InputStream. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + // Makes sure all intermediate data is released by the decoder. + if (decoder != null) + decoder.reset(); + if (in != null) + in.close(); + in = null; + isDone = true; + decoder = null; + } + } + + /** + * This method returns the name of the encoding that is currently in use + * by this object. If the stream has been closed, this method is allowed + * to return null. + * + * @return The current encoding name + */ + public String getEncoding() + { + return in != null ? encoding : null; + } + + /** + * This method checks to see if the stream is ready to be read. It + * will return true if is, or false if it is not. + * If the stream is not ready to be read, it could (although is not required + * to) block on the next read attempt. + * + * @return true if the stream is ready to be read, + * false otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + + return in.available() != 0; + } + + /** + * This method reads up to length characters from the stream into + * the specified array starting at index offset into the + * array. + * + * @param buf The character array to recieve the data read + * @param offset The offset into the array to start storing characters + * @param length The requested number of characters to read. + * + * @return The actual number of characters read, or -1 if end of stream. + * + * @exception IOException If an error occurs + */ + public int read(char[] buf, int offset, int length) throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + if (isDone) + return -1; + if(decoder != null) + { + int totalBytes = (int)((double) length * maxBytesPerChar); + if (byteBuffer != null) + totalBytes = Math.max(totalBytes, byteBuffer.remaining()); + 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) + { + remaining = byteBuffer.remaining(); + byteBuffer.get(bytes, 0, remaining); + } + int read; + if(totalBytes - remaining > 0) + { + read = in.read(bytes, remaining, totalBytes - remaining); + if(read == -1){ + read = remaining; + isDone = true; + } else + read += remaining; + } else + read = remaining; + byteBuffer = ByteBuffer.wrap(bytes, 0, read); + CharBuffer cb = CharBuffer.wrap(buf, offset, length); + int startPos = cb.position(); + + if(hasSavedSurrogate){ + hasSavedSurrogate = false; + cb.put(savedSurrogate); + read++; + } + + CoderResult cr = decoder.decode(byteBuffer, cb, isDone); + decoder.reset(); + // 1 char remains which is the first half of a surrogate pair. + if(cr.isOverflow() && cb.hasRemaining()){ + CharBuffer overflowbuf = CharBuffer.allocate(2); + cr = decoder.decode(byteBuffer, overflowbuf, isDone); + overflowbuf.flip(); + if(overflowbuf.hasRemaining()) + { + cb.put(overflowbuf.get()); + savedSurrogate = overflowbuf.get(); + hasSavedSurrogate = true; + isDone = false; + } + } + + if(byteBuffer.hasRemaining()) { + byteBuffer.compact(); + byteBuffer.flip(); + isDone = false; + } else + byteBuffer = null; + + read = cb.position() - startPos; + + // 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 + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + char[] buf = new char[1]; + int count = read(buf, 0, 1); + return count > 0 ? buf[0] : -1; + } + + /** + * Skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long count) throws IOException + { + if (in == null) + throw new IOException("Reader has been closed"); + + return super.skip(count); + } +} diff --git a/libjava/classpath/java/io/InterruptedIOException.java b/libjava/classpath/java/io/InterruptedIOException.java new file mode 100644 index 000000000..96ec83649 --- /dev/null +++ b/libjava/classpath/java/io/InterruptedIOException.java @@ -0,0 +1,94 @@ +/* InterruptedIOException.java -- an I/O operation was interrupted + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a in process I/O operation is interrupted + * for some reason. The field bytesTransferred will contain the number of + * bytes that were read/written prior to the interruption. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see Thread#interrupt() + * @status updated to 1.4 + */ +public class InterruptedIOException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4020568460727500567L; + + /** + * The number of bytes read/written prior to the interruption. + * + * @serial count of bytes successfully transferred + */ + public int bytesTransferred; + + /** + * Create an extends without a descriptive error message. + */ + public InterruptedIOException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public InterruptedIOException(String message) + { + super(message); + } + + /** + * Create an exception with a descriptive error message and count of + * bytes transferred. + * + * @param message the descriptive error message + * @param bytesTransferred number of bytes tranferred before interruption + */ + InterruptedIOException(String message, int bytesTransferred) + { + super(message); + this.bytesTransferred = bytesTransferred; + } +} // class InterruptedIOException diff --git a/libjava/classpath/java/io/InvalidClassException.java b/libjava/classpath/java/io/InvalidClassException.java new file mode 100644 index 000000000..e31a44f2d --- /dev/null +++ b/libjava/classpath/java/io/InvalidClassException.java @@ -0,0 +1,110 @@ +/* InvalidClassException.java -- deserializing a class failed + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when there is some sort of problem with a + * class during a serialization operation. This could be:

      + *
    • the serial version of the class doesn't match
    • + *
    • the class contains unknown datatypes
    • + *
    • the class does not have an accessible no-arg constructor
    • + *
    . + * + *

    The field classname will contain the name of the + * class that caused the problem if known. The getMessage() method + * for this exception will always include the name of that class + * if known. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class InvalidClassException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4333316296251054416L; + + /** + * The name of the class which encountered the error. + * + * @serial the classname causing the error + */ + public String classname; + + /** + * Create an exception with a descriptive error message, but a null + * classname. + * + * @param message the descriptive error message + */ + public InvalidClassException(String message) + { + super(message); + } + + /** + * Create an exception with a descriptive error message, and the name of + * the class that caused the problem. + * + * @param classname the name of the faulty class + * @param message the descriptive error message + */ + public InvalidClassException(String classname, String message) + { + super(message); + this.classname = classname; + } + + /** + * Returns the descriptive error message for this exception. It will + * include the class name that caused the problem if known, in the format: + * [classname][; ][super.getMessage()]. + * + * @return A descriptive error message, may be null + */ + public String getMessage() + { + String msg = super.getMessage(); + if (msg == null) + return classname; + return (classname == null ? "" : classname + "; ") + msg; + } +} diff --git a/libjava/classpath/java/io/InvalidObjectException.java b/libjava/classpath/java/io/InvalidObjectException.java new file mode 100644 index 000000000..deee876db --- /dev/null +++ b/libjava/classpath/java/io/InvalidObjectException.java @@ -0,0 +1,66 @@ +/* InvalidObjectException.java -- deserialization failed verification + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when an object fails a validation test + * during serialization. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class InvalidObjectException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 3233174318281839583L; + + /** + * Create an exception with a descriptive error message String. This should + * be the cause of the verification failure. + * + * @param message the descriptive error message + */ + public InvalidObjectException(String message) + { + super(message); + } +} // class InvalidObjectException diff --git a/libjava/classpath/java/io/LineNumberInputStream.java b/libjava/classpath/java/io/LineNumberInputStream.java new file mode 100644 index 000000000..2d8e36202 --- /dev/null +++ b/libjava/classpath/java/io/LineNumberInputStream.java @@ -0,0 +1,315 @@ +/* LineNumberInputStream.java -- An input stream which counts line numbers + Copyright (C) 1998, 1999, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class functions like a standard InputStream + * except that it counts line numbers, and canonicalizes newline + * characters. As data is read, whenever the byte sequences "\r", + * "\n", or "\r\n" are encountered, the running line count is + * incremeted by one. Additionally, the whatever line termination + * sequence was encountered will be converted to a "\n" byte. Note + * that this class numbers lines from 0. When the first line + * terminator is encountered, the line number is incremented to 1, and + * so on. + *

    + * This class counts only line termination characters. If the last line + * read from the stream does not end in a line termination sequence, it + * will not be counted as a line. + *

    + * Note that since this class operates as a filter on an underlying + * stream, it has the same mark/reset functionality as the underlying + * stream. The mark() and reset() methods + * in this class handle line numbers correctly. Calling + * reset() resets the line number to the point at which + * mark() was called if the subordinate stream supports + * that functionality. + *

    + * @deprecated This class is deprecated in favor if + * LineNumberReader because it operates on ASCII bytes + * instead of an encoded character stream. This class is for backward + * compatibility only and should not be used in new applications. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class LineNumberInputStream extends FilterInputStream +{ + /** The current line number. */ + private int lineNumber = 0; + + /** The line number when the stream was marked. */ + private int markLineNumber = 0; + + /** Flag to indicate a '\r' was just read so that an immediately + * subsequent '\n' can be ignored. */ + private boolean justReadReturnChar = false; + + /** + * Create a new LineNumberInputStream that reads from the + * specified subordinate InputStream + * + * @param in The subordinate InputStream to read from + */ + public LineNumberInputStream(InputStream in) + { + super(in); + } + + /** + * This method returns the number of bytes that can be read from the + * stream before the stream can block. This method is tricky + * because the subordinate InputStream might return + * only "\r\n" characters, which are replaced by a single "\n" + * character by the read() method of this class. So + * this method can only guarantee that in.available() / + * 2 bytes can actually be read before blocking. In + * practice, considerably more bytes might be read before blocking + *

    + * Note that the stream may not block if additional bytes beyond the count + * returned by this method are read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + // We can only guarantee half the characters that might be available + // without blocking because "\r\n" is treated as a single character. + return in.available() / 2; + } + + /** + * This method returns the current line number + * + * @return The current line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * This method marks a position in the input to which the stream can + * be "reset" byte calling the reset() method. The + * parameter readlimit is the number of bytes that can + * be read from the stream after setting the mark before the mark + * becomes invalid. For example, if mark() is called + * with a read limit of 10, then when 11 bytes of data are read from + * the stream before the reset() method is called, then + * the mark is invalid and the stream object instance is not + * required to remember the mark. + *

    + * In this class, this method will remember the current line number + * as well as the current position in the stream. When the + * reset() method is called, the line number will be + * restored to the saved line number in addition to the stream + * position. + *

    + * This method only works if the subordinate stream supports mark/reset + * functionality. + * + * @param readlimit The number of bytes that can be read before the + * mark becomes invalid + */ + public void mark(int readlimit) + { + in.mark(readlimit); + markLineNumber = lineNumber; + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method will return -1 if the + * end of the stream has been reached. + *

    + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is returned from this method. This means + * that it is possible this method reads two bytes from the subordinate + * stream instead of just one. + *

    + * Note that this method will block until a byte of data is available + * to be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + // Treat "\r\n" as a single character. A '\r' may have been read by + // a previous call to read so we keep an internal flag to avoid having + // to read ahead. + + int ch = in.read(); + + if (ch == '\n') + if (justReadReturnChar) + { + ch = in.read(); + justReadReturnChar = false; + } + else + lineNumber++; + else if (ch == '\r') + { + ch = '\n'; + justReadReturnChar = true; + lineNumber++; + } + else + justReadReturnChar = false; + + return ch; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing data at index offset into + * the buffer and attemps to read len bytes. This method can + * return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returned to indicated the + * end of the stream. + *

    + * This method will block until some data can be read. + *

    + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is stored in the buffer. Only a single + * byte is counted towards the number of bytes read in this case. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream + * + * @exception IOException If an error occurs. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + // This case always succeeds. + if (len == 0) + return 0; + + // The simplest, though not necessarily the most time efficient thing + // to do is simply call read(void) len times. Since this is a deprecated + // class, that should be ok. + final int origOff = off; + while (len-- > 0) + { + int ch = read(); + if (ch < 0) + break; + + b[off++] = (byte) ch; + } + + // This is safe since we already know that some bytes were + // actually requested. + return off == origOff ? -1 : off - origOff; + } + + /** + * This method resets a stream to the point where the + * mark() method was called. Any bytes that were read + * after the mark point was set will be re-read during subsequent + * reads. + *

    + * In this class, this method will also restore the line number that was + * current when the mark() method was called. + *

    + * This method only works if the subordinate stream supports mark/reset + * functionality. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + in.reset(); + lineNumber = markLineNumber; + justReadReturnChar = false; + } + + /** + * This method sets the current line number to the specified value. + * + * @param lineNumber The new line number + */ + public void setLineNumber(int lineNumber) + { + this.lineNumber = lineNumber; + } + + /** + * This method skips up to the requested number of bytes in the + * input stream. The actual number of bytes skipped is returned. If the + * desired number of bytes to skip is negative, no bytes are skipped. + * + * @param n requested number of bytes to skip. + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs. + */ + public long skip(long n) throws IOException + { + if (n <= 0) + return 0L; + + final long origN = n; + + do + { + int ch = read(); + if (ch < 0) + break; + if (ch == '\n' || ch == '\r') + lineNumber++; + } + while (--n > 0); + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/LineNumberReader.java b/libjava/classpath/java/io/LineNumberReader.java new file mode 100644 index 000000000..6ac0b55fe --- /dev/null +++ b/libjava/classpath/java/io/LineNumberReader.java @@ -0,0 +1,416 @@ +/* LineNumberReader.java -- A character input stream which counts line numbers + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * This class functions like a standard Reader except that it + * counts line numbers, and canonicalizes newline characters. As data + * is read, whenever the char sequences "\r", "\n", or "\r\n" are encountered, + * the running line count is incremeted by one. Additionally, the whatever + * line termination sequence was encountered will be converted to a "\n" + * char. Note that this class numbers lines from 0. When the first + * line terminator is encountered, the line number is incremented to 1, and + * so on. Also note that actual "\r" and "\n" characters are looked for. + * The system dependent line separator sequence is ignored. + *

    + * This class counts only line termination characters. If the last line + * read from the stream does not end in a line termination sequence, it + * will not be counted as a line. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @date December 28, 2003. + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + * + * This implementation has the feature that if '\r' is read, it + * does not look for a '\n', but immediately returns '\n'. + * On the next read(), if a '\n' is read, it is skipped. + * This has the advantage that we do not read (and hang) unnecessarily. + * + * This implementation is also minimal in the number of fields it uses. + */ +public class LineNumberReader extends BufferedReader +{ + /** The current line number. */ + private int lineNumber; + /** Whether we already found a new line in the former call. */ + private boolean matchedNewLine; + /** The saved line number when calling mark() */ + private int savedLineNumber; + + /** + * Create a new LineNumberReader that reads from the + * specified subordinate Reader. A default 8K char sized + * buffer will be used for reads. + * + * @param in The subordinate Reader to read from + */ + public LineNumberReader(Reader in) + { + super(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new LineNumberReader to read + * from the specified subordinate Reader using the specified + * read buffer size. + * + * @param in The subordinate Reader to read from + * @param size The buffer size to use for reading + */ + public LineNumberReader(Reader in, int size) + { + super(in, size); + } + + /** + * This method returns the current line number + * + * @return The current line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * This method sets the current line number to the specified value. + * + * @param lineNumber The new line number + */ + public void setLineNumber(int lineNumber) + { + this.lineNumber = lineNumber; + } + + /** + * This method marks a position in the input to which the stream can be + * "reset" char calling the reset() method. The parameter + * readlimit is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if mark() is called with a read limit of 10, + * then when + * 11 chars of data are read from the stream before the reset() + * method is called, then the mark is invalid and the stream object + * instance is not required to remember the mark. + *

    + * In this class, this method will remember the current line number as well + * as the current position in the stream. When the reset() + * method + * is called, the line number will be restored to the saved line number in + * addition to the stream position. + * + * @param readLimit The number of chars that can be read before the + * mark becomes invalid + * + * @exception IOException If an error occurs + */ + public void mark(int readLimit) throws IOException + { + if (readLimit < 0) + throw new IllegalArgumentException("Read-ahead limit is negative"); + + synchronized (lock) + { + // This is basically the same as BufferedReader.mark. + // However, if the previous character was a '\r', we need to + // save that 'r', in case the next character is a '\n'. + if (pos + readLimit > limit) + { + int saveCR = matchedNewLine ? 1 : 0; + char[] old_buffer = buffer; + if (readLimit > limit) + buffer = new char[saveCR + readLimit]; + int copy_start = pos - saveCR; + savedLineNumber = lineNumber; + limit -= copy_start; + System.arraycopy(old_buffer, copy_start, buffer, 0, limit); + pos = saveCR; + } + markPos = pos; + } + } + + /** + * This method resets a stream to the point where the mark() + * method + * was called. Any chars that were read after the mark point was set will + * be re-read during subsequent reads. + *

    + * In this class, this method will also restore the line number that was + * current when the mark() method was called. + * + * @exception IOException If an error occurs + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (markPos < 0) + throw new IOException("mark never set or invalidated"); + lineNumber = savedLineNumber; + pos = markPos; + matchedNewLine = (markPos > 0 && buffer[markPos-1] == '\r'); + } + } + + /** + * This private method fills the input buffer whatever pos is. + * Consequently pos should be checked before calling this method. + * + * @return the number of bytes actually read from the input stream or + * -1 if end of stream. + * @exception IOException If an error occurs. + */ + private int fill() throws IOException + { + if (markPos >= 0 && limit == buffer.length) + markPos = -1; + if (markPos < 0) + pos = limit = 0; + int count = in.read(buffer, limit, buffer.length - limit); + if (count <= 0) + return -1; + limit += count; + + return count; + } + + /** + * This method reads an unsigned char from the input stream and returns it + * as an int in the range of 0-65535. This method will return -1 if the + * end of the stream has been reached. + *

    + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is returned from this method. This means + * that it is possible this method reads two chars from the subordinate + * stream instead of just one. + *

    + * Note that this method will block until a char of data is available + * to be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + synchronized (lock) + { + skipRedundantLF(); + if (pos >= limit && fill() < 0) + return -1; + char ch = buffer[pos++]; + + if ((matchedNewLine = (ch == '\r')) || ch == '\n') + { + lineNumber++; + return '\n'; + } + matchedNewLine = false; + return (int) ch; + } + } + + /** + * This method reads chars from a stream and stores them into a caller + * supplied buffer. It starts storing data at index offset into + * the buffer and attemps to read len chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicated the + * end of the stream. + *

    + * This method will block until some data can be read. + *

    + * Note that if a line termination sequence is encountered (ie, "\r", + * "\n", or "\r\n") then that line termination sequence is converted to + * a single "\n" value which is stored in the buffer. Only a single + * char is counted towards the number of chars read in this case. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param count The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream + * + * @exception IOException If an error occurs. + * @exception NullPointerException If buf is null (in any case). + * @exception IndexOutOfBoundsException If buffer parameters (offset and + * count) lies outside of the buffer capacity. + */ + public int read(char[] buf, int offset, int count) throws IOException + { + if (buf == null) + throw new NullPointerException(); + + if (offset + count > buf.length || offset < 0) + throw new IndexOutOfBoundsException(); + + if (count <= 0) + { + if (count < 0) + throw new IndexOutOfBoundsException(); + return 0; + } + + synchronized (lock) + { + if (pos >= limit && fill() < 0) + return -1; + + int start_offset = offset; + boolean matched = matchedNewLine; + + while (count-- > 0 && pos < limit) + { + char ch = buffer[pos++]; + if (ch == '\r') + { + lineNumber++; + matched = true; + } + else if (ch == '\n' && !matched) + lineNumber++; + else + matched = false; + + buf[offset++] = ch; + } + + matchedNewLine = matched; + return offset - start_offset; + } + } + + private void skipRedundantLF() throws IOException + { + if (pos > 0 && matchedNewLine) + { + if (pos < limit) + { // fast case + if (buffer[pos] == '\n') + pos++; + } + else + { // check whether the next buffer begins with '\n'. + // in that case kill the '\n'. + if (fill() <= 0) + return; + if (buffer[pos] == '\n') + pos++; + } + matchedNewLine = true; + } + } + + /** + * This method reads a line of text from the input stream and returns + * it as a String. A line is considered to be terminated + * by a "\r", "\n", or "\r\n" sequence, not by the system dependent line + * separator. + * + * @return The line read as a String or null + * if end of stream. + * + * @exception IOException If an error occurs + */ + public String readLine() throws IOException + { + // BufferedReader.readLine already does this. Shouldn't need to keep + // track of newlines (since the read method deals with this for us). + // But if the buffer is large, we may not call the read method at all + // and super.readLine can't increment lineNumber itself. + // Though it may seem kludgy, the safest thing to do is to save off + // lineNumber and increment it explicitly when we're done (iff we + // ended with a '\n' or '\r' as opposed to EOF). + // + // Also, we need to undo the special casing done by BufferedReader.readLine + // when a '\r' is the last char in the buffer. That situation is marked + // by 'pos > limit'. + int tmpLineNumber = lineNumber; + skipRedundantLF(); + String str = super.readLine(); + if (pos > limit) + --pos; + + // The only case where you mustn't increment the line number is you are + // at the EOS. + if (str != null) + lineNumber = tmpLineNumber + 1; + + return str; + } + + /** + * This method skips over characters in the stream. This method will + * skip the specified number of characters if possible, but is not required + * to skip them all. The actual number of characters skipped is returned. + * This method returns 0 if the specified number of chars is less than 1. + * + * @param count The specified number of chars to skip. + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip (long count) throws IOException + { + if (count < 0) + throw new IllegalArgumentException("skip() value is negative"); + if (count == 0) + return 0; + + int skipped; + char[] buf = new char[1]; + + for (skipped = 0; skipped < count; skipped++) + { + int ch = read(buf, 0, 1); + + if (ch < 0) + break; + } + + return skipped; + } +} diff --git a/libjava/classpath/java/io/NotActiveException.java b/libjava/classpath/java/io/NotActiveException.java new file mode 100644 index 000000000..949ba8eca --- /dev/null +++ b/libjava/classpath/java/io/NotActiveException.java @@ -0,0 +1,72 @@ +/* NotActiveException.java -- thrown when serialization is not active + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a problem occurs due to the fact that + * serialization is not active. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NotActiveException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -3893467273049808895L; + + /** + * Create an exception without a descriptive error message. + */ + public NotActiveException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public NotActiveException(String message) + { + super(message); + } +} // class NotActiveException diff --git a/libjava/classpath/java/io/NotSerializableException.java b/libjava/classpath/java/io/NotSerializableException.java new file mode 100644 index 000000000..d49c939e3 --- /dev/null +++ b/libjava/classpath/java/io/NotSerializableException.java @@ -0,0 +1,74 @@ +/* NotSerializableException.java -- a Serializable class that isn't + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a class implements Serializable because + * of a superclass, but should not be serialized. The descriptive message + * will consist of the name of the class in question. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NotSerializableException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 2906642554793891381L; + + /** + * Create an exception without a descriptive error message. + */ + public NotSerializableException() + { + } + + /** + * Create an exception with a descriptive error message, which should + * be the name of the class. + * + * @param message the descriptive error message + */ + public NotSerializableException(String message) + { + super(message); + } +} // class NotSerializableException diff --git a/libjava/classpath/java/io/ObjectInput.java b/libjava/classpath/java/io/ObjectInput.java new file mode 100644 index 000000000..f8d51e00d --- /dev/null +++ b/libjava/classpath/java/io/ObjectInput.java @@ -0,0 +1,140 @@ +/* ObjectInput.java -- Read object data from a stream + Copyright (C) 1998,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface extends the DataInput interface to provide a + * facility to read objects as well as primitive types from a stream. It + * also has methods that allow input to be done in a manner similar to + * InputStream + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see DataInput + */ +public interface ObjectInput extends DataInput +{ + /** + * This method returns the number of bytes that can be read without + * blocking. + * + * @return The number of bytes available before blocking + * + * @exception IOException If an error occurs + */ + int available() throws IOException; + + /** + * This method reading a byte of data from a stream. It returns that byte + * as an int. This method blocks if no data is available + * to be read. + * + * @return The byte of data read + * + * @exception IOException If an error occurs + */ + int read() throws IOException; + + /** + * This method reads raw bytes and stores them them a byte array buffer. + * Note that this method will block if no data is available. However, + * it will not necessarily block until it fills the entire buffer. That is, + * a "short count" is possible. + * + * @param buf The byte array to receive the data read + * + * @return The actual number of bytes read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + int read(byte[] buf) throws IOException; + + /** + * This method reads raw bytes and stores them in a byte array buffer + * buf starting at position offset into the + * buffer. A + * maximum of len bytes will be read. Note that this method + * blocks if no data is available, but will not necessarily block until + * it can read len bytes of data. That is, a "short count" is + * possible. + * + * @param buf The byte array to receive the data read + * @param offset The offset into buf to start storing data + * @param len The maximum number of bytes to read + * + * @return The actual number of bytes read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + int read(byte[] buf, int offset, int len) throws IOException; + + /** + * Reads an object instance and returns it. If the class for the object + * being read cannot be found, then a ClassNotFoundException + * will be thrown. + * + * @return The object instance that was read + * + * @exception ClassNotFoundException If a class for the object cannot be + * found + * @exception IOException If any other error occurs + */ + Object readObject() + throws ClassNotFoundException, IOException; + + /** + * This method causes the specified number of bytes to be read and + * discarded. It is possible that fewer than the requested number of bytes + * will actually be skipped. + * + * @param numBytes The number of bytes to skip + * + * @return The actual number of bytes skipped + * + * @exception IOException If an error occurs + */ + long skip(long numBytes) throws IOException; + + /** + * This method closes the input source + * + * @exception IOException If an error occurs + */ + void close() throws IOException; +} diff --git a/libjava/classpath/java/io/ObjectInputStream.java b/libjava/classpath/java/io/ObjectInputStream.java new file mode 100644 index 000000000..c37f7665f --- /dev/null +++ b/libjava/classpath/java/io/ObjectInputStream.java @@ -0,0 +1,2142 @@ +/* ObjectInputStream.java -- Class used to read serialized objects + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Pair; +import gnu.classpath.VMStackWalker; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeSet; + +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class ObjectInputStream extends InputStream + implements ObjectInput, ObjectStreamConstants +{ + /** + * Creates a new ObjectInputStream that will do all of + * its reading from in. This method also checks + * the stream by reading the header information (stream magic number + * and stream version). + * + * @exception IOException Reading stream header from underlying + * stream cannot be completed. + * + * @exception StreamCorruptedException An invalid stream magic + * number or stream version was read from the stream. + * + * @see #readStreamHeader() + */ + public ObjectInputStream(InputStream in) + throws IOException, StreamCorruptedException + { + if (DEBUG) + { + String val = System.getProperty("gcj.dumpobjects"); + if (dump == false && val != null && !val.equals("")) + { + dump = true; + System.out.println ("Serialization debugging enabled"); + } + else if (dump == true && (val == null || val.equals(""))) + { + dump = false; + System.out.println ("Serialization debugging disabled"); + } + } + + this.resolveEnabled = false; + this.blockDataPosition = 0; + this.blockDataBytes = 0; + this.blockData = new byte[BUFFER_SIZE]; + this.blockDataInput = new DataInputStream(this); + this.realInputStream = new DataInputStream(in); + this.nextOID = baseWireHandle; + handles = new HashMap>(); + this.classLookupTable = new Hashtable(); + setBlockDataMode(true); + readStreamHeader(); + } + + + /** + * Returns the next deserialized object read from the underlying stream. + * + * This method can be overriden by a class by implementing + * private void readObject (ObjectInputStream). + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. This method can also throw Errors and + * RuntimeExceptions if caused by existing readResolve() user code. + * + * @return The object read from the underlying stream. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception IOException Exception from underlying + * InputStream. + */ + public final Object readObject() + throws ClassNotFoundException, IOException + { + return readObject(true); + } + + /** + *

    + * Returns the next deserialized object read from the + * underlying stream in an unshared manner. Any object + * returned by this method will not be returned by + * subsequent calls to either this method or {@link #readObject()}. + *

    + *

    + * This behaviour is achieved by: + *

    + *
      + *
    • Marking the handles created by successful calls to this + * method, so that future calls to {@link #readObject()} or + * {@link #readUnshared()} will throw an {@link ObjectStreamException} + * rather than returning the same object reference.
    • + *
    • Throwing an {@link ObjectStreamException} if the next + * element in the stream is a reference to an earlier object.
    • + *
    + * + * @return a reference to the deserialized object. + * @throws ClassNotFoundException if the class of the object being + * deserialized can not be found. + * @throws StreamCorruptedException if information in the stream + * is inconsistent. + * @throws ObjectStreamException if the next object has already been + * returned by an earlier call to this + * method or {@link #readObject()}. + * @throws OptionalDataException if primitive data occurs next in the stream. + * @throws IOException if an I/O error occurs from the stream. + * @since 1.4 + * @see #readObject() + */ + public Object readUnshared() + throws IOException, ClassNotFoundException + { + return readObject(false); + } + + /** + * Returns the next deserialized object read from the underlying stream. + * + * This method can be overriden by a class by implementing + * private void readObject (ObjectInputStream). + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. This method can also throw Errors and + * RuntimeExceptions if caused by existing readResolve() user code. + * + * @param shared true if handles created by this call should be shared + * with later calls. + * @return The object read from the underlying stream. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception IOException Exception from underlying + * InputStream. + */ + private final Object readObject(boolean shared) + throws ClassNotFoundException, IOException + { + if (this.useSubclassMethod) + return readObjectOverride(); + + Object ret_val; + boolean old_mode = setBlockDataMode(false); + byte marker = this.realInputStream.readByte(); + + if (DEBUG) + depth += 2; + + if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " "); + + try + { + ret_val = parseContent(marker, shared); + } + finally + { + setBlockDataMode(old_mode); + if (DEBUG) + depth -= 2; + } + + return ret_val; + } + + /** + * Handles a content block within the stream, which begins with a marker + * byte indicating its type. + * + * @param marker the byte marker. + * @param shared true if handles created by this call should be shared + * with later calls. + * @return an object which represents the parsed content. + * @throws ClassNotFoundException if the class of an object being + * read in cannot be found. + * @throws IOException if invalid data occurs or one is thrown by the + * underlying InputStream. + */ + private Object parseContent(byte marker, boolean shared) + throws ClassNotFoundException, IOException + { + Object ret_val; + boolean is_consumed = false; + + switch (marker) + { + case TC_ENDBLOCKDATA: + { + ret_val = null; + is_consumed = true; + break; + } + + case TC_BLOCKDATA: + case TC_BLOCKDATALONG: + { + if (marker == TC_BLOCKDATALONG) + { if(dump) dumpElementln("BLOCKDATALONG"); } + else + { if(dump) dumpElementln("BLOCKDATA"); } + readNextBlock(marker); + } + + case TC_NULL: + { + if(dump) dumpElementln("NULL"); + ret_val = null; + break; + } + + case TC_REFERENCE: + { + if(dump) dumpElement("REFERENCE "); + int oid = realInputStream.readInt(); + if(dump) dumpElementln(Integer.toHexString(oid)); + ret_val = lookupHandle(oid); + if (!shared) + throw new + InvalidObjectException("References can not be read unshared."); + break; + } + + case TC_CLASS: + { + if(dump) dumpElementln("CLASS"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class clazz = osc.forClass(); + assignNewHandle(clazz,shared); + ret_val = clazz; + break; + } + + case TC_PROXYCLASSDESC: + { + if(dump) dumpElementln("PROXYCLASS"); + +/* GCJ LOCAL */ + // The grammar at this point is + // TC_PROXYCLASSDESC newHandle proxyClassDescInfo + // i.e. we have to assign the handle immediately after + // reading the marker. + int handle = assignNewHandle("Dummy proxy",shared); +/* END GCJ LOCAL */ + + int n_intf = this.realInputStream.readInt(); + String[] intfs = new String[n_intf]; + for (int i = 0; i < n_intf; i++) + { + intfs[i] = this.realInputStream.readUTF(); + } + + boolean oldmode = setBlockDataMode(true); + Class cl = resolveProxyClass(intfs); + setBlockDataMode(oldmode); + + ObjectStreamClass osc = lookupClass(cl); + if (osc.firstNonSerializableParentConstructor == null) + { + osc.realClassIsSerializable = true; + osc.fields = osc.fieldMapping = new ObjectStreamField[0]; + try + { + osc.firstNonSerializableParentConstructor = + Object.class.getConstructor(new Class[0]); + } + catch (NoSuchMethodException x) + { + throw (InternalError) + new InternalError("Object ctor missing").initCause(x); + } + } +/* GCJ LOCAL */ + rememberHandle(osc,shared,handle); +/* END GCJ LOCAL */ + + if (!is_consumed) + { + byte b = this.realInputStream.readByte(); + if (b != TC_ENDBLOCKDATA) + throw new IOException("Data annotated to class was not consumed." + b); + } + else + is_consumed = false; + ObjectStreamClass superosc = (ObjectStreamClass)readObject(); + osc.setSuperclass(superosc); + ret_val = osc; + break; + } + + case TC_CLASSDESC: + { + ObjectStreamClass osc = readClassDescriptor(); + + if (!is_consumed) + { + byte b = this.realInputStream.readByte(); + if (b != TC_ENDBLOCKDATA) + throw new IOException("Data annotated to class was not consumed." + b); + } + else + is_consumed = false; + + osc.setSuperclass ((ObjectStreamClass)readObject()); + ret_val = osc; + break; + } + + case TC_STRING: + { + if(dump) dumpElement("STRING="); + String s = this.realInputStream.readUTF(); + if(dump) dumpElementln(s); + ret_val = processResolution(null, s, assignNewHandle(s,shared), + shared); + break; + } + + case TC_LONGSTRING: + { + if(dump) dumpElement("STRING="); + String s = this.realInputStream.readUTFLong(); + if(dump) dumpElementln(s); + ret_val = processResolution(null, s, assignNewHandle(s,shared), + shared); + break; + } + + case TC_ARRAY: + { + if(dump) dumpElementln("ARRAY"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class componentType = osc.forClass().getComponentType(); + if(dump) dumpElement("ARRAY LENGTH="); + int length = this.realInputStream.readInt(); + if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); + Object array = Array.newInstance(componentType, length); + int handle = assignNewHandle(array,shared); + readArrayElements(array, componentType); + if(dump) + for (int i = 0, len = Array.getLength(array); i < len; i++) + dumpElementln(" ELEMENT[" + i + "]=", Array.get(array, i)); + ret_val = processResolution(null, array, handle, shared); + break; + } + + case TC_OBJECT: + { + if(dump) dumpElementln("OBJECT"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class clazz = osc.forClass(); + + if (!osc.realClassIsSerializable) + throw new NotSerializableException + (clazz + " is not Serializable, and thus cannot be deserialized."); + + if (osc.realClassIsExternalizable) + { + Externalizable obj = osc.newInstance(); + + int handle = assignNewHandle(obj,shared); + + boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); + + boolean oldmode = this.readDataFromBlock; + if (read_from_blocks) + setBlockDataMode(true); + + obj.readExternal(this); + + if (read_from_blocks) + { + setBlockDataMode(oldmode); + if (!oldmode) + if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) + throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); + } + + ret_val = processResolution(osc, obj, handle,shared); + break; + + } // end if (osc.realClassIsExternalizable) + + Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); + + int handle = assignNewHandle(obj,shared); + Object prevObject = this.currentObject; + ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; + TreeSet prevObjectValidators = + this.currentObjectValidators; + + this.currentObject = obj; + this.currentObjectValidators = null; + ObjectStreamClass[] hierarchy = hierarchy(clazz); + + for (int i = 0; i < hierarchy.length; i++) + { + this.currentObjectStreamClass = hierarchy[i]; + if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ()); + + // XXX: should initialize fields in classes in the hierarchy + // that aren't in the stream + // should skip over classes in the stream that aren't in the + // real classes hierarchy + + Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod; + if (readObjectMethod != null) + { + fieldsAlreadyRead = false; + boolean oldmode = setBlockDataMode(true); + callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj); + setBlockDataMode(oldmode); + } + else + { + readFields(obj, currentObjectStreamClass); + } + + if (this.currentObjectStreamClass.hasWriteMethod()) + { + if(dump) dumpElement("ENDBLOCKDATA? "); + try + { + /* Read blocks until an end marker */ + byte writeMarker = this.realInputStream.readByte(); + while (writeMarker != TC_ENDBLOCKDATA) + { + parseContent(writeMarker, shared); + writeMarker = this.realInputStream.readByte(); + } + if(dump) dumpElementln("yes"); + } + catch (EOFException e) + { + throw (IOException) new IOException + ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e); + } + } + } + + this.currentObject = prevObject; + this.currentObjectStreamClass = prevObjectStreamClass; + ret_val = processResolution(osc, obj, handle, shared); + if (currentObjectValidators != null) + invokeValidators(); + this.currentObjectValidators = prevObjectValidators; + + break; + } + + case TC_RESET: + if(dump) dumpElementln("RESET"); + clearHandles(); + ret_val = readObject(); + break; + + case TC_EXCEPTION: + { + if(dump) dumpElement("EXCEPTION="); + Exception e = (Exception)readObject(); + if(dump) dumpElementln(e.toString()); + clearHandles(); + throw new WriteAbortedException("Exception thrown during writing of stream", e); + } + + case TC_ENUM: + { + /* TC_ENUM classDesc newHandle enumConstantName */ + if (dump) + dumpElementln("ENUM="); + ObjectStreamClass osc = (ObjectStreamClass) readObject(); + String constantName = (String) readObject(); + if (dump) + dumpElementln("CONSTANT NAME = " + constantName); + Class clazz = osc.forClass(); + Enum instance = Enum.valueOf(clazz, constantName); + assignNewHandle(instance,shared); + ret_val = instance; + break; + } + + default: + throw new IOException("Unknown marker on stream: " + marker); + } + return ret_val; + } + + /** + * This method makes a partial check of types for the fields + * contained given in arguments. It checks primitive types of + * fields1 against non primitive types of fields2. This method + * assumes the two lists has already been sorted according to + * the Java specification. + * + * @param name Name of the class owning the given fields. + * @param fields1 First list to check. + * @param fields2 Second list to check. + * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present + * in the non primitive part in fields2. + */ + private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2) + throws InvalidClassException + { + int nonPrimitive = 0; + + for (nonPrimitive = 0; + nonPrimitive < fields1.length + && fields1[nonPrimitive].isPrimitive(); nonPrimitive++) + { + } + + if (nonPrimitive == fields1.length) + return; + + int i = 0; + ObjectStreamField f1; + ObjectStreamField f2; + + while (i < fields2.length + && nonPrimitive < fields1.length) + { + f1 = fields1[nonPrimitive]; + f2 = fields2[i]; + + if (!f2.isPrimitive()) + break; + + int compVal = f1.getName().compareTo (f2.getName()); + + if (compVal < 0) + { + nonPrimitive++; + } + else if (compVal > 0) + { + i++; + } + else + { + throw new InvalidClassException + ("invalid field type for " + f2.getName() + + " in class " + name); + } + } + } + + /** + * This method reads a class descriptor from the real input stream + * and use these data to create a new instance of ObjectStreamClass. + * Fields are sorted and ordered for the real read which occurs for + * each instance of the described class. Be aware that if you call that + * method you must ensure that the stream is synchronized, in the other + * case it may be completely desynchronized. + * + * @return A new instance of ObjectStreamClass containing the freshly + * created descriptor. + * @throws ClassNotFoundException if the required class to build the + * descriptor has not been found in the system. + * @throws IOException An input/output error occured. + * @throws InvalidClassException If there was a compatibility problem + * between the class present in the system and the serialized class. + */ + protected ObjectStreamClass readClassDescriptor() + throws ClassNotFoundException, IOException + { + if(dump) dumpElement("CLASSDESC NAME="); + String name = this.realInputStream.readUTF(); + if(dump) dumpElement(name + "; UID="); + long uid = this.realInputStream.readLong (); + if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS="); + byte flags = this.realInputStream.readByte (); + if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT="); + short field_count = this.realInputStream.readShort(); + if(dump) dumpElementln(Short.toString(field_count)); + ObjectStreamField[] fields = new ObjectStreamField[field_count]; + ObjectStreamClass osc = new ObjectStreamClass(name, uid, + flags, fields); + assignNewHandle(osc,true); + + for (int i = 0; i < field_count; i++) + { + if(dump) dumpElement(" TYPE CODE="); + char type_code = (char)this.realInputStream.readByte(); + if(dump) dumpElement(type_code + "; FIELD NAME="); + String field_name = this.realInputStream.readUTF(); + if(dump) dumpElementln(field_name); + String class_name; + + // If the type code is an array or an object we must + // decode a String here. In the other case we convert + // the type code and pass it to ObjectStreamField. + // Type codes are decoded by gnu.java.lang.reflect.TypeSignature. + if (type_code == 'L' || type_code == '[') + class_name = (String)readObject(); + else + class_name = String.valueOf(type_code); + + fields[i] = + new ObjectStreamField(field_name, class_name); + } + + /* Now that fields have been read we may resolve the class + * (and read annotation if needed). */ + Class clazz = resolveClass(osc); + ClassLoader loader = clazz.getClassLoader(); + for (int i = 0; i < field_count; i++) + { + fields[i].resolveType(loader); + } + boolean oldmode = setBlockDataMode(true); + osc.setClass(clazz, lookupClass(clazz.getSuperclass())); + classLookupTable.put(clazz, osc); + setBlockDataMode(oldmode); + + // find the first non-serializable class in clazz's inheritance hierarchy + Class first_nonserial = clazz.getSuperclass(); + // Maybe it is a primitive class, those don't have a super class, + // or Object itself. Otherwise we can keep getting the superclass + // till we hit the Object class, or some other non-serializable class. + + if (first_nonserial == null) + first_nonserial = clazz; + else + while (Serializable.class.isAssignableFrom(first_nonserial)) + first_nonserial = first_nonserial.getSuperclass(); + + final Class local_constructor_class = first_nonserial; + + osc.firstNonSerializableParentConstructor = + (Constructor)AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + try + { + Constructor c = local_constructor_class. + getDeclaredConstructor(new Class[0]); + if (Modifier.isPrivate(c.getModifiers())) + return null; + return c; + } + catch (NoSuchMethodException e) + { + // error will be reported later, in newObject() + return null; + } + } + }); + + osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz); + osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz); + + ObjectStreamField[] stream_fields = osc.fields; + ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields; + ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)]; + + int stream_idx = 0; + int real_idx = 0; + int map_idx = 0; + + /* + * Check that there is no type inconsistencies between the lists. + * A special checking must be done for the two groups: primitive types and + * not primitive types. + */ + checkTypeConsistency(name, real_fields, stream_fields); + checkTypeConsistency(name, stream_fields, real_fields); + + + while (stream_idx < stream_fields.length + || real_idx < real_fields.length) + { + ObjectStreamField stream_field = null; + ObjectStreamField real_field = null; + + if (stream_idx == stream_fields.length) + { + real_field = real_fields[real_idx++]; + } + else if (real_idx == real_fields.length) + { + stream_field = stream_fields[stream_idx++]; + } + else + { + int comp_val = + real_fields[real_idx].compareTo (stream_fields[stream_idx]); + + if (comp_val < 0) + { + real_field = real_fields[real_idx++]; + } + else if (comp_val > 0) + { + stream_field = stream_fields[stream_idx++]; + } + else + { + stream_field = stream_fields[stream_idx++]; + real_field = real_fields[real_idx++]; + if (stream_field.getType() != real_field.getType()) + throw new InvalidClassException + ("invalid field type for " + real_field.getName() + + " in class " + name); + } + } + + /* If some of stream_fields does not correspond to any of real_fields, + * or the opposite, then fieldmapping will go short. + */ + if (map_idx == fieldmapping.length) + { + ObjectStreamField[] newfieldmapping = + new ObjectStreamField[fieldmapping.length + 2]; + System.arraycopy(fieldmapping, 0, + newfieldmapping, 0, fieldmapping.length); + fieldmapping = newfieldmapping; + } + fieldmapping[map_idx++] = stream_field; + fieldmapping[map_idx++] = real_field; + } + osc.fieldMapping = fieldmapping; + + return osc; + } + + /** + * Reads the current objects non-transient, non-static fields from + * the current class from the underlying output stream. + * + * This method is intended to be called from within a object's + * private void readObject (ObjectInputStream) + * method. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception NotActiveException This method was called from a + * context other than from the current object's and current class's + * private void readObject (ObjectInputStream) + * method. + * + * @exception IOException Exception from underlying + * OutputStream. + */ + public void defaultReadObject() + throws ClassNotFoundException, IOException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("defaultReadObject called by non-active" + + " class and/or object"); + + if (fieldsAlreadyRead) + throw new NotActiveException("defaultReadObject called but fields " + + "already read from stream (by " + + "defaultReadObject or readFields)"); + + boolean oldmode = setBlockDataMode(false); + readFields(this.currentObject, this.currentObjectStreamClass); + setBlockDataMode(oldmode); + + fieldsAlreadyRead = true; + } + + + /** + * Registers a ObjectInputValidation to be carried out + * on the object graph currently being deserialized before it is + * returned to the original caller of readObject (). + * The order of validation for multiple + * ObjectInputValidations can be controled using + * priority. Validators with higher priorities are + * called first. + * + * @see java.io.ObjectInputValidation + * + * @exception InvalidObjectException validator is + * null + * + * @exception NotActiveException an attempt was made to add a + * validator outside of the readObject method of the + * object currently being deserialized + */ + public void registerValidation(ObjectInputValidation validator, + int priority) + throws InvalidObjectException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("registerValidation called by non-active " + + "class and/or object"); + + if (validator == null) + throw new InvalidObjectException("attempt to add a null " + + "ObjectInputValidation object"); + + if (currentObjectValidators == null) + currentObjectValidators = new TreeSet(); + + currentObjectValidators.add(new ValidatorAndPriority(validator, priority)); + } + + + /** + * Called when a class is being deserialized. This is a hook to + * allow subclasses to read in information written by the + * annotateClass (Class) method of an + * ObjectOutputStream. + * + * This implementation looks up the active call stack for a + * ClassLoader; if a ClassLoader is found, + * it is used to load the class associated with osc, + * otherwise, the default system ClassLoader is used. + * + * @exception IOException Exception from underlying + * OutputStream. + * + * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) + */ + protected Class resolveClass(ObjectStreamClass osc) + throws ClassNotFoundException, IOException + { + String name = osc.getName(); + try + { + return Class.forName(name, true, currentLoader()); + } + catch(ClassNotFoundException x) + { + if (name.equals("void")) + return Void.TYPE; + else if (name.equals("boolean")) + return Boolean.TYPE; + else if (name.equals("byte")) + return Byte.TYPE; + else if (name.equals("char")) + return Character.TYPE; + else if (name.equals("short")) + return Short.TYPE; + else if (name.equals("int")) + return Integer.TYPE; + else if (name.equals("long")) + return Long.TYPE; + else if (name.equals("float")) + return Float.TYPE; + else if (name.equals("double")) + return Double.TYPE; + else + throw x; + } + } + + /** + * Returns the most recent user defined ClassLoader on the execution stack + * or null if none is found. + */ + private ClassLoader currentLoader() + { + return VMStackWalker.firstNonNullClassLoader(); + } + + /** + * Lookup a class stored in the local hashtable. If it is not + * use the global lookup function in ObjectStreamClass to build + * the ObjectStreamClass. This method is requested according to + * the behaviour detected in the JDK by Kaffe's team. + * + * @param clazz Class to lookup in the hash table or for which + * we must build a descriptor. + * @return A valid instance of ObjectStreamClass corresponding + * to the specified class. + */ + private ObjectStreamClass lookupClass(Class clazz) + { + if (clazz == null) + return null; + + ObjectStreamClass oclazz; + oclazz = classLookupTable.get(clazz); + if (oclazz == null) + return ObjectStreamClass.lookup(clazz); + else + return oclazz; + } + + /** + * 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[] hierarchy(Class clazz) + { + ObjectStreamClass osc = lookupClass(clazz); + + return osc == null ? new ObjectStreamClass[0] : osc.hierarchy(); + } + + /** + * Allows subclasses to resolve objects that are read from the + * stream with other objects to be returned in their place. This + * method is called the first time each object is encountered. + * + * This method must be enabled before it will be called in the + * serialization process. + * + * @exception IOException Exception from underlying + * OutputStream. + * + * @see #enableResolveObject(boolean) + */ + protected Object resolveObject(Object obj) throws IOException + { + return obj; + } + + + protected Class resolveProxyClass(String[] intfs) + throws IOException, ClassNotFoundException + { + ClassLoader cl = currentLoader(); + + Class[] clss = new Class[intfs.length]; + if(cl == null) + { + for (int i = 0; i < intfs.length; i++) + clss[i] = Class.forName(intfs[i]); + cl = ClassLoader.getSystemClassLoader(); + } + else + for (int i = 0; i < intfs.length; i++) + clss[i] = Class.forName(intfs[i], false, cl); + try + { + return Proxy.getProxyClass(cl, clss); + } + catch (IllegalArgumentException e) + { + throw new ClassNotFoundException(null, e); + } + } + + /** + * If enable is true and this object is + * trusted, then resolveObject (Object) will be called + * in subsequent calls to readObject (Object). + * Otherwise, resolveObject (Object) will not be called. + * + * @exception SecurityException This class is not trusted. + */ + protected boolean enableResolveObject (boolean enable) + throws SecurityException + { + if (enable) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SerializablePermission("enableSubstitution")); + } + + boolean old_val = this.resolveEnabled; + this.resolveEnabled = enable; + return old_val; + } + + /** + * Reads stream magic and stream version information from the + * underlying stream. + * + * @exception IOException Exception from underlying stream. + * + * @exception StreamCorruptedException An invalid stream magic + * number or stream version was read from the stream. + */ + protected void readStreamHeader() + throws IOException, StreamCorruptedException + { + if(dump) dumpElement("STREAM MAGIC "); + if (this.realInputStream.readShort() != STREAM_MAGIC) + throw new StreamCorruptedException("Invalid stream magic number"); + + if(dump) dumpElementln("STREAM VERSION "); + if (this.realInputStream.readShort() != STREAM_VERSION) + throw new StreamCorruptedException("Invalid stream version number"); + } + + public int read() throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock(); + return (this.blockData[this.blockDataPosition++] & 0xff); + } + else + return this.realInputStream.read(); + } + + public int read(byte[] data, int offset, int length) throws IOException + { + if (this.readDataFromBlock) + { + int remain = this.blockDataBytes - this.blockDataPosition; + if (remain == 0) + { + readNextBlock(); + remain = this.blockDataBytes - this.blockDataPosition; + } + length = Math.min(length, remain); + System.arraycopy(this.blockData, this.blockDataPosition, + data, offset, length); + this.blockDataPosition += length; + + return length; + } + else + return this.realInputStream.read(data, offset, length); + } + + public int available() throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock (); + + return this.blockDataBytes - this.blockDataPosition; + } + else + return this.realInputStream.available(); + } + + public void close() throws IOException + { + this.realInputStream.close(); + } + + public boolean readBoolean() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode (true); + boolean value = this.dataInputStream.readBoolean (); + if (switchmode) + setBlockDataMode (oldmode); + return value; + } + + public byte readByte() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + byte value = this.dataInputStream.readByte(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readUnsignedByte() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readUnsignedByte(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public short readShort() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + short value = this.dataInputStream.readShort(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readUnsignedShort() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readUnsignedShort(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public char readChar() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + char value = this.dataInputStream.readChar(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readInt() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readInt(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public long readLong() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + long value = this.dataInputStream.readLong(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public float readFloat() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + float value = this.dataInputStream.readFloat(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public double readDouble() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + double value = this.dataInputStream.readDouble(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public void readFully(byte data[]) throws IOException + { + this.dataInputStream.readFully(data); + } + + public void readFully(byte data[], int offset, int size) + throws IOException + { + this.dataInputStream.readFully(data, offset, size); + } + + public int skipBytes(int len) throws IOException + { + return this.dataInputStream.skipBytes(len); + } + + /** + * @deprecated + * @see java.io.DataInputStream#readLine () + */ + public String readLine() throws IOException + { + return this.dataInputStream.readLine(); + } + + public String readUTF() throws IOException + { + return this.dataInputStream.readUTF(); + } + + /** + * This class allows a class to specify exactly which fields should + * be read, and what values should be read for these fields. + * + * XXX: finish up comments + */ + public abstract static class GetField + { + public abstract ObjectStreamClass getObjectStreamClass(); + + public abstract boolean defaulted(String name) + throws IOException, IllegalArgumentException; + + public abstract boolean get(String name, boolean defvalue) + throws IOException, IllegalArgumentException; + + public abstract char get(String name, char defvalue) + throws IOException, IllegalArgumentException; + + public abstract byte get(String name, byte defvalue) + throws IOException, IllegalArgumentException; + + public abstract short get(String name, short defvalue) + throws IOException, IllegalArgumentException; + + public abstract int get(String name, int defvalue) + throws IOException, IllegalArgumentException; + + public abstract long get(String name, long defvalue) + throws IOException, IllegalArgumentException; + + public abstract float get(String name, float defvalue) + throws IOException, IllegalArgumentException; + + public abstract double get(String name, double defvalue) + throws IOException, IllegalArgumentException; + + public abstract Object get(String name, Object defvalue) + throws IOException, IllegalArgumentException; + } + + /** + * This method should be called by a method called 'readObject' in the + * deserializing class (if present). It cannot (and should not)be called + * outside of it. Its goal is to read all fields in the real input stream + * and keep them accessible through the {@link GetField} class. Calling + * this method will not alter the deserializing object. + * + * @return A valid freshly created 'GetField' instance to get access to + * the deserialized stream. + * @throws IOException An input/output exception occured. + * @throws ClassNotFoundException + * @throws NotActiveException + */ + public GetField readFields() + throws IOException, ClassNotFoundException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("readFields called by non-active class and/or object"); + + if (prereadFields != null) + return prereadFields; + + if (fieldsAlreadyRead) + throw new NotActiveException("readFields called but fields already read from" + + " stream (by defaultReadObject or readFields)"); + + final ObjectStreamClass clazz = this.currentObjectStreamClass; + final byte[] prim_field_data = new byte[clazz.primFieldSize]; + final Object[] objs = new Object[clazz.objectFieldCount]; + + // Apparently Block data is not used with GetField as per + // empirical evidence against JDK 1.2. Also see Mauve test + // java.io.ObjectInputOutput.Test.GetPutField. + boolean oldmode = setBlockDataMode(false); + readFully(prim_field_data); + for (int i = 0; i < objs.length; ++ i) + objs[i] = readObject(); + setBlockDataMode(oldmode); + + prereadFields = new GetField() + { + public ObjectStreamClass getObjectStreamClass() + { + return clazz; + } + + public boolean defaulted(String name) + throws IOException, IllegalArgumentException + { + ObjectStreamField f = clazz.getField(name); + + /* First if we have a serialized field use the descriptor */ + if (f != null) + { + /* It is in serialPersistentFields but setClass tells us + * it should not be set. This value is defaulted. + */ + if (f.isPersistent() && !f.isToSet()) + return true; + + return false; + } + + /* This is not a serialized field. There should be + * a default value only if the field really exists. + */ + try + { + return (clazz.forClass().getDeclaredField (name) != null); + } + catch (NoSuchFieldException e) + { + throw new IllegalArgumentException(e); + } + } + + public boolean get(String name, boolean defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Boolean.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset()] == 0 ? false : true; + } + + public char get(String name, char defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Character.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (char)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public byte get(String name, byte defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Byte.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset()]; + } + + public short get(String name, short defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Short.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (short)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public int get(String name, int defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Integer.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF); + } + + public long get(String name, long defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Long.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (long)(((prim_field_data[off++] & 0xFFL) << 56) + | ((prim_field_data[off++] & 0xFFL) << 48) + | ((prim_field_data[off++] & 0xFFL) << 40) + | ((prim_field_data[off++] & 0xFFL) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public float get(String name, float defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Float.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public double get(String name, double defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Double.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return Double.longBitsToDouble + ( (long) (((prim_field_data[off++] & 0xFFL) << 56) + | ((prim_field_data[off++] & 0xFFL) << 48) + | ((prim_field_data[off++] & 0xFFL) << 40) + | ((prim_field_data[off++] & 0xFFL) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF))); + } + + public Object get(String name, Object defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = + getField(name, defvalue == null ? null : defvalue.getClass ()); + + if (field == null) + return defvalue; + + return objs[field.getOffset()]; + } + + private ObjectStreamField getField(String name, Class type) + throws IllegalArgumentException + { + ObjectStreamField field = clazz.getField(name); + boolean illegal = false; + + // XXX This code is horrible and needs to be rewritten! + try + { + try + { + Class field_type = field.getType(); + + if (type == field_type || + (type == null && !field_type.isPrimitive())) + { + /* See defaulted */ + return field; + } + + illegal = true; + throw new IllegalArgumentException + ("Field requested is of type " + + field_type.getName() + + ", but requested type was " + + (type == null ? "Object" : type.getName())); + } + catch (NullPointerException _) + { + /* Here we catch NullPointerException, because it may + only come from the call 'field.getType()'. If field + is null, we have to return null and classpath ethic + say we must try to avoid 'if (xxx == null)'. + */ + } + catch (IllegalArgumentException e) + { + throw e; + } + + return null; + } + finally + { + /* If this is an unassigned field we should return + * the default value. + */ + if (!illegal && field != null && !field.isToSet() && field.isPersistent()) + return null; + + /* We do not want to modify transient fields. They should + * be left to 0. + */ + try + { + Field f = clazz.forClass().getDeclaredField(name); + if (Modifier.isTransient(f.getModifiers())) + throw new IllegalArgumentException + ("no such field (non transient) " + name); + if (field == null && f.getType() != type) + throw new IllegalArgumentException + ("Invalid requested type for field " + name); + } + catch (NoSuchFieldException e) + { + if (field == null) + throw new IllegalArgumentException(e); + } + + } + } + }; + + fieldsAlreadyRead = true; + return prereadFields; + } + + /** + * Protected constructor that allows subclasses to override + * deserialization. This constructor should be called by subclasses + * that wish to override readObject (Object). This + * method does a security check NOTE: currently not + * implemented, then sets a flag that informs + * readObject (Object) to call the subclasses + * readObjectOverride (Object) method. + * + * @see #readObjectOverride() + */ + protected ObjectInputStream() + throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager(); + if (sec_man != null) + sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + this.useSubclassMethod = true; + } + + /** + * This method allows subclasses to override the default + * de serialization mechanism provided by + * ObjectInputStream. To make this method be used for + * writing objects, subclasses must invoke the 0-argument + * constructor on this class from their constructor. + * + * @see #ObjectInputStream() + */ + protected Object readObjectOverride() + throws ClassNotFoundException, IOException, OptionalDataException + { + throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride"); + } + + /** + * Assigns the next available handle to obj. + * + * @param obj The object for which we want a new handle. + * @param shared True if the handle should be shared + * with later calls. + * @return A valid handle for the specified object. + */ + private int assignNewHandle(Object obj, boolean shared) + { + int handle = this.nextOID; + this.nextOID = handle + 1; + rememberHandle(obj,shared,handle); + return handle; + } + + /** + * Remember the object associated with the given handle. + * + * @param obj an object + * @param shared true if the reference should be shared + * with later calls. + * @param handle a handle, must be >= baseWireHandle + * + * @see #lookupHandle + */ + private void rememberHandle(Object obj, boolean shared, + int handle) + { + handles.put(handle, new Pair(shared, 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. + * @throws StreamCorruptedException if the handle is invalid. + * @throws InvalidObjectException if the reference is not shared. + * @see #rememberHandle + */ + private Object lookupHandle(int handle) + throws ObjectStreamException + { + Pair result = handles.get(handle); + if (result == null) + throw new StreamCorruptedException("The handle, " + + Integer.toHexString(handle) + + ", is invalid."); + if (!result.getLeft()) + throw new InvalidObjectException("The handle, " + + Integer.toHexString(handle) + + ", is not shared."); + return result.getRight(); + } + + private Object processResolution(ObjectStreamClass osc, Object obj, int handle, + boolean shared) + throws IOException + { + if (osc != null && obj instanceof Serializable) + { + try + { + Method m = osc.readResolveMethod; + if(m != null) + { + obj = m.invoke(obj, new Object[] {}); + } + } + catch (IllegalAccessException ignore) + { + } + catch (InvocationTargetException exception) + { + Throwable cause = exception.getCause(); + if (cause instanceof ObjectStreamException) + throw (ObjectStreamException) cause; + else if (cause instanceof RuntimeException) + throw (RuntimeException) cause; + else if (cause instanceof Error) + throw (Error) cause; + } + } + + if (this.resolveEnabled) + obj = resolveObject(obj); + + rememberHandle(obj, shared, handle); + if (!shared) + { + if (obj instanceof byte[]) + return ((byte[]) obj).clone(); + if (obj instanceof short[]) + return ((short[]) obj).clone(); + if (obj instanceof int[]) + return ((int[]) obj).clone(); + if (obj instanceof long[]) + return ((long[]) obj).clone(); + if (obj instanceof char[]) + return ((char[]) obj).clone(); + if (obj instanceof boolean[]) + return ((boolean[]) obj).clone(); + if (obj instanceof float[]) + return ((float[]) obj).clone(); + if (obj instanceof double[]) + return ((double[]) obj).clone(); + if (obj instanceof Object[]) + return ((Object[]) obj).clone(); + } + return obj; + } + + private void clearHandles() + { + handles.clear(); + this.nextOID = baseWireHandle; + } + + private void readNextBlock() throws IOException + { + byte marker = this.realInputStream.readByte(); + while (marker == TC_RESET) + { + if(dump) dumpElementln("RESET"); + clearHandles(); + marker = this.realInputStream.readByte(); + } + readNextBlock(marker); + } + + private void readNextBlock(byte marker) throws IOException + { + if (marker == TC_BLOCKDATA) + { + if(dump) dumpElement("BLOCK DATA SIZE="); + this.blockDataBytes = this.realInputStream.readUnsignedByte(); + if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); + } + else if (marker == TC_BLOCKDATALONG) + { + if(dump) dumpElement("BLOCK DATA LONG SIZE="); + this.blockDataBytes = this.realInputStream.readInt(); + if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); + } + else + { + throw new EOFException("Attempt to read primitive data, but no data block is active."); + } + + if (this.blockData.length < this.blockDataBytes) + this.blockData = new byte[this.blockDataBytes]; + + this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); + this.blockDataPosition = 0; + } + + private void readArrayElements (Object array, Class clazz) + throws ClassNotFoundException, IOException + { + if (clazz.isPrimitive()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readBoolean(); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readByte(); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readChar(); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readDouble(); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readFloat(); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readInt(); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readLong(); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readShort(); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = readObject(); + } + } + + private void readFields (Object obj, ObjectStreamClass stream_osc) + throws ClassNotFoundException, IOException + { + ObjectStreamField[] fields = stream_osc.fieldMapping; + + for (int i = 0; i < fields.length; i += 2) + { + ObjectStreamField stream_field = fields[i]; + ObjectStreamField real_field = fields[i + 1]; + boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet()); + boolean set_value = (real_field != null && real_field.isToSet()); + String field_name; + char type; + + if (stream_field != null) + { + field_name = stream_field.getName(); + type = stream_field.getTypeCode(); + } + else + { + field_name = real_field.getName(); + type = real_field.getTypeCode(); + } + + switch(type) + { + case 'Z': + { + boolean value = + read_value ? this.realInputStream.readBoolean() : false; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setBooleanField(obj, value); + break; + } + case 'B': + { + byte value = + read_value ? this.realInputStream.readByte() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setByteField(obj, value); + break; + } + case 'C': + { + char value = + read_value ? this.realInputStream.readChar(): 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setCharField(obj, value); + break; + } + case 'D': + { + double value = + read_value ? this.realInputStream.readDouble() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setDoubleField(obj, value); + break; + } + case 'F': + { + float value = + read_value ? this.realInputStream.readFloat() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setFloatField(obj, value); + break; + } + case 'I': + { + int value = + read_value ? this.realInputStream.readInt() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setIntField(obj, value); + break; + } + case 'J': + { + long value = + read_value ? this.realInputStream.readLong() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setLongField(obj, value); + break; + } + case 'S': + { + short value = + read_value ? this.realInputStream.readShort() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setShortField(obj, value); + break; + } + case 'L': + case '[': + { + Object value = + read_value ? readObject() : null; + if (set_value) + real_field.setObjectField(obj, value); + break; + } + default: + throw new InternalError("Invalid type code: " + type); + } + } + } + + // Toggles writing primitive data to block-data buffer. + private boolean setBlockDataMode (boolean on) + { + boolean oldmode = this.readDataFromBlock; + this.readDataFromBlock = on; + + if (on) + this.dataInputStream = this.blockDataInput; + else + this.dataInputStream = this.realInputStream; + return oldmode; + } + + // returns a new instance of REAL_CLASS that has been constructed + // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) + private Object newObject (Class real_class, Constructor constructor) + throws ClassNotFoundException, IOException + { + if (constructor == null) + throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); + try + { + return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor); + } + catch (InstantiationException e) + { + throw (ClassNotFoundException) new ClassNotFoundException + ("Instance of " + real_class + " could not be created").initCause(e); + } + } + + // runs all registered ObjectInputValidations in prioritized order + // on OBJ + private void invokeValidators() throws InvalidObjectException + { + try + { + Iterator it = currentObjectValidators.iterator(); + while(it.hasNext()) + { + ValidatorAndPriority vap = it.next(); + ObjectInputValidation validator = vap.validator; + validator.validateObject(); + } + } + finally + { + currentObjectValidators = null; + } + } + + private void callReadMethod (Method readObject, Class klass, Object obj) + throws ClassNotFoundException, IOException + { + try + { + readObject.invoke(obj, new Object[] { this }); + } + catch (InvocationTargetException x) + { + /* Rethrow if possible. */ + Throwable exception = x.getTargetException(); + if (exception instanceof RuntimeException) + throw (RuntimeException) exception; + if (exception instanceof IOException) + throw (IOException) exception; + if (exception instanceof ClassNotFoundException) + throw (ClassNotFoundException) exception; + + throw (IOException) new IOException( + "Exception thrown from readObject() on " + klass).initCause(x); + } + catch (Exception x) + { + throw (IOException) new IOException( + "Failure invoking readObject() on " + klass).initCause(x); + } + + // Invalidate fields which has been read through readFields. + prereadFields = null; + } + + private static final int BUFFER_SIZE = 1024; + + private DataInputStream realInputStream; + private DataInputStream dataInputStream; + private DataInputStream blockDataInput; + private int blockDataPosition; + private int blockDataBytes; + private byte[] blockData; + private boolean useSubclassMethod; + private int nextOID; + private boolean resolveEnabled; + private Map> handles; + private Object currentObject; + private ObjectStreamClass currentObjectStreamClass; + private TreeSet currentObjectValidators; + private boolean readDataFromBlock; + private boolean fieldsAlreadyRead; + private Hashtable classLookupTable; + private GetField prereadFields; + + private static boolean dump; + + // The nesting depth for debugging output + private int depth = 0; + + private static final boolean DEBUG = false; + + private void dumpElement (String msg) + { + System.out.print(msg); + } + + private void dumpElementln (String msg) + { + System.out.println(msg); + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + } + + private void dumpElementln (String msg, Object obj) + { + try + { + System.out.print(msg); + if (java.lang.reflect.Proxy.isProxyClass(obj.getClass())) + System.out.println(obj.getClass()); + else + System.out.println(obj); + } + catch (Exception _) + { + } + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + } + + // used to keep a prioritized list of object validators + private static final class ValidatorAndPriority implements Comparable + { + int priority; + ObjectInputValidation validator; + + ValidatorAndPriority (ObjectInputValidation validator, int priority) + { + this.priority = priority; + this.validator = validator; + } + + public int compareTo (Object o) + { + ValidatorAndPriority vap = (ValidatorAndPriority)o; + return this.priority - vap.priority; + } + } +} diff --git a/libjava/classpath/java/io/ObjectInputValidation.java b/libjava/classpath/java/io/ObjectInputValidation.java new file mode 100644 index 000000000..fb6de1905 --- /dev/null +++ b/libjava/classpath/java/io/ObjectInputValidation.java @@ -0,0 +1,67 @@ +/* ObjectInputValidation.java -- Validate an object + Copyright (C) 1998, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This class allows an object to validate that it is valid after + * deserialization has run completely for it and all dependent objects. + * This allows an object to determine if it is invalid even if all + * state data was correctly deserialized from the stream. It can also + * be used to perform re-initialization type activities on an object + * after it has been completely deserialized. + * + * Since this method functions as a type of callback, it must be + * registered through ObjectInputStream.registerValidation + * in order to be invoked. This is typically done in the + * readObject method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see ObjectInputStream#registerValidation + */ +public interface ObjectInputValidation +{ + /** + * This method is called to validate an object after serialization + * is complete. If the object is invalid an exception is thrown. + * + * @exception InvalidObjectException If the object is invalid + */ + void validateObject() throws InvalidObjectException; +} diff --git a/libjava/classpath/java/io/ObjectOutput.java b/libjava/classpath/java/io/ObjectOutput.java new file mode 100644 index 000000000..628f8b97b --- /dev/null +++ b/libjava/classpath/java/io/ObjectOutput.java @@ -0,0 +1,110 @@ +/* ObjectOutput.java -- Interface for writing objects to a stream + Copyright (C) 1998, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface extends DataOutput to provide the additional + * facility of writing object instances to a stream. It also adds some + * additional methods to make the interface more + * OutputStream like. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see DataOutput + */ +public interface ObjectOutput extends DataOutput +{ + /** + * This method writes the specified byte to the output stream. + * + * @param b The byte to write. + * + * @exception IOException If an error occurs. + */ + void write(int b) throws IOException; + + /** + * This method writes all the bytes in the specified byte array to the + * output stream. + * + * @param buf The array of bytes to write. + * + * @exception IOException If an error occurs. + */ + void write(byte[] buf) throws IOException; + + /** + * This method writes len bytes from the specified array + * starting at index offset into that array. + * + * @param buf The byte array to write from. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs. + */ + void write(byte[] buf, int offset, int len) + throws IOException; + + /** + * This method writes a object instance to a stream. The format of the + * data written is determined by the actual implementation of this method + * + * @param obj The object to write + * + * @exception IOException If an error occurs + */ + void writeObject(Object obj) throws IOException; + + /** + * This method causes any buffered data to be flushed out to the underlying + * stream + * + * @exception IOException If an error occurs + */ + void flush() throws IOException; + + /** + * This method closes the underlying stream. + * + * @exception IOException If an error occurs + */ + void close() throws IOException; + +} // interface ObjectOutput diff --git a/libjava/classpath/java/io/ObjectOutputStream.java b/libjava/classpath/java/io/ObjectOutputStream.java new file mode 100644 index 000000000..71d2e0b34 --- /dev/null +++ b/libjava/classpath/java/io/ObjectOutputStream.java @@ -0,0 +1,1490 @@ +/* ObjectOutputStream.java -- Class used to write serialized objects + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.io.ObjectIdentityMap2Int; +import gnu.java.lang.reflect.TypeSignature; +import gnu.java.security.action.SetAccessibleAction; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +/** + * An ObjectOutputStream can be used to write objects + * as well as primitive data in a platform-independent manner to an + * OutputStream. + * + * The data produced by an ObjectOutputStream can be read + * and reconstituted by an ObjectInputStream. + * + * writeObject (Object) is used to write Objects, the + * write<type> methods are used to write primitive + * data (as in DataOutputStream). Strings can be written + * as objects or as primitive data. + * + * Not all objects can be written out using an + * ObjectOutputStream. Only those objects that are an + * instance of java.io.Serializable can be written. + * + * Using default serialization, information about the class of an + * object is written, all of the non-transient, non-static fields of + * the object are written, if any of these fields are objects, they are + * written out in the same manner. + * + * An object is only written out the first time it is encountered. If + * the object is encountered later, a reference to it is written to + * the underlying stream. Thus writing circular object graphs + * does not present a problem, nor are relationships between objects + * in a graph lost. + * + * Example usage: + *
    + * Hashtable map = new Hashtable ();
    + * map.put ("one", new Integer (1));
    + * map.put ("two", new Integer (2));
    + *
    + * ObjectOutputStream oos =
    + * new ObjectOutputStream (new FileOutputStream ("numbers"));
    + * oos.writeObject (map);
    + * oos.close ();
    + *
    + * ObjectInputStream ois =
    + * new ObjectInputStream (new FileInputStream ("numbers"));
    + * Hashtable newmap = (Hashtable)ois.readObject ();
    + *
    + * System.out.println (newmap);
    + * 
    + * + * The default serialization can be overriden in two ways. + * + * By defining a method private void + * writeObject (ObjectOutputStream), a class can dictate exactly + * how information about itself is written. + * defaultWriteObject () may be called from this method to + * carry out default serialization. This method is not + * responsible for dealing with fields of super-classes or subclasses. + * + * By implementing java.io.Externalizable. This gives + * the class complete control over the way it is written to the + * stream. If this approach is used the burden of writing superclass + * and subclass data is transfered to the class implementing + * java.io.Externalizable. + * + * @see java.io.DataOutputStream + * @see java.io.Externalizable + * @see java.io.ObjectInputStream + * @see java.io.Serializable + * @author Tom Tromey (tromey@redhat.com) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class ObjectOutputStream extends OutputStream + implements ObjectOutput, ObjectStreamConstants +{ + /** + * Creates a new ObjectOutputStream that will do all of + * its writing onto out. This method also initializes + * the stream by writing the header information (stream magic number + * and stream version). + * + * @exception IOException Writing stream header to underlying + * stream cannot be completed. + * + * @see #writeStreamHeader() + */ + public ObjectOutputStream (OutputStream out) throws IOException + { + realOutput = new DataOutputStream(out); + blockData = new byte[ BUFFER_SIZE ]; + blockDataCount = 0; + blockDataOutput = new DataOutputStream(this); + setBlockDataMode(true); + replacementEnabled = false; + isSerializing = false; + nextOID = baseWireHandle; + OIDLookupTable = new ObjectIdentityMap2Int(); + protocolVersion = defaultProtocolVersion; + useSubclassMethod = false; + writeStreamHeader(); + + if (DEBUG) + { + String val = System.getProperty("gcj.dumpobjects"); + if (val != null && !val.equals("")) + dump = true; + } + } + + /** + * Writes a representation of obj to the underlying + * output stream by writing out information about its class, then + * writing out each of the objects non-transient, non-static + * fields. If any of these fields are other objects, + * they are written out in the same manner. + * + * This method can be overriden by a class by implementing + * private void writeObject (ObjectOutputStream). + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. + * + * @param obj the object to serialize. + * @exception NotSerializableException An attempt was made to + * serialize an Object that is not serializable. + * + * @exception InvalidClassException Somebody tried to serialize + * an object which is wrongly formatted. + * + * @exception IOException Exception from underlying + * OutputStream. + * @see #writeUnshared(Object) + */ + public final void writeObject(Object obj) throws IOException + { + writeObject(obj, true); + } + + /** + * Writes an object to the stream in the same manner as + * {@link #writeObject(Object)}, but without the use of + * references. As a result, the object is always written + * to the stream in full. Likewise, if an object is written + * by this method and is then later written again by + * {@link #writeObject(Object)}, both calls will write out + * the object in full, as the later call to + * {@link #writeObject(Object)} will know nothing of the + * earlier use of {@link #writeUnshared(Object)}. + * + * @param obj the object to serialize. + * @throws NotSerializableException if the object being + * serialized does not implement + * {@link Serializable}. + * @throws InvalidClassException if a problem occurs with + * the class of the object being + * serialized. + * @throws IOException if an I/O error occurs on the underlying + * OutputStream. + * @since 1.4 + * @see #writeObject(Object) + */ + public void writeUnshared(Object obj) + throws IOException + { + writeObject(obj, false); + } + + /** + * Writes a representation of obj to the underlying + * output stream by writing out information about its class, then + * writing out each of the objects non-transient, non-static + * fields. If any of these fields are other objects, + * they are written out in the same manner. + * + * This method can be overriden by a class by implementing + * private void writeObject (ObjectOutputStream). + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. + * + * @param obj the object to serialize. + * @param shared true if the serialized object should be + * shared with later calls. + * @exception NotSerializableException An attempt was made to + * serialize an Object that is not serializable. + * + * @exception InvalidClassException Somebody tried to serialize + * an object which is wrongly formatted. + * + * @exception IOException Exception from underlying + * OutputStream. + * @see #writeUnshared(Object) + */ + private final void writeObject(Object obj, boolean shared) + throws IOException + { + if (useSubclassMethod) + { + if (dump) + dumpElementln ("WRITE OVERRIDE: " + obj); + + writeObjectOverride(obj); + return; + } + + if (dump) + dumpElementln ("WRITE: ", obj); + + depth += 2; + + boolean was_serializing = isSerializing; + boolean old_mode = setBlockDataMode(false); + try + { + isSerializing = true; + boolean replaceDone = false; + Object replacedObject = null; + + while (true) + { + if (obj == null) + { + realOutput.writeByte(TC_NULL); + break; + } + + int handle = findHandle(obj); + if (handle >= 0 && shared) + { + realOutput.writeByte(TC_REFERENCE); + realOutput.writeInt(handle); + break; + } + + if (obj instanceof Class) + { + Class cl = (Class)obj; + ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl); + realOutput.writeByte(TC_CLASS); + if (!osc.isProxyClass) + { + writeObject (osc); + } + else + {System.err.println("1"); + realOutput.writeByte(TC_PROXYCLASSDESC); + Class[] intfs = cl.getInterfaces(); + realOutput.writeInt(intfs.length); + for (int i = 0; i < intfs.length; i++) + realOutput.writeUTF(intfs[i].getName()); + + boolean oldmode = setBlockDataMode(true); + annotateProxyClass(cl); + setBlockDataMode(oldmode); + realOutput.writeByte(TC_ENDBLOCKDATA); + + writeObject(osc.getSuper()); + } + if (shared) + assignNewHandle(obj); + break; + } + + if (obj instanceof ObjectStreamClass) + { + writeClassDescriptor((ObjectStreamClass) obj); + break; + } + + Class clazz = obj.getClass(); + ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz); + if (osc == null) + throw new NotSerializableException(clazz.getName()); + + if (osc.isEnum()) + { + /* TC_ENUM classDesc newHandle enumConstantName */ + realOutput.writeByte(TC_ENUM); + writeObject(osc); + if (shared) + assignNewHandle(obj); + writeObject(((Enum) obj).name()); + break; + } + + if ((replacementEnabled || obj instanceof Serializable) + && ! replaceDone) + { + replacedObject = obj; + + if (obj instanceof Serializable) + { + try + { + Method m = osc.writeReplaceMethod; + if (m != null) + obj = m.invoke(obj, new Object[0]); + } + catch (IllegalAccessException ignore) + { + } + catch (InvocationTargetException ignore) + { + } + } + + if (replacementEnabled) + obj = replaceObject(obj); + + replaceDone = true; + continue; + } + + if (obj instanceof String) + { + String s = (String)obj; + long l = realOutput.getUTFlength(s, 0, 0); + if (l <= 65535) + { + realOutput.writeByte(TC_STRING); + if (shared) + assignNewHandle(obj); + realOutput.writeUTFShort(s, (int)l); + } + else + { + realOutput.writeByte(TC_LONGSTRING); + if (shared) + assignNewHandle(obj); + realOutput.writeUTFLong(s, l); + } + break; + } + + if (clazz.isArray ()) + { + realOutput.writeByte(TC_ARRAY); + writeObject(osc); + if (shared) + assignNewHandle(obj); + writeArraySizeAndElements(obj, clazz.getComponentType()); + break; + } + + realOutput.writeByte(TC_OBJECT); + writeObject(osc); + + if (shared) + if (replaceDone) + assignNewHandle(replacedObject); + else + assignNewHandle(obj); + + if (obj instanceof Externalizable) + { + if (protocolVersion == PROTOCOL_VERSION_2) + setBlockDataMode(true); + + ((Externalizable)obj).writeExternal(this); + + if (protocolVersion == PROTOCOL_VERSION_2) + { + setBlockDataMode(false); + realOutput.writeByte(TC_ENDBLOCKDATA); + } + + break; + } + + if (obj instanceof Serializable) + { + Object prevObject = this.currentObject; + ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; + currentObject = obj; + ObjectStreamClass[] hierarchy = osc.hierarchy(); + + for (int i = 0; i < hierarchy.length; i++) + { + currentObjectStreamClass = hierarchy[i]; + + fieldsAlreadyWritten = false; + if (currentObjectStreamClass.hasWriteMethod()) + { + if (dump) + dumpElementln ("WRITE METHOD CALLED FOR: ", obj); + setBlockDataMode(true); + callWriteMethod(obj, currentObjectStreamClass); + setBlockDataMode(false); + realOutput.writeByte(TC_ENDBLOCKDATA); + if (dump) + dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj); + } + else + { + if (dump) + dumpElementln ("WRITE FIELDS CALLED FOR: ", obj); + writeFields(obj, currentObjectStreamClass); + } + } + + this.currentObject = prevObject; + this.currentObjectStreamClass = prevObjectStreamClass; + currentPutField = null; + break; + } + + throw new NotSerializableException(clazz.getName() + + " in " + + obj.getClass()); + } // end pseudo-loop + } + catch (ObjectStreamException ose) + { + // Rethrow these are fatal. + throw ose; + } + catch (IOException e) + { + realOutput.writeByte(TC_EXCEPTION); + reset(true); + + setBlockDataMode(false); + try + { + if (DEBUG) + { + e.printStackTrace(System.out); + } + writeObject(e); + } + catch (IOException ioe) + { + StreamCorruptedException ex = + new StreamCorruptedException + (ioe + " thrown while exception was being written to stream."); + if (DEBUG) + { + ex.printStackTrace(System.out); + } + throw ex; + } + + reset (true); + + } + finally + { + isSerializing = was_serializing; + setBlockDataMode(old_mode); + depth -= 2; + + if (dump) + dumpElementln ("END: ", obj); + } + } + + protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException + { + if (osc.isProxyClass) + { + realOutput.writeByte(TC_PROXYCLASSDESC); + Class[] intfs = osc.forClass().getInterfaces(); + realOutput.writeInt(intfs.length); + for (int i = 0; i < intfs.length; i++) + realOutput.writeUTF(intfs[i].getName()); + + assignNewHandle(osc); + + boolean oldmode = setBlockDataMode(true); + annotateProxyClass(osc.forClass()); + setBlockDataMode(oldmode); + realOutput.writeByte(TC_ENDBLOCKDATA); + } + else + { + realOutput.writeByte(TC_CLASSDESC); + realOutput.writeUTF(osc.getName()); + if (osc.isEnum()) + realOutput.writeLong(0L); + else + realOutput.writeLong(osc.getSerialVersionUID()); + assignNewHandle(osc); + + int flags = osc.getFlags(); + + if (protocolVersion == PROTOCOL_VERSION_2 + && osc.isExternalizable()) + flags |= SC_BLOCK_DATA; + + realOutput.writeByte(flags); + + ObjectStreamField[] fields = osc.fields; + + if (fields == ObjectStreamClass.INVALID_FIELDS) + throw new InvalidClassException + (osc.getName(), "serialPersistentFields is invalid"); + + realOutput.writeShort(fields.length); + + ObjectStreamField field; + for (int i = 0; i < fields.length; i++) + { + field = fields[i]; + realOutput.writeByte(field.getTypeCode ()); + realOutput.writeUTF(field.getName ()); + + if (! field.isPrimitive()) + writeObject(field.getTypeString()); + } + + boolean oldmode = setBlockDataMode(true); + annotateClass(osc.forClass()); + setBlockDataMode(oldmode); + realOutput.writeByte(TC_ENDBLOCKDATA); + } + + if (osc.isSerializable() || osc.isExternalizable()) + writeObject(osc.getSuper()); + else + writeObject(null); + } + + /** + * Writes the current objects non-transient, non-static fields from + * the current class to the underlying output stream. + * + * This method is intended to be called from within a object's + * private void writeObject (ObjectOutputStream) + * method. + * + * @exception NotActiveException This method was called from a + * context other than from the current object's and current class's + * private void writeObject (ObjectOutputStream) + * method. + * + * @exception IOException Exception from underlying + * OutputStream. + */ + public void defaultWriteObject() + throws IOException, NotActiveException + { + markFieldsWritten(); + writeFields(currentObject, currentObjectStreamClass); + } + + + private void markFieldsWritten() throws IOException + { + if (currentObject == null || currentObjectStreamClass == null) + throw new NotActiveException + ("defaultWriteObject called by non-active class and/or object"); + + if (fieldsAlreadyWritten) + throw new IOException + ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once"); + + fieldsAlreadyWritten = true; + } + + /** + * Resets stream to state equivalent to the state just after it was + * constructed. + * + * Causes all objects previously written to the stream to be + * forgotten. A notification of this reset is also written to the + * underlying stream. + * + * @exception IOException Exception from underlying + * OutputStream or reset called while serialization is + * in progress. + */ + public void reset() throws IOException + { + reset(false); + } + + + private void reset(boolean internal) throws IOException + { + if (!internal) + { + if (isSerializing) + throw new IOException("Reset called while serialization in progress"); + + realOutput.writeByte(TC_RESET); + } + + clearHandles(); + } + + + /** + * Informs this ObjectOutputStream to write data + * according to the specified protocol. There are currently two + * different protocols, specified by PROTOCOL_VERSION_1 + * and PROTOCOL_VERSION_2. This implementation writes + * data using PROTOCOL_VERSION_2 by default, as is done + * since the JDK 1.2. + *

    + * For an explanation of the differences between the two protocols + * see the Java Object Serialization Specification. + *

    + * + * @param version the version to use. + * + * @throws IllegalArgumentException if version is not a valid + * protocol. + * @throws IllegalStateException if called after the first the first object + * was serialized. + * @throws IOException if an I/O error occurs. + * + * @see ObjectStreamConstants#PROTOCOL_VERSION_1 + * @see ObjectStreamConstants#PROTOCOL_VERSION_2 + * + * @since 1.2 + */ + public void useProtocolVersion(int version) throws IOException + { + if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) + throw new IllegalArgumentException("Invalid protocol version requested."); + + if (nextOID != baseWireHandle) + throw new IllegalStateException("Protocol version cannot be changed " + + "after serialization started."); + + protocolVersion = version; + } + + /** + * An empty hook that allows subclasses to write extra information + * about classes to the stream. This method is called the first + * time each class is seen, and after all of the standard + * information about the class has been written. + * + * @exception IOException Exception from underlying + * OutputStream. + * + * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass) + */ + protected void annotateClass(Class cl) throws IOException + { + } + + protected void annotateProxyClass(Class cl) throws IOException + { + } + + /** + * Allows subclasses to replace objects that are written to the + * stream with other objects to be written in their place. This + * method is called the first time each object is encountered + * (modulo reseting of the stream). + * + * This method must be enabled before it will be called in the + * serialization process. + * + * @exception IOException Exception from underlying + * OutputStream. + * + * @see #enableReplaceObject(boolean) + */ + protected Object replaceObject(Object obj) throws IOException + { + return obj; + } + + + /** + * If enable is true and this object is + * trusted, then replaceObject (Object) will be called + * in subsequent calls to writeObject (Object). + * Otherwise, replaceObject (Object) will not be called. + * + * @exception SecurityException This class is not trusted. + */ + protected boolean enableReplaceObject(boolean enable) + throws SecurityException + { + if (enable) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SerializablePermission("enableSubstitution")); + } + + boolean old_val = replacementEnabled; + replacementEnabled = enable; + return old_val; + } + + + /** + * Writes stream magic and stream version information to the + * underlying stream. + * + * @exception IOException Exception from underlying + * OutputStream. + */ + protected void writeStreamHeader() throws IOException + { + realOutput.writeShort(STREAM_MAGIC); + realOutput.writeShort(STREAM_VERSION); + } + + /** + * Protected constructor that allows subclasses to override + * serialization. This constructor should be called by subclasses + * that wish to override writeObject (Object). This + * method does a security check NOTE: currently not + * implemented, then sets a flag that informs + * writeObject (Object) to call the subclasses + * writeObjectOverride (Object) method. + * + * @see #writeObjectOverride(Object) + */ + protected ObjectOutputStream() throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager (); + if (sec_man != null) + sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + useSubclassMethod = true; + } + + + /** + * This method allows subclasses to override the default + * serialization mechanism provided by + * ObjectOutputStream. To make this method be used for + * writing objects, subclasses must invoke the 0-argument + * constructor on this class from there constructor. + * + * @see #ObjectOutputStream() + * + * @exception NotActiveException Subclass has arranged for this + * method to be called, but did not implement this method. + */ + protected void writeObjectOverride(Object obj) throws NotActiveException, + IOException + { + throw new NotActiveException + ("Subclass of ObjectOutputStream must implement writeObjectOverride"); + } + + + /** + * @see DataOutputStream#write(int) + */ + public void write (int data) throws IOException + { + if (writeDataAsBlocks) + { + if (blockDataCount == BUFFER_SIZE) + drain(); + + blockData[ blockDataCount++ ] = (byte)data; + } + else + realOutput.write(data); + } + + + /** + * @see DataOutputStream#write(byte[]) + */ + public void write(byte[] b) throws IOException + { + write(b, 0, b.length); + } + + + /** + * @see DataOutputStream#write(byte[],int,int) + */ + public void write(byte[] b, int off, int len) throws IOException + { + if (writeDataAsBlocks) + { + if (len < 0) + throw new IndexOutOfBoundsException(); + + if (blockDataCount + len < BUFFER_SIZE) + { + System.arraycopy(b, off, blockData, blockDataCount, len); + blockDataCount += len; + } + else + { + drain(); + writeBlockDataHeader(len); + realOutput.write(b, off, len); + } + } + else + realOutput.write(b, off, len); + } + + + /** + * @see DataOutputStream#flush() + */ + public void flush () throws IOException + { + drain(); + realOutput.flush(); + } + + + /** + * Causes the block-data buffer to be written to the underlying + * stream, but does not flush underlying stream. + * + * @exception IOException Exception from underlying + * OutputStream. + */ + protected void drain() throws IOException + { + if (blockDataCount == 0) + return; + + if (writeDataAsBlocks) + writeBlockDataHeader(blockDataCount); + realOutput.write(blockData, 0, blockDataCount); + blockDataCount = 0; + } + + + /** + * @see java.io.DataOutputStream#close () + */ + public void close() throws IOException + { + flush(); + realOutput.close(); + } + + + /** + * @see java.io.DataOutputStream#writeBoolean (boolean) + */ + public void writeBoolean(boolean data) throws IOException + { + blockDataOutput.writeBoolean(data); + } + + + /** + * @see java.io.DataOutputStream#writeByte (int) + */ + public void writeByte(int data) throws IOException + { + blockDataOutput.writeByte(data); + } + + + /** + * @see java.io.DataOutputStream#writeShort (int) + */ + public void writeShort (int data) throws IOException + { + blockDataOutput.writeShort(data); + } + + + /** + * @see java.io.DataOutputStream#writeChar (int) + */ + public void writeChar(int data) throws IOException + { + blockDataOutput.writeChar(data); + } + + + /** + * @see java.io.DataOutputStream#writeInt (int) + */ + public void writeInt(int data) throws IOException + { + blockDataOutput.writeInt(data); + } + + + /** + * @see java.io.DataOutputStream#writeLong (long) + */ + public void writeLong(long data) throws IOException + { + blockDataOutput.writeLong(data); + } + + + /** + * @see java.io.DataOutputStream#writeFloat (float) + */ + public void writeFloat(float data) throws IOException + { + blockDataOutput.writeFloat(data); + } + + + /** + * @see java.io.DataOutputStream#writeDouble (double) + */ + public void writeDouble(double data) throws IOException + { + blockDataOutput.writeDouble(data); + } + + + /** + * @see java.io.DataOutputStream#writeBytes (java.lang.String) + */ + public void writeBytes(String data) throws IOException + { + blockDataOutput.writeBytes(data); + } + + + /** + * @see java.io.DataOutputStream#writeChars (java.lang.String) + */ + public void writeChars(String data) throws IOException + { + dataOutput.writeChars(data); + } + + + /** + * @see java.io.DataOutputStream#writeUTF (java.lang.String) + */ + public void writeUTF(String data) throws IOException + { + dataOutput.writeUTF(data); + } + + + /** + * This class allows a class to specify exactly which fields should + * be written, and what values should be written for these fields. + * + * XXX: finish up comments + */ + public abstract static class PutField + { + public abstract void put (String name, boolean value); + public abstract void put (String name, byte value); + public abstract void put (String name, char value); + public abstract void put (String name, double value); + public abstract void put (String name, float value); + public abstract void put (String name, int value); + public abstract void put (String name, long value); + public abstract void put (String name, short value); + public abstract void put (String name, Object value); + + /** + * @deprecated + */ + public abstract void write (ObjectOutput out) throws IOException; + } + + public PutField putFields() throws IOException + { + if (currentPutField != null) + return currentPutField; + + currentPutField = new PutField() + { + private byte[] prim_field_data + = new byte[currentObjectStreamClass.primFieldSize]; + private Object[] objs + = new Object[currentObjectStreamClass.objectFieldCount]; + + private ObjectStreamField getField (String name) + { + ObjectStreamField field + = currentObjectStreamClass.getField(name); + + if (field == null) + throw new IllegalArgumentException("no such serializable field " + name); + + return field; + } + + public void put(String name, boolean value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'Z'); + prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0); + } + + public void put(String name, byte value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'B'); + prim_field_data[field.getOffset()] = value; + } + + public void put(String name, char value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'C'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, double value) + { + ObjectStreamField field = getField (name); + + checkType(field, 'D'); + int off = field.getOffset(); + long l_value = Double.doubleToLongBits (value); + prim_field_data[off++] = (byte)(l_value >>> 52); + prim_field_data[off++] = (byte)(l_value >>> 48); + prim_field_data[off++] = (byte)(l_value >>> 40); + prim_field_data[off++] = (byte)(l_value >>> 32); + prim_field_data[off++] = (byte)(l_value >>> 24); + prim_field_data[off++] = (byte)(l_value >>> 16); + prim_field_data[off++] = (byte)(l_value >>> 8); + prim_field_data[off] = (byte)l_value; + } + + public void put(String name, float value) + { + ObjectStreamField field = getField(name); + + checkType(field, 'F'); + int off = field.getOffset(); + int i_value = Float.floatToIntBits(value); + prim_field_data[off++] = (byte)(i_value >>> 24); + prim_field_data[off++] = (byte)(i_value >>> 16); + prim_field_data[off++] = (byte)(i_value >>> 8); + prim_field_data[off] = (byte)i_value; + } + + public void put(String name, int value) + { + ObjectStreamField field = getField(name); + checkType(field, 'I'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 24); + prim_field_data[off++] = (byte)(value >>> 16); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, long value) + { + ObjectStreamField field = getField(name); + checkType(field, 'J'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 52); + prim_field_data[off++] = (byte)(value >>> 48); + prim_field_data[off++] = (byte)(value >>> 40); + prim_field_data[off++] = (byte)(value >>> 32); + prim_field_data[off++] = (byte)(value >>> 24); + prim_field_data[off++] = (byte)(value >>> 16); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, short value) + { + ObjectStreamField field = getField(name); + checkType(field, 'S'); + int off = field.getOffset(); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put(String name, Object value) + { + ObjectStreamField field = getField(name); + + if (value != null && + ! field.getType().isAssignableFrom(value.getClass ())) + throw new IllegalArgumentException("Class " + value.getClass() + + " cannot be cast to " + field.getType()); + objs[field.getOffset()] = value; + } + + public void write(ObjectOutput out) throws IOException + { + // Apparently Block data is not used with PutField as per + // empirical evidence against JDK 1.2. Also see Mauve test + // java.io.ObjectInputOutput.Test.GetPutField. + boolean oldmode = setBlockDataMode(false); + out.write(prim_field_data); + for (int i = 0; i < objs.length; ++ i) + out.writeObject(objs[i]); + setBlockDataMode(oldmode); + } + + private void checkType(ObjectStreamField field, char type) + throws IllegalArgumentException + { + if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0) + != type) + throw new IllegalArgumentException(); + } + }; + // end PutFieldImpl + + return currentPutField; + } + + + public void writeFields() throws IOException + { + if (currentPutField == null) + throw new NotActiveException("writeFields can only be called after putFields has been called"); + + markFieldsWritten(); + currentPutField.write(this); + } + + + // write out the block-data buffer, picking the correct header + // depending on the size of the buffer + private void writeBlockDataHeader(int size) throws IOException + { + if (size < 256) + { + realOutput.writeByte(TC_BLOCKDATA); + realOutput.write(size); + } + else + { + realOutput.writeByte(TC_BLOCKDATALONG); + realOutput.writeInt(size); + } + } + + + // lookup the handle for OBJ, return null if OBJ doesn't have a + // handle yet + private int findHandle(Object obj) + { + return OIDLookupTable.get(obj); + } + + + // assigns the next availible handle to OBJ + private int assignNewHandle(Object obj) + { + OIDLookupTable.put(obj, nextOID); + return nextOID++; + } + + + // resets mapping from objects to handles + private void clearHandles() + { + nextOID = baseWireHandle; + OIDLookupTable.clear(); + } + + + // write out array size followed by each element of the array + private void writeArraySizeAndElements(Object array, Class clazz) + throws IOException + { + int length = Array.getLength(array); + + if (clazz.isPrimitive()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeBoolean(cast_array[i]); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + realOutput.writeInt(length); + realOutput.write(cast_array, 0, length); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeChar(cast_array[i]); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeDouble(cast_array[i]); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeFloat(cast_array[i]); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + realOutput.writeInt(cast_array[i]); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeLong(cast_array[i]); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + realOutput.writeInt (length); + for (int i = 0; i < length; i++) + realOutput.writeShort(cast_array[i]); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + realOutput.writeInt(length); + for (int i = 0; i < length; i++) + writeObject(cast_array[i]); + } + } + + +/* GCJ LOCAL */ + // writes out FIELDS of OBJECT for the specified ObjectStreamClass. + // FIELDS are already supposed already to be in canonical order, but + // under some circumstances (to do with Proxies) this isn't the + // case, so we call ensureFieldsSet(). + private void writeFields(Object obj, ObjectStreamClass osc) + throws IOException + { + osc.ensureFieldsSet(osc.forClass()); +/* END GCJ LOCAL */ + + ObjectStreamField[] fields = osc.fields; + boolean oldmode = setBlockDataMode(false); + + try + { + 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. + // Package-private to avoid a trampoline constructor. + boolean setBlockDataMode(boolean on) throws IOException + { + if (on == writeDataAsBlocks) + return on; + + drain(); + boolean oldmode = writeDataAsBlocks; + writeDataAsBlocks = on; + + if (on) + dataOutput = blockDataOutput; + else + dataOutput = realOutput; + + return oldmode; + } + + + private void callWriteMethod(Object obj, ObjectStreamClass osc) + throws IOException + { + currentPutField = null; + try + { + Object args[] = {this}; + osc.writeObjectMethod.invoke(obj, args); + } + catch (InvocationTargetException x) + { + /* Rethrow if possible. */ + Throwable exception = x.getTargetException(); + if (exception instanceof RuntimeException) + throw (RuntimeException) exception; + if (exception instanceof IOException) + throw (IOException) exception; + + IOException ioe + = new IOException("Exception thrown from writeObject() on " + + osc.forClass().getName() + ": " + + exception.getClass().getName()); + ioe.initCause(exception); + throw ioe; + } + catch (Exception x) + { + IOException ioe + = new IOException("Failure invoking writeObject() on " + + osc.forClass().getName() + ": " + + x.getClass().getName()); + ioe.initCause(x); + throw ioe; + } + } + + private void dumpElementln (String msg, Object obj) + { + try + { + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + System.out.print (msg); + if (java.lang.reflect.Proxy.isProxyClass(obj.getClass())) + System.out.print (obj.getClass()); + else + System.out.print (obj); + } + catch (Exception _) + { + } + finally + { + System.out.println (); + } + } + + private void dumpElementln (String msg) + { + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + System.out.println(msg); + } + + // this value comes from 1.2 spec, but is used in 1.1 as well + private static final int BUFFER_SIZE = 1024; + + private static int defaultProtocolVersion = PROTOCOL_VERSION_2; + + private DataOutputStream dataOutput; + private boolean writeDataAsBlocks; + private DataOutputStream realOutput; + private DataOutputStream blockDataOutput; + private byte[] blockData; + private int blockDataCount; + private Object currentObject; + // Package-private to avoid a trampoline. + ObjectStreamClass currentObjectStreamClass; + private PutField currentPutField; + private boolean fieldsAlreadyWritten; + private boolean replacementEnabled; + private boolean isSerializing; + private int nextOID; + private ObjectIdentityMap2Int OIDLookupTable; + private int protocolVersion; + private boolean useSubclassMethod; + private SetAccessibleAction setAccessible = new SetAccessibleAction(); + + // The nesting depth for debugging output + private int depth = 0; + + // Set if we're generating debugging dumps + private boolean dump = false; + + private static final boolean DEBUG = false; +} diff --git a/libjava/classpath/java/io/ObjectStreamClass.java b/libjava/classpath/java/io/ObjectStreamClass.java new file mode 100644 index 000000000..b71f54895 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamClass.java @@ -0,0 +1,1161 @@ +/* ObjectStreamClass.java -- Class used to write class information + about serialized objects. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.io.NullOutputStream; +import gnu.java.lang.reflect.TypeSignature; +import gnu.java.security.action.SetAccessibleAction; +import gnu.java.security.provider.Gnu; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedAction; +import java.security.Security; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Hashtable; + +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class ObjectStreamClass implements Serializable +{ + static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0]; + + /** + * Returns the ObjectStreamClass for cl. + * If cl is null, or is not Serializable, + * null is returned. ObjectStreamClass's are memorized; + * later calls to this method with the same class will return the + * same ObjectStreamClass object and no recalculation + * will be done. + * + * Warning: If this class contains an invalid serialPersistentField arrays + * lookup will not throw anything. However {@link #getFields()} will return + * an empty array and {@link java.io.ObjectOutputStream#writeObject} will throw an + * {@link java.io.InvalidClassException}. + * + * @see java.io.Serializable + */ + public static ObjectStreamClass lookup(Class cl) + { + if (cl == null) + return null; + if (! (Serializable.class).isAssignableFrom(cl)) + return null; + + return lookupForClassObject(cl); + } + + /** + * This lookup for internal use by ObjectOutputStream. Suppose + * we have a java.lang.Class object C for class A, though A is not + * serializable, but it's okay to serialize C. + */ + static ObjectStreamClass lookupForClassObject(Class cl) + { + if (cl == null) + return null; + + ObjectStreamClass osc = classLookupTable.get(cl); + + if (osc != null) + return osc; + else + { + osc = new ObjectStreamClass(cl); + classLookupTable.put(cl, osc); + return osc; + } + } + + /** + * Returns the name of the class that this + * ObjectStreamClass represents. + * + * @return the name of the class. + */ + public String getName() + { + return name; + } + + /** + * Returns the class that this ObjectStreamClass + * represents. Null could be returned if this + * ObjectStreamClass was read from an + * ObjectInputStream and the class it represents cannot + * be found or loaded. + * + * @see java.io.ObjectInputStream + */ + public Class forClass() + { + return clazz; + } + + /** + * Returns the serial version stream-unique identifier for the class + * represented by this ObjectStreamClass. This SUID is + * either defined by the class as static final long + * serialVersionUID or is calculated as specified in + * Javasoft's "Object Serialization Specification" XXX: add reference + * + * @return the serial version UID. + */ + public long getSerialVersionUID() + { + return uid; + } + + /** + * Returns the serializable (non-static and non-transient) Fields + * of the class represented by this ObjectStreamClass. The Fields + * are sorted by name. + * If fields were obtained using serialPersistentFields and this array + * is faulty then the returned array of this method will be empty. + * + * @return the fields. + */ + public ObjectStreamField[] getFields() + { + ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; + System.arraycopy(fields, 0, copy, 0, fields.length); + return copy; + } + + // XXX doc + // Can't do binary search since fields is sorted by name and + // primitiveness. + public ObjectStreamField getField (String name) + { + for (int i = 0; i < fields.length; i++) + if (fields[i].getName().equals(name)) + return fields[i]; + return null; + } + + /** + * Returns a textual representation of this + * ObjectStreamClass object including the name of the + * class it represents as well as that class's serial version + * stream-unique identifier. + * + * @see #getSerialVersionUID() + * @see #getName() + */ + public String toString() + { + return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; + } + + // Returns true iff the class that this ObjectStreamClass represents + // has the following method: + // + // private void writeObject (ObjectOutputStream) + // + // This method is used by the class to override default + // serialization behavior. + boolean hasWriteMethod() + { + return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; + } + + // Returns true iff the class that this ObjectStreamClass represents + // implements Serializable but does *not* implement Externalizable. + boolean isSerializable() + { + return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // implements Externalizable. + boolean isExternalizable() + { + return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; + } + + // Returns true iff the class that this ObjectStreamClass represents + // implements Externalizable. + boolean isEnum() + { + return (flags & ObjectStreamConstants.SC_ENUM) != 0; + } + + // Returns the ObjectStreamClass that represents the + // class that is the superclass of the class this + // ObjectStreamClass represents. If the superclass is + // not Serializable, null is returned. + ObjectStreamClass getSuper() + { + return superClass; + } + + /** + * 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[] 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. + // The bit-flags that could be present are those defined in + // ObjectStreamConstants that begin with `SC_' + int getFlags() + { + return flags; + } + + + ObjectStreamClass(String name, long uid, byte flags, + ObjectStreamField[] fields) + { + this.name = name; + this.uid = uid; + this.flags = flags; + this.fields = fields; + } + + /** + * This method builds the internal description corresponding to a Java Class. + * As the constructor only assign a name to the current ObjectStreamClass instance, + * that method sets the serial UID, chose the fields which will be serialized, + * and compute the position of the fields in the serialized stream. + * + * @param cl The Java class which is used as a reference for building the descriptor. + * @param superClass The descriptor of the super class for this class descriptor. + * @throws InvalidClassException if an incompatibility between computed UID and + * already set UID is found. + */ + void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException + {hierarchy = null; + this.clazz = cl; + + cacheMethods(); + + long class_uid = getClassUID(cl); + if (uid == 0) + uid = class_uid; + else + { + // Check that the actual UID of the resolved class matches the UID from + // the stream. Mismatches for array classes are ignored. + if (!cl.isArray() && uid != class_uid) + { + String msg = cl + + ": Local class not compatible: stream serialVersionUID=" + + uid + ", local serialVersionUID=" + class_uid; + throw new InvalidClassException (msg); + } + } + + isProxyClass = clazz != null && Proxy.isProxyClass(clazz); + this.superClass = superClass; + calculateOffsets(); + + try + { + ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz); + + if (exportedFields == null) + return; + + ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length]; + int i, j, k; + + /* We now check the import fields against the exported fields. + * There should not be contradiction (e.g. int x and String x) + * but extra virtual fields can be added to the class. + */ + + Arrays.sort(exportedFields); + + i = 0; j = 0; k = 0; + while (i < fields.length && j < exportedFields.length) + { + int comp = fields[i].compareTo(exportedFields[j]); + + if (comp < 0) + { + newFieldList[k] = fields[i]; + fields[i].setPersistent(false); + fields[i].setToSet(false); + i++; + } + else if (comp > 0) + { + /* field not found in imported fields. We add it + * in the list of supported fields. + */ + newFieldList[k] = exportedFields[j]; + newFieldList[k].setPersistent(true); + newFieldList[k].setToSet(false); + try + { + newFieldList[k].lookupField(clazz); + newFieldList[k].checkFieldType(); + } + catch (NoSuchFieldException _) + { + } + j++; + } + else + { + try + { + exportedFields[j].lookupField(clazz); + exportedFields[j].checkFieldType(); + } + catch (NoSuchFieldException _) + { + } + + if (!fields[i].getType().equals(exportedFields[j].getType())) + throw new InvalidClassException + ("serialPersistentFields must be compatible with" + + " imported fields (about " + fields[i].getName() + ")"); + newFieldList[k] = fields[i]; + fields[i].setPersistent(true); + i++; + j++; + } + k++; + } + + if (i < fields.length) + for (;i"); + 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(""); + 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(); + 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; + } + + /** + * Returns the value of CLAZZ's private static final field named + * `serialPersistentFields'. It performs some sanity checks before + * returning the real array. Besides, the returned array is a clean + * copy of the original. So it can be modified. + * + * @param clazz Class to retrieve 'serialPersistentFields' from. + * @return The content of 'serialPersistentFields'. + */ + private ObjectStreamField[] getSerialPersistentFields(Class clazz) + throws NoSuchFieldException, IllegalAccessException + { + ObjectStreamField[] fieldsArray = null; + ObjectStreamField[] o; + + // Use getDeclaredField rather than getField for the same reason + // as above in getDefinedSUID. + Field f = clazz.getDeclaredField("serialPersistentFields"); + f.setAccessible(true); + + int modifiers = f.getModifiers(); + if (!(Modifier.isStatic(modifiers) && + Modifier.isFinal(modifiers) && + Modifier.isPrivate(modifiers))) + return null; + + o = (ObjectStreamField[]) f.get(null); + + if (o == null) + return null; + + fieldsArray = new ObjectStreamField[ o.length ]; + System.arraycopy(o, 0, fieldsArray, 0, o.length); + + return fieldsArray; + } + + /** + * Returns a new instance of the Class this ObjectStreamClass corresponds + * to. + * Note that this should only be used for Externalizable classes. + * + * @return A new instance. + */ + Externalizable newInstance() throws InvalidClassException + { + synchronized(this) + { + if (constructor == null) + { + try + { + final Constructor c = clazz.getConstructor(new Class[0]); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + c.setAccessible(true); + return null; + } + }); + + constructor = c; + } + catch(NoSuchMethodException x) + { + throw new InvalidClassException(clazz.getName(), + "No public zero-argument constructor"); + } + } + } + + try + { + return (Externalizable)constructor.newInstance(); + } + catch(Exception x) + { + throw (InvalidClassException) + new InvalidClassException(clazz.getName(), + "Unable to instantiate").initCause(x); + } + } + + public static final ObjectStreamField[] NO_FIELDS = {}; + + private static Hashtable classLookupTable + = new Hashtable(); + private static final NullOutputStream nullOutputStream = new NullOutputStream(); + private static final Comparator interfaceComparator = new InterfaceComparator(); + private static final Comparator memberComparator = new MemberComparator(); + private static final + Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; + + private ObjectStreamClass superClass; + private Class clazz; + private String name; + private long uid; + private byte flags; + + // this field is package protected so that ObjectInputStream and + // ObjectOutputStream can access it directly + ObjectStreamField[] fields; + + // these are accessed by ObjectIn/OutputStream + int primFieldSize = -1; // -1 if not yet calculated + int objectFieldCount; + + Method readObjectMethod; + Method readResolveMethod; + Method writeReplaceMethod; + Method writeObjectMethod; + boolean realClassIsSerializable; + boolean realClassIsExternalizable; + ObjectStreamField[] fieldMapping; + Constructor firstNonSerializableParentConstructor; + private Constructor constructor; // default constructor for Externalizable + + boolean isProxyClass = false; + +/* GCJ LOCAL */ + // True after setFields() has been called + private boolean fieldsSet = false; +/* END GCJ LOCAL */ + + // This is probably not necessary because this class is special cased already + // but it will avoid showing up as a discrepancy when comparing SUIDs. + private static final long serialVersionUID = -6120832682080437368L; + + + // interfaces are compared only by name + private static final class InterfaceComparator implements Comparator + { + public int compare(Object o1, Object o2) + { + return ((Class) o1).getName().compareTo(((Class) o2).getName()); + } + } + + + // Members (Methods and Constructors) are compared first by name, + // conflicts are resolved by comparing type signatures + private static final class MemberComparator implements Comparator + { + public int compare(Object o1, Object o2) + { + Member m1 = (Member) o1; + Member m2 = (Member) o2; + + int comp = m1.getName().compareTo(m2.getName()); + + if (comp == 0) + return TypeSignature.getEncodingOfMember(m1). + compareTo(TypeSignature.getEncodingOfMember(m2)); + else + return comp; + } + } +} diff --git a/libjava/classpath/java/io/ObjectStreamConstants.java b/libjava/classpath/java/io/ObjectStreamConstants.java new file mode 100644 index 000000000..8f70196c9 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamConstants.java @@ -0,0 +1,226 @@ +/* ObjectStreamConstants.java -- Interface containing constant values + used in reading and writing serialized objects + Copyright (C) 1998, 1999, 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This interface contains constants that are used in object + * serialization. This interface is used by ObjectOutputStream, + * ObjectInputStream, and ObjectStreamClass. + * The values for these constants are specified by the Java library + * specification. + * + * @since 1.1 + */ +public interface ObjectStreamConstants +{ + /** + * The serialization stream protocol version 1. This version was + * the default serialization protocol before JDK 1.2. + * + * @see ObjectOutputStream#useProtocolVersion(int) + * @since 1.2 + */ + int PROTOCOL_VERSION_1 = 1; + + /** + * The serialization stream protocol version 2. This version is + * used as the default serialization protocol since JDK 1.2. + * + * @see ObjectOutputStream#useProtocolVersion(int) + * @since 1.2 + */ + int PROTOCOL_VERSION_2 = 2; + + /** + * The magic number that is written as part of the stream header. + */ + short STREAM_MAGIC = (short)0xaced; + + /** + * The stream version number that is written as part of the stream header. + * Note that this is different from the protocol version that specifies + * the data format for the stream. + */ + short STREAM_VERSION = 5; + + /** + * Token value to designate a null reference in the stream. + */ + byte TC_NULL = (byte)112; //0x70 + + /** + * Token value to designate a reference to an already serialized object. + */ + byte TC_REFERENCE = (byte)113; //0x71 + + /** + * Token value to designate a class descriptor is next in the stream. + */ + byte TC_CLASSDESC = (byte)114; //0x72 + + /** + * Token value to designate a new object is next in the stream. + */ + byte TC_OBJECT = (byte)115; //0x73 + + /** + * Token value to designate a new string is next in the stream. + */ + byte TC_STRING = (byte)116; //0x74 + + /** + * Token value to designate a new array is next in the stream. + */ + byte TC_ARRAY = (byte)117; //0x75 + + /** + * Token reference to designate a reference to a class. + */ + byte TC_CLASS = (byte)118; //0x76 + + /** + * Token value to designate a block of primitive data is next in the stream. + * The next byte in the stream holds the size of the block (in bytes). + */ + byte TC_BLOCKDATA = (byte)119; //0x77 + + /** + * Token value to designate the end of a block of primitve data. + */ + byte TC_ENDBLOCKDATA = (byte)120; //0x78 + + /** + * Token value to designate a reset of the stream state. + */ + byte TC_RESET = (byte)121; //0x79 + + /** + * Token value to designate a long block of primitive data is next in the + * stream. The next long in the stream holds the size of the block + * (in bytes). + */ + byte TC_BLOCKDATALONG = (byte)122; //0x7A + + /** + * Token value to designate an exception occured during serialization. + */ + byte TC_EXCEPTION = (byte)123; //0x7B + + /** + * Token value to designate a long string is next in the stream. + */ + byte TC_LONGSTRING = (byte)124; //0x7C + + /** + * Token value to designate a proxy class descriptor is next in the stream. + */ + byte TC_PROXYCLASSDESC = (byte)125; //0x7D + + /** + * Token value to designate an enum constant is next in the stream. + * + * @since 1.5 + */ + byte TC_ENUM = (byte)126; //0x7E + + /** + * The first token value. + */ + byte TC_BASE = TC_NULL; + + /** + * The last token value. + */ + byte TC_MAX = TC_ENUM; + + /** + * The first handle that will be assigned to an object, for later references. + */ + int baseWireHandle = 0x7e0000; + + /** + * Flag used in ObjectStreamClass to designate that the class + * defines the writeObject method. + */ + byte SC_WRITE_METHOD = 0x01; + + /** + * Flag used in ObjectStreamClass to designate that the class + * is serializeable. + */ + byte SC_SERIALIZABLE = 0x02; + + /** + * Flag used in ObjectStreamClass to designate that the class + * is externalizable. + */ + byte SC_EXTERNALIZABLE = 0x04; + + /** + * Flag used in ObjectStreamClass to designate that + * externalizable data is written in block data mode. + * + * @since 1.2 + */ + byte SC_BLOCK_DATA = 0x08; + + /** + * Flag used in ObjectStreamClass to designate that the class + * is an enum constant. + * + * @since 1.5 + */ + byte SC_ENUM = 0x10; + + /** + * Constant for use with a SecurityManager to check if + * substitution of objects is allowed. + */ + SerializablePermission SUBSTITUTION_PERMISSION + = new SerializablePermission("enableSubstitution"); + + /** + * Constant for use with a SecurityManager to check if + * overriding of the writeObject and readObject + * methods is allowed. + */ + SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION + = new SerializablePermission("enableSubclassImplementation"); +} diff --git a/libjava/classpath/java/io/ObjectStreamException.java b/libjava/classpath/java/io/ObjectStreamException.java new file mode 100644 index 000000000..61d4dd09e --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamException.java @@ -0,0 +1,74 @@ +/* ObjectStreamException.java -- Superclass of all serialization exceptions + Copyright (C) 1998, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when a problem occurs during serialization. + * There are more specific subclasses that give more fine grained + * indications of the precise failure. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ObjectStreamException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7260898174833392607L; + + /** + * Create an exception without a descriptive error message. + */ + protected ObjectStreamException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + protected ObjectStreamException(String message) + { + super(message); + } +} // class ObjectStreamException diff --git a/libjava/classpath/java/io/ObjectStreamField.java b/libjava/classpath/java/io/ObjectStreamField.java new file mode 100644 index 000000000..dfb6728a7 --- /dev/null +++ b/libjava/classpath/java/io/ObjectStreamField.java @@ -0,0 +1,401 @@ +/* ObjectStreamField.java -- Class used to store name and class of fields + Copyright (C) 1998, 1999, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.lang.reflect.TypeSignature; + +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This class intends to describe the field of a class for the serialization + * subsystem. Serializable fields in a serializable class can be explicitly + * exported using an array of ObjectStreamFields. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class ObjectStreamField + implements Comparable +{ + private String name; + private Class type; + private String typename; + private int offset = -1; // XXX make sure this is correct + private boolean unshared; + private boolean persistent = false; + private boolean toset = true; + Field field; + + ObjectStreamField (Field field) + { + this (field.getName(), field.getType()); + this.field = field; + } + + /** + * This constructor creates an ObjectStreamField instance + * which represents a field named name and is + * of the type type. + * + * @param name Name of the field to export. + * @param type Type of the field in the concerned class. + */ + public ObjectStreamField (String name, Class type) + { + this (name, type, false); + } + + /** + * This constructor creates an ObjectStreamField instance + * which represents a field named name and is + * of the type type. + * + * @param name Name of the field to export. + * @param type Type of the field in the concerned class. + * @param unshared true if field will be unshared, false otherwise. + */ + public ObjectStreamField (String name, Class type, boolean unshared) + { + if (name == null) + throw new NullPointerException(); + + this.name = name; + this.type = type; + this.typename = TypeSignature.getEncodingOfClass(type); + this.unshared = unshared; + } + + /** + * There are many cases you can not get java.lang.Class from typename + * if your context class loader cannot load it, then use typename to + * construct the field. + * + * @param name Name of the field to export. + * @param typename The coded name of the type for this field. + */ + ObjectStreamField (String name, String typename) + { + this.name = name; + this.typename = typename; + } + + void resolveType(ClassLoader loader) + { + try + { + type = TypeSignature.getClassForEncoding(typename, true, loader); + } + catch(ClassNotFoundException e) + { + } + } + + /** + * This method returns the name of the field represented by the + * ObjectStreamField instance. + * + * @return A string containing the name of the field. + */ + public String getName () + { + return name; + } + + /** + * This method returns the class representing the type of the + * field which is represented by this instance of ObjectStreamField. + * + * @return A class representing the type of the field. + */ + public Class getType () + { + return type; + } + + /** + * This method returns the char encoded type of the field which + * is represented by this instance of ObjectStreamField. + * + * @return A char representing the type of the field. + */ + public char getTypeCode () + { + return typename.charAt (0); + } + + /** + * This method returns a more explicit type name than + * {@link #getTypeCode()} in the case the type is a real + * class (and not a primitive). + * + * @return The name of the type (class name) if it is not a + * primitive, in the other case null is returned. + */ + public String getTypeString () + { + // use intern() + if (isPrimitive()) + return null; + return typename.intern(); + } + + /** + * This method returns the current offset of the field in + * the serialization stream relatively to the other fields. + * The offset is expressed in bytes. + * + * @return The offset of the field in bytes. + * @see #setOffset(int) + */ + public int getOffset () + { + return offset; + } + + /** + * This method sets the current offset of the field. + * + * @param off The offset of the field in bytes. + * @see #getOffset() + */ + protected void setOffset (int off) + { + offset = off; + } + + /** + * This method returns whether the field represented by this object is + * unshared or not. + * + * @return Tells if this field is unshared or not. + */ + public boolean isUnshared () + { + return unshared; + } + + /** + * This method returns true if the type of the field + * represented by this instance is a primitive. + * + * @return true if the type is a primitive, false + * in the other case. + */ + public boolean isPrimitive () + { + return typename.length() == 1; + } + + /** + * Compares this object to the given object. + * + * @param obj the object to compare to. + * + * @return -1, 0 or 1. + */ + public int compareTo (Object obj) + { + ObjectStreamField f = (ObjectStreamField) obj; + boolean this_is_primitive = isPrimitive (); + boolean f_is_primitive = f.isPrimitive (); + + if (this_is_primitive && !f_is_primitive) + return -1; + + if (!this_is_primitive && f_is_primitive) + return 1; + + return getName ().compareTo (f.getName ()); + } + + /** + * This method is specific to classpath's implementation and so has the default + * access. It changes the state of this field to "persistent". It means that + * the field should not be changed when the stream is read (if it is not + * explicitly specified using serialPersistentFields). + * + * @param persistent True if the field is persistent, false in the + * other cases. + * @see #isPersistent() + */ + void setPersistent(boolean persistent) + { + this.persistent = persistent; + } + + /** + * This method returns true if the field is marked as persistent. + * + * @return True if persistent, false in the other cases. + * @see #setPersistent(boolean) + */ + boolean isPersistent() + { + return persistent; + } + + /** + * This method is specific to classpath's implementation and so + * has the default access. It changes the state of this field as + * to be set by ObjectInputStream. + * + * @param toset True if this field should be set, false in the other + * cases. + * @see #isToSet() + */ + void setToSet(boolean toset) + { + this.toset = toset; + } + + /** + * This method returns true if the field is marked as to be + * set. + * + * @return True if it is to be set, false in the other cases. + * @see #setToSet(boolean) + */ + boolean isToSet() + { + return toset; + } + + /** + * This method searches for its field reference in the specified class + * object. It requests privileges. If an error occurs the internal field + * reference is not modified. + * + * @throws NoSuchFieldException if the field name does not exist in this class. + * @throws SecurityException if there was an error requesting the privileges. + */ + void lookupField(Class clazz) throws NoSuchFieldException, SecurityException + { + final Field f = clazz.getDeclaredField(name); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + f.setAccessible(true); + return null; + } + }); + + this.field = f; + } + + /** + * This method check whether the field described by this + * instance of ObjectStreamField is compatible with the + * actual implementation of this field. + * + * @throws NullPointerException if this field does not exist + * in the real class. + * @throws InvalidClassException if the types are incompatible. + */ + void checkFieldType() throws InvalidClassException + { + Class ftype = field.getType(); + + if (!ftype.isAssignableFrom(type)) + throw new InvalidClassException + ("invalid field type for " + name + + " in class " + field.getDeclaringClass()); + } + + /** + * Returns a string representing this object. + * + * @return the string. + */ + public String toString () + { + return "ObjectStreamField< " + type + " " + name + " >"; + } + + final void setBooleanField(Object obj, boolean val) + { + VMObjectStreamClass.setBooleanNative(field, obj, val); + } + + final void setByteField(Object obj, byte val) + { + VMObjectStreamClass.setByteNative(field, obj, val); + } + + final void setCharField(Object obj, char val) + { + VMObjectStreamClass.setCharNative(field, obj, val); + } + + final void setShortField(Object obj, short val) + { + VMObjectStreamClass.setShortNative(field, obj, val); + } + + final void setIntField(Object obj, int val) + { + VMObjectStreamClass.setIntNative(field, obj, val); + } + + final void setLongField(Object obj, long val) + { + VMObjectStreamClass.setLongNative(field, obj, val); + } + + final void setFloatField(Object obj, float val) + { + VMObjectStreamClass.setFloatNative(field, obj, val); + } + + final void setDoubleField(Object obj, double val) + { + VMObjectStreamClass.setDoubleNative(field, obj, val); + } + + final void setObjectField(Object obj, Object val) + { + VMObjectStreamClass.setObjectNative(field, obj, val); + } +} diff --git a/libjava/classpath/java/io/OptionalDataException.java b/libjava/classpath/java/io/OptionalDataException.java new file mode 100644 index 000000000..8d8b1bda0 --- /dev/null +++ b/libjava/classpath/java/io/OptionalDataException.java @@ -0,0 +1,91 @@ +/* OptionalDataException.java -- indicates unexpected data in serialized stream + Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when unexpected data appears in the input + * stream from which a serialized object is being read. There are two + * cases:
      + *
    • The next stream element is primitive data. eof will + * be false, and count is the number of bytes of primitive + * data available.
    • + *
    • The data consumable by readObject or readExternal has been exhausted. + * eof is true, and count is 0.
    • + *
    + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class OptionalDataException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8011121865681257820L; + + /** + * Whether or not the end of the stream has been reached. + * + * @serial the end of the buffer was reached + */ + public boolean eof; + + /** + * The number of valid bytes that can be read. + * + * @serial the bytes of the buffer remaining + */ + public int length; + + /** + * Create a new OptionalDataException with an eof parameter indicating + * whether or not the end of stream is reached and the number of valid + * bytes that may be read. + * + * @param eof 'true' if end of stream reached, 'false' otherwise + * @param count The number of valid bytes to be read + */ + OptionalDataException(boolean eof, int count) + { + this.eof = eof; + this.length = count; + } +} // class OptionalDataException diff --git a/libjava/classpath/java/io/OutputStream.java b/libjava/classpath/java/io/OutputStream.java new file mode 100644 index 000000000..5ba9478e3 --- /dev/null +++ b/libjava/classpath/java/io/OutputStream.java @@ -0,0 +1,140 @@ +/* OutputStream.java -- Base class for byte output streams + Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This abstract class forms the base of the hierarchy of classes that + * write output as a stream of bytes. It provides a common set of methods + * for writing bytes to stream. Subclasses implement and/or extend these + * methods to write bytes in a particular manner or to a particular + * destination such as a file on disk or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public abstract class OutputStream implements Closeable, Flushable +{ + /** + * This is the default no-argument constructor for this class. This method + * does nothing in this class. + */ + public OutputStream () + { + } + + /** + * This method writes a single byte to the output stream. The byte written + * is the low eight bits of the int passed and a argument. + *

    + * Subclasses must provide an implementation of this abstract method + * + * @param b The byte to be written to the output stream, passed as + * the low eight bits of an int + * + * @exception IOException If an error occurs + */ + public abstract void write (int b) throws IOException; + + /** + * This method all the writes bytes from the passed array to the + * output stream. This method is equivalent to write(b, 0, + * buf.length) which is exactly how it is implemented in this + * class. + * + * @param b The array of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] b) throws IOException, NullPointerException + { + write (b, 0, b.length); + } + + /** + * This method writes len bytes from the specified array + * b starting at index off into the array. + *

    + * This method in this class calls the single byte write() + * method in a loop until all bytes have been written. Subclasses should + * override this method if possible in order to provide a more efficent + * implementation. + * + * @param b The array of bytes to write from + * @param off The index into the array to start writing from + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] b, int off, int len) + throws IOException, NullPointerException, IndexOutOfBoundsException + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException (); + for (int i = 0; i < len; ++i) + write (b[off + i]); + } + + /** + * This method forces any data that may have been buffered to be written + * to the underlying output device. Please note that the host environment + * might perform its own buffering unbeknowst to Java. In that case, a + * write made (for example, to a disk drive) might be cached in OS + * buffers instead of actually being written to disk. + *

    + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + } + + /** + * This method closes the stream. Any internal or native resources + * associated with this stream are freed. Any subsequent attempt to + * access the stream might throw an exception. + *

    + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + } +} diff --git a/libjava/classpath/java/io/OutputStreamWriter.java b/libjava/classpath/java/io/OutputStreamWriter.java new file mode 100644 index 000000000..27067fdb9 --- /dev/null +++ b/libjava/classpath/java/io/OutputStreamWriter.java @@ -0,0 +1,429 @@ +/* OutputStreamWriter.java -- Writer that converts chars to bytes + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.charset.EncodingHelper; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.MalformedInputException; + +/** + * This class writes characters to an output stream that is byte oriented + * It converts the chars that are written to bytes using an encoding layer, + * which is specific to a particular encoding standard. The desired + * encoding can either be specified by name, or if no encoding is specified, + * the system default encoding will be used. The system default encoding + * name is determined from the system property file.encoding. + * The only encodings that are guaranteed to be available are "8859_1" + * (the Latin-1 character set) and "UTF8". Unfortunately, Java does not + * provide a mechanism for listing the encodings that are supported in + * a given implementation. + *

    + * Here is a list of standard encoding names that may be available: + *

    + *

      + *
    • 8859_1 (ISO-8859-1/Latin-1) + *
    • 8859_2 (ISO-8859-2/Latin-2) + *
    • 8859_3 (ISO-8859-3/Latin-3) + *
    • 8859_4 (ISO-8859-4/Latin-4) + *
    • 8859_5 (ISO-8859-5/Latin-5) + *
    • 8859_6 (ISO-8859-6/Latin-6) + *
    • 8859_7 (ISO-8859-7/Latin-7) + *
    • 8859_8 (ISO-8859-8/Latin-8) + *
    • 8859_9 (ISO-8859-9/Latin-9) + *
    • ASCII (7-bit ASCII) + *
    • UTF8 (UCS Transformation Format-8) + *
    • More Later + *
    + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @date April 17, 1998. + */ +public class OutputStreamWriter extends Writer +{ + /** + * The output stream. + */ + private OutputStream out; + + /** + * The charset encoder. + */ + private final CharsetEncoder encoder; + + /** + * java.io canonical name of the encoding. + */ + private final String encodingName; + + /** + * Buffer output before character conversion as it has costly overhead. + */ + private final CharBuffer outputBuffer; + private final static int BUFFER_SIZE = 1024; + + /** + * This method initializes a new instance of OutputStreamWriter + * to write to the specified stream using a caller supplied character + * encoding scheme. Note that due to a deficiency in the Java language + * design, there is no way to determine which encodings are supported. + * + * @param out The OutputStream to write to + * @param encoding_scheme The name of the encoding scheme to use for + * character to byte translation + * + * @exception UnsupportedEncodingException If the named encoding is + * not available. + */ + public OutputStreamWriter (OutputStream out, String encoding_scheme) + throws UnsupportedEncodingException + { + CharsetEncoder encoder; + String encodingName; + this.out = out; + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + + try + { + // Don't use NIO if avoidable + if(EncodingHelper.isISOLatin1(encoding_scheme)) + { + encodingName = "ISO8859_1"; + encoder = null; + } + else + { + /* + * Workaround for encodings with a byte-order-mark. + * We only want to write it once per stream. + */ + try + { + if(encoding_scheme.equalsIgnoreCase("UnicodeBig") || + encoding_scheme.equalsIgnoreCase("UTF-16") || + encoding_scheme.equalsIgnoreCase("UTF16")) + { + encoding_scheme = "UTF-16BE"; + out.write((byte)0xFE); + out.write((byte)0xFF); + } + else if(encoding_scheme.equalsIgnoreCase("UnicodeLittle")) + { + encoding_scheme = "UTF-16LE"; + out.write((byte)0xFF); + out.write((byte)0xFE); + } + } + catch(IOException ioe) + { + } + + Charset cs = EncodingHelper.getCharset(encoding_scheme); + if(cs == null) + throw new UnsupportedEncodingException("Encoding "+encoding_scheme+ + " unknown"); + encoder = cs.newEncoder(); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + } + } + catch(RuntimeException e) + { + // Default to ISO Latin-1, will happen if this is called, for instance, + // before the NIO provider is loadable. + encoder = null; + encodingName = "ISO8859_1"; + } + this.encoder = encoder; + this.encodingName = encodingName; + } + + /** + * This method initializes a new instance of OutputStreamWriter + * to write to the specified stream using the default encoding. + * + * @param out The OutputStream to write to + */ + public OutputStreamWriter (OutputStream out) + { + CharsetEncoder encoder; + String encodingName; + this.out = out; + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + try + { + String encoding = System.getProperty("file.encoding"); + Charset cs = Charset.forName(encoding); + encoder = cs.newEncoder(); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + } + catch(RuntimeException e) + { + encoder = null; + encodingName = "ISO8859_1"; + } + + if(encoder != null) + { + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + } + this.encoder = encoder; + this.encodingName = encodingName; + } + + /** + * This method initializes a new instance of OutputStreamWriter + * to write to the specified stream using a given Charset. + * + * @param out The OutputStream to write to + * @param cs The Charset of the encoding to use + * + * @since 1.5 + */ + public OutputStreamWriter(OutputStream out, Charset cs) + { + this.out = out; + encoder = cs.newEncoder(); + encoder.onMalformedInput(CodingErrorAction.REPLACE); + encoder.onUnmappableCharacter(CodingErrorAction.REPLACE); + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + encodingName = EncodingHelper.getOldCanonical(cs.name()); + } + + /** + * This method initializes a new instance of OutputStreamWriter + * to write to the specified stream using a given + * CharsetEncoder. + * + * @param out The OutputStream to write to + * @param enc The CharsetEncoder to encode the output with + * + * @since 1.5 + */ + public OutputStreamWriter(OutputStream out, CharsetEncoder enc) + { + this.out = out; + encoder = enc; + outputBuffer = CharBuffer.allocate(BUFFER_SIZE); + Charset cs = enc.charset(); + if (cs == null) + encodingName = "US-ASCII"; + else + encodingName = EncodingHelper.getOldCanonical(cs.name()); + } + + /** + * This method closes this stream, and the underlying + * OutputStream + * + * @exception IOException If an error occurs + */ + public void close () throws IOException + { + if(out == null) + return; + flush(); + out.close (); + out = null; + } + + /** + * This method returns the name of the character encoding scheme currently + * in use by this stream. If the stream has been closed, then this method + * may return null. + * + * @return The encoding scheme name + */ + public String getEncoding () + { + return out != null ? encodingName : null; + } + + /** + * This method flushes any buffered bytes to the underlying output sink. + * + * @exception IOException If an error occurs + */ + public void flush () throws IOException + { + if(out != null){ + if(outputBuffer != null){ + char[] buf = new char[outputBuffer.position()]; + if(buf.length > 0){ + outputBuffer.flip(); + outputBuffer.get(buf); + writeConvert(buf, 0, buf.length); + outputBuffer.clear(); + } + } + out.flush (); + } + } + + /** + * This method writes count characters from the specified + * array to the output stream starting at position offset + * into the array. + * + * @param buf The array of character to write from + * @param offset The offset into the array to start writing chars from + * @param count The number of chars to write. + * + * @exception IOException If an error occurs + */ + public void write (char[] buf, int offset, int count) throws IOException + { + if(out == null) + throw new IOException("Stream is closed."); + if(buf == null) + throw new IOException("Buffer is null."); + + if(outputBuffer != null) + { + if(count >= outputBuffer.remaining()) + { + int r = outputBuffer.remaining(); + outputBuffer.put(buf, offset, r); + writeConvert(outputBuffer.array(), 0, BUFFER_SIZE); + outputBuffer.clear(); + offset += r; + count -= r; + // if the remaining bytes is larger than the whole buffer, + // just don't buffer. + if(count >= outputBuffer.remaining()){ + writeConvert(buf, offset, count); + return; + } + } + outputBuffer.put(buf, offset, count); + } else writeConvert(buf, offset, count); + } + + /** + * Converts and writes characters. + */ + private void writeConvert (char[] buf, int offset, int count) + throws IOException + { + if(encoder == null) + { + byte[] b = new byte[count]; + for(int i=0;icount bytes from the specified + * String starting at position offset into the + * String. + * + * @param str The String to write chars from + * @param offset The position in the String to start + * writing chars from + * @param count The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write (String str, int offset, int count) throws IOException + { + if(str == null) + throw new IOException("String is null."); + + write(str.toCharArray(), offset, count); + } + + /** + * This method writes a single character to the output stream. + * + * @param ch The char to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write (int ch) throws IOException + { + // No buffering, no encoding ... just pass through + if (encoder == null && outputBuffer == null) { + out.write(nullConversion((char)ch)); + } else { + if (outputBuffer != null) { + if (outputBuffer.remaining() == 0) { + writeConvert(outputBuffer.array(), 0, BUFFER_SIZE); + outputBuffer.clear(); + } + outputBuffer.put((char)ch); + } else { + writeConvert(new char[]{ (char)ch }, 0, 1); + } + } + } +} // class OutputStreamWriter diff --git a/libjava/classpath/java/io/PipedInputStream.java b/libjava/classpath/java/io/PipedInputStream.java new file mode 100644 index 000000000..0171a1b6f --- /dev/null +++ b/libjava/classpath/java/io/PipedInputStream.java @@ -0,0 +1,413 @@ +/* PipedInputStream.java -- Read portion of piped streams. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +// NOTE: This implementation is very similar to that of PipedReader. If you +// fix a bug in here, chances are you should make a similar change to the +// PipedReader code. + +/** + * An input stream that reads its bytes from an output stream + * to which it is connected. + *

    + * Data is read and written to an internal buffer. It is highly recommended + * that the PipedInputStream and connected + * PipedOutputStream + * be part of different threads. If they are not, the read and write + * operations could deadlock their thread. + * + * @specnote The JDK implementation appears to have some undocumented + * functionality where it keeps track of what thread is writing + * to pipe and throws an IOException if that thread susequently + * dies. This behaviour seems dubious and unreliable - we don't + * implement it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedInputStream extends InputStream +{ + /** PipedOutputStream to which this is connected. Null only if this + * InputStream hasn't been connected yet. */ + PipedOutputStream source; + + /** Set to true if close() has been called on this InputStream. */ + boolean closed; + + + /** + * The size of the internal buffer used for input/output. + */ + /* The "Constant Field Values" Javadoc of the Sun J2SE 1.4 + * specifies 1024. + */ + protected static final int PIPE_SIZE = 1024; + + + /** + * This is the internal circular buffer used for storing bytes written + * to the pipe and from which bytes are read by this stream + */ + protected byte[] buffer = null; + + /** + * The index into buffer where the next byte from the connected + * PipedOutputStream will be written. If this variable is + * equal to out, then the buffer is full. If set to < 0, + * the buffer is empty. + */ + protected int in = -1; + + /** + * This index into the buffer where bytes will be read from. + */ + protected int out = 0; + + /** Buffer used to implement single-argument read/receive */ + private byte[] read_buf = new byte[1]; + + /** + * Creates a new PipedInputStream that is not connected to a + * PipedOutputStream. It must be connected before bytes can + * be read from this stream. + */ + public PipedInputStream() + { + this(PIPE_SIZE); + } + + /** + * Creates a new PipedInputStream of the given size that is not + * connected to a PipedOutputStream. + * It must be connected before bytes can be read from this stream. + * + * @since 1.6 + * @since IllegalArgumentException If pipeSize <= 0. + */ + public PipedInputStream(int pipeSize) throws IllegalArgumentException + { + if (pipeSize <= 0) + throw new IllegalArgumentException("pipeSize must be > 0"); + + this.buffer = new byte[pipeSize]; + } + + /** + * This constructor creates a new PipedInputStream and connects + * it to the passed in PipedOutputStream. The stream is then + * ready for reading. + * + * @param source The PipedOutputStream to connect this + * stream to + * + * @exception IOException If source is already connected. + */ + public PipedInputStream(PipedOutputStream source) throws IOException + { + this(); + connect(source); + } + + /** + * This constructor creates a new PipedInputStream of the given + * size and connects it to the passed in PipedOutputStream. + * The stream is then ready for reading. + * + * @param source The PipedOutputStream to connect this + * stream to + * + * @since 1.6 + * @exception IOException If source is already connected. + */ + public PipedInputStream(PipedOutputStream source, int pipeSize) + throws IOException + { + this(pipeSize); + connect(source); + } + + /** + * This method connects this stream to the passed in + * PipedOutputStream. + * This stream is then ready for reading. If this stream is already + * connected or has been previously closed, then an exception is thrown + * + * @param source The PipedOutputStream to connect this stream to + * + * @exception IOException If this PipedInputStream or source + * has been connected already. + */ + public void connect(PipedOutputStream source) throws IOException + { + // The JDK (1.3) does not appear to check for a previously closed + // connection here. + + if (this.source != null || source.sink != null) + throw new IOException ("Already connected"); + + source.sink = this; + this.source = source; + } + + /** + * This method receives a byte of input from the source PipedOutputStream. + * If the internal circular buffer is full, this method blocks. + * + * @param val The byte to write to this stream + * + * @exception IOException if error occurs + * @specnote Weird. This method must be some sort of accident. + */ + protected synchronized void receive(int val) throws IOException + { + read_buf[0] = (byte) (val & 0xff); + receive (read_buf, 0, 1); + } + + /** + * This method is used by the connected PipedOutputStream to + * write bytes into the buffer. + * + * @param buf The array containing bytes to write to this stream + * @param offset The offset into the array to start writing from + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs + * @specnote This code should be in PipedOutputStream.write, but we + * put it here in order to support that bizarre recieve(int) + * method. + */ + synchronized void receive(byte[] buf, int offset, int len) + throws IOException + { + if (closed) + throw new IOException ("Pipe closed"); + + int bufpos = offset; + int copylen; + + while (len > 0) + { + try + { + while (in == out) + { + // The pipe is full. Wake up any readers and wait for them. + notifyAll(); + wait(); + // The pipe could have been closed while we were waiting. + if (closed) + throw new IOException ("Pipe closed"); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException (); + } + + if (in < 0) // The pipe is empty. + in = 0; + + // Figure out how many bytes from buf can be copied without + // overrunning out or going past the length of the buffer. + if (in < out) + copylen = Math.min (len, out - in); + else + copylen = Math.min (len, buffer.length - in); + + // Copy bytes until the pipe is filled, wrapping if necessary. + System.arraycopy(buf, bufpos, buffer, in, copylen); + len -= copylen; + bufpos += copylen; + in += copylen; + if (in == buffer.length) + in = 0; + } + // Notify readers that new data is in the pipe. + notifyAll(); + } + + /** + * This method reads one byte from the stream. + * -1 is returned to indicated that no bytes can be read + * because the end of the stream was reached. If the stream is already + * closed, a -1 will again be returned to indicate the end of the stream. + * + *

    This method will block if no byte is available to be read.

    + * + * @return the value of the read byte value, or -1 of the end of the stream + * was reached + * + * @throws IOException if an error occured + */ + public int read() throws IOException + { + // Method operates by calling the multibyte overloaded read method + // Note that read_buf is an internal instance variable. I allocate it + // there to avoid constant reallocation overhead for applications that + // call this method in a loop at the cost of some unneeded overhead + // if this method is never called. + + int r = read(read_buf, 0, 1); + return r != -1 ? (read_buf[0] & 0xff) : -1; + } + + /** + * This method reads bytes from the stream into a caller supplied buffer. + * It starts storing bytes at position offset into the + * buffer and + * reads a maximum of len bytes. Note that this method + * can actually + * read fewer than len bytes. The actual number of bytes + * read is + * returned. A -1 is returned to indicated that no bytes can be read + * because the end of the stream was reached - ie close() was called on the + * connected PipedOutputStream. + *

    + * This method will block if no bytes are available to be read. + * + * @param buf The buffer into which bytes will be stored + * @param offset The index into the buffer at which to start writing. + * @param len The maximum number of bytes to read. + * + * @exception IOException If close() was called on this Piped + * InputStream. + */ + public synchronized int read(byte[] buf, int offset, int len) + throws IOException + { + if (source == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + // Don't block if nothing was requested. + if (len == 0) + return 0; + + // If the buffer is empty, wait until there is something in the pipe + // to read. + try + { + while (in < 0) + { + if (source.closed) + return -1; + wait(); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException(); + } + + int total = 0; + int copylen; + + while (true) + { + // Figure out how many bytes from the pipe can be copied without + // overrunning in or going past the length of buf. + if (out < in) + copylen = Math.min (len, in - out); + else + copylen = Math.min (len, buffer.length - out); + + System.arraycopy (buffer, out, buf, offset, copylen); + offset += copylen; + len -= copylen; + out += copylen; + total += copylen; + + if (out == buffer.length) + out = 0; + + if (out == in) + { + // Pipe is now empty. + in = -1; + out = 0; + } + + // If output buffer is filled or the pipe is empty, we're done. + if (len == 0 || in == -1) + { + // Notify any waiting outputstream that there is now space + // to write. + notifyAll(); + return total; + } + } + } + + /** + * This method returns the number of bytes that can be read from this stream + * before blocking could occur. This is the number of bytes that are + * currently unread in the internal circular buffer. Note that once this + * many additional bytes are read, the stream may block on a subsequent + * read, but it not guaranteed to block. + * + * @return The number of bytes that can be read before blocking might occur + * + * @exception IOException If an error occurs + */ + public synchronized int available() throws IOException + { + // The JDK 1.3 implementation does not appear to check for the closed or + // unconnected stream conditions here. + + if (in < 0) + return 0; + else if (out < in) + return in - out; + else + return (buffer.length - out) + in; + } + + /** + * This methods closes the stream so that no more data can be read + * from it. + * + * @exception IOException If an error occurs + */ + public synchronized void close() throws IOException + { + closed = true; + // Wake any thread which may be in receive() waiting to write data. + notifyAll(); + } +} diff --git a/libjava/classpath/java/io/PipedOutputStream.java b/libjava/classpath/java/io/PipedOutputStream.java new file mode 100644 index 000000000..839cb1e22 --- /dev/null +++ b/libjava/classpath/java/io/PipedOutputStream.java @@ -0,0 +1,181 @@ +/* PipedOutputStream.java -- Write portion of piped streams. + Copyright (C) 1998, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// NOTE: This implementation is very similar to that of PipedWriter. If you +// fix a bug in here, chances are you should make a similar change to the +// PipedWriter code. + +/** + * This class writes its bytes to a PipedInputStream to + * which it is connected. + *

    + * It is highly recommended that a PipedOutputStream and its + * connected PipedInputStream be in different threads. If + * they are in the same thread, read and write operations could deadlock + * the thread. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedOutputStream extends OutputStream +{ + /** Target PipedInputStream to which this is connected. Null only if this + * OutputStream hasn't been connected yet. */ + PipedInputStream sink; + + /** Set to true if close() has been called on this OutputStream. */ + boolean closed; + + /** + * Create an unconnected PipedOutputStream. It must be connected + * to a PipedInputStream using the connect + * method prior to writing any data or an exception will be thrown. + */ + public PipedOutputStream() + { + } + + /** + * Create a new PipedOutputStream instance + * to write to the specified PipedInputStream. This stream + * is then ready for writing. + * + * @param sink The PipedInputStream to connect this stream to. + * + * @exception IOException If sink has already been connected + * to a different PipedOutputStream. + */ + public PipedOutputStream(PipedInputStream sink) throws IOException + { + sink.connect(this); + } + + /** + * Connects this object to the specified PipedInputStream + * object. This stream will then be ready for writing. + * + * @param sink The PipedInputStream to connect this stream to + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void connect(PipedInputStream sink) throws IOException + { + if (this.sink != null || sink.source != null) + throw new IOException ("Already connected"); + sink.connect(this); + } + + /** + * Write a single byte of date to the stream. Note that this method will + * block if the PipedInputStream to which this object is + * connected has a full buffer. + * + * @param b The byte of data to be written, passed as an int. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(int b) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive (b); + } + + /** + * This method writes len bytes of data from the byte array + * buf starting at index offset in the array + * to the stream. Note that this method will block if the + * PipedInputStream to which this object is connected has + * a buffer that cannot hold all of the bytes to be written. + * + * @param buffer The array containing bytes to write to the stream. + * @param offset The index into the array to start writing bytes from. + * @param len The number of bytes to write. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(byte[] buffer, int offset, int len) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive(buffer, offset, len); + } + + /** + * This method does nothing. + * + * @exception IOException If the stream is closed. + * @specnote You'd think that this method would block until the sink + * had read all available data. Thats not the case - this method + * appears to be a no-op? + */ + public void flush() throws IOException + { + } + + /** + * This method closes this stream so that no more data can be written + * to it. Any further attempts to write to this stream may throw an + * exception + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + // A close call on an unconnected PipedOutputStream has no effect. + if (sink != null) + { + closed = true; + // Notify any waiting readers that the stream is now closed. + synchronized (sink) + { + sink.notifyAll(); + } + } + } +} diff --git a/libjava/classpath/java/io/PipedReader.java b/libjava/classpath/java/io/PipedReader.java new file mode 100644 index 000000000..4f449ffce --- /dev/null +++ b/libjava/classpath/java/io/PipedReader.java @@ -0,0 +1,364 @@ +/* PipedReader.java -- Read portion of piped character streams. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +// NOTE: This implementation is very similar to that of PipedInputStream. +// If you fix a bug in here, chances are you should make a similar change to +// the PipedInputStream code. + +/** + * An input stream that reads characters from a piped writer to which it is + * connected. + *

    + * Data is read and written to an internal buffer. It is highly recommended + * that the PipedReader and connected PipedWriter + * be part of different threads. If they are not, there is a possibility + * that the read and write operations could deadlock their thread. + * + * @specnote The JDK implementation appears to have some undocumented + * functionality where it keeps track of what thread is writing + * to pipe and throws an IOException if that thread susequently + * dies. This behaviour seems dubious and unreliable - we don't + * implement it. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedReader extends Reader +{ + /** PipedWriter to which this is connected. Null only if this + * Reader hasn't been connected yet. */ + PipedWriter source; + + /** Set to true if close() has been called on this Reader. */ + boolean closed; + + /** + * The size of the internal buffer used for input/output. + */ + static final int PIPE_SIZE = 2048; + + /** + * This is the internal circular buffer used for storing chars written + * to the pipe and from which chars are read by this stream + */ + char[] buffer = new char[PIPE_SIZE]; + + /** + * The index into buffer where the next char from the connected + * PipedWriter will be written. If this variable is + * equal to out, then the buffer is full. If set to < 0, + * the buffer is empty. + */ + int in = -1; + + /** + * This index into the buffer where chars will be read from. + */ + int out = 0; + + /** Buffer used to implement single-argument read/receive */ + char[] read_buf = new char[1]; + + /** + * Creates a new PipedReader that is not connected to a + * PipedWriter. It must be connected before chars can + * be read from this stream. + */ + public PipedReader() + { + } + + /** + * This constructor creates a new PipedReader and connects + * it to the passed in PipedWriter. The stream is then + * ready for reading. + * + * @param source The PipedWriter to connect this stream to + * + * @exception IOException If source is already connected. + */ + public PipedReader(PipedWriter source) throws IOException + { + connect(source); + } + + /** + * This method connects this stream to the passed in + * PipedWriter. + * This stream is then ready for reading. If this stream is already + * connected or has been previously closed, then an exception is thrown + * + * @param source The PipedWriter to connect this stream to + * + * @exception IOException If this PipedReader or source + * has been connected already. + */ + public void connect(PipedWriter source) throws IOException + { + // The JDK (1.3) does not appear to check for a previously closed + // connection here. + + if (this.source != null || source.sink != null) + throw new IOException ("Already connected"); + + source.sink = this; + this.source = source; + } + + /** + * This method is used by the connected PipedWriter to + * write chars into the buffer. + * + * @param buf The array containing chars to write to this stream + * @param offset The offset into the array to start writing from + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + * @specnote This code should be in PipedWriter.write, but we + * put it here in order to support that bizarre recieve(int) + * method. + */ + void receive(char[] buf, int offset, int len) + throws IOException + { + synchronized (lock) + { + if (closed) + throw new IOException ("Pipe closed"); + + int bufpos = offset; + int copylen; + + while (len > 0) + { + try + { + while (in == out) + { + // The pipe is full. Wake up any readers and wait for them. + lock.notifyAll(); + lock.wait(); + // The pipe could have been closed while we were waiting. + if (closed) + throw new IOException ("Pipe closed"); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException (); + } + + if (in < 0) // The pipe is empty. + in = 0; + + // Figure out how many chars from buf can be copied without + // overrunning out or going past the length of the buffer. + if (in < out) + copylen = Math.min (len, out - in); + else + copylen = Math.min (len, buffer.length - in); + + // Copy chars until the pipe is filled, wrapping if necessary. + System.arraycopy(buf, bufpos, buffer, in, copylen); + len -= copylen; + bufpos += copylen; + in += copylen; + if (in == buffer.length) + in = 0; + } + // Notify readers that new data is in the pipe. + lock.notifyAll(); + } + } + + /** + * This method reads chars from the stream into a caller supplied buffer. + * It starts storing chars at position offset into the + * buffer and + * reads a maximum of len chars. Note that this method + * can actually + * read fewer than len chars. The actual number of chars + * read is + * returned. A -1 is returned to indicated that no chars can be read + * because the end of the stream was reached. If the stream is already + * closed, a -1 will again be returned to indicate the end of the stream. + *

    + * This method will block if no char is available to be read. + */ + public int read() throws IOException + { + // Method operates by calling the multichar overloaded read method + // Note that read_buf is an internal instance variable. I allocate it + // there to avoid constant reallocation overhead for applications that + // call this method in a loop at the cost of some unneeded overhead + // if this method is never called. + + int r = read(read_buf, 0, 1); + return r != -1 ? read_buf[0] : -1; + } + + /** + * This method reads characters from the stream into a caller supplied + * buffer. It starts storing chars at position offset into + * the buffer and reads a maximum of len chars. Note that + * this method can actually read fewer than len chars. + * The actual number of chars read is + * returned. A -1 is returned to indicated that no chars can be read + * because the end of the stream was reached - ie close() was called on the + * connected PipedWriter. + *

    + * This method will block if no chars are available to be read. + * + * @param buf The buffer into which chars will be stored + * @param offset The index into the buffer at which to start writing. + * @param len The maximum number of chars to read. + * + * @exception IOException If close() was called on this Piped + * Reader. + */ + public int read(char[] buf, int offset, int len) + throws IOException + { + synchronized (lock) + { + if (source == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + // Don't block if nothing was requested. + if (len == 0) + return 0; + + // If the buffer is empty, wait until there is something in the pipe + // to read. + try + { + while (in < 0) + { + if (source.closed) + return -1; + lock.wait(); + } + } + catch (InterruptedException ix) + { + throw new InterruptedIOException(); + } + + int total = 0; + int copylen; + + while (true) + { + // Figure out how many chars from the pipe can be copied without + // overrunning in or going past the length of buf. + if (out < in) + copylen = Math.min (len, in - out); + else + copylen = Math.min (len, buffer.length - out); + + System.arraycopy (buffer, out, buf, offset, copylen); + offset += copylen; + len -= copylen; + out += copylen; + total += copylen; + + if (out == buffer.length) + out = 0; + + if (out == in) + { + // Pipe is now empty. + in = -1; + out = 0; + } + + // If output buffer is filled or the pipe is empty, we're done. + if (len == 0 || in == -1) + { + // Notify any waiting Writer that there is now space + // to write. + lock.notifyAll(); + return total; + } + } + } + } + + public boolean ready() throws IOException + { + // The JDK 1.3 implementation does not appear to check for the closed or + // unconnected stream conditions here. However, checking for a + // closed stream is explicitly required by the JDK 1.2 and 1.3 + // documentation (for Reader.close()), so we do it. + + synchronized (lock) + { + if (closed) + throw new IOException("Pipe closed"); + + if (in < 0) + return false; + + int count; + if (out < in) + count = in - out; + else + count = (buffer.length - out) - in; + + return (count > 0); + } + } + + /** + * This methods closes the stream so that no more data can be read + * from it. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + synchronized (lock) + { + closed = true; + // Wake any thread which may be in receive() waiting to write data. + lock.notifyAll(); + } + } +} diff --git a/libjava/classpath/java/io/PipedWriter.java b/libjava/classpath/java/io/PipedWriter.java new file mode 100644 index 000000000..0d48ab0e4 --- /dev/null +++ b/libjava/classpath/java/io/PipedWriter.java @@ -0,0 +1,182 @@ +/* PipedWriter.java -- Write portion of piped character streams. + Copyright (C) 1998, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// NOTE: This implementation is very similar to that of PipedOutputStream. +// If you fix a bug in here, chances are you should make a similar change to +// the PipedOutputStream code. + +/** + * This class writes its chars to a PipedReader to + * which it is connected. + *

    + * It is highly recommended that a PipedWriter and its + * connected PipedReader be in different threads. If + * they are in the same thread, read and write operations could deadlock + * the thread. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedWriter extends Writer +{ + /** Target PipedReader to which this is connected. Null only if this + * Writer hasn't been connected yet. */ + PipedReader sink; + + /** Set to true if close() has been called on this Writer. */ + boolean closed; + + /** Buffer used to implement single-argument write */ + char[] read_buf = new char[1]; + + /** + * Create an unconnected PipedWriter. It must be connected + * to a PipedReader using the connect + * method prior to writing any data or an exception will be thrown. + */ + public PipedWriter() + { + } + + /** + * Create a new PipedWriter instance + * to write to the specified PipedReader. This stream + * is then ready for writing. + * + * @param sink The PipedReader to connect this stream to. + * + * @exception IOException If sink has already been connected + * to a different PipedWriter. + */ + public PipedWriter(PipedReader sink) throws IOException + { + sink.connect(this); + } + + /** + * Connects this object to the specified PipedReader + * object. This stream will then be ready for writing. + * + * @param sink The PipedReader to connect this stream to + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void connect(PipedReader sink) throws IOException + { + if (this.sink != null || sink.source != null) + throw new IOException ("Already connected"); + sink.connect(this); + } + + /** + * Write a single char of date to the stream. Note that this method will + * block if the PipedReader to which this object is + * connected has a full buffer. + * + * @param b The char of data to be written, passed as an int. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(int b) throws IOException + { + read_buf[0] = (char) (b & 0xffff); + sink.receive (read_buf, 0, 1); + } + + /** + * This method writes len chars of data from the char array + * buf starting at index offset in the array + * to the stream. Note that this method will block if the + * PipedReader to which this object is connected has + * a buffer that cannot hold all of the chars to be written. + * + * @param buffer The array containing chars to write to the stream. + * @param offset The index into the array to start writing chars from. + * @param len The number of chars to write. + * + * @exception IOException If the stream has not been connected or has + * been closed. + */ + public void write(char[] buffer, int offset, int len) throws IOException + { + if (sink == null) + throw new IOException ("Not connected"); + if (closed) + throw new IOException ("Pipe closed"); + + sink.receive(buffer, offset, len); + } + + /** + * This method does nothing. + * + * @exception IOException If the stream is closed. + * @specnote You'd think that this method would block until the sink + * had read all available data. Thats not the case - this method + * appears to be a no-op? + */ + public void flush() throws IOException + { + if (closed) + throw new IOException ("Pipe closed"); + } + + /** + * This method closes this stream so that no more data can be written + * to it. Any further attempts to write to this stream may throw an + * exception + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + // A close call on an unconnected PipedWriter has no effect. + if (sink != null) + { + closed = true; + // Notify any waiting readers that the stream is now closed. + synchronized (sink) + { + sink.notifyAll(); + } + } + } +} diff --git a/libjava/classpath/java/io/PrintStream.java b/libjava/classpath/java/io/PrintStream.java new file mode 100644 index 000000000..eaab7c3d4 --- /dev/null +++ b/libjava/classpath/java/io/PrintStream.java @@ -0,0 +1,675 @@ +/* PrintStream.java -- OutputStream for printing output + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.util.Locale; +import java.util.Formatter; + +import gnu.classpath.SystemProperties; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Believed complete and correct to 1.3 + */ + +/** + * This class prints Java primitive values and object to a stream as + * text. None of the methods in this class throw an exception. However, + * errors can be detected by calling the checkError() method. + * Additionally, this stream can be designated as "autoflush" when + * created so that any writes are automatically flushed to the underlying + * output sink when the current line is terminated. + *

    + * This class converts char's into byte's using the system default encoding. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class PrintStream extends FilterOutputStream implements Appendable +{ + /* Notice the implementation is quite similar to OutputStreamWriter. + * This leads to some minor duplication, because neither inherits + * from the other, and we want to maximize performance. */ + + // Line separator string. + private static final char[] line_separator + = SystemProperties.getProperty("line.separator", "\n").toCharArray(); + + /** + * Encoding name + */ + private final String encoding; + + /** + * This boolean indicates whether or not an error has ever occurred + * on this stream. + */ + private boolean error_occurred = false; + + /** + * This is true if auto-flush is enabled, + * false otherwise + */ + private final boolean auto_flush; + + /** + * This method initializes a new PrintStream object to write + * to the specified output File. Doesn't autoflush. + * + * @param file The File to write to. + * @throws FileNotFoundException if an error occurs while opening the file. + * + * @since 1.5 + */ + public PrintStream (File file) + throws FileNotFoundException + { + this (new FileOutputStream(file), false); + } + + /** + * This method initializes a new PrintStream object to write + * to the specified output File. Doesn't autoflush. + * + * @param file The File to write to. + * @param encoding The name of the character encoding to use for this + * object. + * @throws FileNotFoundException If an error occurs while opening the file. + * @throws UnsupportedEncodingException If the charset specified by + * encoding is invalid. + * + * @since 1.5 + */ + public PrintStream (File file, String encoding) + throws FileNotFoundException,UnsupportedEncodingException + { + this (new FileOutputStream(file), false, encoding); + } + + /** + * This method initializes a new PrintStream object to write + * to the specified output File. Doesn't autoflush. + * + * @param fileName The name of the File to write to. + * @throws FileNotFoundException if an error occurs while opening the file, + * + * @since 1.5 + */ + public PrintStream (String fileName) + throws FileNotFoundException + { + this (new FileOutputStream(new File(fileName)), false); + } + + /** + * This method initializes a new PrintStream object to write + * to the specified output File. Doesn't autoflush. + * + * @param fileName The name of the File to write to. + * @param encoding The name of the character encoding to use for this + * object. + * @throws FileNotFoundException if an error occurs while opening the file. + * @throws UnsupportedEncodingException If the charset specified by + * encoding is invalid. + * + * @since 1.5 + */ + public PrintStream (String fileName, String encoding) + throws FileNotFoundException,UnsupportedEncodingException + { + this (new FileOutputStream(new File(fileName)), false, encoding); + } + + /** + * This method initializes a new PrintStream object to write + * to the specified output sink. Doesn't autoflush. + * + * @param out The OutputStream to write to. + */ + public PrintStream (OutputStream out) + { + this (out, false); + } + + /** + * This method initializes a new PrintStream object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every print or println call, when the + * write methods with array arguments are called, or when a + * single new-line character is written. + *

    + * + * @param out The OutputStream to write to. + * @param auto_flush true to flush the stream after every + * line, false otherwise + */ + public PrintStream (OutputStream out, boolean auto_flush) + { + super (out); + String encoding; + try { + encoding = SystemProperties.getProperty("file.encoding"); + } catch (SecurityException e){ + encoding = "ISO8859_1"; + } catch (IllegalArgumentException e){ + encoding = "ISO8859_1"; + } catch (NullPointerException e){ + encoding = "ISO8859_1"; + } + this.encoding = encoding; + this.auto_flush = auto_flush; + } + + /** + * This method initializes a new PrintStream object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every print or println call, when the + * write methods with array arguments are called, or when a + * single new-line character is written. + *

    + * + * @param out The OutputStream to write to. + * @param auto_flush true to flush the stream after every + * line, false otherwise + * @param encoding The name of the character encoding to use for this + * object. + */ + public PrintStream (OutputStream out, boolean auto_flush, String encoding) + throws UnsupportedEncodingException + { + super (out); + + new String(new byte[]{0}, encoding); // check if encoding is supported + this.encoding = encoding; + this.auto_flush = auto_flush; + } + + /** + * This method checks to see if an error has occurred on this stream. Note + * that once an error has occurred, this method will continue to report + * true forever for this stream. Before checking for an + * error condition, this method flushes the stream. + * + * @return true if an error has occurred, + * false otherwise + */ + public boolean checkError () + { + flush (); + return error_occurred; + } + + /** + * This method can be called by subclasses to indicate that an error + * has occurred and should be reported by checkError. + */ + protected void setError () + { + error_occurred = true; + } + + /** + * This method closes this stream and all underlying streams. + */ + public void close () + { + try + { + flush(); + out.close(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + /** + * This method flushes any buffered bytes to the underlying stream and + * then flushes that stream as well. + */ + public void flush () + { + try + { + out.flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private synchronized void print (String str, boolean println) + { + try + { + writeChars(str, 0, str.length()); + if (println) + writeChars(line_separator, 0, line_separator.length); + if (auto_flush) + flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private synchronized void print (char[] chars, int pos, int len, + boolean println) + { + try + { + writeChars(chars, pos, len); + if (println) + writeChars(line_separator, 0, line_separator.length); + if (auto_flush) + flush(); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread().interrupt(); + } + catch (IOException e) + { + setError (); + } + } + + private void writeChars(char[] buf, int offset, int count) + throws IOException + { + byte[] bytes = (new String(buf, offset, count)).getBytes(encoding); + out.write(bytes, 0, bytes.length); + } + + private void writeChars(String str, int offset, int count) + throws IOException + { + byte[] bytes = str.substring(offset, offset+count).getBytes(encoding); + out.write(bytes, 0, bytes.length); + } + + /** + * This methods prints a boolean value to the stream. true + * values are printed as "true" and false values are printed + * as "false". + * + * @param bool The boolean value to print + */ + public void print (boolean bool) + { + print(String.valueOf(bool), false); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the String.valueOf() method. + * + * @param inum The int value to be printed + */ + public void print (int inum) + { + print(String.valueOf(inum), false); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the String.valueOf() method. + * + * @param lnum The long value to be printed + */ + public void print (long lnum) + { + print(String.valueOf(lnum), false); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the String.valueOf() method. + * + * @param fnum The float value to be printed + */ + public void print (float fnum) + { + print(String.valueOf(fnum), false); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the String.valueOf() method. + * + * @param dnum The double value to be printed + */ + public void print (double dnum) + { + print(String.valueOf(dnum), false); + } + + /** + * This method prints an Object to the stream. The actual + * value printed is determined by calling the String.valueOf() + * method. + * + * @param obj The Object to print. + */ + public void print (Object obj) + { + print(obj == null ? "null" : obj.toString(), false); + } + + /** + * This method prints a String to the stream. The actual + * value printed depends on the system default encoding. + * + * @param str The String to print. + */ + public void print (String str) + { + print(str == null ? "null" : str, false); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * @param ch The char value to be printed + */ + public synchronized void print (char ch) + { + print(new char[]{ch}, 0, 1, false); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * @param charArray The array of characters to print. + */ + public void print (char[] charArray) + { + print(charArray, 0, charArray.length, false); + } + + /** + * This method prints a line separator sequence to the stream. The value + * printed is determined by the system property

    line.separator + * and is not necessarily the Unix '\n' newline character. + */ + public void println () + { + print(line_separator, 0, line_separator.length, false); + } + + /** + * This methods prints a boolean value to the stream. true + * values are printed as "true" and false values are printed + * as "false". + *

    + * This method prints a line termination sequence after printing the value. + * + * @param bool The boolean value to print + */ + public void println (boolean bool) + { + print(String.valueOf(bool), true); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the String.valueOf() method. + *

    + * This method prints a line termination sequence after printing the value. + * + * @param inum The int value to be printed + */ + public void println (int inum) + { + print(String.valueOf(inum), true); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the String.valueOf() method. + *

    + * This method prints a line termination sequence after printing the value. + * + * @param lnum The long value to be printed + */ + public void println (long lnum) + { + print(String.valueOf(lnum), true); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the String.valueOf() method. + *

    + * This method prints a line termination sequence after printing the value. + * + * @param fnum The float value to be printed + */ + public void println (float fnum) + { + print(String.valueOf(fnum), true); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the String.valueOf() method. + *

    + * This method prints a line termination sequence after printing the value. + * + * @param dnum The double value to be printed + */ + public void println (double dnum) + { + print(String.valueOf(dnum), true); + } + + /** + * This method prints an Object to the stream. The actual + * value printed is determined by calling the String.valueOf() + * method. + *

    + * This method prints a line termination sequence after printing the value. + * + * @param obj The Object to print. + */ + public void println (Object obj) + { + print(obj == null ? "null" : obj.toString(), true); + } + + /** + * This method prints a String to the stream. The actual + * value printed depends on the system default encoding. + *

    + * This method prints a line termination sequence after printing the value. + * + * @param str The String to print. + */ + public void println (String str) + { + print (str == null ? "null" : str, true); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + *

    + * This method prints a line termination sequence after printing the value. + * + * @param ch The char value to be printed + */ + public synchronized void println (char ch) + { + print(new char[]{ch}, 0, 1, true); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + *

    + * This method prints a line termination sequence after printing the value. + * + * @param charArray The array of characters to print. + */ + public void println (char[] charArray) + { + print(charArray, 0, charArray.length, true); + } + + /** + * This method writes a byte of data to the stream. If auto-flush is + * enabled, printing a newline character will cause the stream to be + * flushed after the character is written. + * + * @param oneByte The byte to be written + */ + public void write (int oneByte) + { + try + { + out.write (oneByte & 0xff); + + if (auto_flush && (oneByte == '\n')) + flush (); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread ().interrupt (); + } + catch (IOException e) + { + setError (); + } + } + + /** + * This method writes len bytes from the specified array + * starting at index offset into the array. + * + * @param buffer The array of bytes to write + * @param offset The index into the array to start writing from + * @param len The number of bytes to write + */ + public void write (byte[] buffer, int offset, int len) + { + try + { + out.write (buffer, offset, len); + + if (auto_flush) + flush (); + } + catch (InterruptedIOException iioe) + { + Thread.currentThread ().interrupt (); + } + catch (IOException e) + { + setError (); + } + } + + /** @since 1.5 */ + public PrintStream append(char c) + { + print(c); + return this; + } + + /** @since 1.5 */ + public PrintStream append(CharSequence cs) + { + print(cs == null ? "null" : cs.toString()); + return this; + } + + /** @since 1.5 */ + public PrintStream append(CharSequence cs, int start, int end) + { + print(cs == null ? "null" : cs.subSequence(start, end).toString()); + return this; + } + + /** @since 1.5 */ + public PrintStream printf(String format, Object... args) + { + return format(format, args); + } + + /** @since 1.5 */ + public PrintStream printf(Locale locale, String format, Object... args) + { + return format(locale, format, args); + } + + /** @since 1.5 */ + public PrintStream format(String format, Object... args) + { + return format(Locale.getDefault(), format, args); + } + + /** @since 1.5 */ + public PrintStream format(Locale locale, String format, Object... args) + { + Formatter f = new Formatter(this, locale); + f.format(format, args); + return this; + } +} // class PrintStream diff --git a/libjava/classpath/java/io/PrintWriter.java b/libjava/classpath/java/io/PrintWriter.java new file mode 100644 index 000000000..a67a7f8aa --- /dev/null +++ b/libjava/classpath/java/io/PrintWriter.java @@ -0,0 +1,689 @@ +/* PrintWriter.java -- prints primitive values and objects to a stream as text + Copyright (C) 1998, 1999, 2000, 2001, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +import java.util.Locale; +import java.util.Formatter; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + * However, should use native methods for conversion. + */ + +/** + * This class prints Java primitive values and objects to a stream as + * text. None of the methods in this class throw an exception. However, + * errors can be detected by calling the checkError() method. + * Additionally, this stream can be designated as "autoflush" when + * created so that any writes are automatically flushed to the underlying + * output sink whenever one of the println methods is + * called. (Note that this differs from the PrintStream + * class which also auto-flushes when it encounters a newline character + * in the chars written). + * + * @author Per Bothner (bothner@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date April 17, 1998. + */ +public class PrintWriter extends Writer +{ + /** + * true if auto-flush is enabled, false otherwise + */ + private boolean autoflush; + + /** + * This boolean indicates whether or not an error has ever occurred + * on this stream. + */ + private boolean error; + + /** + * Indicates whether or not the stream has been closed. + */ + private boolean closed; + + /** + * This is the underlying Writer we are sending output + * to + */ + protected Writer out; + + /** + * This method intializes a new PrintWriter object to write + * to the specified output sink. The form of the constructor does not + * enable auto-flush functionality. + * + * @param wr The Writer to write to. + */ + public PrintWriter(Writer wr) + { + super(wr.lock); + this.out = wr; + } + + /** + * This method intializes a new PrintWriter object to write + * to the specified output sink. This constructor also allows "auto-flush" + * functionality to be specified where the stream will be flushed after + * every line is terminated or newline character is written. + * + * @param wr The Writer to write to. + * @param autoflush true to flush the stream after every + * line, false otherwise + */ + public PrintWriter(Writer wr, boolean autoflush) + { + super(wr.lock); + this.out = wr; + this.autoflush = autoflush; + } + + /** + * This method initializes a new PrintWriter object to write + * to the specified OutputStream. Characters will be converted + * to chars using the system default encoding. Auto-flush functionality + * will not be enabled. + * + * @param out The OutputStream to write to + */ + public PrintWriter(OutputStream out) + { + super(); + this.out = new OutputStreamWriter(out); + this.lock = this.out; + } + + /** + * This method initializes a new PrintWriter object to write + * to the specified OutputStream. Characters will be converted + * to chars using the system default encoding. This form of the + * constructor allows auto-flush functionality to be enabled if desired + * + * @param out The OutputStream to write to + * @param autoflush true to flush the stream after every + * println call, false otherwise. + */ + public PrintWriter(OutputStream out, boolean autoflush) + { + this(out); + this.autoflush = autoflush; + } + + /** + * This initializes a new PrintWriter object to write to the specified + * file. It creates a FileOutputStream object and wraps it in an + * OutputStreamWriter using the default encoding. + * @param file name of the file to write to + * @throws FileNotFoundException if the file cannot be written or created + * + * @since 1.5 + */ + public PrintWriter(String file) throws FileNotFoundException + { + this(new FileOutputStream(file)); + } + + /** + * This initializes a new PrintWriter object to write to the specified + * file. It creates a FileOutputStream object and wraps it in an + * OutputStreamWriter using the specified encoding. + * @param file name of the file to write to + * @param enc the encoding to use + * @throws FileNotFoundException if the file cannot be written or created + * @throws UnsupportedEncodingException if the encoding is not supported + * + * @since 1.5 + */ + public PrintWriter(String file, String enc) + throws FileNotFoundException, UnsupportedEncodingException + { + this(new OutputStreamWriter(new FileOutputStream(file), enc)); + } + + /** + * This initializes a new PrintWriter object to write to the specified + * file. It creates a FileOutputStream object and wraps it in an + * OutputStreamWriter using the default encoding. + * @param file the file to write to + * @throws FileNotFoundException if the file cannot be written or created + * + * @since 1.5 + */ + public PrintWriter(File file) throws FileNotFoundException + { + this(new FileOutputStream(file)); + } + + /** + * This initializes a new PrintWriter object to write to the specified + * file. It creates a FileOutputStream object and wraps it in an + * OutputStreamWriter using the specified encoding. + * @param file the file to write to + * @param enc the encoding to use + * @throws FileNotFoundException if the file cannot be written or created + * @throws UnsupportedEncodingException if the encoding is not supported + * + * @since 1.5 + */ + public PrintWriter(File file, String enc) + throws FileNotFoundException, UnsupportedEncodingException + { + this(new OutputStreamWriter(new FileOutputStream(file), enc)); + } + + /** + * This method can be called by subclasses to indicate that an error + * has occurred and should be reported by checkError. + */ + protected void setError() + { + error = true; + } + + /** + * This method checks to see if an error has occurred on this stream. Note + * that once an error has occurred, this method will continue to report + * true forever for this stream. Before checking for an + * error condition, this method flushes the stream. + * + * @return true if an error has occurred, + * false otherwise + */ + public boolean checkError() + { + if (! closed) + flush(); + return error; + } + + /** + * This method flushes any buffered chars to the underlying stream and + * then flushes that stream as well. + */ + public void flush() + { + try + { + out.flush(); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method closes this stream and all underlying streams. + */ + public void close() + { + try + { + out.close(); + closed = true; + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method prints a String to the stream. The actual + * value printed depends on the system default encoding. + * + * @param str The String to print. + */ + public void print(String str) + { + write(str == null ? "null" : str); + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * @param ch The char value to be printed + */ + public void print(char ch) + { + write((int) ch); + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * @param charArray The array of characters to print. + */ + public void print(char[] charArray) + { + write(charArray, 0, charArray.length); + } + + /** + * This methods prints a boolean value to the stream. true + * values are printed as "true" and false values are printed + * as "false". + * + * @param bool The boolean value to print + */ + public void print(boolean bool) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write (bool ? "true" : "false"); + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the String.valueOf() method. + * + * @param inum The int value to be printed + */ + public void print(int inum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Integer.toString(inum)); + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the String.valueOf() method. + * + * @param lnum The long value to be printed + */ + public void print(long lnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Long.toString(lnum)); + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the String.valueOf() method. + * + * @param fnum The float value to be printed + */ + public void print(float fnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Float.toString(fnum)); + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the String.valueOf() method. + * + * @param dnum The double value to be printed + */ + public void print(double dnum) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(Double.toString(dnum)); + } + + /** + * This method prints an Object to the stream. The actual + * value printed is determined by calling the String.valueOf() + * method. + * + * @param obj The Object to print. + */ + public void print(Object obj) + { + // We purposely call write() and not print() here. This preserves + // compatibility with JDK 1.2. + write(obj == null ? "null" : obj.toString()); + } + + /** + * This is the system dependent line separator + */ + private static final char[] line_separator + = System.getProperty("line.separator", "\n").toCharArray(); + + /** + * This method prints a line separator sequence to the stream. The value + * printed is determined by the system property

    line.separator + * and is not necessarily the Unix '\n' newline character. + */ + public void println() + { + synchronized (lock) + { + try + { + write(line_separator, 0, line_separator.length); + if (autoflush) + out.flush(); + } + catch (IOException ex) + { + error = true; + } + } + } + + /** + * This methods prints a boolean value to the stream. true + * values are printed as "true" and false values are printed + * as "false". + * + * This method prints a line termination sequence after printing the value. + * + * @param bool The boolean value to print + */ + public void println(boolean bool) + { + synchronized (lock) + { + print(bool); + println(); + } + } + + /** + * This method prints an integer to the stream. The value printed is + * determined using the String.valueOf() method. + * + * This method prints a line termination sequence after printing the value. + * + * @param inum The int value to be printed + */ + public void println(int inum) + { + synchronized (lock) + { + print(inum); + println(); + } + } + + /** + * This method prints a long to the stream. The value printed is + * determined using the String.valueOf() method. + * + * This method prints a line termination sequence after printing the value. + * + * @param lnum The long value to be printed + */ + public void println(long lnum) + { + synchronized (lock) + { + print(lnum); + println(); + } + } + + /** + * This method prints a float to the stream. The value printed is + * determined using the String.valueOf() method. + * + * This method prints a line termination sequence after printing the value. + * + * @param fnum The float value to be printed + */ + public void println(float fnum) + { + synchronized (lock) + { + print(fnum); + println(); + } + } + + /** + * This method prints a double to the stream. The value printed is + * determined using the String.valueOf() method. + * + * This method prints a line termination sequence after printing the value. + * + * @param dnum The double value to be printed + */ + public void println(double dnum) + { + synchronized (lock) + { + print(dnum); + println(); + } + } + + /** + * This method prints an Object to the stream. The actual + * value printed is determined by calling the String.valueOf() + * method. + * + * This method prints a line termination sequence after printing the value. + * + * @param obj The Object to print. + */ + public void println(Object obj) + { + synchronized (lock) + { + print(obj); + println(); + } + } + + /** + * This method prints a String to the stream. The actual + * value printed depends on the system default encoding. + * + * This method prints a line termination sequence after printing the value. + * + * @param str The String to print. + */ + public void println(String str) + { + synchronized (lock) + { + print(str); + println(); + } + } + + /** + * This method prints a char to the stream. The actual value printed is + * determined by the character encoding in use. + * + * This method prints a line termination sequence after printing the value. + * + * @param ch The char value to be printed + */ + public void println(char ch) + { + synchronized (lock) + { + print(ch); + println(); + } + } + + /** + * This method prints an array of characters to the stream. The actual + * value printed depends on the system default encoding. + * + * This method prints a line termination sequence after printing the value. + * + * @param charArray The array of characters to print. + */ + public void println(char[] charArray) + { + synchronized (lock) + { + print(charArray); + println(); + } + } + + /** + * This method writes a single char to the stream. + * + * @param ch The char to be written, passed as a int + */ + public void write(int ch) + { + try + { + out.write(ch); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method writes count chars from the specified array + * starting at index offset into the array. + * + * @param charArray The array of chars to write + * @param offset The index into the array to start writing from + * @param count The number of chars to write + */ + public void write(char[] charArray, int offset, int count) + { + try + { + out.write(charArray, offset, count); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method writes count chars from the specified + * String to the output starting at character position + * offset into the String + * + * @param str The String to write chars from + * @param offset The offset into the String to start writing from + * @param count The number of chars to write. + */ + public void write(String str, int offset, int count) + { + try + { + out.write(str, offset, count); + } + catch (IOException ex) + { + error = true; + } + } + + /** + * This method write all the chars in the specified array to the output. + * + * @param charArray The array of characters to write + */ + public void write(char[] charArray) + { + write(charArray, 0, charArray.length); + } + + /** + * This method writes the contents of the specified String + * to the underlying stream. + * + * @param str The String to write + */ + public void write(String str) + { + write(str, 0, str.length()); + } + + /** @since 1.5 */ + public PrintWriter append(char c) + { + write(c); + return this; + } + + /** @since 1.5 */ + public PrintWriter append(CharSequence cs) + { + write(cs == null ? "null" : cs.toString()); + return this; + } + + /** @since 1.5 */ + public PrintWriter append(CharSequence cs, int start, int end) + { + write(cs == null ? "null" : cs.subSequence(start, end).toString()); + return this; + } + + /** @since 1.5 */ + public PrintWriter printf(String format, Object... args) + { + return format(format, args); + } + + /** @since 1.5 */ + public PrintWriter printf(Locale locale, String format, Object... args) + { + return format(locale, format, args); + } + + /** @since 1.5 */ + public PrintWriter format(String format, Object... args) + { + return format(Locale.getDefault(), format, args); + } + + /** @since 1.5 */ + public PrintWriter format(Locale locale, String format, Object... args) + { + Formatter f = new Formatter(this, locale); + f.format(format, args); + return this; + } +} diff --git a/libjava/classpath/java/io/PushbackInputStream.java b/libjava/classpath/java/io/PushbackInputStream.java new file mode 100644 index 000000000..62737870c --- /dev/null +++ b/libjava/classpath/java/io/PushbackInputStream.java @@ -0,0 +1,335 @@ +/* PushbackInputStream.java -- An input stream that can unread bytes + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/** + * This subclass of FilterInputStream provides the ability to + * unread data from a stream. It maintains an internal buffer of unread + * data that is supplied to the next read operation. This is conceptually + * similar to mark/reset functionality, except that in this case the + * position to reset the stream to does not need to be known in advance. + *

    + * The default pushback buffer size one byte, but this can be overridden + * by the creator of the stream. + *

    + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class PushbackInputStream extends FilterInputStream +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 1; + + /** + * This is the buffer that is used to store the pushed back data + */ + protected byte[] buf; + + /** + * This is the position in the buffer from which the next byte will be + * read. Bytes are stored in reverse order in the buffer, starting from + * buf[buf.length - 1] to buf[0]. Thus when + * pos is 0 the buffer is full and buf.length when + * it is empty + */ + protected int pos; + + /** + * This method initializes a PushbackInputStream to + * read from the specified subordinate InputStream + * with a default pushback buffer size of 1. + * + * @param in The subordinate stream to read from + */ + public PushbackInputStream(InputStream in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a PushbackInputStream to + * read from the specified subordinate InputStream with + * the specified buffer size + * + * @param in The subordinate InputStream to read from + * @param size The pushback buffer size to use + */ + public PushbackInputStream(InputStream in, int size) + { + super(in); + if (size < 0) + throw new IllegalArgumentException(); + buf = new byte[size]; + pos = buf.length; + } + + /** + * This method returns the number of bytes that can be read from this + * stream before a read can block. A return of 0 indicates that blocking + * might (or might not) occur on the very next read attempt. + *

    + * This method will return the number of bytes available from the + * pushback buffer plus the number of bytes available from the + * underlying stream. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + try + { + return (buf.length - pos) + super.available(); + } + catch (NullPointerException npe) + { + throw new IOException ("Stream closed"); + } + } + + /** + * This method closes the stream and releases any associated resources. + * + * @exception IOException If an error occurs. + */ + public synchronized void close() throws IOException + { + buf = null; + super.close(); + } + + /** + * This method returns false to indicate that it does + * not support mark/reset functionality. + * + * @return This method returns false to indicate that + * this class does not support mark/reset functionality + */ + public boolean markSupported() + { + return false; + } + + /** + * This method always throws an IOException in this class because + * mark/reset functionality is not supported. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("Mark not supported in this class"); + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. The byte returned will be read + * from the pushback buffer, unless the buffer is empty, in which case + * the byte will be read from the underlying stream. + *

    + * This method will block until the byte can be read. + * + * @return The byte read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public synchronized int read() throws IOException + { + if (pos < buf.length) + return ((int) buf[pos++]) & 0xFF; + + return super.read(); + } + + /** + * This method read bytes from a stream and stores them into a + * caller supplied buffer. It starts storing the data at index + * offset into the buffer and attempts to read + * len bytes. This method can return before reading the + * number of bytes requested. The actual number of bytes read is + * returned as an int. A -1 is returned to indicate the end of the + * stream. + *

    + * This method will block until some data can be read. + *

    + * This method first reads bytes from the pushback buffer in order to + * satisfy the read request. If the pushback buffer cannot provide all + * of the bytes requested, the remaining bytes are read from the + * underlying stream. + * + * @param b The array into which the bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public synchronized int read(byte[] b, int off, int len) throws IOException + { + int numBytes = Math.min(buf.length - pos, len); + + if (numBytes > 0) + { + System.arraycopy (buf, pos, b, off, numBytes); + pos += numBytes; + len -= numBytes; + off += numBytes; + } + + if (len > 0) + { + len = super.read(b, off, len); + if (len == -1) //EOF + return numBytes > 0 ? numBytes : -1; + numBytes += len; + } + return numBytes; + } + + /** + * This method pushes a single byte of data into the pushback buffer. + * The byte pushed back is the one that will be returned as the first byte + * of the next read. + *

    + * If the pushback buffer is full, this method throws an exception. + *

    + * The argument to this method is an int. Only the low + * eight bits of this value are pushed back. + * + * @param b The byte to be pushed back, passed as an int + * + * @exception IOException If the pushback buffer is full. + */ + public synchronized void unread(int b) throws IOException + { + if (pos <= 0) + throw new IOException("Insufficient space in pushback buffer"); + + buf[--pos] = (byte) b; + } + + /** + * This method pushes all of the bytes in the passed byte array into + * the pushback bfer. These bytes are pushed in reverse order so that + * the next byte read from the stream after this operation will be + * b[0] followed by b[1], etc. + *

    + * If the pushback buffer cannot hold all of the requested bytes, an + * exception is thrown. + * + * @param b The byte array to be pushed back + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(byte[] b) throws IOException + { + unread(b, 0, b.length); + } + + /** + * This method pushed back bytes from the passed in array into the + * pushback buffer. The bytes from b[offset] to + * b[offset + len] are pushed in reverse order so that + * the next byte read from the stream after this operation will be + * b[offset] followed by b[offset + 1], + * etc. + *

    + * If the pushback buffer cannot hold all of the requested bytes, an + * exception is thrown. + * + * @param b The byte array to be pushed back + * @param off The index into the array where the bytes to be push start + * @param len The number of bytes to be pushed. + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(byte[] b, int off, int len) + throws IOException + { + if (pos < len) + throw new IOException("Insufficient space in pushback buffer"); + + // Note the order that these bytes are being added is the opposite + // of what would be done if they were added to the buffer one at a time. + // See the Java Class Libraries book p. 1390. + System.arraycopy(b, off, buf, pos - len, len); + + // Don't put this into the arraycopy above, an exception might be thrown + // and in that case we don't want to modify pos. + pos -= len; + } + + /** + * This method skips the specified number of bytes in the stream. It + * returns the actual number of bytes skipped, which may be less than the + * requested amount. + *

    + * This method first discards bytes from the buffer, then calls the + * skip method on the underlying InputStream to + * skip additional bytes if necessary. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public synchronized long skip(long n) throws IOException + { + final long origN = n; + + if (n > 0L) + { + int numread = (int) Math.min((long) (buf.length - pos), n); + pos += numread; + n -= numread; + if (n > 0) + n -= super.skip(n); + } + + return origN - n; + } +} diff --git a/libjava/classpath/java/io/PushbackReader.java b/libjava/classpath/java/io/PushbackReader.java new file mode 100644 index 000000000..43bf826a8 --- /dev/null +++ b/libjava/classpath/java/io/PushbackReader.java @@ -0,0 +1,383 @@ +/* PushbackReader.java -- An character stream that can unread chars + Copyright (C) 1998, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This subclass of FilterReader provides the ability to + * unread data from a stream. It maintains an internal buffer of unread + * data that is supplied to the next read operation. This is conceptually + * similar to mark/reset functionality, except that in this case the + * position to reset the stream to does not need to be known in advance. + *

    + * The default pushback buffer size one char, but this can be overridden + * by the creator of the stream. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class PushbackReader extends FilterReader +{ + /** + * This is the default buffer size + */ + private static final int DEFAULT_BUFFER_SIZE = 1; + + /** + * This is the buffer that is used to store the pushed back data + */ + private char[] buf; + + /** + * This is the position in the buffer from which the next char will be + * read. Bytes are stored in reverse order in the buffer, starting from + * buf[buf.length - 1] to buf[0]. Thus when + * pos is 0 the buffer is full and buf.length when + * it is empty + */ + private int pos; + + /** + * This method initializes a PushbackReader to read from the + * specified subordinate Reader with a default pushback buffer + * size of 1. + * + * @param in The subordinate stream to read from + */ + public PushbackReader(Reader in) + { + this(in, DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a PushbackReader to read from the + * specified subordinate Reader with the specified buffer + * size + * + * @param in The subordinate Reader to read from + * @param bufsize The pushback buffer size to use + */ + public PushbackReader(Reader in, int bufsize) + { + super(in); + + if (bufsize < 0) + throw new IllegalArgumentException("buffer size must be positive"); + + buf = new char[bufsize]; + pos = bufsize; + } + + /** + * This method closes the stream and frees any associated resources. + * + * @exception IOException If an error occurs. + */ + public void close() throws IOException + { + synchronized (lock) + { + buf = null; + super.close(); + } + } + + /** + * This method throws an exception when called since this class does + * not support mark/reset. + * + * @param read_limit Not used. + * + * @exception IOException Always thrown to indicate mark/reset not supported. + */ + public void mark(int read_limit) throws IOException + { + throw new IOException("mark not supported in this class"); + } + + /** + * This method returns false to indicate that it does not support + * mark/reset functionality. + * + * @return This method returns false to indicate that this + * class does not support mark/reset functionality + * + */ + public boolean markSupported() + { + return(false); + } + + /** + * This method always throws an IOException in this class because + * mark/reset functionality is not supported. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("reset not supported in this class"); + } + + /** + * This method determines whether or not this stream is ready to be read. + * If it returns false to indicate that the stream is not + * ready, any attempt to read from the stream could (but is not + * guaranteed to) block. + *

    + * This stream is ready to read if there are either chars waiting to be + * read in the pushback buffer or if the underlying stream is ready to + * be read. + * + * @return true if this stream is ready to be read, + * false otherwise + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException ("stream closed"); + + if (((buf.length - pos) > 0) || super.ready()) + return(true); + else + return(false); + } + } + + // Don't delete this method just because the spec says it shouldn't be there! + // See the CVS log for details. + /** + * This method skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + *

    + * This method first discards chars from the buffer, then calls the + * skip method on the underlying Reader to + * skip additional chars if necessary. + * + * @param num_chars The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long num_chars) throws IOException + { + synchronized (lock) + { + if (num_chars <= 0) + return(0); + + if ((buf.length - pos) >= num_chars) + { + pos += num_chars; + return(num_chars); + } + + int chars_discarded = buf.length - pos; + pos = buf.length; + + long chars_skipped = in.skip(num_chars - chars_discarded); + + return(chars_discarded + chars_skipped); + } + } + + /** + * This method reads an unsigned char from the input stream and returns it + * as an int in the range of 0-65535. This method also will return -1 if + * the end of the stream has been reached. The char returned will be read + * from the pushback buffer, unless the buffer is empty, in which case + * the char will be read from the underlying stream. + *

    + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + + if (pos == buf.length) + return(super.read()); + + ++pos; + return((buf[pos - 1] & 0xFFFF)); + } + } + + /** + * This method read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index offset + * into + * the buffer and attempts to read len chars. This method can + * return before reading the number of chars requested. The actual number + * of chars read is returned as an int. A -1 is returned to indicate the + * end of the stream. + *

    + * This method will block until some data can be read. + *

    + * This method first reads chars from the pushback buffer in order to + * satisfy the read request. If the pushback buffer cannot provide all + * of the chars requested, the remaining chars are read from the + * underlying stream. + * + * @param buffer The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param length The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public synchronized int read(char[] buffer, int offset, int length) + throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + + if (offset < 0 || length < 0 || offset + length > buffer.length) + throw new ArrayIndexOutOfBoundsException(); + + int numBytes = Math.min(buf.length - pos, length); + if (numBytes > 0) + { + System.arraycopy (buf, pos, buffer, offset, numBytes); + pos += numBytes; + return numBytes; + } + + return super.read(buffer, offset, length); + } + } + + /** + * This method pushes a single char of data into the pushback buffer. + * The char pushed back is the one that will be returned as the first char + * of the next read. + *

    + * If the pushback buffer is full, this method throws an exception. + *

    + * The argument to this method is an int. Only the low eight + * bits of this value are pushed back. + * + * @param b The char to be pushed back, passed as an int + * + * @exception IOException If the pushback buffer is full. + */ + public void unread(int b) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + if (pos == 0) + throw new IOException("Pushback buffer is full"); + + --pos; + buf[pos] = (char)(b & 0xFFFF); + } + } + + /** + * This method pushes all of the chars in the passed char array into + * the pushback buffer. These chars are pushed in reverse order so that + * the next char read from the stream after this operation will be + * buf[0] followed by buf[1], etc. + *

    + * If the pushback buffer cannot hold all of the requested chars, an + * exception is thrown. + * + * @param buf The char array to be pushed back + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(char[] buf) throws IOException + { + unread(buf, 0, buf.length); + } + + /** + * This method pushed back chars from the passed in array into the pushback + * buffer. The chars from buf[offset] to + * buf[offset + len] + * are pushed in reverse order so that the next char read from the stream + * after this operation will be buf[offset] followed by + * buf[offset + 1], etc. + *

    + * If the pushback buffer cannot hold all of the requested chars, an + * exception is thrown. + * + * @param buffer The char array to be pushed back + * @param offset The index into the array where the chars to be push start + * @param length The number of chars to be pushed. + * + * @exception IOException If the pushback buffer is full + */ + public synchronized void unread(char[] buffer, int offset, int length) + throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("stream closed"); + if (pos < length) + throw new IOException("Pushback buffer is full"); + + // Note the order that these chars are being added is the opposite + // of what would be done if they were added to the buffer one at a time. + // See the Java Class Libraries book p. 1397. + System.arraycopy(buffer, offset, buf, pos - length, length); + + // Don't put this into the arraycopy above, an exception might be thrown + // and in that case we don't want to modify pos. + pos -= length; + } + } +} diff --git a/libjava/classpath/java/io/RandomAccessFile.java b/libjava/classpath/java/io/RandomAccessFile.java new file mode 100644 index 000000000..da0c81272 --- /dev/null +++ b/libjava/classpath/java/io/RandomAccessFile.java @@ -0,0 +1,1049 @@ +/* RandomAccessFile.java -- Class supporting random file I/O + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.java.nio.FileChannelImpl; + +import java.nio.channels.FileChannel; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * Status: Believe complete and correct to 1.1. + */ + +/** + * This class allows reading and writing of files at random locations. + * Most Java I/O classes are either pure sequential input or output. This + * class fulfills the need to be able to read the bytes of a file in an + * arbitrary order. In addition, this class implements the + * DataInput and DataOutput interfaces to allow + * the reading and writing of Java primitives. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class RandomAccessFile implements DataOutput, DataInput, Closeable +{ + + // The underlying file. + private FileChannelImpl ch; + private FileDescriptor fd; + // The corresponding input and output streams. + private DataOutputStream out; + private DataInputStream in; + + + /** + * This method initializes a new instance of RandomAccessFile + * to read from the specified File object with the specified + * access mode. The access mode is either "r" for read only access or "rw" + * for read-write access. + *

    + * Note that a SecurityManager check is made prior to + * opening the file to determine whether or not this file is allowed to + * be read or written. + * + * @param file The File object to read and/or write. + * @param mode "r" for read only or "rw" for read-write access to the file + * + * @exception IllegalArgumentException If mode has an + * illegal value + * @exception SecurityException If the requested access to the file + * is not allowed + * @exception FileNotFoundException If the file is a directory, or + * any other error occurs + */ + public RandomAccessFile (File file, String mode) + throws FileNotFoundException + { + int fdmode; + if (mode.equals("r")) + fdmode = FileChannelImpl.READ; + else if (mode.equals("rw")) + fdmode = FileChannelImpl.READ | FileChannelImpl.WRITE; + else if (mode.equals("rws")) + { + fdmode = (FileChannelImpl.READ | FileChannelImpl.WRITE + | FileChannelImpl.SYNC); + } + else if (mode.equals("rwd")) + { + fdmode = (FileChannelImpl.READ | FileChannelImpl.WRITE + | FileChannelImpl.DSYNC); + } + else + throw new IllegalArgumentException ("invalid mode: " + mode); + + final String fileName = file.getPath(); + + // The obligatory SecurityManager stuff + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + s.checkRead(fileName); + + if ((fdmode & FileChannelImpl.WRITE) != 0) + s.checkWrite(fileName); + } + + try + { + ch = FileChannelImpl.create(file, fdmode); + } + catch (FileNotFoundException fnfe) + { + throw fnfe; + } + catch (IOException ioe) + { + FileNotFoundException fnfe = new FileNotFoundException(file.getPath()); + fnfe.initCause(ioe); + throw fnfe; + } + fd = new FileDescriptor(ch); + if ((fdmode & FileChannelImpl.WRITE) != 0) + out = new DataOutputStream (new FileOutputStream (fd)); + else + out = null; + in = new DataInputStream (new FileInputStream (fd)); + } + + /** + * This method initializes a new instance of RandomAccessFile + * to read from the specified file name with the specified access mode. + * The access mode is either "r" for read only access, "rw" for read + * write access, "rws" for synchronized read/write access of both + * content and metadata, or "rwd" for read/write access + * where only content is required to be synchronous. + *

    + * Note that a SecurityManager check is made prior to + * opening the file to determine whether or not this file is allowed to + * be read or written. + * + * @param fileName The name of the file to read and/or write + * @param mode "r", "rw", "rws", or "rwd" + * + * @exception IllegalArgumentException If mode has an + * illegal value + * @exception SecurityException If the requested access to the file + * is not allowed + * @exception FileNotFoundException If the file is a directory or + * any other error occurs + */ + public RandomAccessFile (String fileName, String mode) + throws FileNotFoundException + { + this (new File(fileName), mode); + } + + /** + * This method closes the file and frees up all file related system + * resources. Since most operating systems put a limit on how many files + * may be opened at any given time, it is a good idea to close all files + * when no longer needed to avoid hitting this limit + */ + public void close () throws IOException + { + ch.close(); + } + + /** + * This method returns a FileDescriptor object that + * represents the native file handle for this file. + * + * @return The FileDescriptor object for this file + * + * @exception IOException If an error occurs + */ + public final FileDescriptor getFD () throws IOException + { + synchronized (this) + { + if (fd == null) + fd = new FileDescriptor (ch); + return fd; + } + } + + /** + * This method returns the current offset in the file at which the next + * read or write will occur + * + * @return The current file position + * + * @exception IOException If an error occurs + */ + public long getFilePointer () throws IOException + { + return ch.position(); + } + + /** + * This method sets the length of the file to the specified length. + * If the currently length of the file is longer than the specified + * length, then the file is truncated to the specified length (the + * file position is set to the end of file in this case). If the + * current length of the file is shorter than the specified length, + * the file is extended with bytes of an undefined value (the file + * position is unchanged in this case). + *

    + * The file must be open for write access for this operation to succeed. + * + * @param newLen The new length of the file + * + * @exception IOException If an error occurs + */ + public void setLength (long newLen) throws IOException + { + // FIXME: Extending a file should probably be done by one method call. + + // FileChannel.truncate() can only shrink a file. + // To expand it we need to seek forward and write at least one byte. + if (newLen < length()) + ch.truncate (newLen); + else if (newLen > length()) + { + long pos = getFilePointer(); + seek(newLen - 1); + write(0); + seek(pos); + } + } + + /** + * This method returns the length of the file in bytes + * + * @return The length of the file + * + * @exception IOException If an error occurs + */ + public long length () throws IOException + { + return ch.size(); + } + + /** + * This method reads a single byte of data from the file and returns it + * as an integer. + * + * @return The byte read as an int, or -1 if the end of the file was reached. + * + * @exception IOException If an error occurs + */ + public int read () throws IOException + { + return in.read(); + } + + /** + * This method reads bytes from the file into the specified array. The + * bytes are stored starting at the beginning of the array and up to + * buf.length bytes can be read. + * + * @param buffer The buffer to read bytes from the file into + * + * @return The actual number of bytes read or -1 if end of file + * + * @exception IOException If an error occurs + */ + public int read (byte[] buffer) throws IOException + { + return in.read (buffer); + } + + /** + * This methods reads up to len bytes from the file into the + * specified array starting at position offset into the array. + * + * @param buffer The array to read the bytes into + * @param offset The index into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of file + * + * @exception IOException If an error occurs + */ + public int read (byte[] buffer, int offset, int len) throws IOException + { + return in.read (buffer, offset, len); + } + + /** + * This method reads a Java boolean value from an input stream. It does + * so by reading a single byte of data. If that byte is zero, then the + * value returned is false If the byte is non-zero, then + * the value returned is true + *

    + * This method can read a boolean written by an object + * implementing the + * writeBoolean() method in the DataOutput + * interface. + * + * @return The boolean value read + * + * @exception EOFException If end of file is reached before reading the + * boolean + * @exception IOException If any other error occurs + */ + public final boolean readBoolean () throws IOException + { + return in.readBoolean (); + } + + /** + * This method reads a Java byte value from an input stream. The value + * is in the range of -128 to 127. + *

    + * This method can read a byte written by an object + * implementing the + * writeByte() method in the DataOutput interface. + * + * @return The byte value read + * + * @exception EOFException If end of file is reached before reading the byte + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final byte readByte () throws IOException + { + return in.readByte (); + } + + /** + * This method reads a Java char value from an input stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java char The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 and byte2 represent + * the first + * and second byte read from the stream respectively, they will be + * transformed to a char in the following manner: + *

    + * (char)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF) + *

    + * This method can read a char written by an object + * implementing the + * writeChar() method in the DataOutput interface. + * + * @return The char value read + * + * @exception EOFException If end of file is reached before reading the char + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final char readChar () throws IOException + { + return in.readChar(); + } + + /** + * This method reads a Java double value from an input stream. It operates + * by first reading a logn value from the stream by calling the + * readLong() method in this interface, then + * converts that long + * to a double using the longBitsToDouble + * method in the class java.lang.Double + *

    + * This method can read a double written by an object + * implementing the + * writeDouble() method in the DataOutput + * interface. + * + * @return The double value read + * + * @exception EOFException If end of file is reached before reading + * the double + * @exception IOException If any other error occurs + * + * @see java.lang.Double + * @see DataOutput + */ + public final double readDouble () throws IOException + { + return in.readDouble (); + } + + /** + * This method reads a Java float value from an input stream. It operates + * by first reading an int value from the stream by calling the + * readInt() method in this interface, then converts + * that int + * to a float using the intBitsToFloat method in + * the class java.lang.Float + *

    + * This method can read a float written by an object + * implementing the + * writeFloat() method in the DataOutput interface. + * + * @return The float value read + * + * @exception EOFException If end of file is reached before reading the float + * @exception IOException If any other error occurs + * + * @see java.lang.Float + * @see DataOutput + */ + public final float readFloat () throws IOException + { + return in.readFloat(); + } + + /** + * This method reads raw bytes into the passed array until the array is + * full. Note that this method blocks until the data is available and + * throws an exception if there is not enough data left in the stream to + * fill the buffer + * + * @param buffer The buffer into which to read the data + * + * @exception EOFException If end of file is reached before filling the + * buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buffer) throws IOException + { + in.readFully(buffer); + } + + /** + * This method reads raw bytes into the passed array buf + * starting + * offset bytes into the buffer. The number of bytes read + * will be + * exactly len Note that this method blocks until the data is + * available and throws an exception if there is not enough data left in + * the stream to read len bytes. + * + * @param buffer The buffer into which to read the data + * @param offset The offset into the buffer to start storing data + * @param count The number of bytes to read into the buffer + * + * @exception EOFException If end of file is reached before filling + * the buffer + * @exception IOException If any other error occurs + */ + public final void readFully (byte[] buffer, int offset, int count) + throws IOException + { + in.readFully (buffer, offset, count); + } + + /** + * This method reads a Java int value from an input stream + * It operates by reading four bytes from the stream and converting them to + * a single Java int The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 through byte4 + * represent the first + * four bytes read from the stream, they will be + * transformed to an int in the following manner: + *

    + * (int)(((byte1 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) + + * ((byte3 & 0xFF) << 8) + (byte4 & 0xFF))) + *

    + * The value returned is in the range of 0 to 65535. + *

    + * This method can read an int written by an object + * implementing the + * writeInt() method in the DataOutput interface. + * + * @return The int value read + * + * @exception EOFException If end of file is reached before reading the int + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final int readInt () throws IOException + { + return in.readInt(); + } + + /** + * This method reads the next line of text data from an input stream. + * It operates by reading bytes and converting those bytes to + * char + * values by treating the byte read as the low eight bits of the + * char + * and using 0 as the high eight bits. Because of this, it does + * not support the full 16-bit Unicode character set. + *

    + * The reading of bytes ends when either the end of file or a line terminator + * is encountered. The bytes read are then returned as a String + * A line terminator is a byte sequence consisting of either + * \r \n or \r\n These + * termination charaters are + * discarded and are not returned as part of the string. + *

    + * This method can read data that was written by an object implementing the + * writeLine() method in DataOutput + * + * @return The line read as a String + * + * @exception IOException If an error occurs + * + * @see DataOutput + */ + public final String readLine () throws IOException + { + return in.readLine (); + } + + /** + * This method reads a Java long value from an input stream + * It operates by reading eight bytes from the stream and converting them to + * a single Java long The bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 through byte8 + * represent the first + * eight bytes read from the stream, they will be + * transformed to an long in the following manner: + *

    + * + * (long)((((long)byte1 & 0xFF) << 56) + (((long)byte2 & 0xFF) << 48) + + * (((long)byte3 & 0xFF) << 40) + (((long)byte4 & 0xFF) << 32) + + * (((long)byte5 & 0xFF) << 24) + (((long)byte6 & 0xFF) << 16) + + * (((long)byte7 & 0xFF) << 8) + ((long)byte9 & 0xFF))) + *

    + * The value returned is in the range of 0 to 65535. + *

    + * This method can read an long written by an object + * implementing the + * writeLong() method in the DataOutput interface. + * + * @return The long value read + * + * @exception EOFException If end of file is reached before reading the long + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final long readLong () throws IOException + { + return in.readLong(); + } + + /** + * This method reads a signed 16-bit value into a Java in from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single 16-bit Java short The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 and byte2 + * represent the first + * and second byte read from the stream respectively, they will be + * transformed to a short in the following manner: + *

    + * (short)(((byte1 & 0xFF) << 8) | (byte2 & 0xFF) + *

    + * The value returned is in the range of -32768 to 32767. + *

    + * This method can read a short written by an object + * implementing the + * writeShort() method in the DataOutput interface. + * + * @return The short value read + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final short readShort () throws IOException + { + return in.readShort(); + } + + /** + * This method reads 8 unsigned bits into a Java int value + * from the + * stream. The value returned is in the range of 0 to 255. + *

    + * This method can read an unsigned byte written by an object implementing + * the writeUnsignedByte() method in the + * DataOutput interface. + * + * @return The unsigned bytes value read as a Java int + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final int readUnsignedByte () throws IOException + { + return in.readUnsignedByte(); + } + + /** + * This method reads 16 unsigned bits into a Java int value from the stream. + * It operates by reading two bytes from the stream and converting them to + * a single Java int The two bytes are stored most + * significant byte first (i.e., "big endian") regardless of the native + * host byte ordering. + *

    + * As an example, if byte1 and byte2 + * represent the first + * and second byte read from the stream respectively, they will be + * transformed to an int in the following manner: + *

    + * (int)(((byte1 & 0xFF) << 8) + (byte2 & 0xFF)) + *

    + * The value returned is in the range of 0 to 65535. + *

    + * This method can read an unsigned short written by an object implementing + * the writeUnsignedShort() method in the + * DataOutput interface. + * + * @return The unsigned short value read as a Java int + * + * @exception EOFException If end of file is reached before reading the value + * @exception IOException If any other error occurs + */ + public final int readUnsignedShort () throws IOException + { + return in.readUnsignedShort(); + } + + /** + * This method reads a String from an input stream that + * is encoded in + * a modified UTF-8 format. This format has a leading two byte sequence + * that contains the remaining number of bytes to read. This two byte + * sequence is read using the readUnsignedShort() method of this + * interface. + *

    + * After the number of remaining bytes have been determined, these bytes + * are read an transformed into char values. + * These char values + * are encoded in the stream using either a one, two, or three byte format. + * The particular format in use can be determined by examining the first + * byte read. + *

    + * If the first byte has a high order bit of 0 then + * that character consists on only one byte. This character value consists + * of seven bits that are at positions 0 through 6 of the byte. As an + * example, if byte1 is the byte read from the stream, it would + * be converted to a char like so: + *

    + * (char)byte1 + *

    + * If the first byte has 110 as its high order bits, then the + * character consists of two bytes. The bits that make up the character + * value are in positions 0 through 4 of the first byte and bit positions + * 0 through 5 of the second byte. (The second byte should have + * 10 as its high order bits). These values are in most significant + * byte first (i.e., "big endian") order. + *

    + * As an example, if byte1 and byte2 + * are the first two bytes + * read respectively, and the high order bits of them match the patterns + * which indicate a two byte character encoding, then they would be + * converted to a Java char like so: + *

    + * (char)(((byte1 & 0x1F) << 6) | (byte2 & 0x3F)) + *

    + * If the first byte has a 1110 as its high order bits, then the + * character consists of three bytes. The bits that make up the character + * value are in positions 0 through 3 of the first byte and bit positions + * 0 through 5 of the other two bytes. (The second and third bytes should + * have 10 as their high order bits). These values are in most + * significant byte first (i.e., "big endian") order. + *

    + * As an example, if byte1 byte2 + * and byte3 are the + * three bytes read, and the high order bits of them match the patterns + * which indicate a three byte character encoding, then they would be + * converted to a Java char like so: + *

    + * (char)(((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | + * (byte3 & 0x3F)) + *

    + * Note that all characters are encoded in the method that requires the + * fewest number of bytes with the exception of the character with the + * value of \u0000 which is encoded as two bytes. This is + * a modification of the UTF standard used to prevent C language style + * NUL values from appearing in the byte stream. + *

    + * This method can read data that was written by an object implementing the + * writeUTF() method in DataOutput + * + * @return The String read + * + * @exception EOFException If end of file is reached before reading the + * String + * @exception UTFDataFormatException If the data is not in UTF-8 format + * @exception IOException If any other error occurs + * + * @see DataOutput + */ + public final String readUTF () throws IOException + { + return in.readUTF(); + } + + /** + * This method sets the current file position to the specified offset + * from the beginning of the file. Note that some operating systems will + * allow the file pointer to be set past the current end of the file. + * + * @param pos The offset from the beginning of the file at which to set + * the file pointer + * + * @exception IOException If an error occurs + */ + public void seek (long pos) throws IOException + { + ch.position(pos); + } + + /** + * This method attempts to skip and discard the specified number of bytes + * in the input stream. It may actually skip fewer bytes than requested. + * The actual number of bytes skipped is returned. This method will not + * skip any bytes if passed a negative number of bytes to skip. + * + * @param numBytes The requested number of bytes to skip. + * + * @return The number of bytes actually skipped. + * + * @exception IOException If an error occurs. + */ + public int skipBytes (int numBytes) throws IOException + { + if (numBytes < 0) + throw new IllegalArgumentException ("Can't skip negative bytes: " + + numBytes); + + if (numBytes == 0) + return 0; + + long oldPos = ch.position(); + long newPos = oldPos + numBytes; + long size = ch.size(); + if (newPos > size) + newPos = size; + ch.position(newPos); + return (int) (ch.position() - oldPos); + } + + /** + * This method writes a single byte of data to the file. The file must + * be open for read-write in order for this operation to succeed. + * + * @param oneByte The byte of data to write, passed as an int. + * + * @exception IOException If an error occurs + */ + public void write (int oneByte) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.write(oneByte); + } + + /** + * This method writes all the bytes in the specified array to the file. + * The file must be open read-write in order for this operation to succeed. + * + * @param buffer The array of bytes to write to the file + */ + public void write (byte[] buffer) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.write(buffer); + } + + /** + * This method writes len bytes to the file from the specified + * array starting at index offset into the array. + * + * @param buffer The array of bytes to write to the file + * @param offset The index into the array to start writing file + * @param len The number of bytes to write + * + * @exception IOException If an error occurs + */ + public void write (byte[] buffer, int offset, int len) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.write (buffer, offset, len); + } + + /** + * This method writes a Java boolean to the underlying output + * stream. For a value of true, 1 is written to the stream. + * For a value of false, 0 is written. + * + * @param val The boolean value to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBoolean (boolean val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeBoolean(val); + } + + /** + * This method writes a Java byte value to the underlying + * output stream. + * + * @param val The byte to write to the stream, passed + * as an int. + * + * @exception IOException If an error occurs + */ + public final void writeByte (int val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeByte(val); + } + + /** + * This method writes a Java short to the stream, high byte + * first. This method requires two bytes to encode the value. + * + * @param val The short value to write to the stream, + * passed as an int. + * + * @exception IOException If an error occurs + */ + public final void writeShort (int val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeShort(val); + } + + /** + * This method writes a single char value to the stream, + * high byte first. + * + * @param val The char value to write, passed as + * an int. + * + * @exception IOException If an error occurs + */ + public final void writeChar (int val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeChar(val); + } + + /** + * This method writes a Java int to the stream, high bytes + * first. This method requires four bytes to encode the value. + * + * @param val The int value to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeInt (int val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeInt(val); + } + + /** + * This method writes a Java long to the stream, high bytes + * first. This method requires eight bytes to encode the value. + * + * @param val The long value to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeLong (long val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeLong(val); + } + + /** + * This method writes a Java float value to the stream. This + * value is written by first calling the method + * Float.floatToIntBits + * to retrieve an int representing the floating point number, + * then writing this int value to the stream exactly the same + * as the writeInt() method does. + * + * @param val The floating point number to write to the stream. + * + * @exception IOException If an error occurs + * + * @see #writeInt(int) + */ + public final void writeFloat (float val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeFloat(val); + } + + /** + * This method writes a Java double value to the stream. This + * value is written by first calling the method + * Double.doubleToLongBits + * to retrieve an long representing the floating point number, + * then writing this long value to the stream exactly the same + * as the writeLong() method does. + * + * @param val The double precision floating point number to write to the + * stream. + * + * @exception IOException If an error occurs + * + * @see #writeLong(long) + */ + public final void writeDouble (double val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeDouble(val); + } + + /** + * This method writes all the bytes in a String out to the + * stream. One byte is written for each character in the String. + * The high eight bits of each character are discarded. + * + * @param val The String to write to the stream + * + * @exception IOException If an error occurs + */ + public final void writeBytes (String val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeBytes(val); + } + + /** + * This method writes all the characters in a String to the + * stream. There will be two bytes for each character value. The high + * byte of the character will be written first. + * + * @param val The String to write to the stream. + * + * @exception IOException If an error occurs + */ + public final void writeChars (String val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeChars(val); + } + + /** + * This method writes a Java String to the stream in a modified + * UTF-8 format. First, two bytes are written to the stream indicating the + * number of bytes to follow. Note that this is the number of bytes in the + * encoded String not the String length. Next + * come the encoded characters. Each character in the String + * is encoded as either one, two or three bytes. For characters in the + * range of \u0001 to \u007F, + * one byte is used. The character + * value goes into bits 0-7 and bit eight is 0. For characters in the range + * of \u0080 to \u007FF, two + * bytes are used. Bits + * 6-10 of the character value are encoded bits 0-4 of the first byte, with + * the high bytes having a value of "110". Bits 0-5 of the character value + * are stored in bits 0-5 of the second byte, with the high bits set to + * "10". This type of encoding is also done for the null character + * \u0000. This eliminates any C style NUL character values + * in the output. All remaining characters are stored as three bytes. + * Bits 12-15 of the character value are stored in bits 0-3 of the first + * byte. The high bits of the first bytes are set to "1110". Bits 6-11 + * of the character value are stored in bits 0-5 of the second byte. The + * high bits of the second byte are set to "10". And bits 0-5 of the + * character value are stored in bits 0-5 of byte three, with the high bits + * of that byte set to "10". + * + * @param val The String to write to the output in UTF format + * + * @exception IOException If an error occurs + */ + public final void writeUTF (String val) throws IOException + { + if (out == null) + throw new IOException("Bad file descriptor"); + + out.writeUTF(val); + } + + /** + * This method creates a java.nio.channels.FileChannel. + * Nio does not allow one to create a file channel directly. + * A file channel must be created by first creating an instance of + * Input/Output/RandomAccessFile and invoking the getChannel() method on it. + */ + public final synchronized FileChannel getChannel () + { + return ch; + } +} diff --git a/libjava/classpath/java/io/Reader.java b/libjava/classpath/java/io/Reader.java new file mode 100644 index 000000000..11a12f812 --- /dev/null +++ b/libjava/classpath/java/io/Reader.java @@ -0,0 +1,286 @@ +/* Reader.java -- base class of classes that read input as a stream of chars + Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +import java.nio.CharBuffer; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This abstract class forms the base of the hierarchy of classes that read + * input as a stream of characters. It provides a common set of methods for + * reading characters from streams. Subclasses implement and extend these + * methods to read characters from a particular input source such as a file + * or network connection. + * + * @author Per Bothner (bothner@cygnus.com) + * @date April 21, 1998. + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class Reader implements Closeable, Readable +{ + /** + * This is the Object used for synchronizing critical code + * sections. Subclasses should use this variable instead of a + * synchronized method or an explicit synchronization on this + */ + protected Object lock; + + /** + * Unitializes a Reader that will use the object + * itself for synchronization of critical code sections. + */ + protected Reader() + { + this.lock = this; + } + + /** + * Initializes a Reader that will use the specified + * Object for synchronization of critical code sections. + * + * @param lock The Object to use for synchronization + */ + protected Reader(Object lock) + { + this.lock = lock; + } + + /** + * Read chars from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index offset + * into the buffer and attempts to read len chars. This method + * can return before reading the number of chars requested. The actual + * number of chars read is returned as an int. A -1 is returned to indicate + * the end of the stream. + *

    + * This method will block until some data can be read. + *

    + * This method operates by calling the single char read() method + * in a loop until the desired number of chars are read. The read loop + * stops short if the end of the stream is encountered or if an IOException + * is encountered on any read operation except the first. If the first + * attempt to read a chars fails, the IOException is allowed to propagate + * upward. And subsequent IOException is caught and treated identically + * to an end of stream condition. Subclasses can (and should if possible) + * override this method to provide a more efficient implementation. + * + * @param buf The array into which the chars read should be stored + * @param offset The offset into the array to start storing chars + * @param count The requested number of chars to read + * + * @return The actual number of chars read, or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public abstract int read(char buf[], int offset, int count) + throws IOException; + + /** + * Reads chars from a stream and stores them into a caller + * supplied buffer. This method attempts to completely fill the buffer, + * but can return before doing so. The actual number of chars read is + * returned as an int. A -1 is returned to indicate the end of the stream. + *

    + * This method will block until some data can be read. + *

    + * This method operates by calling an overloaded read method like so: + * read(buf, 0, buf.length) + * + * @param buf The buffer into which the chars read will be stored. + * + * @return The number of chars read or -1 if end of stream. + * + * @exception IOException If an error occurs. + */ + public int read(char buf[]) throws IOException + { + return read(buf, 0, buf.length); + } + + /** + * Reads an char from the input stream and returns it + * as an int in the range of 0-65535. This method also will return -1 if + * the end of the stream has been reached. + *

    + * This method will block until the char can be read. + * + * @return The char read or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + char[] buf = new char[1]; + int count = read(buf, 0, 1); + return count > 0 ? buf[0] : -1; + } + + /** @since 1.5 */ + public int read(CharBuffer buffer) throws IOException + { + // We want to call put(), so we don't manipulate the CharBuffer + // directly. + int rem = buffer.remaining(); + char[] buf = new char[rem]; + int result = read(buf, 0, rem); + if (result != -1) + buffer.put(buf, 0, result); + return result; + } + + /** + * Closes the stream. Any futher attempts to read from the + * stream may generate an IOException. + * + * @exception IOException If an error occurs + */ + public abstract void close() throws IOException; + + /** + * Returns a boolean that indicates whether the mark/reset + * methods are supported in this class. Those methods can be used to + * remember a specific point in the stream and reset the stream to that + * point. + *

    + * This method always returns false in this class, but + * subclasses can override this method to return true if they + * support mark/reset functionality. + * + * @return true if mark/reset functionality is supported, + * false otherwise + * + */ + public boolean markSupported() + { + return false; + } + + /** + * Marks a position in the input to which the stream can be + * "reset" by calling the reset() method. The parameter + * readlimit is the number of chars that can be read from the + * stream after setting the mark before the mark becomes invalid. For + * example, if mark() is called with a read limit of 10, then + * when 11 chars of data are read from the stream before the + * reset() method is called, then the mark is invalid and the + * stream object instance is not required to remember the mark. + * + * @param readLimit The number of chars that can be read before the mark + * becomes invalid + * + * @exception IOException If an error occurs such as mark not being + * supported for this class + */ + public void mark(int readLimit) throws IOException + { + throw new IOException("mark not supported"); + } + + /** + * Resets a stream to the point where the mark() + * method was called. Any chars that were read after the mark point was + * set will be re-read during subsequent reads. + *

    + * This method always throws an IOException in this class, but subclasses + * can override this method if they provide mark/reset functionality. + * + * @exception IOException Always thrown for this class + */ + public void reset() throws IOException + { + throw new IOException("reset not supported"); + } + + /** + * Determines whether or not this stream is ready to be + * read. If it returns false the stream may block if a + * read is attempted, but it is not guaranteed to do so. + *

    + * This method always returns false in this class + * + * @return true if the stream is ready to be read, + * false otherwise. + * + * @exception IOException If an error occurs + */ + public boolean ready() throws IOException + { + return false; + } + + /** + * Skips the specified number of chars in the stream. It + * returns the actual number of chars skipped, which may be less than the + * requested amount. + *

    + * This method reads and discards chars into a 256 char array until the + * specified number of chars were skipped or until either the end of stream + * is reached or a read attempt returns a short count. Subclasses can + * override this method to provide a more efficient implementation where + * one exists. + * + * @param count The requested number of chars to skip + * + * @return The actual number of chars skipped. + * + * @exception IOException If an error occurs + */ + public long skip(long count) throws IOException + { + if (count <= 0) + return 0; + int bsize = count > 1024 ? 1024 : (int) count; + char[] buffer = new char[bsize]; + long todo = count; + synchronized (lock) + { + while (todo > 0) + { + int skipped = read(buffer, 0, bsize > todo ? (int) todo : bsize); + if (skipped <= 0) + break; + todo -= skipped; + } + } + return count - todo; + } +} diff --git a/libjava/classpath/java/io/SequenceInputStream.java b/libjava/classpath/java/io/SequenceInputStream.java new file mode 100644 index 000000000..92f032ccf --- /dev/null +++ b/libjava/classpath/java/io/SequenceInputStream.java @@ -0,0 +1,223 @@ +/* SequenceInputStream.java -- Reads multiple input streams in sequence + Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.util.Enumeration; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This class merges a sequence of multiple InputStream's in + * order to form a single logical stream that can be read by applications + * that expect only one stream. + *

    + * The streams passed to the constructor method are read in order until + * they return -1 to indicate they are at end of stream. When a stream + * reports end of stream, it is closed, then the next stream is read. + * When the last stream is closed, the next attempt to read from this + * stream will return a -1 to indicate it is at end of stream. + *

    + * If this stream is closed prior to all subordinate streams being read + * to completion, all subordinate streams are closed. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class SequenceInputStream extends InputStream +{ + /** The handle for the current input stream. */ + private InputStream in; + + /** Secondary input stream; not used if constructed w/ enumeration. */ + private InputStream in2; + + /** + * The enumeration handle; not used if constructed w/ 2 explicit + * input streams. + */ + private Enumeration e; + + /** + * This method creates a new SequenceInputStream that obtains + * its list of subordinate InputStreams from the specified + * Enumeration + * + * @param e An Enumeration that will return a list of + * InputStreams to read in sequence + */ + public SequenceInputStream(Enumeration e) + { + this.e = e; + in = e.nextElement(); + in2 = null; + } + + /** + * This method creates a new SequenceInputStream that will read + * the two specified subordinate InputStreams in sequence. + * + * @param s1 The first InputStream to read + * @param s2 The second InputStream to read + */ + public SequenceInputStream(InputStream s1, InputStream s2) + { + in = s1; + in2 = s2; + } + + /** + * This method returns the number of bytes than can be read from the + * currently being read subordinate stream before that stream could + * block. Note that it is possible more bytes than this can actually + * be read without the stream blocking. If a 0 is returned, then the + * stream could block on the very next read. + * + * @return The number of bytes that can be read before blocking could occur + * + * @exception IOException If an error occurs + */ + public int available() throws IOException + { + if (in == null) + return 0; + + return in.available(); + } + + /** + * Closes this stream. This will cause any remaining unclosed subordinate + * InputStream's to be closed as well. Subsequent attempts to + * read from this stream may cause an exception. + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + while (in != null) + { + in.close(); + in = getNextStream (); + } + } + + /** + * This method reads an unsigned byte from the input stream and returns it + * as an int in the range of 0-255. This method also will return -1 if + * the end of the stream has been reached. This will only happen when + * all of the subordinate streams have been read. + *

    + * This method will block until the byte can be read. + * + * @return The byte read, or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read() throws IOException + { + int ch = -1; + + while (in != null && (ch = in.read()) < 0) + { + in.close(); + in = getNextStream(); + } + + return ch; + } + + /** + * This method reads bytes from a stream and stores them into a caller + * supplied buffer. It starts storing the data at index offset + * into the buffer and attempts to read len bytes. This method + * can return before reading the number of bytes requested. The actual number + * of bytes read is returned as an int. A -1 is returend to indicate the + * end of the stream. This will only happen when all of the subordinate + * streams have been read. + *

    + * This method will block until at least one byte can be read. + * + * @param b The array into which bytes read should be stored + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream + * + * @exception IOException If an error occurs + */ + public int read(byte[] b, int off, int len) throws IOException + { + int ch = -1; + + // The validity of the parameters will be checked by in.read so + // don't bother doing it here. + while (in != null && (ch = in.read(b, off, len)) < 0) + { + in.close(); + in = getNextStream(); + } + + return ch; + } + + /** + * This private method is used to get the next InputStream to + * read from. Returns null when no more streams are available. + */ + private InputStream getNextStream() + { + InputStream nextIn = null; + + if (e != null) + { + if (e.hasMoreElements()) + nextIn = e.nextElement(); + } + else if (in2 != null) + { + nextIn = in2; + in2 = null; + } + + return nextIn; + } +} diff --git a/libjava/classpath/java/io/Serializable.java b/libjava/classpath/java/io/Serializable.java new file mode 100644 index 000000000..869fa6de8 --- /dev/null +++ b/libjava/classpath/java/io/Serializable.java @@ -0,0 +1,54 @@ +/* Serializable.java -- Interface to indicate a class may be serialized + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * Status: Believed complete + */ + +/** + * This interface has no methods. It simply serves to indicate that + * the implementing class may be serialized. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface Serializable +{ +} // interface Serializable diff --git a/libjava/classpath/java/io/SerializablePermission.java b/libjava/classpath/java/io/SerializablePermission.java new file mode 100644 index 000000000..88846cc35 --- /dev/null +++ b/libjava/classpath/java/io/SerializablePermission.java @@ -0,0 +1,112 @@ +/* SerializablePermission.java -- Basic permissions related to serialization. + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import java.security.BasicPermission; + +/** + * This class models permissions related to serialization. As a subclass + * of BasicPermission, this class has permissions that have + * a name only. There is no associated action list. + *

    + * There are currently two allowable permission names for this class: + *

      + *
    • enableSubclassImplementation - Allows a subclass to + * override the default serialization behavior of objects.
    • + *
    • enableSubstitution - Allows substitution of one object + * for another during serialization or deserialization.
    • + *
    + * + * @see java.security.BasicPermission + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class SerializablePermission extends BasicPermission +{ + static final long serialVersionUID = 8537212141160296410L; + + /* + * Class Variables + */ + + private static final String[] legal_names = { "enableSubclassImplementation", + "enableSubstitution" }; + /* + * Constructors + */ + + /** + * This method initializes a new instance of + * SerializablePermission + * that has the specified name. + * + * @param name The name of the permission. + * + * @exception IllegalArgumentException If the name is not valid for + * this class. + */ + public SerializablePermission(String name) + { + this(name, null); + } + + /** + * This method initializes a new instance of + * SerializablePermission + * that has the specified name and action list. Note that the action list + * is unused in this class. + * + * @param name The name of the permission. + * @param actions The action list (unused). + * + * @exception IllegalArgumentException If the name is not valid for + * this class. + */ + public SerializablePermission(String name, String actions) + { + super(name, actions); + + for (int i = 0; i < legal_names.length; i++) + if (legal_names[i].equals(name)) + return; + + throw new IllegalArgumentException("Bad permission name: " + name); + } + +} // class SerializablePermission diff --git a/libjava/classpath/java/io/StreamCorruptedException.java b/libjava/classpath/java/io/StreamCorruptedException.java new file mode 100644 index 000000000..d24d12150 --- /dev/null +++ b/libjava/classpath/java/io/StreamCorruptedException.java @@ -0,0 +1,73 @@ +/* StreamCorruptedException.java -- Error in stream during serialization + Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when there is an error in the data that is + * read from a stream during de-serialization. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class StreamCorruptedException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8983558202217591746L; + + /** + * Create an exception without a descriptive error message. + */ + public StreamCorruptedException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public StreamCorruptedException(String message) + { + super(message); + } +} // class StreamCorruptedException diff --git a/libjava/classpath/java/io/StreamTokenizer.java b/libjava/classpath/java/io/StreamTokenizer.java new file mode 100644 index 000000000..0245fbf51 --- /dev/null +++ b/libjava/classpath/java/io/StreamTokenizer.java @@ -0,0 +1,718 @@ +/* StreamTokenizer.java -- parses streams of characters into tokens + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +import gnu.java.lang.CPStringBuilder; + +/** + * This class parses streams of characters into tokens. There are a + * million-zillion flags that can be set to control the parsing, as + * described under the various method headings. + * + * @author Warren Levy (warrenl@cygnus.com) + * @date October 25, 1998. + */ +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +public class StreamTokenizer +{ + /** A constant indicating that the end of the stream has been read. */ + public static final int TT_EOF = -1; + + /** A constant indicating that the end of the line has been read. */ + public static final int TT_EOL = '\n'; + + /** A constant indicating that a number token has been read. */ + public static final int TT_NUMBER = -2; + + /** A constant indicating that a word token has been read. */ + public static final int TT_WORD = -3; + + /** A constant indicating that no tokens have been read yet. */ + private static final int TT_NONE = -4; + + /** + * Contains the type of the token read resulting from a call to nextToken + * The rules are as follows: + *
      + *
    • For a token consisting of a single ordinary character, this is the + * value of that character.
    • + *
    • For a quoted string, this is the value of the quote character
    • + *
    • For a word, this is TT_WORD
    • + *
    • For a number, this is TT_NUMBER
    • + *
    • For the end of the line, this is TT_EOL
    • + *
    • For the end of the stream, this is TT_EOF
    • + *
    + */ + public int ttype = TT_NONE; + + /** The String associated with word and string tokens. */ + public String sval; + + /** The numeric value associated with number tokens. */ + public double nval; + + /* Indicates whether end-of-line is recognized as a token. */ + private boolean eolSignificant = false; + + /* Indicates whether word tokens are automatically made lower case. */ + private boolean lowerCase = false; + + /* Indicates whether C++ style comments are recognized and skipped. */ + private boolean slashSlash = false; + + /* Indicates whether C style comments are recognized and skipped. */ + private boolean slashStar = false; + + /* Attribute tables of each byte from 0x00 to 0xFF. */ + private boolean[] whitespace = new boolean[256]; + private boolean[] alphabetic = new boolean[256]; + private boolean[] numeric = new boolean[256]; + private boolean[] quote = new boolean[256]; + private boolean[] comment = new boolean[256]; + + /* The Reader associated with this class. */ + private PushbackReader in; + + /* Indicates if a token has been pushed back. */ + private boolean pushedBack = false; + + /* Contains the current line number of the reader. */ + private int lineNumber = 1; + + /** + * This method reads bytes from an InputStream and tokenizes + * them. For details on how this method operates by default, see + * StreamTokenizer(Reader). + * + * @param is The InputStream to read from + * + * @deprecated Since JDK 1.1. + */ + public StreamTokenizer(InputStream is) + { + this(new InputStreamReader(is)); + } + + /** + * This method initializes a new StreamTokenizer to read + * characters from a Reader and parse them. The char values + * have their hight bits masked so that the value is treated a character + * in the range of 0x0000 to 0x00FF. + *

    + * This constructor sets up the parsing table to parse the stream in the + * following manner: + *

      + *
    • The values 'A' through 'Z', 'a' through 'z' and 0xA0 through 0xFF + * are initialized as alphabetic
    • + *
    • The values 0x00 through 0x20 are initialized as whitespace
    • + *
    • The values '\'' and '"' are initialized as quote characters
    • + *
    • '/' is a comment character
    • + *
    • Numbers will be parsed
    • + *
    • EOL is not treated as significant
    • + *
    • C and C++ (//) comments are not recognized
    • + *
    + * + * @param r The Reader to read chars from + */ + public StreamTokenizer(Reader r) + { + in = new PushbackReader(r); + + whitespaceChars(0x00, 0x20); + wordChars('A', 'Z'); + wordChars('a', 'z'); + wordChars(0xA0, 0xFF); + commentChar('/'); + quoteChar('\''); + quoteChar('"'); + parseNumbers(); + } + + /** + * This method sets the comment attribute on the specified + * character. Other attributes for the character are cleared. + * + * @param ch The character to set the comment attribute for, passed as an int + */ + public void commentChar(int ch) + { + if (ch >= 0 && ch <= 255) + { + comment[ch] = true; + whitespace[ch] = false; + alphabetic[ch] = false; + numeric[ch] = false; + quote[ch] = false; + } + } + + /** + * This method sets a flag that indicates whether or not the end of line + * sequence terminates and is a token. The defaults to false + * + * @param flag true if EOF is significant, false + * otherwise + */ + public void eolIsSignificant(boolean flag) + { + eolSignificant = flag; + } + + /** + * This method returns the current line number. Note that if the + * pushBack() method is called, it has no effect on the + * line number returned by this method. + * + * @return The current line number + */ + public int lineno() + { + return lineNumber; + } + + /** + * This method sets a flag that indicates whether or not alphabetic + * tokens that are returned should be converted to lower case. + * + * @param flag true to convert to lower case, + * false otherwise + */ + public void lowerCaseMode(boolean flag) + { + lowerCase = flag; + } + + private boolean isWhitespace(int ch) + { + return (ch >= 0 && ch <= 255 && whitespace[ch]); + } + + private boolean isAlphabetic(int ch) + { + return ((ch > 255) || (ch >= 0 && alphabetic[ch])); + } + + private boolean isNumeric(int ch) + { + return (ch >= 0 && ch <= 255 && numeric[ch]); + } + + private boolean isQuote(int ch) + { + return (ch >= 0 && ch <= 255 && quote[ch]); + } + + private boolean isComment(int ch) + { + return (ch >= 0 && ch <= 255 && comment[ch]); + } + + /** + * This method reads the next token from the stream. It sets the + * ttype variable to the appropriate token type and + * returns it. It also can set sval or nval + * as described below. The parsing strategy is as follows: + *
      + *
    • Skip any whitespace characters.
    • + *
    • If a numeric character is encountered, attempt to parse a numeric + * value. Leading '-' characters indicate a numeric only if followed by + * another non-'-' numeric. The value of the numeric token is terminated + * by either the first non-numeric encountered, or the second occurrence of + * '-' or '.'. The token type returned is TT_NUMBER and nval + * is set to the value parsed.
    • + *
    • If an alphabetic character is parsed, all subsequent characters + * are read until the first non-alphabetic or non-numeric character is + * encountered. The token type returned is TT_WORD and the value parsed + * is stored in sval. If lower case mode is set, the token + * stored in sval is converted to lower case. The end of line + * sequence terminates a word only if EOL signficance has been turned on. + * The start of a comment also terminates a word. Any character with a + * non-alphabetic and non-numeric attribute (such as white space, a quote, + * or a commet) are treated as non-alphabetic and terminate the word.
    • + *
    • If a comment character is parsed, then all remaining characters on + * the current line are skipped and another token is parsed. Any EOL or + * EOF's encountered are not discarded, but rather terminate the comment.
    • + *
    • If a quote character is parsed, then all characters up to the + * second occurrence of the same quote character are parsed into a + * String. This String is stored as + * sval, but is not converted to lower case, even if lower case + * mode is enabled. The token type returned is the value of the quote + * character encountered. Any escape sequences + * (\b (backspace), \t (HTAB), \n (linefeed), \f (form feed), \r + * (carriage return), \" (double quote), \' (single quote), \\ + * (backslash), \XXX (octal esacpe)) are converted to the appropriate + * char values. Invalid esacape sequences are left in untranslated. + * Unicode characters like ('\ u0000') are not recognized.
    • + *
    • If the C++ comment sequence "//" is encountered, and the parser + * is configured to handle that sequence, then the remainder of the line + * is skipped and another token is read exactly as if a character with + * the comment attribute was encountered.
    • + *
    • If the C comment sequence "/*" is encountered, and the parser + * is configured to handle that sequence, then all characters up to and + * including the comment terminator sequence are discarded and another + * token is parsed.
    • + *
    • If all cases above are not met, then the character is an ordinary + * character that is parsed as a token by itself. The char encountered + * is returned as the token type.
    • + *
    + * + * @return The token type + * @exception IOException If an I/O error occurs + */ + public int nextToken() throws IOException + { + if (pushedBack) + { + pushedBack = false; + if (ttype != TT_NONE) + return ttype; + } + + sval = null; + int ch; + + // Skip whitespace. Deal with EOL along the way. + while (isWhitespace(ch = in.read())) + if (ch == '\n' || ch == '\r') + { + lineNumber++; + + // Throw away \n if in combination with \r. + if (ch == '\r' && (ch = in.read()) != '\n') + { + if (ch != TT_EOF) + in.unread(ch); + } + if (eolSignificant) + return (ttype = TT_EOL); + } + + if (ch == '/') + if ((ch = in.read()) == '/' && slashSlash) + { + while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) + ; + + if (ch != TT_EOF) + in.unread(ch); + return nextToken(); // Recursive, but not too deep in normal cases + } + else if (ch == '*' && slashStar) + { + while (true) + { + ch = in.read(); + if (ch == '*') + { + if ((ch = in.read()) == '/') + break; + else if (ch != TT_EOF) + in.unread(ch); + } + else if (ch == '\n' || ch == '\r') + { + lineNumber++; + if (ch == '\r' && (ch = in.read()) != '\n') + { + if (ch != TT_EOF) + in.unread(ch); + } + } + else if (ch == TT_EOF) + { + break; + } + } + return nextToken(); // Recursive, but not too deep in normal cases + } + else + { + if (ch != TT_EOF) + in.unread(ch); + ch = '/'; + } + + if (ch == TT_EOF) + ttype = TT_EOF; + else if (isNumeric(ch)) + { + boolean isNegative = false; + if (ch == '-') + { + // Read ahead to see if this is an ordinary '-' rather than numeric. + ch = in.read(); + if (isNumeric(ch) && ch != '-') + { + isNegative = true; + } + else + { + if (ch != TT_EOF) + in.unread(ch); + return (ttype = '-'); + } + } + + CPStringBuilder tokbuf = new CPStringBuilder(); + tokbuf.append((char) ch); + + int decCount = 0; + while (isNumeric(ch = in.read()) && ch != '-') + if (ch == '.' && decCount++ > 0) + break; + else + tokbuf.append((char) ch); + + if (ch != TT_EOF) + in.unread(ch); + ttype = TT_NUMBER; + try + { + nval = Double.valueOf(tokbuf.toString()).doubleValue(); + } + catch (NumberFormatException _) + { + nval = 0.0; + } + if (isNegative) + nval = -nval; + } + else if (isAlphabetic(ch)) + { + CPStringBuilder tokbuf = new CPStringBuilder(); + tokbuf.append((char) ch); + while (isAlphabetic(ch = in.read()) || isNumeric(ch)) + tokbuf.append((char) ch); + if (ch != TT_EOF) + in.unread(ch); + ttype = TT_WORD; + sval = tokbuf.toString(); + if (lowerCase) + sval = sval.toLowerCase(); + } + else if (isComment(ch)) + { + while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) + ; + + if (ch != TT_EOF) + in.unread(ch); + return nextToken(); // Recursive, but not too deep in normal cases. + } + else if (isQuote(ch)) + { + ttype = ch; + CPStringBuilder tokbuf = new CPStringBuilder(); + while ((ch = in.read()) != ttype && ch != '\n' && ch != '\r' && + ch != TT_EOF) + { + if (ch == '\\') + switch (ch = in.read()) + { + case 'a': ch = 0x7; + break; + case 'b': ch = '\b'; + break; + case 'f': ch = 0xC; + break; + case 'n': ch = '\n'; + break; + case 'r': ch = '\r'; + break; + case 't': ch = '\t'; + break; + case 'v': ch = 0xB; + break; + case '\n': ch = '\n'; + break; + case '\r': ch = '\r'; + break; + case '\"': + case '\'': + case '\\': + break; + default: + int ch1, nextch; + if ((nextch = ch1 = ch) >= '0' && ch <= '7') + { + ch -= '0'; + if ((nextch = in.read()) >= '0' && nextch <= '7') + { + ch = ch * 8 + nextch - '0'; + if ((nextch = in.read()) >= '0' && nextch <= '7' && + ch1 >= '0' && ch1 <= '3') + { + ch = ch * 8 + nextch - '0'; + nextch = in.read(); + } + } + } + + if (nextch != TT_EOF) + in.unread(nextch); + } + + tokbuf.append((char) ch); + } + + // Throw away matching quote char. + if (ch != ttype && ch != TT_EOF) + in.unread(ch); + + sval = tokbuf.toString(); + } + else + { + ttype = ch; + } + + return ttype; + } + + private void resetChar(int ch) + { + whitespace[ch] = alphabetic[ch] = numeric[ch] = quote[ch] = comment[ch] = + false; + } + + /** + * This method makes the specified character an ordinary character. This + * means that none of the attributes (whitespace, alphabetic, numeric, + * quote, or comment) will be set on this character. This character will + * parse as its own token. + * + * @param ch The character to make ordinary, passed as an int + */ + public void ordinaryChar(int ch) + { + if (ch >= 0 && ch <= 255) + resetChar(ch); + } + + /** + * This method makes all the characters in the specified range, range + * terminators included, ordinary. This means the none of the attributes + * (whitespace, alphabetic, numeric, quote, or comment) will be set on + * any of the characters in the range. This makes each character in this + * range parse as its own token. + * + * @param low The low end of the range of values to set the whitespace + * attribute for + * @param hi The high end of the range of values to set the whitespace + * attribute for + */ + public void ordinaryChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + resetChar(i); + } + + /** + * This method sets the numeric attribute on the characters '0' - '9' and + * the characters '.' and '-'. + * When this method is used, the result of giving other attributes + * (whitespace, quote, or comment) to the numeric characters may + * vary depending on the implementation. For example, if + * parseNumbers() and then whitespaceChars('1', '1') are called, + * this implementation reads "121" as 2, while some other implementation + * will read it as 21. + */ + public void parseNumbers() + { + for (int i = 0; i <= 9; i++) + numeric['0' + i] = true; + + numeric['.'] = true; + numeric['-'] = true; + } + + /** + * Puts the current token back into the StreamTokenizer so + * nextToken will return the same value on the next call. + * May cause the lineno method to return an incorrect value + * if lineno is called before the next call to nextToken. + */ + public void pushBack() + { + pushedBack = true; + } + + /** + * This method sets the quote attribute on the specified character. + * Other attributes for the character are cleared. + * + * @param ch The character to set the quote attribute for, passed as an int. + */ + public void quoteChar(int ch) + { + if (ch >= 0 && ch <= 255) + { + quote[ch] = true; + comment[ch] = false; + whitespace[ch] = false; + alphabetic[ch] = false; + numeric[ch] = false; + } + } + + /** + * This method removes all attributes (whitespace, alphabetic, numeric, + * quote, and comment) from all characters. It is equivalent to calling + * ordinaryChars(0x00, 0xFF). + * + * @see #ordinaryChars(int, int) + */ + public void resetSyntax() + { + ordinaryChars(0x00, 0xFF); + } + + /** + * This method sets a flag that indicates whether or not "C++" language style + * comments ("//" comments through EOL ) are handled by the parser. + * If this is true commented out sequences are skipped and + * ignored by the parser. This defaults to false. + * + * @param flag true to recognized and handle "C++" style + * comments, false otherwise + */ + public void slashSlashComments(boolean flag) + { + slashSlash = flag; + } + + /** + * This method sets a flag that indicates whether or not "C" language style + * comments (with nesting not allowed) are handled by the parser. + * If this is true commented out sequences are skipped and + * ignored by the parser. This defaults to false. + * + * @param flag true to recognized and handle "C" style comments, + * false otherwise + */ + public void slashStarComments(boolean flag) + { + slashStar = flag; + } + + /** + * This method returns the current token value as a String in + * the form "Token[x], line n", where 'n' is the current line numbers and + * 'x' is determined as follows. + *

    + *

      + *
    • If no token has been read, then 'x' is "NOTHING" and 'n' is 0
    • + *
    • If ttype is TT_EOF, then 'x' is "EOF"
    • + *
    • If ttype is TT_EOL, then 'x' is "EOL"
    • + *
    • If ttype is TT_WORD, then 'x' is sval
    • + *
    • If ttype is TT_NUMBER, then 'x' is "n=strnval" where + * 'strnval' is String.valueOf(nval).
    • + *
    • If ttype is a quote character, then 'x' is + * sval
    • + *
    • For all other cases, 'x' is ttype
    • + *
    + */ + public String toString() + { + String tempstr; + if (ttype == TT_EOF) + tempstr = "EOF"; + else if (ttype == TT_EOL) + tempstr = "EOL"; + else if (ttype == TT_WORD) + tempstr = sval; + else if (ttype == TT_NUMBER) + tempstr = "n=" + nval; + else if (ttype == TT_NONE) + tempstr = "NOTHING"; + else // must be an ordinary char. + tempstr = "\'" + (char) ttype + "\'"; + + return "Token[" + tempstr + "], line " + lineno(); + } + + /** + * This method sets the whitespace attribute for all characters in the + * specified range, range terminators included. + * + * @param low The low end of the range of values to set the whitespace + * attribute for + * @param hi The high end of the range of values to set the whitespace + * attribute for + */ + public void whitespaceChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + { + resetChar(i); + whitespace[i] = true; + } + } + + /** + * This method sets the alphabetic attribute for all characters in the + * specified range, range terminators included. + * + * @param low The low end of the range of values to set the alphabetic + * attribute for + * @param hi The high end of the range of values to set the alphabetic + * attribute for + */ + public void wordChars(int low, int hi) + { + if (low < 0) + low = 0; + if (hi > 255) + hi = 255; + for (int i = low; i <= hi; i++) + alphabetic[i] = true; + } +} diff --git a/libjava/classpath/java/io/StringBufferInputStream.java b/libjava/classpath/java/io/StringBufferInputStream.java new file mode 100644 index 000000000..28405d938 --- /dev/null +++ b/libjava/classpath/java/io/StringBufferInputStream.java @@ -0,0 +1,187 @@ +/* StringBufferInputStream.java -- Read an String as a stream + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. Deprecated in JDK 1.1. + */ + +/** + * This class permits a String to be read as an input stream. + * The low eight bits of each character in the String are the + * bytes that are returned. The high eight bits of each character are + * discarded. + *

    + * The mark/reset functionality in this class behaves differently than + * normal. The mark() method is always ignored and the + * reset() method always resets in stream to start reading from + * position 0 in the String. Note that since this method does not override + * markSupported() in InputStream, calling that + * method will return false. + *

    + * Note that this class is deprecated because it does not properly handle + * 16-bit Java characters. It is provided for backwards compatibility only + * and should not be used for new development. The StringReader + * class should be used instead. + * + * @deprecated + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public class StringBufferInputStream extends InputStream +{ + /** The String which is the input to this stream. */ + protected String buffer; + + /** Position of the next byte in buffer to be read. */ + protected int pos = 0; + + /** The length of the String buffer. */ + protected int count; + + /** + * Create a new StringBufferInputStream that will read bytes + * from the passed in String. This stream will read from the + * beginning to the end of the String. + * + * @param s The String this stream will read from. + */ + public StringBufferInputStream(String s) + { + buffer = s; + count = s.length(); + } + + /** + * This method returns the number of bytes available to be read from this + * stream. The value returned will be equal to count - pos. + * + * @return The number of bytes that can be read from this stream before + * blocking, which is all of them + */ + public int available() + { + return count - pos; + } + + /** + * This method reads one byte from the stream. The pos counter + * is advanced to the next byte to be read. The byte read is returned as + * an int in the range of 0-255. If the stream position is already at the + * end of the buffer, no byte is read and a -1 is returned in order to + * indicate the end of the stream. + * + * @return The byte read, or -1 if end of stream + */ + public int read() + { + if (pos >= count) + return -1; // EOF + + return ((int) buffer.charAt(pos++)) & 0xFF; + } + +/** + * This method reads bytes from the stream and stores them into a caller + * supplied buffer. It starts storing the data at index offset + * into the buffer and attempts to read len bytes. This method + * can return before reading the number of bytes requested if the end of the + * stream is encountered first. The actual number of bytes read is + * returned. If no bytes can be read because the stream is already at + * the end of stream position, a -1 is returned. + *

    + * This method does not block. + * + * @param b The array into which the bytes read should be stored. + * @param off The offset into the array to start storing bytes + * @param len The requested number of bytes to read + * + * @return The actual number of bytes read, or -1 if end of stream. + */ + public int read(byte[] b, int off, int len) + { + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + if (pos >= count) + return -1; // EOF + + int numRead = Math.min(len, count - pos); + if (numRead < 0) + return 0; + + buffer.getBytes(pos, pos + numRead, b, off); + pos += numRead; + return numRead; + } + + /** + * This method sets the read position in the stream to the beginning + * setting the pos variable equal to 0. Note that this differs + * from the common implementation of the reset() method. + */ + public void reset() + { + pos = 0; + } + + /** + * This method attempts to skip the requested number of bytes in the + * input stream. It does this by advancing the pos value by the + * specified number of bytes. It this would exceed the length of the + * buffer, then only enough bytes are skipped to position the stream at + * the end of the buffer. The actual number of bytes skipped is returned. + * + * @param n The requested number of bytes to skip + * + * @return The actual number of bytes skipped. + */ + public long skip(long n) + { + if (n < 0) + return 0L; + + long actualSkip = Math.min(n, count - pos); + pos += actualSkip; + return actualSkip; + } +} diff --git a/libjava/classpath/java/io/StringReader.java b/libjava/classpath/java/io/StringReader.java new file mode 100644 index 000000000..c4021a8fb --- /dev/null +++ b/libjava/classpath/java/io/StringReader.java @@ -0,0 +1,208 @@ +/* StringReader.java -- permits a String to be read as a character input stream + Copyright (C) 1998, 1999, 2000, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct + */ + +/** + * This class permits a String to be read as a character + * input stream. + *

    + * The mark/reset functionality in this class behaves differently than + * normal. If no mark has been set, then calling the reset() + * method rewinds the read pointer to the beginning of the String. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @date October 19, 1998. + */ +public class StringReader extends Reader +{ + /* A String provided by the creator of the stream. */ + private String buf; + + /* Position of the next char in buf to be read. */ + private int pos; + + /* The currently marked position in the stream. */ + private int markedPos; + + /* The index in buf one greater than the last valid character. */ + private int count; + + /** + * Create a new StringReader that will read chars from the + * passed in String. This stream will read from the beginning + * to the end of the String. + * + * @param buffer The String this stream will read from. + */ + public StringReader(String buffer) + { + super(); + buf = buffer; + + count = buffer.length(); + markedPos = pos = 0; + } + + public void close() + { + synchronized (lock) + { + buf = null; + } + } + + public void mark(int readAheadLimit) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // readAheadLimit is ignored per Java Class Lib. book, p. 1692. + markedPos = pos; + } + } + + public boolean markSupported() + { + return true; + } + + public int read() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + if (pos < count) + return ((int) buf.charAt(pos++)) & 0xFFFF; + return -1; + } + } + + public int read(char[] b, int off, int len) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + /* Don't need to check pos value, arraycopy will check it. */ + if (off < 0 || len < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + + if (pos >= count) + return -1; + + int lastChar = Math.min(count, pos + len); + buf.getChars(pos, lastChar, b, off); + int numChars = lastChar - pos; + pos = lastChar; + return numChars; + } + } + + /** + * This method determines if the stream is ready to be read. This class + * is always ready to read and so always returns true, unless + * close() has previously been called in which case an IOException is + * thrown. + * + * @return true to indicate that this object is ready to be read. + * @exception IOException If the stream is closed. + */ + public boolean ready() throws IOException + { + if (buf == null) + throw new IOException("Stream closed"); + + return true; + } + + /** + * Sets the read position in the stream to the previously + * marked position or to 0 (i.e., the beginning of the stream) if the mark + * has not already been set. + */ + public void reset() throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + pos = markedPos; + } + } + + /** + * This method attempts to skip the requested number of chars in the + * input stream. It does this by advancing the pos value by + * the specified number of chars. It this would exceed the length of the + * buffer, then only enough chars are skipped to position the stream at + * the end of the buffer. The actual number of chars skipped is returned. + * + * @param n The requested number of chars to skip + * + * @return The actual number of chars skipped. + */ + public long skip(long n) throws IOException + { + synchronized (lock) + { + if (buf == null) + throw new IOException("Stream closed"); + + // Even though the var numChars is a long, in reality it can never + // be larger than an int since the result of subtracting 2 positive + // ints will always fit in an int. Since we have to return a long + // anyway, numChars might as well just be a long. + long numChars = Math.min((long) (count - pos), n < 0 ? 0L : n); + pos += numChars; + return numChars; + } + } +} diff --git a/libjava/classpath/java/io/StringWriter.java b/libjava/classpath/java/io/StringWriter.java new file mode 100644 index 000000000..85500b5cb --- /dev/null +++ b/libjava/classpath/java/io/StringWriter.java @@ -0,0 +1,212 @@ +/* StringWriter.java -- Writes bytes to a StringBuffer + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +// Wow is this a dumb class. CharArrayWriter can do all this and +// more. I would redirect all calls to one in fact, but the javadocs say +// use a StringBuffer so I will comply. + +/** + * This class writes chars to an internal StringBuffer that + * can then be used to retrieve a String. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class StringWriter extends Writer +{ + /** + * This is the default size of the buffer if the user doesn't specify it. + * @specnote The JCL Volume 1 says that 16 is the default size. + */ + private static final int DEFAULT_BUFFER_SIZE = 16; + + /** + * This method closes the stream. The contents of the internal buffer + * can still be retrieved, but future writes are not guaranteed to work. + * + * @exception IOException If an error orrurs. + */ + public void close () throws IOException + { + // JCL says this does nothing. This seems to violate the Writer + // contract, in that other methods should still throw an + // IOException after a close. Still, we just follow JCL. + } + + /** + * This method flushes any buffered characters to the underlying output. + * It does nothing in this class. + */ + public void flush () + { + } + + /** + * This method returns the StringBuffer object that this + * object is writing to. Note that this is the actual internal buffer, so + * any operations performed on it will affect this stream object. + * + * @return The StringBuffer object being written to + */ + public StringBuffer getBuffer () + { + return buffer; + } + + /** + * This method initializes a new StringWriter to write to a + * StringBuffer initially sized to a default size of 16 + * chars. + */ + public StringWriter () + { + this (DEFAULT_BUFFER_SIZE); + } + + /** + * This method initializes a new StringWriter to write to a + * StringBuffer with the specified initial size. + * + * @param size The initial size to make the StringBuffer + */ + public StringWriter (int size) + { + super (); + buffer = new StringBuffer (size); + lock = buffer; + } + + /** + * This method returns the contents of the internal StringBuffer + * as a String. + * + * @return A String representing the chars written to + * this stream. + */ + public String toString () + { + return buffer.toString(); + } + + /** + * This method writes a single character to the output, storing it in + * the internal buffer. + * + * @param oneChar The char to write, passed as an int. + */ + public void write (int oneChar) + { + buffer.append((char) (oneChar & 0xFFFF)); + } + + /** + * This method writes len chars from the specified + * array starting at index offset in that array to this + * stream by appending the chars to the end of the internal buffer. + * + * @param chars The array of chars to write + * @param offset The index into the array to start writing from + * @param len The number of chars to write + */ + public void write (char[] chars, int offset, int len) + { + buffer.append(chars, offset, len); + } + + /** + * This method writes the characters in the specified String + * to the stream by appending them to the end of the internal buffer. + * + * @param str The String to write to the stream. + */ + public void write (String str) + { + buffer.append(str); + } + + /** + * This method writes out len characters of the specified + * String to the stream starting at character position + * offset into the stream. This is done by appending the + * characters to the internal buffer. + * + * @param str The String to write characters from + * @param offset The character position to start writing from + * @param len The number of characters to write. + */ + public void write (String str, int offset, int len) + { +// char[] tmpbuf = new char[len]; +// str.getChars(offset, offset+len, tmpbuf, 0); +// buf.append(tmpbuf, 0, tmpbuf.length); + // This implementation assumes that String.substring is more + // efficient than using String.getChars and copying the data + // twice. For libgcj, this is true. For Classpath, it is not. + // FIXME. + buffer.append(str.substring(offset, offset + len)); + } + + /** @since 1.5 */ + public StringWriter append(char c) + { + write(c); + return this; + } + + /** @since 1.5 */ + public StringWriter append(CharSequence cs) + { + write(cs == null ? "null" : cs.toString()); + return this; + } + + /** @since 1.5 */ + public StringWriter append(CharSequence cs, int start, int end) + { + write(cs == null ? "null" : cs.subSequence(start, end).toString()); + return this; + } + + /** + * This is the StringBuffer that we use to store bytes that + * are written. + */ + private StringBuffer buffer; +} diff --git a/libjava/classpath/java/io/SyncFailedException.java b/libjava/classpath/java/io/SyncFailedException.java new file mode 100644 index 000000000..c514c44f2 --- /dev/null +++ b/libjava/classpath/java/io/SyncFailedException.java @@ -0,0 +1,66 @@ +/* SyncFailedException.java -- a file sync failed + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * Thrown when a file synchronization fails. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see FileDescriptor#sync() + * @since 1.1 + * @status updated to 1.4 + */ +public class SyncFailedException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2353342684412443330L; + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public SyncFailedException(String message) + { + super(message); + } +} // class SyncFailedException diff --git a/libjava/classpath/java/io/UTFDataFormatException.java b/libjava/classpath/java/io/UTFDataFormatException.java new file mode 100644 index 000000000..6bb76aebd --- /dev/null +++ b/libjava/classpath/java/io/UTFDataFormatException.java @@ -0,0 +1,74 @@ +/* UTFDataFormatException.java -- thrown on bad format in UTF data + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * When reading a UTF string from an input stream, this exception is thrown + * to indicate that the data read is invalid. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @see DataInput + * @see DataInputStream#readUTF(DataInput) + * @status updated to 1.4 + */ +public class UTFDataFormatException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 420743449228280612L; + + /** + * Create a new UTFDataFormatException without a descriptive error message. + */ + public UTFDataFormatException() + { + } + + /** + * Create a new UTFDataFormatException with a descriptive error message. + * + * @param message the descriptive error message + */ + public UTFDataFormatException(String message) + { + super(message); + } +} // class UTFDataFormatException diff --git a/libjava/classpath/java/io/UnsupportedEncodingException.java b/libjava/classpath/java/io/UnsupportedEncodingException.java new file mode 100644 index 000000000..597597563 --- /dev/null +++ b/libjava/classpath/java/io/UnsupportedEncodingException.java @@ -0,0 +1,73 @@ +/* UnsupportedEncodingException.java -- the requested encoding isn't supported + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when the requested character encoding is + * not supported. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class UnsupportedEncodingException extends IOException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4274276298326136670L; + + /** + * Create an exception without a descriptive error message. + */ + public UnsupportedEncodingException() + { + } + + /** + * Create an exception with a descriptive error message. + * + * @param message the descriptive error message + */ + public UnsupportedEncodingException(String message) + { + super(message); + } +} // class UnsupportedEncodingException diff --git a/libjava/classpath/java/io/WriteAbortedException.java b/libjava/classpath/java/io/WriteAbortedException.java new file mode 100644 index 000000000..f051dc975 --- /dev/null +++ b/libjava/classpath/java/io/WriteAbortedException.java @@ -0,0 +1,109 @@ +/* WriteAbortedException.java -- wraps an exception thrown while writing + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/** + * This exception is thrown when another ObjectStreamException occurs during + * a serialization read or write. The stream is reset, and deserialized + * objects are discarded. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class WriteAbortedException extends ObjectStreamException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -3326426625597282442L; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Exception detail; + + /** + * Create a new WriteAbortedException with a specified message and + * cause. + * + * @param msg the message + * @param detail the cause + */ + public WriteAbortedException(String msg, Exception detail) + { + super(msg); + initCause(detail); + this.detail = detail; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * super.getMessage() + (detail == null ? "" : "; " + detail). + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; " + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the detail field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if detail == this, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} // class WriteAbortedException diff --git a/libjava/classpath/java/io/Writer.java b/libjava/classpath/java/io/Writer.java new file mode 100644 index 000000000..5b3e7073a --- /dev/null +++ b/libjava/classpath/java/io/Writer.java @@ -0,0 +1,211 @@ +/* Writer.java -- Base class for character output streams + Copyright (C) 1998, 1999, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This abstract class forms the base of the hierarchy of classes that + * write output as a stream of chars. It provides a common set of methods + * for writing chars to stream. Subclasses implement and/or extend these + * methods to write chars in a particular manner or to a particular + * destination such as a file on disk or network connection. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public abstract class Writer implements Appendable, Closeable, Flushable +{ + /** + * This is the object used to synchronize criticial code sections for + * thread safety. Subclasses should use this field instead of using + * synchronized methods or explicity synchronizations on this + */ + protected Object lock; + + /** + * This is the default no-argument constructor for this class. This method + * will set up the class to synchronize criticial sections on itself. + */ + protected Writer() + { + lock = this; + } + + /** + * This method initializes a Writer that will synchronize + * on the specified Object. + * + * @param lock The Object to use for synchronizing critical + * sections. Must not be null. + */ + protected Writer(Object lock) + { + if (lock == null) + throw new NullPointerException(); + + this.lock = lock; + } + + /** + * This method forces any data that may have been buffered to be written + * to the underlying output device. Please note that the host environment + * might perform its own buffering unbeknowst to Java. In that case, a + * write made (for example, to a disk drive) might be cached in OS + * buffers instead of actually being written to disk. + * + * @exception IOException If an error occurs + */ + public abstract void flush() throws IOException; + + /** + * This method closes the stream. Any internal or native resources + * associated + * with this stream are freed. Any subsequent attempt to access the stream + * might throw an exception. + *

    + * This method in this class does nothing. + * + * @exception IOException If an error occurs + */ + public abstract void close() throws IOException; + + /** + * This method writes a single char to the output stream. + * + * @param b The char to be written to the output stream, passed as an int + * + * @exception IOException If an error occurs + */ + public void write(int b) throws IOException + { + char[] buf = new char[1]; + + buf[0] = (char)b; + write(buf, 0, buf.length); + } + + /** + * This method all the writes char from the passed array to the output + * stream. This method is equivalent to + * write(buf, 0, buf.length) which + * is exactly how it is implemented in this class. + * + * @param buf The array of char to write + * + * @exception IOException If an error occurs + */ + public void write(char[] buf) throws IOException + { + write(buf, 0, buf.length); + } + + /** + * This method writes len char from the specified array + * buf starting at index offset into the array. + *

    + * Subclasses must provide an implementation of this abstract method. + * + * @param buf The array of char to write from + * @param offset The index into the array to start writing from + * @param len The number of char to write + * + * @exception IOException If an error occurs + */ + public abstract void write(char[] buf, int offset, int len) + throws IOException; + + /** + * This method writes all the characters in a String to the + * output. + * + * @param str The String whose chars are to be written. + * + * @exception IOException If an error occurs + */ + public void write(String str) throws IOException + { + write(str, 0, str.length()); + } + + /** + * This method writes len chars from the String + * starting at position offset. + * + * @param str The String that is to be written + * @param offset The character offset into the String to start + * writing from + * @param len The number of chars to write + * + * @exception IOException If an error occurs + */ + public void write(String str, int offset, int len) throws IOException + { + // FIXME - for libgcj re-write using native code to not require + // copied buffer. + char[] buf = new char[len]; + + str.getChars(offset, offset + len, buf, 0); + write(buf, 0, len); + } + + /** @since 1.5 */ + public Writer append(char c) throws IOException + { + write(c); + return this; + } + + /** @since 1.5 */ + public Writer append(CharSequence cs) throws IOException + { + write(cs == null ? "null" : cs.toString()); + return this; + } + + /** @since 1.5 */ + public Writer append(CharSequence cs, int start, int end) throws IOException + { + write(cs == null ? "null" : cs.subSequence(start, end).toString()); + return this; + } +} diff --git a/libjava/classpath/java/io/package.html b/libjava/classpath/java/io/package.html new file mode 100644 index 000000000..02e1c5bc3 --- /dev/null +++ b/libjava/classpath/java/io/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.io + + +

    Classes for manipulating character and byte streams and files.

    + + + diff --git a/libjava/classpath/java/lang/AbstractMethodError.java b/libjava/classpath/java/lang/AbstractMethodError.java new file mode 100644 index 000000000..b9eb622ee --- /dev/null +++ b/libjava/classpath/java/lang/AbstractMethodError.java @@ -0,0 +1,75 @@ +/* AbstractMethodError.java -- thrown if an abstract method is invoked + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An AbstractMethodError is thrown when an application attempts + * to access an abstract method. Compilers typically detect this error, but + * it can be thrown at run time if the definition of a class has changed + * since the application was last compiled. This can also occur when + * reflecting on methods. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class AbstractMethodError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -1654391082989018462L; + + /** + * Create an error without a message. + */ + public AbstractMethodError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public AbstractMethodError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/AbstractStringBuffer.java b/libjava/classpath/java/lang/AbstractStringBuffer.java new file mode 100644 index 000000000..242afdcb4 --- /dev/null +++ b/libjava/classpath/java/lang/AbstractStringBuffer.java @@ -0,0 +1,1037 @@ +/* AbstractStringBuffer.java -- Growable strings + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.Serializable; + +/** + * This class is based on gnu.classpath.ClasspathStringBuffer but + * is package-private to java.lang so it can be used as the basis + * for StringBuffer and StringBuilder. + * If you modify this, please consider also modifying that code. + */ +abstract class AbstractStringBuffer + implements Serializable, CharSequence, Appendable +{ + + /** + * Index of next available character (and thus the size of the current + * string contents). Note that this has permissions set this way so that + * String can get the value. + * + * @serial the number of characters in the buffer + */ + int count; + + /** + * The buffer. Note that this has permissions set this way so that String + * can get the value. + * + * @serial the buffer + */ + char[] value; + + /** + * The default capacity of a buffer. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * Create a new AbstractStringBuffer with default capacity 16. + */ + AbstractStringBuffer() + { + this(DEFAULT_CAPACITY); + } + + /** + * Create an empty StringBuffer with the specified initial + * capacity. + * + * @param capacity the initial capacity + * @throws NegativeArraySizeException if capacity is negative + */ + AbstractStringBuffer(int capacity) + { + value = new char[capacity]; + } + + /** + * Create a new StringBuffer with the characters in the + * specified String. Initial capacity will be the size of the + * String plus 16. + * + * @param str the String to convert + * @throws NullPointerException if str is null + */ + AbstractStringBuffer(String str) + { + count = str.count; + value = new char[count + DEFAULT_CAPACITY]; + str.getChars(0, count, value, 0); + } + + /** + * Create a new StringBuffer with the characters in the + * specified CharSequence. Initial capacity will be the + * length of the sequence plus 16; if the sequence reports a length + * less than or equal to 0, then the initial capacity will be 16. + * + * @param seq the initializing CharSequence + * @throws NullPointerException if str is null + * @since 1.5 + */ + AbstractStringBuffer(CharSequence seq) + { + int len = seq.length(); + count = len <= 0 ? 0 : len; + value = new char[count + DEFAULT_CAPACITY]; + for (int i = 0; i < len; ++i) + value[i] = seq.charAt(i); + } + + /** + * Increase the capacity of this StringBuffer. This will + * ensure that an expensive growing operation will not occur until + * minimumCapacity is reached. The buffer is grown to the + * larger of minimumCapacity and + * capacity() * 2 + 2, if it is not already large enough. + * + * @param minimumCapacity the new capacity + * @see #capacity() + */ + public void ensureCapacity(int minimumCapacity) + { + ensureCapacity_unsynchronized(minimumCapacity); + } + + /** + * Set the length of this StringBuffer. If the new length is greater than + * the current length, all the new characters are set to '\0'. If the new + * length is less than the current length, the first newLength + * characters of the old array will be preserved, and the remaining + * characters are truncated. + * + * @param newLength the new length + * @throws IndexOutOfBoundsException if the new length is negative + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #length() + */ + public void setLength(int newLength) + { + if (newLength < 0) + throw new StringIndexOutOfBoundsException(newLength); + + int valueLength = value.length; + + /* Always call ensureCapacity_unsynchronized in order to preserve + copy-on-write semantics. */ + ensureCapacity_unsynchronized(newLength); + + if (newLength < valueLength) + { + /* If the StringBuffer's value just grew, then we know that + value is newly allocated and the region between count and + newLength is filled with '\0'. */ + count = newLength; + } + else + { + /* The StringBuffer's value doesn't need to grow. However, + we should clear out any cruft that may exist. */ + while (count < newLength) + value[count++] = '\0'; + } + } + + /** + * Get the character at the specified index. + * + * @param index the index of the character to get, starting at 0 + * @return the character at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public char charAt(int index) + { + if (index < 0 || index >= count) + throw new StringIndexOutOfBoundsException(index); + return value[index]; + } + + /** + * Get the code point at the specified index. This is like #charAt(int), + * but if the character is the start of a surrogate pair, and the + * following character completes the pair, then the corresponding + * supplementary code point is returned. + * @param index the index of the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public int codePointAt(int index) + { + return Character.codePointAt(value, index, count); + } + + /** + * Get the code point before the specified index. This is like + * #codePointAt(int), but checks the characters at index-1 and + * index-2 to see if they form a supplementary code point. + * @param index the index just past the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public int codePointBefore(int index) + { + // Character.codePointBefore() doesn't perform this check. We + // could use the CharSequence overload, but this is just as easy. + if (index >= count) + throw new IndexOutOfBoundsException(); + return Character.codePointBefore(value, index, 1); + } + + /** + * Get the specified array of characters. srcOffset - srcEnd + * characters will be copied into the array you pass in. + * + * @param srcOffset the index to start copying from (inclusive) + * @param srcEnd the index to stop copying from (exclusive) + * @param dst the array to copy into + * @param dstOffset the index to start copying into + * @throws NullPointerException if dst is null + * @throws IndexOutOfBoundsException if any source or target indices are + * out of range (while unspecified, source problems cause a + * StringIndexOutOfBoundsException, and dest problems cause an + * ArrayIndexOutOfBoundsException) + * @see System#arraycopy(Object, int, Object, int, int) + */ + public void getChars(int srcOffset, int srcEnd, + char[] dst, int dstOffset) + { + if (srcOffset < 0 || srcEnd > count || srcEnd < srcOffset) + throw new StringIndexOutOfBoundsException(); + VMSystem.arraycopy(value, srcOffset, dst, dstOffset, srcEnd - srcOffset); + } + + /** + * Set the character at the specified index. + * + * @param index the index of the character to set starting at 0 + * @param ch the value to set that character to + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public void setCharAt(int index, char ch) + { + if (index < 0 || index >= count) + throw new StringIndexOutOfBoundsException(index); + // Call ensureCapacity to enforce copy-on-write. + ensureCapacity_unsynchronized(count); + value[index] = ch; + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param obj the Object to convert and append + * @return this StringBuffer + * @see String#valueOf(Object) + * @see #append(String) + */ + public AbstractStringBuffer append(Object obj) + { + return append(String.valueOf(obj)); + } + + /** + * Append the String to this StringBuffer. If + * str is null, the String "null" is appended. + * + * @param str the String to append + * @return this StringBuffer + */ + public AbstractStringBuffer append(String str) + { + if (str == null) + str = "null"; + int len = str.count; + ensureCapacity_unsynchronized(count + len); + str.getChars(0, len, value, count); + count += len; + return this; + } + + /** + * Append the StringBuilder value of the argument to this + * StringBuilder. This behaves the same as + * append((Object) stringBuffer), except it is more efficient. + * + * @param stringBuffer the StringBuilder to convert and append + * @return this StringBuilder + * @see #append(Object) + */ + public AbstractStringBuffer append(StringBuffer stringBuffer) + { + if (stringBuffer == null) + return append("null"); + synchronized (stringBuffer) + { + int len = stringBuffer.count; + ensureCapacity(count + len); + VMSystem.arraycopy(stringBuffer.value, 0, value, count, len); + count += len; + } + return this; + } + + /** + * Append the char array to this StringBuffer. + * This is similar (but more efficient) than + * append(new String(data)), except in the case of null. + * + * @param data the char[] to append + * @return this StringBuffer + * @throws NullPointerException if str is null + * @see #append(char[], int, int) + */ + public AbstractStringBuffer append(char[] data) + { + return append(data, 0, data.length); + } + + /** + * Append part of the char array to this + * StringBuffer. This is similar (but more efficient) than + * append(new String(data, offset, count)), except in the case + * of null. + * + * @param data the char[] to append + * @param offset the start location in str + * @param count the number of characters to get from str + * @return this StringBuffer + * @throws NullPointerException if str is null + * @throws IndexOutOfBoundsException if offset or count is out of range + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public AbstractStringBuffer append(char[] data, int offset, int count) + { + if (offset < 0 || count < 0 || offset > data.length - count) + throw new StringIndexOutOfBoundsException(); + ensureCapacity_unsynchronized(this.count + count); + VMSystem.arraycopy(data, offset, value, this.count, count); + this.count += count; + return this; + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param bool the boolean to convert and append + * @return this StringBuffer + * @see String#valueOf(boolean) + */ + public AbstractStringBuffer append(boolean bool) + { + return append(bool ? "true" : "false"); + } + + /** + * Append the char to this StringBuffer. + * + * @param ch the char to append + * @return this StringBuffer + */ + public AbstractStringBuffer append(char ch) + { + ensureCapacity_unsynchronized(count + 1); + value[count++] = ch; + return this; + } + + /** + * Append the characters in the CharSequence to this + * buffer. + * + * @param seq the CharSequence providing the characters + * @return this StringBuffer + * @since 1.5 + */ + public AbstractStringBuffer append(CharSequence seq) + { + return append(seq, 0, seq.length()); + } + + /** + * Append some characters from the CharSequence to this + * buffer. If the argument is null, the seq is assumed + * to be equal to the string "null". + * + * @param seq the CharSequence providing the characters + * @param start the starting index + * @param end one past the final index + * @return this StringBuffer + * @since 1.5 + */ + public AbstractStringBuffer append(CharSequence seq, int start, int end) + { + if (seq == null) + seq = "null"; + if (end - start > 0) + { + ensureCapacity_unsynchronized(count + end - start); + for (; start < end; ++start) + value[count++] = seq.charAt(start); + } + return this; + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param inum the int to convert and append + * @return this StringBuffer + * @see String#valueOf(int) + */ + // This is native in libgcj, for efficiency. + public AbstractStringBuffer append(int inum) + { + return append(String.valueOf(inum)); + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param lnum the long to convert and append + * @return this StringBuffer + * @see String#valueOf(long) + */ + public AbstractStringBuffer append(long lnum) + { + return append(Long.toString(lnum, 10)); + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param fnum the float to convert and append + * @return this StringBuffer + * @see String#valueOf(float) + */ + public AbstractStringBuffer append(float fnum) + { + return append(Float.toString(fnum)); + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param dnum the double to convert and append + * @return this StringBuffer + * @see String#valueOf(double) + */ + public AbstractStringBuffer append(double dnum) + { + return append(Double.toString(dnum)); + } + + /** + * Append the code point to this StringBuffer. + * This is like #append(char), but will append two characters + * if a supplementary code point is given. + * + * @param code the code point to append + * @return this StringBuffer + * @see Character#toChars(int, char[], int) + * @since 1.5 + */ + public AbstractStringBuffer appendCodePoint(int code) + { + int len = Character.charCount(code); + ensureCapacity_unsynchronized(count + len); + Character.toChars(code, value, count); + count += len; + return this; + } + + /** + * Delete characters from this StringBuffer. + * delete(10, 12) will delete 10 and 11, but not 12. It is + * harmless for end to be larger than length(). + * + * @param start the first character to delete + * @param end the index after the last character to delete + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + * @since 1.2 + */ + public AbstractStringBuffer delete(int start, int end) + { + if (start < 0 || start > count || start > end) + throw new StringIndexOutOfBoundsException(start); + if (end > count) + end = count; + ensureCapacity_unsynchronized(count); + if (count - end != 0) + VMSystem.arraycopy(value, end, value, start, count - end); + count -= end - start; + return this; + } + + /** + * Delete a character from this StringBuffer. + * + * @param index the index of the character to delete + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if index is out of bounds + * @since 1.2 + */ + public AbstractStringBuffer deleteCharAt(int index) + { + return delete(index, index + 1); + } + + /** + * Replace characters between index start (inclusive) and + * end (exclusive) with str. If end + * is larger than the size of this StringBuffer, all characters after + * start are replaced. + * + * @param start the beginning index of characters to delete (inclusive) + * @param end the ending index of characters to delete (exclusive) + * @param str the new String to insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + * @throws NullPointerException if str is null + * @since 1.2 + */ + public AbstractStringBuffer replace(int start, int end, String str) + { + if (start < 0 || start > count || start > end) + throw new StringIndexOutOfBoundsException(start); + + int len = str.count; + // Calculate the difference in 'count' after the replace. + int delta = len - (end > count ? count : end) + start; + ensureCapacity_unsynchronized(count + delta); + + if (delta != 0 && end < count) + VMSystem.arraycopy(value, end, value, end + delta, count - end); + + str.getChars(0, len, value, start); + count += delta; + return this; + } + + /** + * Insert a subarray of the char[] argument into this + * StringBuffer. + * + * @param offset the place to insert in this buffer + * @param str the char[] to insert + * @param str_offset the index in str to start inserting from + * @param len the number of characters to insert + * @return this StringBuffer + * @throws NullPointerException if str is null + * @throws StringIndexOutOfBoundsException if any index is out of bounds + * @since 1.2 + */ + public AbstractStringBuffer insert(int offset, char[] str, int str_offset, int len) + { + if (offset < 0 || offset > count || len < 0 + || str_offset < 0 || str_offset > str.length - len) + throw new StringIndexOutOfBoundsException(); + ensureCapacity_unsynchronized(count + len); + VMSystem.arraycopy(value, offset, value, offset + len, count - offset); + VMSystem.arraycopy(str, str_offset, value, offset, len); + count += len; + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param obj the Object to convert and insert + * @return this StringBuffer + * @exception StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(Object) + */ + public AbstractStringBuffer insert(int offset, Object obj) + { + return insert(offset, obj == null ? "null" : obj.toString()); + } + + /** + * Insert the String argument into this + * StringBuffer. If str is null, the String "null" is used + * instead. + * + * @param offset the place to insert in this buffer + * @param str the String to insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public AbstractStringBuffer insert(int offset, String str) + { + if (offset < 0 || offset > count) + throw new StringIndexOutOfBoundsException(offset); + if (str == null) + str = "null"; + int len = str.count; + ensureCapacity_unsynchronized(count + len); + VMSystem.arraycopy(value, offset, value, offset + len, count - offset); + str.getChars(0, len, value, offset); + count += len; + return this; + } + + /** + * Insert the CharSequence argument into this + * StringBuffer. If the sequence is null, the String + * "null" is used instead. + * + * @param offset the place to insert in this buffer + * @param sequence the CharSequence to insert + * @return this StringBuffer + * @throws IndexOutOfBoundsException if offset is out of bounds + * @since 1.5 + */ + public AbstractStringBuffer insert(int offset, CharSequence sequence) + { + if (sequence == null) + sequence = "null"; + return insert(offset, sequence, 0, sequence.length()); + } + + /** + * Insert a subsequence of the CharSequence argument into this + * StringBuffer. If the sequence is null, the String + * "null" is used instead. + * + * @param offset the place to insert in this buffer + * @param sequence the CharSequence to insert + * @param start the starting index of the subsequence + * @param end one past the ending index of the subsequence + * @return this StringBuffer + * @throws IndexOutOfBoundsException if offset, start, + * or end are out of bounds + * @since 1.5 + */ + public AbstractStringBuffer insert(int offset, CharSequence sequence, int start, int end) + { + if (sequence == null) + sequence = "null"; + if (start < 0 || end < 0 || start > end || end > sequence.length()) + throw new IndexOutOfBoundsException(); + int len = end - start; + ensureCapacity_unsynchronized(count + len); + VMSystem.arraycopy(value, offset, value, offset + len, count - offset); + for (int i = start; i < end; ++i) + value[offset++] = sequence.charAt(i); + count += len; + return this; + } + + /** + * Insert the char[] argument into this + * StringBuffer. + * + * @param offset the place to insert in this buffer + * @param data the char[] to insert + * @return this StringBuffer + * @throws NullPointerException if data is null + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see #insert(int, char[], int, int) + */ + public AbstractStringBuffer insert(int offset, char[] data) + { + return insert(offset, data, 0, data.length); + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param bool the boolean to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(boolean) + */ + public AbstractStringBuffer insert(int offset, boolean bool) + { + return insert(offset, bool ? "true" : "false"); + } + + /** + * Insert the char argument into this StringBuffer. + * + * @param offset the place to insert in this buffer + * @param ch the char to insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public AbstractStringBuffer insert(int offset, char ch) + { + if (offset < 0 || offset > count) + throw new StringIndexOutOfBoundsException(offset); + ensureCapacity_unsynchronized(count + 1); + VMSystem.arraycopy(value, offset, value, offset + 1, count - offset); + value[offset] = ch; + count++; + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param inum the int to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(int) + */ + public AbstractStringBuffer insert(int offset, int inum) + { + return insert(offset, String.valueOf(inum)); + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param lnum the long to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(long) + */ + public AbstractStringBuffer insert(int offset, long lnum) + { + return insert(offset, Long.toString(lnum, 10)); + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param fnum the float to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(float) + */ + public AbstractStringBuffer insert(int offset, float fnum) + { + return insert(offset, Float.toString(fnum)); + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param dnum the double to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(double) + */ + public AbstractStringBuffer insert(int offset, double dnum) + { + return insert(offset, Double.toString(dnum)); + } + + /** + * Finds the first instance of a substring in this StringBuilder. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @see #indexOf(String, int) + */ + public int indexOf(String str) + { + return indexOf(str, 0); + } + + /** + * Finds the first instance of a String in this StringBuffer, starting at + * a given index. If starting index is less than 0, the search starts at + * the beginning of this String. If the starting index is greater than the + * length of this String, or the substring is not found, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @since 1.4 + */ + public int indexOf(String str, int fromIndex) + { + if (fromIndex < 0) + fromIndex = 0; + int limit = count - str.count; + for ( ; fromIndex <= limit; fromIndex++) + if (regionMatches(fromIndex, str)) + return fromIndex; + return -1; + } + + /** + * Finds the last instance of a substring in this StringBuffer. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @see #lastIndexOf(String, int) + * @since 1.4 + */ + public int lastIndexOf(String str) + { + return lastIndexOf(str, count - str.count); + } + + /** + * Finds the last instance of a String in this StringBuffer, starting at a + * given index. If starting index is greater than the maximum valid index, + * then the search begins at the end of this String. If the starting index + * is less than zero, or the substring is not found, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @since 1.4 + */ + public int lastIndexOf(String str, int fromIndex) + { + fromIndex = Math.min(fromIndex, count - str.count); + for ( ; fromIndex >= 0; fromIndex--) + if (regionMatches(fromIndex, str)) + return fromIndex; + return -1; + } + + /** + * Reverse the characters in this StringBuffer. The same sequence of + * characters exists, but in the reverse index ordering. + * + * @return this StringBuffer + */ + public AbstractStringBuffer reverse() + { + // Call ensureCapacity to enforce copy-on-write. + ensureCapacity_unsynchronized(count); + for (int i = count >> 1, j = count - i; --i >= 0; ++j) + { + char c = value[i]; + value[i] = value[j]; + value[j] = c; + } + return this; + } + + /** + * This may reduce the amount of memory used by the StringBuffer, + * by resizing the internal array to remove unused space. However, + * this method is not required to resize, so this behavior cannot + * be relied upon. + * @since 1.5 + */ + public void trimToSize() + { + int wouldSave = value.length - count; + // Some random heuristics: if we save less than 20 characters, who + // cares. + if (wouldSave < 20) + return; + // If we save more than 200 characters, shrink. + // If we save more than 1/4 of the buffer, shrink. + if (wouldSave > 200 || wouldSave * 4 > value.length) + { + char[] newValue = new char[count]; + VMSystem.arraycopy(value, 0, newValue, 0, count); + value = newValue; + } + } + + /** + * Return the number of code points between two indices in the + * StringBuffer. An unpaired surrogate counts as a + * code point for this purpose. Characters outside the indicated + * range are not examined, even if the range ends in the middle of a + * surrogate pair. + * + * @param start the starting index + * @param end one past the ending index + * @return the number of code points + * @since 1.5 + */ + public int codePointCount(int start, int end) + { + if (start < 0 || end >= count || start > end) + throw new StringIndexOutOfBoundsException(); + + int count = 0; + while (start < end) + { + char base = value[start]; + if (base < Character.MIN_HIGH_SURROGATE + || base > Character.MAX_HIGH_SURROGATE + || start == end + || start == count + || value[start + 1] < Character.MIN_LOW_SURROGATE + || value[start + 1] > Character.MAX_LOW_SURROGATE) + { + // Nothing. + } + else + { + // Surrogate pair. + ++start; + } + ++start; + ++count; + } + return count; + } + + /** + * Starting at the given index, this counts forward by the indicated + * number of code points, and then returns the resulting index. An + * unpaired surrogate counts as a single code point for this + * purpose. + * + * @param start the starting index + * @param codePoints the number of code points + * @return the resulting index + * @since 1.5 + */ + public int offsetByCodePoints(int start, int codePoints) + { + while (codePoints > 0) + { + char base = value[start]; + if (base < Character.MIN_HIGH_SURROGATE + || base > Character.MAX_HIGH_SURROGATE + || start == count + || value[start + 1] < Character.MIN_LOW_SURROGATE + || value[start + 1] > Character.MAX_LOW_SURROGATE) + { + // Nothing. + } + else + { + // Surrogate pair. + ++start; + } + ++start; + --codePoints; + } + return start; + } + + /** + * Increase the capacity of this StringBuilder. This will + * ensure that an expensive growing operation will not occur until + * minimumCapacity is reached. The buffer is grown to the + * larger of minimumCapacity and + * capacity() * 2 + 2, if it is not already large enough. + * + * @param minimumCapacity the new capacity + * @see #capacity() + */ + void ensureCapacity_unsynchronized(int minimumCapacity) + { + if (minimumCapacity > value.length) + { + int max = value.length * 2 + 2; + minimumCapacity = (minimumCapacity < max ? max : minimumCapacity); + char[] nb = new char[minimumCapacity]; + VMSystem.arraycopy(value, 0, nb, 0, count); + value = nb; + } + } + + /** + * Predicate which determines if a substring of this matches another String + * starting at a specified offset for each String and continuing for a + * specified length. This is more efficient than creating a String to call + * indexOf on. + * + * @param toffset index to start comparison at for this String + * @param other non-null String to compare to region of this + * @return true if regions match, false otherwise + * @see #indexOf(String, int) + * @see #lastIndexOf(String, int) + * @see String#regionMatches(boolean, int, String, int, int) + */ + private boolean regionMatches(int toffset, String other) + { + int len = other.count; + int index = other.offset; + while (--len >= 0) + if (value[toffset++] != other.value[index++]) + return false; + return true; + } + +} diff --git a/libjava/classpath/java/lang/Appendable.java b/libjava/classpath/java/lang/Appendable.java new file mode 100644 index 000000000..c09667733 --- /dev/null +++ b/libjava/classpath/java/lang/Appendable.java @@ -0,0 +1,122 @@ +/* Appendable.java -- Something to which characters can be appended + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.IOException; + +/** + *

    + * An Appendable object is one to which a sequence of Unicode + * characters can be added. The appended characters must be valid Unicode + * characters, and may include supplementary characters, composed of multiple + * 16-bit char values. + *

    + *

    + * The behaviour of the Appendable object is heavily dependent + * on the particular implementation being used. Some implementations may be + * thread-safe, while others may not. Likewise, some implementing classes + * may produce errors which aren't propogated to the invoking class, due + * to differences in the error handling used. + *

    + *

    + * Note: implementation of this interface is required for + * any class that wishes to receive data from a Formatter + * instance. + *

    + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Appendable +{ + + /** + * Appends the Unicode character, c, to this Appendable + * object. + * + * @param c the character to append. + * @return a reference to this object. + * @throws IOException if an I/O error occurs. + */ + Appendable append(char c) + throws IOException; + + /** + * Appends the specified sequence of Unicode characters to this + * Appendable object. The entire sequence may not + * be appended, if constrained by the underlying implementation. + * For example, a buffer may reach its size limit before the entire + * sequence is appended. + * + * @param seq the character sequence to append. If seq is null, + * then the string "null" (the string representation of null) + * is appended. + * @return a reference to this object. + * @throws IOException if an I/O error occurs. + */ + Appendable append(CharSequence seq) + throws IOException; + + /** + * Appends the specified subsequence of Unicode characters to this + * Appendable object, starting and ending at the specified + * positions within the sequence. The entire sequence may not + * be appended, if constrained by the underlying implementation. + * For example, a buffer may reach its size limit before the entire + * sequence is appended. The behaviour of this method matches the + * behaviour of append(seq.subSequence(start,end)) when + * the sequence is not null. + * + * @param seq the character sequence to append. If seq is null, + * then the string "null" (the string representation of null) + * is appended. + * @param start the index of the first Unicode character to use from + * the sequence. + * @param end the index of the last Unicode character to use from the + * sequence. + * @return a reference to this object. + * @throws IOException if an I/O error occurs. + * @throws IndexOutOfBoundsException if either of the indices are negative, + * the start index occurs after the end index, or the end index is + * beyond the end of the sequence. + */ + Appendable append(CharSequence seq, int start, int end) + throws IOException; + +} diff --git a/libjava/classpath/java/lang/ArithmeticException.java b/libjava/classpath/java/lang/ArithmeticException.java new file mode 100644 index 000000000..5acea4353 --- /dev/null +++ b/libjava/classpath/java/lang/ArithmeticException.java @@ -0,0 +1,77 @@ +/* ArithmeticException.java -- exception thrown to indicate conditions + like divide by zero. + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a math error has occured, such as trying to divide an + * integer by zero. For example:
    + *
    + * int i = 0;
    + * int j = 2 / i;
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ArithmeticException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 2256477558314496007L; + + /** + * Create an exception without a message. + */ + public ArithmeticException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ArithmeticException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/ArrayIndexOutOfBoundsException.java b/libjava/classpath/java/lang/ArrayIndexOutOfBoundsException.java new file mode 100644 index 000000000..371623bda --- /dev/null +++ b/libjava/classpath/java/lang/ArrayIndexOutOfBoundsException.java @@ -0,0 +1,87 @@ +/* ArrayIndexOutOfBoundsException.java -- exception thrown when accessing + an illegal index. + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when attempting to access a position outside the valid range of + * an array. For example:
    + *
    + * int[] i = { 1 };
    + * i[1] = 2;
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5116101128118950844L; + + /** + * Create an exception without a message. + */ + public ArrayIndexOutOfBoundsException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ArrayIndexOutOfBoundsException(String s) + { + super(s); + } + + /** + * Create an exception indicating the illegal index. + * + * @param index the invalid index + */ + public ArrayIndexOutOfBoundsException(int index) + { + super("Array index out of range: " + index); + } +} diff --git a/libjava/classpath/java/lang/ArrayStoreException.java b/libjava/classpath/java/lang/ArrayStoreException.java new file mode 100644 index 000000000..042e78c55 --- /dev/null +++ b/libjava/classpath/java/lang/ArrayStoreException.java @@ -0,0 +1,77 @@ +/* ArrayStoreException.java -- exception thrown to when trying to store an + object into an array of a different type. + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when trying to store an object of the wrong runtime type in an + * array. For example:
    + *
    + * Object[] o = new Integer[1];
    + * o[0] = "oops";
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ArrayStoreException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4522193890499838241L; + + /** + * Create an exception without a message. + */ + public ArrayStoreException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ArrayStoreException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/AssertionError.java b/libjava/classpath/java/lang/AssertionError.java new file mode 100644 index 000000000..778eb5830 --- /dev/null +++ b/libjava/classpath/java/lang/AssertionError.java @@ -0,0 +1,148 @@ +/* AssertionError.java -- indication of a failed assertion + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An assertion error normally occurs as a result of the assert + * statement added in JDK 1.4, to indicate that an assertion failed. There + * are enough constructors to ensure that + * new AssertionError(expression) will work for all + * expressions, regardless of type, as if the error message were given by + * the string "" + expression. This extends Error, + * because you usually do not want to inadvertently trap an assertion failure. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class AssertionError extends Error +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -5013299493970297370L; + + /** + * Construct an AssertionError with no detail message. + */ + public AssertionError() + { + } + + /** + * Construct an AssertionError with the string conversion of the given + * object as its error message. If the object is a Throwable, it is also + * set as the cause of this error. + * + * @param msg the source of the error message + * @see Throwable#getCause() + */ + public AssertionError(Object msg) + { + super("" + msg); + if (msg instanceof Throwable) + initCause((Throwable) msg); + } + + /** + * Construct an AssertionError with the string conversion of the given + * boolean as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(boolean msg) + { + super(msg ? "true" : "false"); + } + + /** + * Construct an AssertionError with the string conversion of the given + * char as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(char msg) + { + super(String.valueOf(msg)); + } + + /** + * Construct an AssertionError with the string conversion of the given + * int as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(int msg) + { + super(Integer.toString(msg, 10)); + } + + /** + * Construct an AssertionError with the string conversion of the given + * long as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(long msg) + { + super(Long.toString(msg)); + } + + /** + * Construct an AssertionError with the string conversion of the given + * float as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(float msg) + { + super(Float.toString(msg)); + } + + /** + * Construct an AssertionError with the string conversion of the given + * double as its error message. + * + * @param msg the source of the error message + */ + public AssertionError(double msg) + { + super(Double.toString(msg)); + } +} diff --git a/libjava/classpath/java/lang/Boolean.java b/libjava/classpath/java/lang/Boolean.java new file mode 100644 index 000000000..f2eaf4125 --- /dev/null +++ b/libjava/classpath/java/lang/Boolean.java @@ -0,0 +1,251 @@ +/* Boolean.java -- object wrapper for boolean + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.Serializable; + +/** + * Instances of class Boolean represent primitive + * boolean values. + * + * @author Paul Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.5 + */ +public final class Boolean implements Serializable, Comparable +{ + /** + * Compatible with JDK 1.0.2+. + */ + private static final long serialVersionUID = -3665804199014368530L; + + /** + * This field is a Boolean object representing the + * primitive value true. This instance is returned + * by the static valueOf() methods if they return + * a Boolean representing true. + */ + public static final Boolean TRUE = new Boolean(true); + + /** + * This field is a Boolean object representing the + * primitive value false. This instance is returned + * by the static valueOf() methods if they return + * a Boolean representing false. + */ + public static final Boolean FALSE = new Boolean(false); + + /** + * The primitive type boolean is represented by this + * Class object. + * + * @since 1.1 + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass('Z'); + + /** + * The immutable value of this Boolean. + * @serial the wrapped value + */ + private final boolean value; + + /** + * Create a Boolean object representing the value of the + * argument value. In general the use of the static + * method valueof(boolean) is more efficient since it will + * not create a new object. + * + * @param value the primitive value of this Boolean + * @see #valueOf(boolean) + */ + public Boolean(boolean value) + { + this.value = value; + } + + /** + * Creates a Boolean object representing the primitive + * true if and only if s matches + * the string "true" ignoring case, otherwise the object will represent + * the primitive false. In general the use of the static + * method valueof(String) is more efficient since it will + * not create a new object. + * + * @param s the String representation of true + * or false + */ + public Boolean(String s) + { + value = "true".equalsIgnoreCase(s); + } + + /** + * Return the primitive boolean value of this + * Boolean object. + * + * @return true or false, depending on the value of this Boolean + */ + public boolean booleanValue() + { + return value; + } + + /** + * Returns the Boolean TRUE if the given boolean is + * true, otherwise it will return the Boolean + * FALSE. + * + * @param b the boolean to wrap + * @return the wrapper object + * @see #TRUE + * @see #FALSE + * @since 1.4 + */ + public static Boolean valueOf(boolean b) + { + return b ? TRUE : FALSE; + } + + /** + * Returns the Boolean TRUE if and only if the given + * String is equal, ignoring case, to the the String "true", otherwise + * it will return the Boolean FALSE. + * + * @param s the string to convert + * @return a wrapped boolean from the string + */ + public static Boolean valueOf(String s) + { + return "true".equalsIgnoreCase(s) ? TRUE : FALSE; + } + + /** + * Returns "true" if the value of the give boolean is true and + * returns "false" if the value of the given boolean is false. + * + * @param b the boolean to convert + * @return the string representation of the boolean + * @since 1.4 + */ + public static String toString(boolean b) + { + return b ? "true" : "false"; + } + + /** + * Returns "true" if the value of this object is true and + * returns "false" if the value of this object is false. + * + * @return the string representation of this + */ + public String toString() + { + return value ? "true" : "false"; + } + + /** + * Returns the integer 1231 if this object represents + * the primitive true and the integer 1237 + * otherwise. + * + * @return the hash code + */ + public int hashCode() + { + return value ? 1231 : 1237; + } + + /** + * If the obj is an instance of Boolean and + * has the same primitive value as this object then true + * is returned. In all other cases, including if the obj + * is null, false is returned. + * + * @param obj possibly an instance of any Class + * @return true if obj equals this + */ + public boolean equals(Object obj) + { + return obj instanceof Boolean && value == ((Boolean) obj).value; + } + + /** + * If the value of the system property name matches + * "true" ignoring case then the function returns true. + * + * @param name the property name to look up + * @return true if the property resulted in "true" + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + */ + public static boolean getBoolean(String name) + { + if (name == null || "".equals(name)) + return false; + return "true".equalsIgnoreCase(System.getProperty(name)); + } + + /** + * Compares this Boolean to another. + * + * @param other the Boolean to compare this Boolean to + * @return 0 if both Booleans represent the same value, a positive number + * if this Boolean represents true and the other false, and a negative + * number otherwise. + * @since 1.5 + */ + public int compareTo(Boolean other) + { + return value == other.value ? 0 : (value ? 1 : -1); + } + + /** + * If the String argument is "true", ignoring case, return true. + * Otherwise, return false. + * + * @param b String to parse + * @since 1.5 + */ + public static boolean parseBoolean(String b) + { + return "true".equalsIgnoreCase(b) ? true : false; + } + +} diff --git a/libjava/classpath/java/lang/Byte.java b/libjava/classpath/java/lang/Byte.java new file mode 100644 index 000000000..a1536e1be --- /dev/null +++ b/libjava/classpath/java/lang/Byte.java @@ -0,0 +1,373 @@ +/* Byte.java -- object wrapper for byte + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class Byte represent primitive byte + * values. + * + * Additionally, this class provides various helper functions and variables + * useful to bytes. + * + * @author Paul Fisher + * @author John Keiser + * @author Per Bothner + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.1 + * @status updated to 1.5 + */ +public final class Byte extends Number implements Comparable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7183698231559129828L; + + /** + * The minimum value a byte can represent is -128 (or + * -27). + */ + public static final byte MIN_VALUE = -128; + + /** + * The maximum value a byte can represent is 127 (or + * 27 - 1). + */ + public static final byte MAX_VALUE = 127; + + /** + * The primitive type byte is represented by this + * Class object. + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass('B'); + + /** + * The number of bits needed to represent a byte. + * @since 1.5 + */ + public static final int SIZE = 8; + + // This caches Byte values, and is used by boxing conversions via + // valueOf(). We're required to cache all possible values here. + private static Byte[] byteCache = new Byte[MAX_VALUE - MIN_VALUE + 1]; + static + { + for (int i=MIN_VALUE; i <= MAX_VALUE; i++) + byteCache[i - MIN_VALUE] = new Byte((byte) i); + } + + + /** + * The immutable value of this Byte. + * + * @serial the wrapped byte + */ + private final byte value; + + /** + * Create a Byte object representing the value of the + * byte argument. + * + * @param value the value to use + */ + public Byte(byte value) + { + this.value = value; + } + + /** + * Create a Byte object representing the value specified + * by the String argument + * + * @param s the string to convert + * @throws NumberFormatException if the String does not contain a byte + * @see #valueOf(String) + */ + public Byte(String s) + { + value = parseByte(s, 10); + } + + /** + * Converts the byte to a String and assumes + * a radix of 10. + * + * @param b the byte to convert to String + * @return the String representation of the argument + */ + public static String toString(byte b) + { + return String.valueOf(b); + } + + /** + * Converts the specified String into a byte. + * This function assumes a radix of 10. + * + * @param s the String to convert + * @return the byte value of s + * @throws NumberFormatException if s cannot be parsed as a + * byte + * @see #parseByte(String) + */ + public static byte parseByte(String s) + { + return parseByte(s, 10); + } + + /** + * Converts the specified String into an int + * using the specified radix (base). The string must not be null + * or empty. It may begin with an optional '-', which will negate the answer, + * provided that there are also valid digits. Each digit is parsed as if by + * Character.digit(d, radix), and must be in the range + * 0 to radix - 1. Finally, the result must be + * within MIN_VALUE to MAX_VALUE, inclusive. + * Unlike Double.parseDouble, you may not have a leading '+'. + * + * @param s the String to convert + * @param radix the radix (base) to use in the conversion + * @return the String argument converted to byte + * @throws NumberFormatException if s cannot be parsed as a + * byte + */ + public static byte parseByte(String s, int radix) + { + int i = Integer.parseInt(s, radix, false); + if ((byte) i != i) + throw new NumberFormatException(); + return (byte) i; + } + + /** + * Creates a new Byte object using the String + * and specified radix (base). + * + * @param s the String to convert + * @param radix the radix (base) to convert with + * @return the new Byte + * @throws NumberFormatException if s cannot be parsed as a + * byte + * @see #parseByte(String, int) + */ + public static Byte valueOf(String s, int radix) + { + return valueOf(parseByte(s, radix)); + } + + /** + * Creates a new Byte object using the String, + * assuming a radix of 10. + * + * @param s the String to convert + * @return the new Byte + * @throws NumberFormatException if s cannot be parsed as a + * byte + * @see #Byte(String) + * @see #parseByte(String) + */ + public static Byte valueOf(String s) + { + return valueOf(parseByte(s, 10)); + } + + /** + * Returns a Byte object wrapping the value. + * In contrast to the Byte constructor, this method + * will cache some values. It is used by boxing conversion. + * + * @param val the value to wrap + * @return the Byte + */ + public static Byte valueOf(byte val) + { + return byteCache[val - MIN_VALUE]; + } + + /** + * Convert the specified String into a Byte. + * The String may represent decimal, hexadecimal, or + * octal numbers. + * + *

    The extended BNF grammar is as follows:
    + *

    +   * DecodableString:
    +   *      ( [ - ] DecimalNumber )
    +   *    | ( [ - ] ( 0x | 0X
    +   *              | # ) { HexDigit }+ )
    +   *    | ( [ - ] 0 { OctalDigit } )
    +   * DecimalNumber:
    +   *        DecimalDigit except '0' { DecimalDigit }
    +   * DecimalDigit:
    +   *        Character.digit(d, 10) has value 0 to 9
    +   * OctalDigit:
    +   *        Character.digit(d, 8) has value 0 to 7
    +   * DecimalDigit:
    +   *        Character.digit(d, 16) has value 0 to 15
    +   * 
    + * Finally, the value must be in the range MIN_VALUE to + * MAX_VALUE, or an exception is thrown. + * + * @param s the String to interpret + * @return the value of the String as a Byte + * @throws NumberFormatException if s cannot be parsed as a + * byte + * @throws NullPointerException if s is null + * @see Integer#decode(String) + */ + public static Byte decode(String s) + { + int i = Integer.parseInt(s, 10, true); + if ((byte) i != i) + throw new NumberFormatException(); + return valueOf((byte) i); + } + + /** + * Return the value of this Byte. + * + * @return the byte value + */ + public byte byteValue() + { + return value; + } + + /** + * Return the value of this Byte as a short. + * + * @return the short value + */ + public short shortValue() + { + return value; + } + + /** + * Return the value of this Byte as an int. + * + * @return the int value + */ + public int intValue() + { + return value; + } + + /** + * Return the value of this Byte as a long. + * + * @return the long value + */ + public long longValue() + { + return value; + } + + /** + * Return the value of this Byte as a float. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this Byte as a double. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Converts the Byte value to a String and + * assumes a radix of 10. + * + * @return the String representation of this Byte + * @see Integer#toString() + */ + public String toString() + { + return String.valueOf(value); + } + + /** + * Return a hashcode representing this Object. Byte's hash + * code is simply its value. + * + * @return this Object's hash code + */ + public int hashCode() + { + return value; + } + + /** + * Returns true if obj is an instance of + * Byte and represents the same byte value. + * + * @param obj the object to compare + * @return whether these Objects are semantically equal + */ + public boolean equals(Object obj) + { + return obj instanceof Byte && value == ((Byte) obj).value; + } + + /** + * Compare two Bytes numerically by comparing their byte values. + * The result is positive if the first is greater, negative if the second + * is greater, and 0 if the two are equal. + * + * @param b the Byte to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Byte b) + { + return value - b.value; + } + +} diff --git a/libjava/classpath/java/lang/CharSequence.java b/libjava/classpath/java/lang/CharSequence.java new file mode 100644 index 000000000..5c014e173 --- /dev/null +++ b/libjava/classpath/java/lang/CharSequence.java @@ -0,0 +1,99 @@ +/* CharSequence.java -- Anything that has an indexed sequence of chars + Copyright (C) 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * General functions on a sequence of chars. This interface is implemented + * by String, StringBuffer and + * CharBuffer to give a uniform way to get chars at a certain + * index, the number of characters in the sequence and a subrange of the + * chars. Indexes start at 0 and the last index is length()-1. + * + *

    Even when classes implement this interface they are not always + * exchangeble because they might implement their compare, equals or hash + * function differently. This means that in general one should not use a + * CharSequence as keys in collections since two sequences + * with the same chars at the same indexes with the same length might not + * have the same hash code, be equal or be comparable since the are + * represented by different classes. + * + * @author Mark Wielaard (mark@klomp.org) + * @since 1.4 + * @status updated to 1.4 + */ +public interface CharSequence +{ + /** + * Returns the character at the given index. + * + * @param i the index to retrieve from + * @return the character at that location + * @throws IndexOutOfBoundsException if i < 0 || i >= length() - 1 + */ + char charAt(int i); + + /** + * Returns the length of the sequence. This is the number of 16-bit + * characters in the sequence, which may differ from the length of the + * underlying encoding. + * + * @return the sequence length + */ + int length(); + + /** + * Returns a new CharSequence of the indicated range. + * + * @param begin the start index (inclusive) + * @param end the end index (exclusive) + * @return a subsequence of this + * @throws IndexOutOfBoundsException if begin > end || begin < 0 || + * end > length() + */ + CharSequence subSequence(int begin, int end); + + /** + * Returns the complete CharSequence as a String. + * Classes that implement this interface should return a String + * which contains only the characters in the sequence in the correct order. + * + * @return the character sequence as a String + */ + String toString(); +} diff --git a/libjava/classpath/java/lang/Character.java b/libjava/classpath/java/lang/Character.java new file mode 100644 index 000000000..05e641c3a --- /dev/null +++ b/libjava/classpath/java/lang/Character.java @@ -0,0 +1,4552 @@ +/* java.lang.Character -- Wrapper class for char, and Unicode subsets + Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.java.lang.CharData; + +import java.io.Serializable; +import java.text.Collator; +import java.util.Locale; + +/** + * Wrapper class for the primitive char data type. In addition, this class + * allows one to retrieve property information and perform transformations + * on the defined characters in the Unicode Standard, Version 4.0.0. + * java.lang.Character is designed to be very dynamic, and as such, it + * retrieves information on the Unicode character set from a separate + * database, gnu.java.lang.CharData, which can be easily upgraded. + * + *

    For predicates, boundaries are used to describe + * the set of characters for which the method will return true. + * This syntax uses fairly normal regular expression notation. + * See 5.13 of the Unicode Standard, Version 4.0, for the + * boundary specification. + * + *

    See http://www.unicode.org + * for more information on the Unicode Standard. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Paul N. Fisher + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see CharData + * @since 1.0 + * @status partly updated to 1.5; some things still missing + */ +public final class Character implements Serializable, Comparable +{ + /** + * A subset of Unicode blocks. + * + * @author Paul N. Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + */ + public static class Subset + { + /** The name of the subset. */ + private final String name; + + /** + * Construct a new subset of characters. + * + * @param name the name of the subset + * @throws NullPointerException if name is null + */ + protected Subset(String name) + { + // Note that name.toString() is name, unless name was null. + this.name = name.toString(); + } + + /** + * Compares two Subsets for equality. This is final, and + * restricts the comparison on the == operator, so it returns + * true only for the same object. + * + * @param o the object to compare + * @return true if o is this + */ + public final boolean equals(Object o) + { + return o == this; + } + + /** + * Makes the original hashCode of Object final, to be consistent with + * equals. + * + * @return the hash code for this object + */ + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Returns the name of the subset. + * + * @return the name + */ + public final String toString() + { + return name; + } + } // class Subset + + /** + * A family of character subsets in the Unicode specification. A character + * is in at most one of these blocks. + * + * This inner class was generated automatically from + * doc/unicode/Blocks-4.0.0.txt, by some perl scripts. + * This Unicode definition file can be found on the + * http://www.unicode.org website. + * JDK 1.5 uses Unicode version 4.0.0. + * + * @author scripts/unicode-blocks.pl (written by Eric Blake) + * @since 1.2 + */ + public static final class UnicodeBlock extends Subset + { + /** The start of the subset. */ + private final int start; + + /** The end of the subset. */ + private final int end; + + /** The canonical name of the block according to the Unicode standard. */ + private final String canonicalName; + + /** Enumeration for the forName() method */ + private enum NameType { CANONICAL, NO_SPACES, CONSTANT; } + + /** + * Constructor for strictly defined blocks. + * + * @param start the start character of the range + * @param end the end character of the range + * @param name the block name + * @param canonicalName the name of the block as defined in the Unicode + * standard. + */ + private UnicodeBlock(int start, int end, String name, + String canonicalName) + { + super(name); + this.start = start; + this.end = end; + this.canonicalName = canonicalName; + } + + /** + * Returns the Unicode character block which a character belongs to. + * Note: This method does not support the use of + * supplementary characters. For such support, of(int) + * should be used instead. + * + * @param ch the character to look up + * @return the set it belongs to, or null if it is not in one + */ + public static UnicodeBlock of(char ch) + { + return of((int) ch); + } + + /** + * Returns the Unicode character block which a code point belongs to. + * + * @param codePoint the character to look up + * @return the set it belongs to, or null if it is not in one. + * @throws IllegalArgumentException if the specified code point is + * invalid. + * @since 1.5 + */ + public static UnicodeBlock of(int codePoint) + { + if (codePoint > MAX_CODE_POINT) + throw new IllegalArgumentException("The supplied integer value is " + + "too large to be a codepoint."); + // Simple binary search for the correct block. + int low = 0; + int hi = sets.length - 1; + while (low <= hi) + { + int mid = (low + hi) >> 1; + UnicodeBlock b = sets[mid]; + if (codePoint < b.start) + hi = mid - 1; + else if (codePoint > b.end) + low = mid + 1; + else + return b; + } + return null; + } + + /** + *

    + * Returns the UnicodeBlock with the given name, as defined + * by the Unicode standard. The version of Unicode in use is defined by + * the Character class, and the names are given in the + * Blocks-.txt file corresponding to that version. + * The name may be specified in one of three ways: + *

    + *
      + *
    1. The canonical, human-readable name used by the Unicode standard. + * This is the name with all spaces and hyphens retained. For example, + * `Basic Latin' retrieves the block, UnicodeBlock.BASIC_LATIN.
    2. + *
    3. The canonical name with all spaces removed e.g. `BasicLatin'.
    4. + *
    5. The name used for the constants specified by this class, which + * is the canonical name with all spaces and hyphens replaced with + * underscores e.g. `BASIC_LATIN'
    6. + *
    + *

    + * The names are compared case-insensitively using the case comparison + * associated with the U.S. English locale. The method recognises the + * previous names used for blocks as well as the current ones. At + * present, this simply means that the deprecated `SURROGATES_AREA' + * will be recognised by this method (the of() methods + * only return one of the three new surrogate blocks). + *

    + * + * @param blockName the name of the block to look up. + * @return the specified block. + * @throws NullPointerException if the blockName is + * null. + * @throws IllegalArgumentException if the name does not match any Unicode + * block. + * @since 1.5 + */ + public static final UnicodeBlock forName(String blockName) + { + NameType type; + if (blockName.indexOf(' ') != -1) + type = NameType.CANONICAL; + else if (blockName.indexOf('_') != -1) + type = NameType.CONSTANT; + else + type = NameType.NO_SPACES; + Collator usCollator = Collator.getInstance(Locale.US); + usCollator.setStrength(Collator.PRIMARY); + /* Special case for deprecated blocks not in sets */ + switch (type) + { + case CANONICAL: + if (usCollator.compare(blockName, "Surrogates Area") == 0) + return SURROGATES_AREA; + break; + case NO_SPACES: + if (usCollator.compare(blockName, "SurrogatesArea") == 0) + return SURROGATES_AREA; + break; + case CONSTANT: + if (usCollator.compare(blockName, "SURROGATES_AREA") == 0) + return SURROGATES_AREA; + break; + } + /* Other cases */ + switch (type) + { + case CANONICAL: + for (UnicodeBlock block : sets) + if (usCollator.compare(blockName, block.canonicalName) == 0) + return block; + break; + case NO_SPACES: + for (UnicodeBlock block : sets) + { + String nsName = block.canonicalName.replaceAll(" ",""); + if (usCollator.compare(blockName, nsName) == 0) + return block; + } + break; + case CONSTANT: + for (UnicodeBlock block : sets) + if (usCollator.compare(blockName, block.toString()) == 0) + return block; + break; + } + throw new IllegalArgumentException("No Unicode block found for " + + blockName + "."); + } + + /** + * Basic Latin. + * 0x0000 - 0x007F. + */ + public static final UnicodeBlock BASIC_LATIN + = new UnicodeBlock(0x0000, 0x007F, + "BASIC_LATIN", + "Basic Latin"); + + /** + * Latin-1 Supplement. + * 0x0080 - 0x00FF. + */ + public static final UnicodeBlock LATIN_1_SUPPLEMENT + = new UnicodeBlock(0x0080, 0x00FF, + "LATIN_1_SUPPLEMENT", + "Latin-1 Supplement"); + + /** + * Latin Extended-A. + * 0x0100 - 0x017F. + */ + public static final UnicodeBlock LATIN_EXTENDED_A + = new UnicodeBlock(0x0100, 0x017F, + "LATIN_EXTENDED_A", + "Latin Extended-A"); + + /** + * Latin Extended-B. + * 0x0180 - 0x024F. + */ + public static final UnicodeBlock LATIN_EXTENDED_B + = new UnicodeBlock(0x0180, 0x024F, + "LATIN_EXTENDED_B", + "Latin Extended-B"); + + /** + * IPA Extensions. + * 0x0250 - 0x02AF. + */ + public static final UnicodeBlock IPA_EXTENSIONS + = new UnicodeBlock(0x0250, 0x02AF, + "IPA_EXTENSIONS", + "IPA Extensions"); + + /** + * Spacing Modifier Letters. + * 0x02B0 - 0x02FF. + */ + public static final UnicodeBlock SPACING_MODIFIER_LETTERS + = new UnicodeBlock(0x02B0, 0x02FF, + "SPACING_MODIFIER_LETTERS", + "Spacing Modifier Letters"); + + /** + * Combining Diacritical Marks. + * 0x0300 - 0x036F. + */ + public static final UnicodeBlock COMBINING_DIACRITICAL_MARKS + = new UnicodeBlock(0x0300, 0x036F, + "COMBINING_DIACRITICAL_MARKS", + "Combining Diacritical Marks"); + + /** + * Greek. + * 0x0370 - 0x03FF. + */ + public static final UnicodeBlock GREEK + = new UnicodeBlock(0x0370, 0x03FF, + "GREEK", + "Greek"); + + /** + * Cyrillic. + * 0x0400 - 0x04FF. + */ + public static final UnicodeBlock CYRILLIC + = new UnicodeBlock(0x0400, 0x04FF, + "CYRILLIC", + "Cyrillic"); + + /** + * Cyrillic Supplementary. + * 0x0500 - 0x052F. + * @since 1.5 + */ + public static final UnicodeBlock CYRILLIC_SUPPLEMENTARY + = new UnicodeBlock(0x0500, 0x052F, + "CYRILLIC_SUPPLEMENTARY", + "Cyrillic Supplementary"); + + /** + * Armenian. + * 0x0530 - 0x058F. + */ + public static final UnicodeBlock ARMENIAN + = new UnicodeBlock(0x0530, 0x058F, + "ARMENIAN", + "Armenian"); + + /** + * Hebrew. + * 0x0590 - 0x05FF. + */ + public static final UnicodeBlock HEBREW + = new UnicodeBlock(0x0590, 0x05FF, + "HEBREW", + "Hebrew"); + + /** + * Arabic. + * 0x0600 - 0x06FF. + */ + public static final UnicodeBlock ARABIC + = new UnicodeBlock(0x0600, 0x06FF, + "ARABIC", + "Arabic"); + + /** + * Syriac. + * 0x0700 - 0x074F. + * @since 1.4 + */ + public static final UnicodeBlock SYRIAC + = new UnicodeBlock(0x0700, 0x074F, + "SYRIAC", + "Syriac"); + + /** + * Thaana. + * 0x0780 - 0x07BF. + * @since 1.4 + */ + public static final UnicodeBlock THAANA + = new UnicodeBlock(0x0780, 0x07BF, + "THAANA", + "Thaana"); + + /** + * Devanagari. + * 0x0900 - 0x097F. + */ + public static final UnicodeBlock DEVANAGARI + = new UnicodeBlock(0x0900, 0x097F, + "DEVANAGARI", + "Devanagari"); + + /** + * Bengali. + * 0x0980 - 0x09FF. + */ + public static final UnicodeBlock BENGALI + = new UnicodeBlock(0x0980, 0x09FF, + "BENGALI", + "Bengali"); + + /** + * Gurmukhi. + * 0x0A00 - 0x0A7F. + */ + public static final UnicodeBlock GURMUKHI + = new UnicodeBlock(0x0A00, 0x0A7F, + "GURMUKHI", + "Gurmukhi"); + + /** + * Gujarati. + * 0x0A80 - 0x0AFF. + */ + public static final UnicodeBlock GUJARATI + = new UnicodeBlock(0x0A80, 0x0AFF, + "GUJARATI", + "Gujarati"); + + /** + * Oriya. + * 0x0B00 - 0x0B7F. + */ + public static final UnicodeBlock ORIYA + = new UnicodeBlock(0x0B00, 0x0B7F, + "ORIYA", + "Oriya"); + + /** + * Tamil. + * 0x0B80 - 0x0BFF. + */ + public static final UnicodeBlock TAMIL + = new UnicodeBlock(0x0B80, 0x0BFF, + "TAMIL", + "Tamil"); + + /** + * Telugu. + * 0x0C00 - 0x0C7F. + */ + public static final UnicodeBlock TELUGU + = new UnicodeBlock(0x0C00, 0x0C7F, + "TELUGU", + "Telugu"); + + /** + * Kannada. + * 0x0C80 - 0x0CFF. + */ + public static final UnicodeBlock KANNADA + = new UnicodeBlock(0x0C80, 0x0CFF, + "KANNADA", + "Kannada"); + + /** + * Malayalam. + * 0x0D00 - 0x0D7F. + */ + public static final UnicodeBlock MALAYALAM + = new UnicodeBlock(0x0D00, 0x0D7F, + "MALAYALAM", + "Malayalam"); + + /** + * Sinhala. + * 0x0D80 - 0x0DFF. + * @since 1.4 + */ + public static final UnicodeBlock SINHALA + = new UnicodeBlock(0x0D80, 0x0DFF, + "SINHALA", + "Sinhala"); + + /** + * Thai. + * 0x0E00 - 0x0E7F. + */ + public static final UnicodeBlock THAI + = new UnicodeBlock(0x0E00, 0x0E7F, + "THAI", + "Thai"); + + /** + * Lao. + * 0x0E80 - 0x0EFF. + */ + public static final UnicodeBlock LAO + = new UnicodeBlock(0x0E80, 0x0EFF, + "LAO", + "Lao"); + + /** + * Tibetan. + * 0x0F00 - 0x0FFF. + */ + public static final UnicodeBlock TIBETAN + = new UnicodeBlock(0x0F00, 0x0FFF, + "TIBETAN", + "Tibetan"); + + /** + * Myanmar. + * 0x1000 - 0x109F. + * @since 1.4 + */ + public static final UnicodeBlock MYANMAR + = new UnicodeBlock(0x1000, 0x109F, + "MYANMAR", + "Myanmar"); + + /** + * Georgian. + * 0x10A0 - 0x10FF. + */ + public static final UnicodeBlock GEORGIAN + = new UnicodeBlock(0x10A0, 0x10FF, + "GEORGIAN", + "Georgian"); + + /** + * Hangul Jamo. + * 0x1100 - 0x11FF. + */ + public static final UnicodeBlock HANGUL_JAMO + = new UnicodeBlock(0x1100, 0x11FF, + "HANGUL_JAMO", + "Hangul Jamo"); + + /** + * Ethiopic. + * 0x1200 - 0x137F. + * @since 1.4 + */ + public static final UnicodeBlock ETHIOPIC + = new UnicodeBlock(0x1200, 0x137F, + "ETHIOPIC", + "Ethiopic"); + + /** + * Cherokee. + * 0x13A0 - 0x13FF. + * @since 1.4 + */ + public static final UnicodeBlock CHEROKEE + = new UnicodeBlock(0x13A0, 0x13FF, + "CHEROKEE", + "Cherokee"); + + /** + * Unified Canadian Aboriginal Syllabics. + * 0x1400 - 0x167F. + * @since 1.4 + */ + public static final UnicodeBlock UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS + = new UnicodeBlock(0x1400, 0x167F, + "UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS", + "Unified Canadian Aboriginal Syllabics"); + + /** + * Ogham. + * 0x1680 - 0x169F. + * @since 1.4 + */ + public static final UnicodeBlock OGHAM + = new UnicodeBlock(0x1680, 0x169F, + "OGHAM", + "Ogham"); + + /** + * Runic. + * 0x16A0 - 0x16FF. + * @since 1.4 + */ + public static final UnicodeBlock RUNIC + = new UnicodeBlock(0x16A0, 0x16FF, + "RUNIC", + "Runic"); + + /** + * Tagalog. + * 0x1700 - 0x171F. + * @since 1.5 + */ + public static final UnicodeBlock TAGALOG + = new UnicodeBlock(0x1700, 0x171F, + "TAGALOG", + "Tagalog"); + + /** + * Hanunoo. + * 0x1720 - 0x173F. + * @since 1.5 + */ + public static final UnicodeBlock HANUNOO + = new UnicodeBlock(0x1720, 0x173F, + "HANUNOO", + "Hanunoo"); + + /** + * Buhid. + * 0x1740 - 0x175F. + * @since 1.5 + */ + public static final UnicodeBlock BUHID + = new UnicodeBlock(0x1740, 0x175F, + "BUHID", + "Buhid"); + + /** + * Tagbanwa. + * 0x1760 - 0x177F. + * @since 1.5 + */ + public static final UnicodeBlock TAGBANWA + = new UnicodeBlock(0x1760, 0x177F, + "TAGBANWA", + "Tagbanwa"); + + /** + * Khmer. + * 0x1780 - 0x17FF. + * @since 1.4 + */ + public static final UnicodeBlock KHMER + = new UnicodeBlock(0x1780, 0x17FF, + "KHMER", + "Khmer"); + + /** + * Mongolian. + * 0x1800 - 0x18AF. + * @since 1.4 + */ + public static final UnicodeBlock MONGOLIAN + = new UnicodeBlock(0x1800, 0x18AF, + "MONGOLIAN", + "Mongolian"); + + /** + * Limbu. + * 0x1900 - 0x194F. + * @since 1.5 + */ + public static final UnicodeBlock LIMBU + = new UnicodeBlock(0x1900, 0x194F, + "LIMBU", + "Limbu"); + + /** + * Tai Le. + * 0x1950 - 0x197F. + * @since 1.5 + */ + public static final UnicodeBlock TAI_LE + = new UnicodeBlock(0x1950, 0x197F, + "TAI_LE", + "Tai Le"); + + /** + * Khmer Symbols. + * 0x19E0 - 0x19FF. + * @since 1.5 + */ + public static final UnicodeBlock KHMER_SYMBOLS + = new UnicodeBlock(0x19E0, 0x19FF, + "KHMER_SYMBOLS", + "Khmer Symbols"); + + /** + * Phonetic Extensions. + * 0x1D00 - 0x1D7F. + * @since 1.5 + */ + public static final UnicodeBlock PHONETIC_EXTENSIONS + = new UnicodeBlock(0x1D00, 0x1D7F, + "PHONETIC_EXTENSIONS", + "Phonetic Extensions"); + + /** + * Latin Extended Additional. + * 0x1E00 - 0x1EFF. + */ + public static final UnicodeBlock LATIN_EXTENDED_ADDITIONAL + = new UnicodeBlock(0x1E00, 0x1EFF, + "LATIN_EXTENDED_ADDITIONAL", + "Latin Extended Additional"); + + /** + * Greek Extended. + * 0x1F00 - 0x1FFF. + */ + public static final UnicodeBlock GREEK_EXTENDED + = new UnicodeBlock(0x1F00, 0x1FFF, + "GREEK_EXTENDED", + "Greek Extended"); + + /** + * General Punctuation. + * 0x2000 - 0x206F. + */ + public static final UnicodeBlock GENERAL_PUNCTUATION + = new UnicodeBlock(0x2000, 0x206F, + "GENERAL_PUNCTUATION", + "General Punctuation"); + + /** + * Superscripts and Subscripts. + * 0x2070 - 0x209F. + */ + public static final UnicodeBlock SUPERSCRIPTS_AND_SUBSCRIPTS + = new UnicodeBlock(0x2070, 0x209F, + "SUPERSCRIPTS_AND_SUBSCRIPTS", + "Superscripts and Subscripts"); + + /** + * Currency Symbols. + * 0x20A0 - 0x20CF. + */ + public static final UnicodeBlock CURRENCY_SYMBOLS + = new UnicodeBlock(0x20A0, 0x20CF, + "CURRENCY_SYMBOLS", + "Currency Symbols"); + + /** + * Combining Marks for Symbols. + * 0x20D0 - 0x20FF. + */ + public static final UnicodeBlock COMBINING_MARKS_FOR_SYMBOLS + = new UnicodeBlock(0x20D0, 0x20FF, + "COMBINING_MARKS_FOR_SYMBOLS", + "Combining Marks for Symbols"); + + /** + * Letterlike Symbols. + * 0x2100 - 0x214F. + */ + public static final UnicodeBlock LETTERLIKE_SYMBOLS + = new UnicodeBlock(0x2100, 0x214F, + "LETTERLIKE_SYMBOLS", + "Letterlike Symbols"); + + /** + * Number Forms. + * 0x2150 - 0x218F. + */ + public static final UnicodeBlock NUMBER_FORMS + = new UnicodeBlock(0x2150, 0x218F, + "NUMBER_FORMS", + "Number Forms"); + + /** + * Arrows. + * 0x2190 - 0x21FF. + */ + public static final UnicodeBlock ARROWS + = new UnicodeBlock(0x2190, 0x21FF, + "ARROWS", + "Arrows"); + + /** + * Mathematical Operators. + * 0x2200 - 0x22FF. + */ + public static final UnicodeBlock MATHEMATICAL_OPERATORS + = new UnicodeBlock(0x2200, 0x22FF, + "MATHEMATICAL_OPERATORS", + "Mathematical Operators"); + + /** + * Miscellaneous Technical. + * 0x2300 - 0x23FF. + */ + public static final UnicodeBlock MISCELLANEOUS_TECHNICAL + = new UnicodeBlock(0x2300, 0x23FF, + "MISCELLANEOUS_TECHNICAL", + "Miscellaneous Technical"); + + /** + * Control Pictures. + * 0x2400 - 0x243F. + */ + public static final UnicodeBlock CONTROL_PICTURES + = new UnicodeBlock(0x2400, 0x243F, + "CONTROL_PICTURES", + "Control Pictures"); + + /** + * Optical Character Recognition. + * 0x2440 - 0x245F. + */ + public static final UnicodeBlock OPTICAL_CHARACTER_RECOGNITION + = new UnicodeBlock(0x2440, 0x245F, + "OPTICAL_CHARACTER_RECOGNITION", + "Optical Character Recognition"); + + /** + * Enclosed Alphanumerics. + * 0x2460 - 0x24FF. + */ + public static final UnicodeBlock ENCLOSED_ALPHANUMERICS + = new UnicodeBlock(0x2460, 0x24FF, + "ENCLOSED_ALPHANUMERICS", + "Enclosed Alphanumerics"); + + /** + * Box Drawing. + * 0x2500 - 0x257F. + */ + public static final UnicodeBlock BOX_DRAWING + = new UnicodeBlock(0x2500, 0x257F, + "BOX_DRAWING", + "Box Drawing"); + + /** + * Block Elements. + * 0x2580 - 0x259F. + */ + public static final UnicodeBlock BLOCK_ELEMENTS + = new UnicodeBlock(0x2580, 0x259F, + "BLOCK_ELEMENTS", + "Block Elements"); + + /** + * Geometric Shapes. + * 0x25A0 - 0x25FF. + */ + public static final UnicodeBlock GEOMETRIC_SHAPES + = new UnicodeBlock(0x25A0, 0x25FF, + "GEOMETRIC_SHAPES", + "Geometric Shapes"); + + /** + * Miscellaneous Symbols. + * 0x2600 - 0x26FF. + */ + public static final UnicodeBlock MISCELLANEOUS_SYMBOLS + = new UnicodeBlock(0x2600, 0x26FF, + "MISCELLANEOUS_SYMBOLS", + "Miscellaneous Symbols"); + + /** + * Dingbats. + * 0x2700 - 0x27BF. + */ + public static final UnicodeBlock DINGBATS + = new UnicodeBlock(0x2700, 0x27BF, + "DINGBATS", + "Dingbats"); + + /** + * Miscellaneous Mathematical Symbols-A. + * 0x27C0 - 0x27EF. + * @since 1.5 + */ + public static final UnicodeBlock MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A + = new UnicodeBlock(0x27C0, 0x27EF, + "MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A", + "Miscellaneous Mathematical Symbols-A"); + + /** + * Supplemental Arrows-A. + * 0x27F0 - 0x27FF. + * @since 1.5 + */ + public static final UnicodeBlock SUPPLEMENTAL_ARROWS_A + = new UnicodeBlock(0x27F0, 0x27FF, + "SUPPLEMENTAL_ARROWS_A", + "Supplemental Arrows-A"); + + /** + * Braille Patterns. + * 0x2800 - 0x28FF. + * @since 1.4 + */ + public static final UnicodeBlock BRAILLE_PATTERNS + = new UnicodeBlock(0x2800, 0x28FF, + "BRAILLE_PATTERNS", + "Braille Patterns"); + + /** + * Supplemental Arrows-B. + * 0x2900 - 0x297F. + * @since 1.5 + */ + public static final UnicodeBlock SUPPLEMENTAL_ARROWS_B + = new UnicodeBlock(0x2900, 0x297F, + "SUPPLEMENTAL_ARROWS_B", + "Supplemental Arrows-B"); + + /** + * Miscellaneous Mathematical Symbols-B. + * 0x2980 - 0x29FF. + * @since 1.5 + */ + public static final UnicodeBlock MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B + = new UnicodeBlock(0x2980, 0x29FF, + "MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B", + "Miscellaneous Mathematical Symbols-B"); + + /** + * Supplemental Mathematical Operators. + * 0x2A00 - 0x2AFF. + * @since 1.5 + */ + public static final UnicodeBlock SUPPLEMENTAL_MATHEMATICAL_OPERATORS + = new UnicodeBlock(0x2A00, 0x2AFF, + "SUPPLEMENTAL_MATHEMATICAL_OPERATORS", + "Supplemental Mathematical Operators"); + + /** + * Miscellaneous Symbols and Arrows. + * 0x2B00 - 0x2BFF. + * @since 1.5 + */ + public static final UnicodeBlock MISCELLANEOUS_SYMBOLS_AND_ARROWS + = new UnicodeBlock(0x2B00, 0x2BFF, + "MISCELLANEOUS_SYMBOLS_AND_ARROWS", + "Miscellaneous Symbols and Arrows"); + + /** + * CJK Radicals Supplement. + * 0x2E80 - 0x2EFF. + * @since 1.4 + */ + public static final UnicodeBlock CJK_RADICALS_SUPPLEMENT + = new UnicodeBlock(0x2E80, 0x2EFF, + "CJK_RADICALS_SUPPLEMENT", + "CJK Radicals Supplement"); + + /** + * Kangxi Radicals. + * 0x2F00 - 0x2FDF. + * @since 1.4 + */ + public static final UnicodeBlock KANGXI_RADICALS + = new UnicodeBlock(0x2F00, 0x2FDF, + "KANGXI_RADICALS", + "Kangxi Radicals"); + + /** + * Ideographic Description Characters. + * 0x2FF0 - 0x2FFF. + * @since 1.4 + */ + public static final UnicodeBlock IDEOGRAPHIC_DESCRIPTION_CHARACTERS + = new UnicodeBlock(0x2FF0, 0x2FFF, + "IDEOGRAPHIC_DESCRIPTION_CHARACTERS", + "Ideographic Description Characters"); + + /** + * CJK Symbols and Punctuation. + * 0x3000 - 0x303F. + */ + public static final UnicodeBlock CJK_SYMBOLS_AND_PUNCTUATION + = new UnicodeBlock(0x3000, 0x303F, + "CJK_SYMBOLS_AND_PUNCTUATION", + "CJK Symbols and Punctuation"); + + /** + * Hiragana. + * 0x3040 - 0x309F. + */ + public static final UnicodeBlock HIRAGANA + = new UnicodeBlock(0x3040, 0x309F, + "HIRAGANA", + "Hiragana"); + + /** + * Katakana. + * 0x30A0 - 0x30FF. + */ + public static final UnicodeBlock KATAKANA + = new UnicodeBlock(0x30A0, 0x30FF, + "KATAKANA", + "Katakana"); + + /** + * Bopomofo. + * 0x3100 - 0x312F. + */ + public static final UnicodeBlock BOPOMOFO + = new UnicodeBlock(0x3100, 0x312F, + "BOPOMOFO", + "Bopomofo"); + + /** + * Hangul Compatibility Jamo. + * 0x3130 - 0x318F. + */ + public static final UnicodeBlock HANGUL_COMPATIBILITY_JAMO + = new UnicodeBlock(0x3130, 0x318F, + "HANGUL_COMPATIBILITY_JAMO", + "Hangul Compatibility Jamo"); + + /** + * Kanbun. + * 0x3190 - 0x319F. + */ + public static final UnicodeBlock KANBUN + = new UnicodeBlock(0x3190, 0x319F, + "KANBUN", + "Kanbun"); + + /** + * Bopomofo Extended. + * 0x31A0 - 0x31BF. + * @since 1.4 + */ + public static final UnicodeBlock BOPOMOFO_EXTENDED + = new UnicodeBlock(0x31A0, 0x31BF, + "BOPOMOFO_EXTENDED", + "Bopomofo Extended"); + + /** + * Katakana Phonetic Extensions. + * 0x31F0 - 0x31FF. + * @since 1.5 + */ + public static final UnicodeBlock KATAKANA_PHONETIC_EXTENSIONS + = new UnicodeBlock(0x31F0, 0x31FF, + "KATAKANA_PHONETIC_EXTENSIONS", + "Katakana Phonetic Extensions"); + + /** + * Enclosed CJK Letters and Months. + * 0x3200 - 0x32FF. + */ + public static final UnicodeBlock ENCLOSED_CJK_LETTERS_AND_MONTHS + = new UnicodeBlock(0x3200, 0x32FF, + "ENCLOSED_CJK_LETTERS_AND_MONTHS", + "Enclosed CJK Letters and Months"); + + /** + * CJK Compatibility. + * 0x3300 - 0x33FF. + */ + public static final UnicodeBlock CJK_COMPATIBILITY + = new UnicodeBlock(0x3300, 0x33FF, + "CJK_COMPATIBILITY", + "CJK Compatibility"); + + /** + * CJK Unified Ideographs Extension A. + * 0x3400 - 0x4DBF. + * @since 1.4 + */ + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A + = new UnicodeBlock(0x3400, 0x4DBF, + "CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A", + "CJK Unified Ideographs Extension A"); + + /** + * Yijing Hexagram Symbols. + * 0x4DC0 - 0x4DFF. + * @since 1.5 + */ + public static final UnicodeBlock YIJING_HEXAGRAM_SYMBOLS + = new UnicodeBlock(0x4DC0, 0x4DFF, + "YIJING_HEXAGRAM_SYMBOLS", + "Yijing Hexagram Symbols"); + + /** + * CJK Unified Ideographs. + * 0x4E00 - 0x9FFF. + */ + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS + = new UnicodeBlock(0x4E00, 0x9FFF, + "CJK_UNIFIED_IDEOGRAPHS", + "CJK Unified Ideographs"); + + /** + * Yi Syllables. + * 0xA000 - 0xA48F. + * @since 1.4 + */ + public static final UnicodeBlock YI_SYLLABLES + = new UnicodeBlock(0xA000, 0xA48F, + "YI_SYLLABLES", + "Yi Syllables"); + + /** + * Yi Radicals. + * 0xA490 - 0xA4CF. + * @since 1.4 + */ + public static final UnicodeBlock YI_RADICALS + = new UnicodeBlock(0xA490, 0xA4CF, + "YI_RADICALS", + "Yi Radicals"); + + /** + * Hangul Syllables. + * 0xAC00 - 0xD7AF. + */ + public static final UnicodeBlock HANGUL_SYLLABLES + = new UnicodeBlock(0xAC00, 0xD7AF, + "HANGUL_SYLLABLES", + "Hangul Syllables"); + + /** + * High Surrogates. + * 0xD800 - 0xDB7F. + * @since 1.5 + */ + public static final UnicodeBlock HIGH_SURROGATES + = new UnicodeBlock(0xD800, 0xDB7F, + "HIGH_SURROGATES", + "High Surrogates"); + + /** + * High Private Use Surrogates. + * 0xDB80 - 0xDBFF. + * @since 1.5 + */ + public static final UnicodeBlock HIGH_PRIVATE_USE_SURROGATES + = new UnicodeBlock(0xDB80, 0xDBFF, + "HIGH_PRIVATE_USE_SURROGATES", + "High Private Use Surrogates"); + + /** + * Low Surrogates. + * 0xDC00 - 0xDFFF. + * @since 1.5 + */ + public static final UnicodeBlock LOW_SURROGATES + = new UnicodeBlock(0xDC00, 0xDFFF, + "LOW_SURROGATES", + "Low Surrogates"); + + /** + * Private Use Area. + * 0xE000 - 0xF8FF. + */ + public static final UnicodeBlock PRIVATE_USE_AREA + = new UnicodeBlock(0xE000, 0xF8FF, + "PRIVATE_USE_AREA", + "Private Use Area"); + + /** + * CJK Compatibility Ideographs. + * 0xF900 - 0xFAFF. + */ + public static final UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS + = new UnicodeBlock(0xF900, 0xFAFF, + "CJK_COMPATIBILITY_IDEOGRAPHS", + "CJK Compatibility Ideographs"); + + /** + * Alphabetic Presentation Forms. + * 0xFB00 - 0xFB4F. + */ + public static final UnicodeBlock ALPHABETIC_PRESENTATION_FORMS + = new UnicodeBlock(0xFB00, 0xFB4F, + "ALPHABETIC_PRESENTATION_FORMS", + "Alphabetic Presentation Forms"); + + /** + * Arabic Presentation Forms-A. + * 0xFB50 - 0xFDFF. + */ + public static final UnicodeBlock ARABIC_PRESENTATION_FORMS_A + = new UnicodeBlock(0xFB50, 0xFDFF, + "ARABIC_PRESENTATION_FORMS_A", + "Arabic Presentation Forms-A"); + + /** + * Variation Selectors. + * 0xFE00 - 0xFE0F. + * @since 1.5 + */ + public static final UnicodeBlock VARIATION_SELECTORS + = new UnicodeBlock(0xFE00, 0xFE0F, + "VARIATION_SELECTORS", + "Variation Selectors"); + + /** + * Combining Half Marks. + * 0xFE20 - 0xFE2F. + */ + public static final UnicodeBlock COMBINING_HALF_MARKS + = new UnicodeBlock(0xFE20, 0xFE2F, + "COMBINING_HALF_MARKS", + "Combining Half Marks"); + + /** + * CJK Compatibility Forms. + * 0xFE30 - 0xFE4F. + */ + public static final UnicodeBlock CJK_COMPATIBILITY_FORMS + = new UnicodeBlock(0xFE30, 0xFE4F, + "CJK_COMPATIBILITY_FORMS", + "CJK Compatibility Forms"); + + /** + * Small Form Variants. + * 0xFE50 - 0xFE6F. + */ + public static final UnicodeBlock SMALL_FORM_VARIANTS + = new UnicodeBlock(0xFE50, 0xFE6F, + "SMALL_FORM_VARIANTS", + "Small Form Variants"); + + /** + * Arabic Presentation Forms-B. + * 0xFE70 - 0xFEFF. + */ + public static final UnicodeBlock ARABIC_PRESENTATION_FORMS_B + = new UnicodeBlock(0xFE70, 0xFEFF, + "ARABIC_PRESENTATION_FORMS_B", + "Arabic Presentation Forms-B"); + + /** + * Halfwidth and Fullwidth Forms. + * 0xFF00 - 0xFFEF. + */ + public static final UnicodeBlock HALFWIDTH_AND_FULLWIDTH_FORMS + = new UnicodeBlock(0xFF00, 0xFFEF, + "HALFWIDTH_AND_FULLWIDTH_FORMS", + "Halfwidth and Fullwidth Forms"); + + /** + * Specials. + * 0xFFF0 - 0xFFFF. + */ + public static final UnicodeBlock SPECIALS + = new UnicodeBlock(0xFFF0, 0xFFFF, + "SPECIALS", + "Specials"); + + /** + * Linear B Syllabary. + * 0x10000 - 0x1007F. + * @since 1.5 + */ + public static final UnicodeBlock LINEAR_B_SYLLABARY + = new UnicodeBlock(0x10000, 0x1007F, + "LINEAR_B_SYLLABARY", + "Linear B Syllabary"); + + /** + * Linear B Ideograms. + * 0x10080 - 0x100FF. + * @since 1.5 + */ + public static final UnicodeBlock LINEAR_B_IDEOGRAMS + = new UnicodeBlock(0x10080, 0x100FF, + "LINEAR_B_IDEOGRAMS", + "Linear B Ideograms"); + + /** + * Aegean Numbers. + * 0x10100 - 0x1013F. + * @since 1.5 + */ + public static final UnicodeBlock AEGEAN_NUMBERS + = new UnicodeBlock(0x10100, 0x1013F, + "AEGEAN_NUMBERS", + "Aegean Numbers"); + + /** + * Old Italic. + * 0x10300 - 0x1032F. + * @since 1.5 + */ + public static final UnicodeBlock OLD_ITALIC + = new UnicodeBlock(0x10300, 0x1032F, + "OLD_ITALIC", + "Old Italic"); + + /** + * Gothic. + * 0x10330 - 0x1034F. + * @since 1.5 + */ + public static final UnicodeBlock GOTHIC + = new UnicodeBlock(0x10330, 0x1034F, + "GOTHIC", + "Gothic"); + + /** + * Ugaritic. + * 0x10380 - 0x1039F. + * @since 1.5 + */ + public static final UnicodeBlock UGARITIC + = new UnicodeBlock(0x10380, 0x1039F, + "UGARITIC", + "Ugaritic"); + + /** + * Deseret. + * 0x10400 - 0x1044F. + * @since 1.5 + */ + public static final UnicodeBlock DESERET + = new UnicodeBlock(0x10400, 0x1044F, + "DESERET", + "Deseret"); + + /** + * Shavian. + * 0x10450 - 0x1047F. + * @since 1.5 + */ + public static final UnicodeBlock SHAVIAN + = new UnicodeBlock(0x10450, 0x1047F, + "SHAVIAN", + "Shavian"); + + /** + * Osmanya. + * 0x10480 - 0x104AF. + * @since 1.5 + */ + public static final UnicodeBlock OSMANYA + = new UnicodeBlock(0x10480, 0x104AF, + "OSMANYA", + "Osmanya"); + + /** + * Cypriot Syllabary. + * 0x10800 - 0x1083F. + * @since 1.5 + */ + public static final UnicodeBlock CYPRIOT_SYLLABARY + = new UnicodeBlock(0x10800, 0x1083F, + "CYPRIOT_SYLLABARY", + "Cypriot Syllabary"); + + /** + * Byzantine Musical Symbols. + * 0x1D000 - 0x1D0FF. + * @since 1.5 + */ + public static final UnicodeBlock BYZANTINE_MUSICAL_SYMBOLS + = new UnicodeBlock(0x1D000, 0x1D0FF, + "BYZANTINE_MUSICAL_SYMBOLS", + "Byzantine Musical Symbols"); + + /** + * Musical Symbols. + * 0x1D100 - 0x1D1FF. + * @since 1.5 + */ + public static final UnicodeBlock MUSICAL_SYMBOLS + = new UnicodeBlock(0x1D100, 0x1D1FF, + "MUSICAL_SYMBOLS", + "Musical Symbols"); + + /** + * Tai Xuan Jing Symbols. + * 0x1D300 - 0x1D35F. + * @since 1.5 + */ + public static final UnicodeBlock TAI_XUAN_JING_SYMBOLS + = new UnicodeBlock(0x1D300, 0x1D35F, + "TAI_XUAN_JING_SYMBOLS", + "Tai Xuan Jing Symbols"); + + /** + * Mathematical Alphanumeric Symbols. + * 0x1D400 - 0x1D7FF. + * @since 1.5 + */ + public static final UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS + = new UnicodeBlock(0x1D400, 0x1D7FF, + "MATHEMATICAL_ALPHANUMERIC_SYMBOLS", + "Mathematical Alphanumeric Symbols"); + + /** + * CJK Unified Ideographs Extension B. + * 0x20000 - 0x2A6DF. + * @since 1.5 + */ + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B + = new UnicodeBlock(0x20000, 0x2A6DF, + "CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B", + "CJK Unified Ideographs Extension B"); + + /** + * CJK Compatibility Ideographs Supplement. + * 0x2F800 - 0x2FA1F. + * @since 1.5 + */ + public static final UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT + = new UnicodeBlock(0x2F800, 0x2FA1F, + "CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT", + "CJK Compatibility Ideographs Supplement"); + + /** + * Tags. + * 0xE0000 - 0xE007F. + * @since 1.5 + */ + public static final UnicodeBlock TAGS + = new UnicodeBlock(0xE0000, 0xE007F, + "TAGS", + "Tags"); + + /** + * Variation Selectors Supplement. + * 0xE0100 - 0xE01EF. + * @since 1.5 + */ + public static final UnicodeBlock VARIATION_SELECTORS_SUPPLEMENT + = new UnicodeBlock(0xE0100, 0xE01EF, + "VARIATION_SELECTORS_SUPPLEMENT", + "Variation Selectors Supplement"); + + /** + * Supplementary Private Use Area-A. + * 0xF0000 - 0xFFFFF. + * @since 1.5 + */ + public static final UnicodeBlock SUPPLEMENTARY_PRIVATE_USE_AREA_A + = new UnicodeBlock(0xF0000, 0xFFFFF, + "SUPPLEMENTARY_PRIVATE_USE_AREA_A", + "Supplementary Private Use Area-A"); + + /** + * Supplementary Private Use Area-B. + * 0x100000 - 0x10FFFF. + * @since 1.5 + */ + public static final UnicodeBlock SUPPLEMENTARY_PRIVATE_USE_AREA_B + = new UnicodeBlock(0x100000, 0x10FFFF, + "SUPPLEMENTARY_PRIVATE_USE_AREA_B", + "Supplementary Private Use Area-B"); + + /** + * Surrogates Area. + * 'D800' - 'DFFF'. + * @deprecated As of 1.5, the three areas, + * HIGH_SURROGATES, + * HIGH_PRIVATE_USE_SURROGATES + * and LOW_SURROGATES, as defined + * by the Unicode standard, should be used in preference to + * this. These are also returned from calls to of(int) + * and of(char). + */ + @Deprecated + public static final UnicodeBlock SURROGATES_AREA + = new UnicodeBlock(0xD800, 0xDFFF, + "SURROGATES_AREA", + "Surrogates Area"); + + /** + * The defined subsets. + */ + private static final UnicodeBlock sets[] = { + BASIC_LATIN, + LATIN_1_SUPPLEMENT, + LATIN_EXTENDED_A, + LATIN_EXTENDED_B, + IPA_EXTENSIONS, + SPACING_MODIFIER_LETTERS, + COMBINING_DIACRITICAL_MARKS, + GREEK, + CYRILLIC, + CYRILLIC_SUPPLEMENTARY, + ARMENIAN, + HEBREW, + ARABIC, + SYRIAC, + THAANA, + DEVANAGARI, + BENGALI, + GURMUKHI, + GUJARATI, + ORIYA, + TAMIL, + TELUGU, + KANNADA, + MALAYALAM, + SINHALA, + THAI, + LAO, + TIBETAN, + MYANMAR, + GEORGIAN, + HANGUL_JAMO, + ETHIOPIC, + CHEROKEE, + UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS, + OGHAM, + RUNIC, + TAGALOG, + HANUNOO, + BUHID, + TAGBANWA, + KHMER, + MONGOLIAN, + LIMBU, + TAI_LE, + KHMER_SYMBOLS, + PHONETIC_EXTENSIONS, + LATIN_EXTENDED_ADDITIONAL, + GREEK_EXTENDED, + GENERAL_PUNCTUATION, + SUPERSCRIPTS_AND_SUBSCRIPTS, + CURRENCY_SYMBOLS, + COMBINING_MARKS_FOR_SYMBOLS, + LETTERLIKE_SYMBOLS, + NUMBER_FORMS, + ARROWS, + MATHEMATICAL_OPERATORS, + MISCELLANEOUS_TECHNICAL, + CONTROL_PICTURES, + OPTICAL_CHARACTER_RECOGNITION, + ENCLOSED_ALPHANUMERICS, + BOX_DRAWING, + BLOCK_ELEMENTS, + GEOMETRIC_SHAPES, + MISCELLANEOUS_SYMBOLS, + DINGBATS, + MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A, + SUPPLEMENTAL_ARROWS_A, + BRAILLE_PATTERNS, + SUPPLEMENTAL_ARROWS_B, + MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B, + SUPPLEMENTAL_MATHEMATICAL_OPERATORS, + MISCELLANEOUS_SYMBOLS_AND_ARROWS, + CJK_RADICALS_SUPPLEMENT, + KANGXI_RADICALS, + IDEOGRAPHIC_DESCRIPTION_CHARACTERS, + CJK_SYMBOLS_AND_PUNCTUATION, + HIRAGANA, + KATAKANA, + BOPOMOFO, + HANGUL_COMPATIBILITY_JAMO, + KANBUN, + BOPOMOFO_EXTENDED, + KATAKANA_PHONETIC_EXTENSIONS, + ENCLOSED_CJK_LETTERS_AND_MONTHS, + CJK_COMPATIBILITY, + CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A, + YIJING_HEXAGRAM_SYMBOLS, + CJK_UNIFIED_IDEOGRAPHS, + YI_SYLLABLES, + YI_RADICALS, + HANGUL_SYLLABLES, + HIGH_SURROGATES, + HIGH_PRIVATE_USE_SURROGATES, + LOW_SURROGATES, + PRIVATE_USE_AREA, + CJK_COMPATIBILITY_IDEOGRAPHS, + ALPHABETIC_PRESENTATION_FORMS, + ARABIC_PRESENTATION_FORMS_A, + VARIATION_SELECTORS, + COMBINING_HALF_MARKS, + CJK_COMPATIBILITY_FORMS, + SMALL_FORM_VARIANTS, + ARABIC_PRESENTATION_FORMS_B, + HALFWIDTH_AND_FULLWIDTH_FORMS, + SPECIALS, + LINEAR_B_SYLLABARY, + LINEAR_B_IDEOGRAMS, + AEGEAN_NUMBERS, + OLD_ITALIC, + GOTHIC, + UGARITIC, + DESERET, + SHAVIAN, + OSMANYA, + CYPRIOT_SYLLABARY, + BYZANTINE_MUSICAL_SYMBOLS, + MUSICAL_SYMBOLS, + TAI_XUAN_JING_SYMBOLS, + MATHEMATICAL_ALPHANUMERIC_SYMBOLS, + CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, + CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT, + TAGS, + VARIATION_SELECTORS_SUPPLEMENT, + SUPPLEMENTARY_PRIVATE_USE_AREA_A, + SUPPLEMENTARY_PRIVATE_USE_AREA_B, + }; + } // class UnicodeBlock + + /** + * A class to encompass all the properties of characters in the + * private use blocks in the Unicode standard. This class extends + * UnassignedCharacters because the return type from getType() is + * different. + * @author Anthony Balkissoon abalkiss at redhat dot com + * + */ + private static class PrivateUseCharacters extends UnassignedCharacters + { + /** + * Returns the type of the character cp. + */ + static int getType(int cp) + { + // The upper 2 code points in any plane are considered unassigned, + // even in the private-use planes. + if ((cp & 0xffff) >= 0xfffe) + return UnassignedCharacters.getType(cp); + return PRIVATE_USE; + } + + /** + * Returns true if the character cp is defined. + */ + static boolean isDefined(int cp) + { + // The upper 2 code points in any plane are considered unassigned, + // even in the private-use planes. + if ((cp & 0xffff) >= 0xfffe) + return UnassignedCharacters.isDefined(cp); + return true; + } + + /** + * Gets the directionality for the character cp. + */ + static byte getDirectionality(int cp) + { + if ((cp & 0xffff) >= 0xfffe) + return UnassignedCharacters.getDirectionality(cp); + return DIRECTIONALITY_LEFT_TO_RIGHT; + } + } + + /** + * A class to encompass all the properties of code points that are + * currently undefined in the Unicode standard. + * @author Anthony Balkissoon abalkiss at redhat dot com + * + */ + private static class UnassignedCharacters + { + /** + * Returns the numeric value for the unassigned characters. + * @param cp the character + * @param radix the radix (not used) + * @return the numeric value of this character in this radix + */ + static int digit(int cp, int radix) + { + return -1; + } + + /** + * Returns the Unicode directionality property for unassigned + * characters. + * @param cp the character + * @return DIRECTIONALITY_UNDEFINED + */ + static byte getDirectionality(int cp) + { + return DIRECTIONALITY_UNDEFINED; + } + + /** + * Returns -1, the numeric value for unassigned Unicode characters. + * @param cp the character + * @return -1 + */ + static int getNumericValue(int cp) + { + return -1; + } + + /** + * Returns UNASSIGNED, the type of unassigned Unicode characters. + * @param cp the character + * @return UNASSIGNED + */ + static int getType(int cp) + { + return UNASSIGNED; + } + + /** + * Returns false to indiciate that the character is not defined in the + * Unicode standard. + * @param cp the character + * @return false + */ + static boolean isDefined(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character is not a digit. + * @param cp the character + * @return false + */ + static boolean isDigit(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character cannot be ignored + * within an identifier + * @param cp the character + * @return false + */ + static boolean isIdentifierIgnorable(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character cannot be part of a + * Java identifier. + * @param cp the character + * @return false + */ + static boolean isJavaIdentifierPart(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character cannot be start a + * Java identifier. + * @param cp the character + * @return false + */ + static boolean isJavaIdentiferStart(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character is not a letter. + * @param cp the character + * @return false + */ + static boolean isLetter(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character cannot is neither a letter + * nor a digit. + * @param cp the character + * @return false + */ + static boolean isLetterOrDigit(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character is not a lowercase letter. + * @param cp the character + * @return false + */ + static boolean isLowerCase(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character cannot is not mirrored. + * @param cp the character + * @return false + */ + static boolean isMirrored(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character is not a space character. + * @param cp the character + * @return false + */ + static boolean isSpaceChar(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character it not a titlecase letter. + * @param cp the character + * @return false + */ + static boolean isTitleCase(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character cannot be part of a + * Unicode identifier. + * @param cp the character + * @return false + */ + static boolean isUnicodeIdentifierPart(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character cannot start a + * Unicode identifier. + * @param cp the character + * @return false + */ + static boolean isUnicodeIdentifierStart(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character is not an uppercase letter. + * @param cp the character + * @return false + */ + static boolean isUpperCase(int cp) + { + return false; + } + + /** + * Returns false to indicate that the character is not a whitespace + * character. + * @param cp the character + * @return false + */ + static boolean isWhiteSpace(int cp) + { + return false; + } + + /** + * Returns cp to indicate this character has no lowercase conversion. + * @param cp the character + * @return cp + */ + static int toLowerCase(int cp) + { + return cp; + } + + /** + * Returns cp to indicate this character has no titlecase conversion. + * @param cp the character + * @return cp + */ + static int toTitleCase(int cp) + { + return cp; + } + + /** + * Returns cp to indicate this character has no uppercase conversion. + * @param cp the character + * @return cp + */ + static int toUpperCase(int cp) + { + return cp; + } + } + + /** + * The immutable value of this Character. + * + * @serial the value of this Character + */ + private final char value; + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3786198910865385080L; + + /** + * Smallest value allowed for radix arguments in Java. This value is 2. + * + * @see #digit(char, int) + * @see #forDigit(int, int) + * @see Integer#toString(int, int) + * @see Integer#valueOf(String) + */ + public static final int MIN_RADIX = 2; + + /** + * Largest value allowed for radix arguments in Java. This value is 36. + * + * @see #digit(char, int) + * @see #forDigit(int, int) + * @see Integer#toString(int, int) + * @see Integer#valueOf(String) + */ + public static final int MAX_RADIX = 36; + + /** + * The minimum value the char data type can hold. + * This value is '\\u0000'. + */ + public static final char MIN_VALUE = '\u0000'; + + /** + * The maximum value the char data type can hold. + * This value is '\\uFFFF'. + */ + public static final char MAX_VALUE = '\uFFFF'; + + /** + * The minimum Unicode 4.0 code point. This value is 0. + * @since 1.5 + */ + public static final int MIN_CODE_POINT = 0; + + /** + * The maximum Unicode 4.0 code point, which is greater than the range + * of the char data type. + * This value is 0x10FFFF. + * @since 1.5 + */ + public static final int MAX_CODE_POINT = 0x10FFFF; + + /** + * The minimum Unicode high surrogate code unit, or + * leading-surrogate, in the UTF-16 character encoding. + * This value is '\uD800'. + * @since 1.5 + */ + public static final char MIN_HIGH_SURROGATE = '\uD800'; + + /** + * The maximum Unicode high surrogate code unit, or + * leading-surrogate, in the UTF-16 character encoding. + * This value is '\uDBFF'. + * @since 1.5 + */ + public static final char MAX_HIGH_SURROGATE = '\uDBFF'; + + /** + * The minimum Unicode low surrogate code unit, or + * trailing-surrogate, in the UTF-16 character encoding. + * This value is '\uDC00'. + * @since 1.5 + */ + public static final char MIN_LOW_SURROGATE = '\uDC00'; + + /** + * The maximum Unicode low surrogate code unit, or + * trailing-surrogate, in the UTF-16 character encoding. + * This value is '\uDFFF'. + * @since 1.5 + */ + public static final char MAX_LOW_SURROGATE = '\uDFFF'; + + /** + * The minimum Unicode surrogate code unit in the UTF-16 character encoding. + * This value is '\uD800'. + * @since 1.5 + */ + public static final char MIN_SURROGATE = MIN_HIGH_SURROGATE; + + /** + * The maximum Unicode surrogate code unit in the UTF-16 character encoding. + * This value is '\uDFFF'. + * @since 1.5 + */ + public static final char MAX_SURROGATE = MAX_LOW_SURROGATE; + + /** + * The lowest possible supplementary Unicode code point (the first code + * point outside the basic multilingual plane (BMP)). + * This value is 0x10000. + */ + public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x10000; + + /** + * Class object representing the primitive char data type. + * + * @since 1.1 + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass('C'); + + /** + * The number of bits needed to represent a char. + * @since 1.5 + */ + public static final int SIZE = 16; + + // This caches some Character values, and is used by boxing + // conversions via valueOf(). We must cache at least 0..127; + // this constant controls how much we actually cache. + private static final int MAX_CACHE = 127; + private static Character[] charCache = new Character[MAX_CACHE + 1]; + static + { + for (char i=0; i <= MAX_CACHE; i++) + charCache[i] = new Character(i); + } + + /** + * Lu = Letter, Uppercase (Informative). + * + * @since 1.1 + */ + public static final byte UPPERCASE_LETTER = 1; + + /** + * Ll = Letter, Lowercase (Informative). + * + * @since 1.1 + */ + public static final byte LOWERCASE_LETTER = 2; + + /** + * Lt = Letter, Titlecase (Informative). + * + * @since 1.1 + */ + public static final byte TITLECASE_LETTER = 3; + + /** + * Mn = Mark, Non-Spacing (Normative). + * + * @since 1.1 + */ + public static final byte NON_SPACING_MARK = 6; + + /** + * Mc = Mark, Spacing Combining (Normative). + * + * @since 1.1 + */ + public static final byte COMBINING_SPACING_MARK = 8; + + /** + * Me = Mark, Enclosing (Normative). + * + * @since 1.1 + */ + public static final byte ENCLOSING_MARK = 7; + + /** + * Nd = Number, Decimal Digit (Normative). + * + * @since 1.1 + */ + public static final byte DECIMAL_DIGIT_NUMBER = 9; + + /** + * Nl = Number, Letter (Normative). + * + * @since 1.1 + */ + public static final byte LETTER_NUMBER = 10; + + /** + * No = Number, Other (Normative). + * + * @since 1.1 + */ + public static final byte OTHER_NUMBER = 11; + + /** + * Zs = Separator, Space (Normative). + * + * @since 1.1 + */ + public static final byte SPACE_SEPARATOR = 12; + + /** + * Zl = Separator, Line (Normative). + * + * @since 1.1 + */ + public static final byte LINE_SEPARATOR = 13; + + /** + * Zp = Separator, Paragraph (Normative). + * + * @since 1.1 + */ + public static final byte PARAGRAPH_SEPARATOR = 14; + + /** + * Cc = Other, Control (Normative). + * + * @since 1.1 + */ + public static final byte CONTROL = 15; + + /** + * Cf = Other, Format (Normative). + * + * @since 1.1 + */ + public static final byte FORMAT = 16; + + /** + * Cs = Other, Surrogate (Normative). + * + * @since 1.1 + */ + public static final byte SURROGATE = 19; + + /** + * Co = Other, Private Use (Normative). + * + * @since 1.1 + */ + public static final byte PRIVATE_USE = 18; + + /** + * Cn = Other, Not Assigned (Normative). + * + * @since 1.1 + */ + public static final byte UNASSIGNED = 0; + + /** + * Lm = Letter, Modifier (Informative). + * + * @since 1.1 + */ + public static final byte MODIFIER_LETTER = 4; + + /** + * Lo = Letter, Other (Informative). + * + * @since 1.1 + */ + public static final byte OTHER_LETTER = 5; + + /** + * Pc = Punctuation, Connector (Informative). + * + * @since 1.1 + */ + public static final byte CONNECTOR_PUNCTUATION = 23; + + /** + * Pd = Punctuation, Dash (Informative). + * + * @since 1.1 + */ + public static final byte DASH_PUNCTUATION = 20; + + /** + * Ps = Punctuation, Open (Informative). + * + * @since 1.1 + */ + public static final byte START_PUNCTUATION = 21; + + /** + * Pe = Punctuation, Close (Informative). + * + * @since 1.1 + */ + public static final byte END_PUNCTUATION = 22; + + /** + * Pi = Punctuation, Initial Quote (Informative). + * + * @since 1.4 + */ + public static final byte INITIAL_QUOTE_PUNCTUATION = 29; + + /** + * Pf = Punctuation, Final Quote (Informative). + * + * @since 1.4 + */ + public static final byte FINAL_QUOTE_PUNCTUATION = 30; + + /** + * Po = Punctuation, Other (Informative). + * + * @since 1.1 + */ + public static final byte OTHER_PUNCTUATION = 24; + + /** + * Sm = Symbol, Math (Informative). + * + * @since 1.1 + */ + public static final byte MATH_SYMBOL = 25; + + /** + * Sc = Symbol, Currency (Informative). + * + * @since 1.1 + */ + public static final byte CURRENCY_SYMBOL = 26; + + /** + * Sk = Symbol, Modifier (Informative). + * + * @since 1.1 + */ + public static final byte MODIFIER_SYMBOL = 27; + + /** + * So = Symbol, Other (Informative). + * + * @since 1.1 + */ + public static final byte OTHER_SYMBOL = 28; + + /** + * Undefined bidirectional character type. Undefined char values have + * undefined directionality in the Unicode specification. + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_UNDEFINED = -1; + + /** + * Strong bidirectional character type "L". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = 0; + + /** + * Strong bidirectional character type "R". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = 1; + + /** + * Strong bidirectional character type "AL". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = 2; + + /** + * Weak bidirectional character type "EN". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = 3; + + /** + * Weak bidirectional character type "ES". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = 4; + + /** + * Weak bidirectional character type "ET". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = 5; + + /** + * Weak bidirectional character type "AN". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_ARABIC_NUMBER = 6; + + /** + * Weak bidirectional character type "CS". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = 7; + + /** + * Weak bidirectional character type "NSM". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_NONSPACING_MARK = 8; + + /** + * Weak bidirectional character type "BN". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = 9; + + /** + * Neutral bidirectional character type "B". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = 10; + + /** + * Neutral bidirectional character type "S". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = 11; + + /** + * Strong bidirectional character type "WS". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_WHITESPACE = 12; + + /** + * Neutral bidirectional character type "ON". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_OTHER_NEUTRALS = 13; + + /** + * Strong bidirectional character type "LRE". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = 14; + + /** + * Strong bidirectional character type "LRO". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = 15; + + /** + * Strong bidirectional character type "RLE". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = 16; + + /** + * Strong bidirectional character type "RLO". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = 17; + + /** + * Weak bidirectional character type "PDF". + * + * @since 1.4 + */ + public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18; + + /** + * Stores unicode block offset lookup table. Exploit package visibility of + * String.value to avoid copying the array. + * @see #readCodePoint(int) + * @see CharData#BLOCKS + */ + private static final char[][] blocks = + new char[][]{ + String.zeroBasedStringValue(CharData.BLOCKS[0]), + String.zeroBasedStringValue(CharData.BLOCKS[1]), + String.zeroBasedStringValue(CharData.BLOCKS[2]), + String.zeroBasedStringValue(CharData.BLOCKS[3]), + String.zeroBasedStringValue(CharData.BLOCKS[4]), + String.zeroBasedStringValue(CharData.BLOCKS[5]), + String.zeroBasedStringValue(CharData.BLOCKS[6]), + String.zeroBasedStringValue(CharData.BLOCKS[7]), + String.zeroBasedStringValue(CharData.BLOCKS[8]), + String.zeroBasedStringValue(CharData.BLOCKS[9]), + String.zeroBasedStringValue(CharData.BLOCKS[10]), + String.zeroBasedStringValue(CharData.BLOCKS[11]), + String.zeroBasedStringValue(CharData.BLOCKS[12]), + String.zeroBasedStringValue(CharData.BLOCKS[13]), + String.zeroBasedStringValue(CharData.BLOCKS[14]), + String.zeroBasedStringValue(CharData.BLOCKS[15]), + String.zeroBasedStringValue(CharData.BLOCKS[16])}; + + /** + * Stores unicode attribute offset lookup table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#DATA + */ + private static final char[][] data = + new char[][]{ + String.zeroBasedStringValue(CharData.DATA[0]), + String.zeroBasedStringValue(CharData.DATA[1]), + String.zeroBasedStringValue(CharData.DATA[2]), + String.zeroBasedStringValue(CharData.DATA[3]), + String.zeroBasedStringValue(CharData.DATA[4]), + String.zeroBasedStringValue(CharData.DATA[5]), + String.zeroBasedStringValue(CharData.DATA[6]), + String.zeroBasedStringValue(CharData.DATA[7]), + String.zeroBasedStringValue(CharData.DATA[8]), + String.zeroBasedStringValue(CharData.DATA[9]), + String.zeroBasedStringValue(CharData.DATA[10]), + String.zeroBasedStringValue(CharData.DATA[11]), + String.zeroBasedStringValue(CharData.DATA[12]), + String.zeroBasedStringValue(CharData.DATA[13]), + String.zeroBasedStringValue(CharData.DATA[14]), + String.zeroBasedStringValue(CharData.DATA[15]), + String.zeroBasedStringValue(CharData.DATA[16])}; + + /** + * Stores unicode numeric value attribute table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#NUM_VALUE + */ + private static final char[][] numValue = + new char[][]{ + String.zeroBasedStringValue(CharData.NUM_VALUE[0]), + String.zeroBasedStringValue(CharData.NUM_VALUE[1]), + String.zeroBasedStringValue(CharData.NUM_VALUE[2]), + String.zeroBasedStringValue(CharData.NUM_VALUE[3]), + String.zeroBasedStringValue(CharData.NUM_VALUE[4]), + String.zeroBasedStringValue(CharData.NUM_VALUE[5]), + String.zeroBasedStringValue(CharData.NUM_VALUE[6]), + String.zeroBasedStringValue(CharData.NUM_VALUE[7]), + String.zeroBasedStringValue(CharData.NUM_VALUE[8]), + String.zeroBasedStringValue(CharData.NUM_VALUE[9]), + String.zeroBasedStringValue(CharData.NUM_VALUE[10]), + String.zeroBasedStringValue(CharData.NUM_VALUE[11]), + String.zeroBasedStringValue(CharData.NUM_VALUE[12]), + String.zeroBasedStringValue(CharData.NUM_VALUE[13]), + String.zeroBasedStringValue(CharData.NUM_VALUE[14]), + String.zeroBasedStringValue(CharData.NUM_VALUE[15]), + String.zeroBasedStringValue(CharData.NUM_VALUE[16])}; + + /** + * Stores unicode uppercase attribute table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#UPPER + */ + private static final char[][] upper = + new char[][]{ + String.zeroBasedStringValue(CharData.UPPER[0]), + String.zeroBasedStringValue(CharData.UPPER[1]), + String.zeroBasedStringValue(CharData.UPPER[2]), + String.zeroBasedStringValue(CharData.UPPER[3]), + String.zeroBasedStringValue(CharData.UPPER[4]), + String.zeroBasedStringValue(CharData.UPPER[5]), + String.zeroBasedStringValue(CharData.UPPER[6]), + String.zeroBasedStringValue(CharData.UPPER[7]), + String.zeroBasedStringValue(CharData.UPPER[8]), + String.zeroBasedStringValue(CharData.UPPER[9]), + String.zeroBasedStringValue(CharData.UPPER[10]), + String.zeroBasedStringValue(CharData.UPPER[11]), + String.zeroBasedStringValue(CharData.UPPER[12]), + String.zeroBasedStringValue(CharData.UPPER[13]), + String.zeroBasedStringValue(CharData.UPPER[14]), + String.zeroBasedStringValue(CharData.UPPER[15]), + String.zeroBasedStringValue(CharData.UPPER[16])}; + + /** + * Stores unicode lowercase attribute table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#LOWER + */ + private static final char[][] lower = + new char[][]{ + String.zeroBasedStringValue(CharData.LOWER[0]), + String.zeroBasedStringValue(CharData.LOWER[1]), + String.zeroBasedStringValue(CharData.LOWER[2]), + String.zeroBasedStringValue(CharData.LOWER[3]), + String.zeroBasedStringValue(CharData.LOWER[4]), + String.zeroBasedStringValue(CharData.LOWER[5]), + String.zeroBasedStringValue(CharData.LOWER[6]), + String.zeroBasedStringValue(CharData.LOWER[7]), + String.zeroBasedStringValue(CharData.LOWER[8]), + String.zeroBasedStringValue(CharData.LOWER[9]), + String.zeroBasedStringValue(CharData.LOWER[10]), + String.zeroBasedStringValue(CharData.LOWER[11]), + String.zeroBasedStringValue(CharData.LOWER[12]), + String.zeroBasedStringValue(CharData.LOWER[13]), + String.zeroBasedStringValue(CharData.LOWER[14]), + String.zeroBasedStringValue(CharData.LOWER[15]), + String.zeroBasedStringValue(CharData.LOWER[16])}; + + /** + * Stores unicode direction attribute table. Exploit package visibility + * of String.value to avoid copying the array. + * @see CharData#DIRECTION + */ + // Package visible for use by String. + static final char[][] direction = + new char[][]{ + String.zeroBasedStringValue(CharData.DIRECTION[0]), + String.zeroBasedStringValue(CharData.DIRECTION[1]), + String.zeroBasedStringValue(CharData.DIRECTION[2]), + String.zeroBasedStringValue(CharData.DIRECTION[3]), + String.zeroBasedStringValue(CharData.DIRECTION[4]), + String.zeroBasedStringValue(CharData.DIRECTION[5]), + String.zeroBasedStringValue(CharData.DIRECTION[6]), + String.zeroBasedStringValue(CharData.DIRECTION[7]), + String.zeroBasedStringValue(CharData.DIRECTION[8]), + String.zeroBasedStringValue(CharData.DIRECTION[9]), + String.zeroBasedStringValue(CharData.DIRECTION[10]), + String.zeroBasedStringValue(CharData.DIRECTION[11]), + String.zeroBasedStringValue(CharData.DIRECTION[12]), + String.zeroBasedStringValue(CharData.DIRECTION[13]), + String.zeroBasedStringValue(CharData.DIRECTION[14]), + String.zeroBasedStringValue(CharData.DIRECTION[15]), + String.zeroBasedStringValue(CharData.DIRECTION[16])}; + + /** + * Stores unicode titlecase table. Exploit package visibility of + * String.value to avoid copying the array. + * @see CharData#TITLE + */ + private static final char[] title = String.zeroBasedStringValue(CharData.TITLE); + + /** + * Mask for grabbing the type out of the contents of data. + * @see CharData#DATA + */ + private static final int TYPE_MASK = 0x1F; + + /** + * Mask for grabbing the non-breaking space flag out of the contents of + * data. + * @see CharData#DATA + */ + private static final int NO_BREAK_MASK = 0x20; + + /** + * Mask for grabbing the mirrored directionality flag out of the contents + * of data. + * @see CharData#DATA + */ + private static final int MIRROR_MASK = 0x40; + + /** + * Grabs an attribute offset from the Unicode attribute database. The lower + * 5 bits are the character type, the next 2 bits are flags, and the top + * 9 bits are the offset into the attribute tables. + * + * @param codePoint the character to look up + * @return the character's attribute offset and type + * @see #TYPE_MASK + * @see #NO_BREAK_MASK + * @see #MIRROR_MASK + * @see CharData#DATA + * @see CharData#SHIFT + */ + // Package visible for use in String. + static char readCodePoint(int codePoint) + { + int plane = codePoint >>> 16; + char offset = (char) (codePoint & 0xffff); + return data[plane][(char) (blocks[plane][offset >> CharData.SHIFT[plane]] + offset)]; + } + + /** + * Wraps up a character. + * + * @param value the character to wrap + */ + public Character(char value) + { + this.value = value; + } + + /** + * Returns the character which has been wrapped by this class. + * + * @return the character wrapped + */ + public char charValue() + { + return value; + } + + /** + * Returns the numerical value (unsigned) of the wrapped character. + * Range of returned values: 0x0000-0xFFFF. + * + * @return the value of the wrapped character + */ + public int hashCode() + { + return value; + } + + /** + * Determines if an object is equal to this object. This is only true for + * another Character object wrapping the same value. + * + * @param o object to compare + * @return true if o is a Character with the same value + */ + public boolean equals(Object o) + { + return o instanceof Character && value == ((Character) o).value; + } + + /** + * Converts the wrapped character into a String. + * + * @return a String containing one character -- the wrapped character + * of this instance + */ + public String toString() + { + // Package constructor avoids an array copy. + return new String(new char[] { value }, 0, 1, true); + } + + /** + * Returns a String of length 1 representing the specified character. + * + * @param ch the character to convert + * @return a String containing the character + * @since 1.4 + */ + public static String toString(char ch) + { + // Package constructor avoids an array copy. + return new String(new char[] { ch }, 0, 1, true); + } + + /** + * Determines if a character is a Unicode lowercase letter. For example, + * 'a' is lowercase. Returns true if getType() returns + * LOWERCASE_LETTER. + *
    + * lowercase = [Ll] + * + * @param ch character to test + * @return true if ch is a Unicode lowercase letter, else false + * @see #isUpperCase(char) + * @see #isTitleCase(char) + * @see #toLowerCase(char) + * @see #getType(char) + */ + public static boolean isLowerCase(char ch) + { + return isLowerCase((int)ch); + } + + /** + * Determines if a character is a Unicode lowercase letter. For example, + * 'a' is lowercase. Returns true if getType() returns + * LOWERCASE_LETTER. + *
    + * lowercase = [Ll] + * + * @param codePoint character to test + * @return true if ch is a Unicode lowercase letter, else false + * @see #isUpperCase(char) + * @see #isTitleCase(char) + * @see #toLowerCase(char) + * @see #getType(char) + * + * @since 1.5 + */ + public static boolean isLowerCase(int codePoint) + { + return getType(codePoint) == LOWERCASE_LETTER; + } + + /** + * Determines if a character is a Unicode uppercase letter. For example, + * 'A' is uppercase. Returns true if getType() returns + * UPPERCASE_LETTER. + *
    + * uppercase = [Lu] + * + * @param ch character to test + * @return true if ch is a Unicode uppercase letter, else false + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #toUpperCase(char) + * @see #getType(char) + */ + public static boolean isUpperCase(char ch) + { + return isUpperCase((int)ch); + } + + /** + * Determines if a character is a Unicode uppercase letter. For example, + * 'A' is uppercase. Returns true if getType() returns + * UPPERCASE_LETTER. + *
    + * uppercase = [Lu] + * + * @param codePoint character to test + * @return true if ch is a Unicode uppercase letter, else false + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #toUpperCase(char) + * @see #getType(char) + * + * @since 1.5 + */ + public static boolean isUpperCase(int codePoint) + { + return getType(codePoint) == UPPERCASE_LETTER; + } + + /** + * Determines if a character is a Unicode titlecase letter. For example, + * the character "Lj" (Latin capital L with small letter j) is titlecase. + * True if getType() returns TITLECASE_LETTER. + *
    + * titlecase = [Lt] + * + * @param ch character to test + * @return true if ch is a Unicode titlecase letter, else false + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toTitleCase(char) + * @see #getType(char) + */ + public static boolean isTitleCase(char ch) + { + return isTitleCase((int)ch); + } + + /** + * Determines if a character is a Unicode titlecase letter. For example, + * the character "Lj" (Latin capital L with small letter j) is titlecase. + * True if getType() returns TITLECASE_LETTER. + *
    + * titlecase = [Lt] + * + * @param codePoint character to test + * @return true if ch is a Unicode titlecase letter, else false + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toTitleCase(char) + * @see #getType(char) + * + * @since 1.5 + */ + public static boolean isTitleCase(int codePoint) + { + return getType(codePoint) == TITLECASE_LETTER; + } + + + /** + * Determines if a character is a Unicode decimal digit. For example, + * '0' is a digit. A character is a Unicode digit if + * getType() returns DECIMAL_DIGIT_NUMBER. + *
    + * Unicode decimal digit = [Nd] + * + * @param ch character to test + * @return true if ch is a Unicode decimal digit, else false + * @see #digit(char, int) + * @see #forDigit(int, int) + * @see #getType(char) + */ + public static boolean isDigit(char ch) + { + return isDigit((int)ch); + } + + /** + * Determines if a character is a Unicode decimal digit. For example, + * '0' is a digit. A character is a Unicode digit if + * getType() returns DECIMAL_DIGIT_NUMBER. + *
    + * Unicode decimal digit = [Nd] + * + * @param codePoint character to test + * @return true if ch is a Unicode decimal digit, else false + * @see #digit(char, int) + * @see #forDigit(int, int) + * @see #getType(char) + * + * @since 1.5 + */ + + public static boolean isDigit(int codePoint) + { + return getType(codePoint) == DECIMAL_DIGIT_NUMBER; + } + + /** + * Determines if a character is part of the Unicode Standard. This is an + * evolving standard, but covers every character in the data file. + *
    + * defined = not [Cn] + * + * @param ch character to test + * @return true if ch is a Unicode character, else false + * @see #isDigit(char) + * @see #isLetter(char) + * @see #isLetterOrDigit(char) + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #isUpperCase(char) + */ + public static boolean isDefined(char ch) + { + return isDefined((int)ch); + } + + /** + * Determines if a character is part of the Unicode Standard. This is an + * evolving standard, but covers every character in the data file. + *
    + * defined = not [Cn] + * + * @param codePoint character to test + * @return true if ch is a Unicode character, else false + * @see #isDigit(char) + * @see #isLetter(char) + * @see #isLetterOrDigit(char) + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #isUpperCase(char) + * + * @since 1.5 + */ + public static boolean isDefined(int codePoint) + { + return getType(codePoint) != UNASSIGNED; + } + + /** + * Determines if a character is a Unicode letter. Not all letters have case, + * so this may return true when isLowerCase and isUpperCase return false. + * A character is a Unicode letter if getType() returns one of + * UPPERCASE_LETTER, LOWERCASE_LETTER, TITLECASE_LETTER, MODIFIER_LETTER, + * or OTHER_LETTER. + *
    + * letter = [Lu]|[Ll]|[Lt]|[Lm]|[Lo] + * + * @param ch character to test + * @return true if ch is a Unicode letter, else false + * @see #isDigit(char) + * @see #isJavaIdentifierStart(char) + * @see #isJavaLetter(char) + * @see #isJavaLetterOrDigit(char) + * @see #isLetterOrDigit(char) + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #isUnicodeIdentifierStart(char) + * @see #isUpperCase(char) + */ + public static boolean isLetter(char ch) + { + return isLetter((int)ch); + } + + /** + * Determines if a character is a Unicode letter. Not all letters have case, + * so this may return true when isLowerCase and isUpperCase return false. + * A character is a Unicode letter if getType() returns one of + * UPPERCASE_LETTER, LOWERCASE_LETTER, TITLECASE_LETTER, MODIFIER_LETTER, + * or OTHER_LETTER. + *
    + * letter = [Lu]|[Ll]|[Lt]|[Lm]|[Lo] + * + * @param codePoint character to test + * @return true if ch is a Unicode letter, else false + * @see #isDigit(char) + * @see #isJavaIdentifierStart(char) + * @see #isJavaLetter(char) + * @see #isJavaLetterOrDigit(char) + * @see #isLetterOrDigit(char) + * @see #isLowerCase(char) + * @see #isTitleCase(char) + * @see #isUnicodeIdentifierStart(char) + * @see #isUpperCase(char) + * + * @since 1.5 + */ + public static boolean isLetter(int codePoint) + { + return ((1 << getType(codePoint)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER))) != 0; + } + /** + * Returns the index into the given CharSequence that is offset + * codePointOffset code points from index. + * @param seq the CharSequence + * @param index the start position in the CharSequence + * @param codePointOffset the number of code points offset from the start + * position + * @return the index into the CharSequence that is codePointOffset code + * points offset from index + * + * @throws NullPointerException if seq is null + * @throws IndexOutOfBoundsException if index is negative or greater than the + * length of the sequence. + * @throws IndexOutOfBoundsException if codePointOffset is positive and the + * subsequence from index to the end of seq has fewer than codePointOffset + * code points + * @throws IndexOutOfBoundsException if codePointOffset is negative and the + * subsequence from the start of seq to index has fewer than + * (-codePointOffset) code points + * @since 1.5 + */ + public static int offsetByCodePoints(CharSequence seq, + int index, + int codePointOffset) + { + int len = seq.length(); + if (index < 0 || index > len) + throw new IndexOutOfBoundsException(); + + int numToGo = codePointOffset; + int offset = index; + int adjust = 1; + if (numToGo >= 0) + { + for (; numToGo > 0; offset++) + { + numToGo--; + if (Character.isHighSurrogate(seq.charAt(offset)) + && (offset + 1) < len + && Character.isLowSurrogate(seq.charAt(offset + 1))) + offset++; + } + return offset; + } + else + { + numToGo *= -1; + for (; numToGo > 0;) + { + numToGo--; + offset--; + if (Character.isLowSurrogate(seq.charAt(offset)) + && (offset - 1) >= 0 + && Character.isHighSurrogate(seq.charAt(offset - 1))) + offset--; + } + return offset; + } + } + + /** + * Returns the index into the given char subarray that is offset + * codePointOffset code points from index. + * @param a the char array + * @param start the start index of the subarray + * @param count the length of the subarray + * @param index the index to be offset + * @param codePointOffset the number of code points offset from index + * + * @return the index into the char array + * + * @throws NullPointerException if a is null + * @throws IndexOutOfBoundsException if start or count is negative or if + * start + count is greater than the length of the array + * @throws IndexOutOfBoundsException if index is less than start or larger + * than start + count + * @throws IndexOutOfBoundsException if codePointOffset is positive and the + * subarray from index to start + count - 1 has fewer than codePointOffset + * code points. + * @throws IndexOutOfBoundsException if codePointOffset is negative and the + * subarray from start to index - 1 has fewer than (-codePointOffset) code + * points + * + * @since 1.5 + */ + public static int offsetByCodePoints(char[] a, + int start, + int count, + int index, + int codePointOffset) + { + int len = a.length; + int end = start + count; + if (start < 0 || count < 0 || end > len || index < start || index > end) + throw new IndexOutOfBoundsException(); + + int numToGo = codePointOffset; + int offset = index; + int adjust = 1; + if (numToGo >= 0) + { + for (; numToGo > 0; offset++) + { + numToGo--; + if (Character.isHighSurrogate(a[offset]) + && (offset + 1) < len + && Character.isLowSurrogate(a[offset + 1])) + offset++; + } + return offset; + } + else + { + numToGo *= -1; + for (; numToGo > 0;) + { + numToGo--; + offset--; + if (Character.isLowSurrogate(a[offset]) + && (offset - 1) >= 0 + && Character.isHighSurrogate(a[offset - 1])) + offset--; + if (offset < start) + throw new IndexOutOfBoundsException(); + } + return offset; + } + + } + + /** + * Returns the number of Unicode code points in the specified range of the + * given CharSequence. The first char in the range is at position + * beginIndex and the last one is at position endIndex - 1. Paired + * surrogates (supplementary characters are represented by a pair of chars - + * one from the high surrogates and one from the low surrogates) + * count as just one code point. + * @param seq the CharSequence to inspect + * @param beginIndex the beginning of the range + * @param endIndex the end of the range + * @return the number of Unicode code points in the given range of the + * sequence + * @throws NullPointerException if seq is null + * @throws IndexOutOfBoundsException if beginIndex is negative, endIndex is + * larger than the length of seq, or if beginIndex is greater than endIndex. + * @since 1.5 + */ + public static int codePointCount(CharSequence seq, int beginIndex, + int endIndex) + { + int len = seq.length(); + if (beginIndex < 0 || endIndex > len || beginIndex > endIndex) + throw new IndexOutOfBoundsException(); + + int count = 0; + for (int i = beginIndex; i < endIndex; i++) + { + count++; + // If there is a pairing, count it only once. + if (isHighSurrogate(seq.charAt(i)) && (i + 1) < endIndex + && isLowSurrogate(seq.charAt(i + 1))) + i ++; + } + return count; + } + + /** + * Returns the number of Unicode code points in the specified range of the + * given char array. The first char in the range is at position + * offset and the length of the range is count. Paired surrogates + * (supplementary characters are represented by a pair of chars - + * one from the high surrogates and one from the low surrogates) + * count as just one code point. + * @param a the char array to inspect + * @param offset the beginning of the range + * @param count the length of the range + * @return the number of Unicode code points in the given range of the + * array + * @throws NullPointerException if a is null + * @throws IndexOutOfBoundsException if offset or count is negative or if + * offset + countendIndex is larger than the length of a. + * @since 1.5 + */ + public static int codePointCount(char[] a, int offset, + int count) + { + int len = a.length; + int end = offset + count; + if (offset < 0 || count < 0 || end > len) + throw new IndexOutOfBoundsException(); + + int counter = 0; + for (int i = offset; i < end; i++) + { + counter++; + // If there is a pairing, count it only once. + if (isHighSurrogate(a[i]) && (i + 1) < end + && isLowSurrogate(a[i + 1])) + i ++; + } + return counter; + } + + /** + * Determines if a character is a Unicode letter or a Unicode digit. This + * is the combination of isLetter and isDigit. + *
    + * letter or digit = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nd] + * + * @param ch character to test + * @return true if ch is a Unicode letter or a Unicode digit, else false + * @see #isDigit(char) + * @see #isJavaIdentifierPart(char) + * @see #isJavaLetter(char) + * @see #isJavaLetterOrDigit(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierPart(char) + */ + public static boolean isLetterOrDigit(char ch) + { + return isLetterOrDigit((int)ch); + } + + /** + * Determines if a character is a Unicode letter or a Unicode digit. This + * is the combination of isLetter and isDigit. + *
    + * letter or digit = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nd] + * + * @param codePoint character to test + * @return true if ch is a Unicode letter or a Unicode digit, else false + * @see #isDigit(char) + * @see #isJavaIdentifierPart(char) + * @see #isJavaLetter(char) + * @see #isJavaLetterOrDigit(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierPart(char) + * + * @since 1.5 + */ + public static boolean isLetterOrDigit(int codePoint) + { + return ((1 << getType(codePoint)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << DECIMAL_DIGIT_NUMBER))) != 0; + } + + /** + * Determines if a character can start a Java identifier. This is the + * combination of isLetter, any character where getType returns + * LETTER_NUMBER, currency symbols (like '$'), and connecting punctuation + * (like '_'). + * + * @param ch character to test + * @return true if ch can start a Java identifier, else false + * @deprecated Replaced by {@link #isJavaIdentifierStart(char)} + * @see #isJavaLetterOrDigit(char) + * @see #isJavaIdentifierStart(char) + * @see #isJavaIdentifierPart(char) + * @see #isLetter(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierStart(char) + */ + public static boolean isJavaLetter(char ch) + { + return isJavaIdentifierStart(ch); + } + + /** + * Determines if a character can follow the first letter in + * a Java identifier. This is the combination of isJavaLetter (isLetter, + * type of LETTER_NUMBER, currency, connecting punctuation) and digit, + * numeric letter (like Roman numerals), combining marks, non-spacing marks, + * or isIdentifierIgnorable. + * + * @param ch character to test + * @return true if ch can follow the first letter in a Java identifier + * @deprecated Replaced by {@link #isJavaIdentifierPart(char)} + * @see #isJavaLetter(char) + * @see #isJavaIdentifierStart(char) + * @see #isJavaIdentifierPart(char) + * @see #isLetter(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierPart(char) + * @see #isIdentifierIgnorable(char) + */ + public static boolean isJavaLetterOrDigit(char ch) + { + return isJavaIdentifierPart(ch); + } + + /** + * Determines if a character can start a Java identifier. This is the + * combination of isLetter, any character where getType returns + * LETTER_NUMBER, currency symbols (like '$'), and connecting punctuation + * (like '_'). + *
    + * Java identifier start = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Sc]|[Pc] + * + * @param ch character to test + * @return true if ch can start a Java identifier, else false + * @see #isJavaIdentifierPart(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierStart(char) + * @since 1.1 + */ + public static boolean isJavaIdentifierStart(char ch) + { + return isJavaIdentifierStart((int)ch); + } + + /** + * Determines if a character can start a Java identifier. This is the + * combination of isLetter, any character where getType returns + * LETTER_NUMBER, currency symbols (like '$'), and connecting punctuation + * (like '_'). + *
    + * Java identifier start = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Sc]|[Pc] + * + * @param codePoint character to test + * @return true if ch can start a Java identifier, else false + * @see #isJavaIdentifierPart(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierStart(char) + * @since 1.5 + */ + public static boolean isJavaIdentifierStart(int codePoint) + { + return ((1 << getType(codePoint)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << LETTER_NUMBER) + | (1 << CURRENCY_SYMBOL) + | (1 << CONNECTOR_PUNCTUATION))) != 0; + } + + /** + * Determines if a character can follow the first letter in + * a Java identifier. This is the combination of isJavaLetter (isLetter, + * type of LETTER_NUMBER, currency, connecting punctuation) and digit, + * numeric letter (like Roman numerals), combining marks, non-spacing marks, + * or isIdentifierIgnorable. + *
    + * Java identifier extender = + * [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Sc]|[Pc]|[Mn]|[Mc]|[Nd]|[Cf] + * |U+0000-U+0008|U+000E-U+001B|U+007F-U+009F + * + * @param ch character to test + * @return true if ch can follow the first letter in a Java identifier + * @see #isIdentifierIgnorable(char) + * @see #isJavaIdentifierStart(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.1 + */ + public static boolean isJavaIdentifierPart(char ch) + { + return isJavaIdentifierPart((int)ch); + } + + /** + * Determines if a character can follow the first letter in + * a Java identifier. This is the combination of isJavaLetter (isLetter, + * type of LETTER_NUMBER, currency, connecting punctuation) and digit, + * numeric letter (like Roman numerals), combining marks, non-spacing marks, + * or isIdentifierIgnorable. + *
    + * Java identifier extender = + * [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Sc]|[Pc]|[Mn]|[Mc]|[Nd]|[Cf] + * |U+0000-U+0008|U+000E-U+001B|U+007F-U+009F + * + * @param codePoint character to test + * @return true if ch can follow the first letter in a Java identifier + * @see #isIdentifierIgnorable(char) + * @see #isJavaIdentifierStart(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.5 + */ + public static boolean isJavaIdentifierPart(int codePoint) + { + int category = getType(codePoint); + return ((1 << category) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << NON_SPACING_MARK) + | (1 << COMBINING_SPACING_MARK) + | (1 << DECIMAL_DIGIT_NUMBER) + | (1 << LETTER_NUMBER) + | (1 << CURRENCY_SYMBOL) + | (1 << CONNECTOR_PUNCTUATION) + | (1 << FORMAT))) != 0 + || (category == CONTROL && isIdentifierIgnorable(codePoint)); + } + + /** + * Determines if a character can start a Unicode identifier. Only + * letters can start a Unicode identifier, but this includes characters + * in LETTER_NUMBER. + *
    + * Unicode identifier start = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl] + * + * @param ch character to test + * @return true if ch can start a Unicode identifier, else false + * @see #isJavaIdentifierStart(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.1 + */ + public static boolean isUnicodeIdentifierStart(char ch) + { + return isUnicodeIdentifierStart((int)ch); + } + + /** + * Determines if a character can start a Unicode identifier. Only + * letters can start a Unicode identifier, but this includes characters + * in LETTER_NUMBER. + *
    + * Unicode identifier start = [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl] + * + * @param codePoint character to test + * @return true if ch can start a Unicode identifier, else false + * @see #isJavaIdentifierStart(char) + * @see #isLetter(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.5 + */ + public static boolean isUnicodeIdentifierStart(int codePoint) + { + return ((1 << getType(codePoint)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << LETTER_NUMBER))) != 0; + } + + /** + * Determines if a character can follow the first letter in + * a Unicode identifier. This includes letters, connecting punctuation, + * digits, numeric letters, combining marks, non-spacing marks, and + * isIdentifierIgnorable. + *
    + * Unicode identifier extender = + * [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Mn]|[Mc]|[Nd]|[Pc]|[Cf]| + * |U+0000-U+0008|U+000E-U+001B|U+007F-U+009F + * + * @param ch character to test + * @return true if ch can follow the first letter in a Unicode identifier + * @see #isIdentifierIgnorable(char) + * @see #isJavaIdentifierPart(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierStart(char) + * @since 1.1 + */ + public static boolean isUnicodeIdentifierPart(char ch) + { + return isUnicodeIdentifierPart((int)ch); + } + + /** + * Determines if a character can follow the first letter in + * a Unicode identifier. This includes letters, connecting punctuation, + * digits, numeric letters, combining marks, non-spacing marks, and + * isIdentifierIgnorable. + *
    + * Unicode identifier extender = + * [Lu]|[Ll]|[Lt]|[Lm]|[Lo]|[Nl]|[Mn]|[Mc]|[Nd]|[Pc]|[Cf]| + * |U+0000-U+0008|U+000E-U+001B|U+007F-U+009F + * + * @param codePoint character to test + * @return true if ch can follow the first letter in a Unicode identifier + * @see #isIdentifierIgnorable(char) + * @see #isJavaIdentifierPart(char) + * @see #isLetterOrDigit(char) + * @see #isUnicodeIdentifierStart(char) + * @since 1.5 + */ + public static boolean isUnicodeIdentifierPart(int codePoint) + { + int category = getType(codePoint); + return ((1 << category) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << TITLECASE_LETTER) + | (1 << MODIFIER_LETTER) + | (1 << OTHER_LETTER) + | (1 << NON_SPACING_MARK) + | (1 << COMBINING_SPACING_MARK) + | (1 << DECIMAL_DIGIT_NUMBER) + | (1 << LETTER_NUMBER) + | (1 << CONNECTOR_PUNCTUATION) + | (1 << FORMAT))) != 0 + || (category == CONTROL && isIdentifierIgnorable(codePoint)); + } + + /** + * Determines if a character is ignorable in a Unicode identifier. This + * includes the non-whitespace ISO control characters ('\u0000' + * through '\u0008', '\u000E' through + * '\u001B', and '\u007F' through + * '\u009F'), and FORMAT characters. + *
    + * Unicode identifier ignorable = [Cf]|U+0000-U+0008|U+000E-U+001B + * |U+007F-U+009F + * + * @param ch character to test + * @return true if ch is ignorable in a Unicode or Java identifier + * @see #isJavaIdentifierPart(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.1 + */ + public static boolean isIdentifierIgnorable(char ch) + { + return isIdentifierIgnorable((int)ch); + } + + /** + * Determines if a character is ignorable in a Unicode identifier. This + * includes the non-whitespace ISO control characters ('\u0000' + * through '\u0008', '\u000E' through + * '\u001B', and '\u007F' through + * '\u009F'), and FORMAT characters. + *
    + * Unicode identifier ignorable = [Cf]|U+0000-U+0008|U+000E-U+001B + * |U+007F-U+009F + * + * @param codePoint character to test + * @return true if ch is ignorable in a Unicode or Java identifier + * @see #isJavaIdentifierPart(char) + * @see #isUnicodeIdentifierPart(char) + * @since 1.5 + */ + public static boolean isIdentifierIgnorable(int codePoint) + { + if ((codePoint >= 0 && codePoint <= 0x0008) + || (codePoint >= 0x000E && codePoint <= 0x001B) + || (codePoint >= 0x007F && codePoint <= 0x009F) + || getType(codePoint) == FORMAT) + return true; + return false; + } + + /** + * Converts a Unicode character into its lowercase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isLowerCase(toLowerCase(ch)) does not always return true. + * + * @param ch character to convert to lowercase + * @return lowercase mapping of ch, or ch if lowercase mapping does + * not exist + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toTitleCase(char) + * @see #toUpperCase(char) + */ + public static char toLowerCase(char ch) + { + return (char) (lower[0][readCodePoint((int)ch) >>> 7] + ch); + } + + /** + * Converts a Unicode character into its lowercase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isLowerCase(toLowerCase(ch)) does not always return true. + * + * @param codePoint character to convert to lowercase + * @return lowercase mapping of ch, or ch if lowercase mapping does + * not exist + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toTitleCase(char) + * @see #toUpperCase(char) + * + * @since 1.5 + */ + public static int toLowerCase(int codePoint) + { + // If the code point is unassigned or in one of the private use areas + // then we delegate the call to the appropriate private static inner class. + int plane = codePoint >>> 16; + if (plane > 2 && plane < 14) + return UnassignedCharacters.toLowerCase(codePoint); + if (plane > 14) + return PrivateUseCharacters.toLowerCase(codePoint); + + // The short value stored in lower[plane] is the signed difference between + // codePoint and its lowercase conversion. + return ((short)lower[plane][readCodePoint(codePoint) >>> 7]) + codePoint; + } + + /** + * Converts a Unicode character into its uppercase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isUpperCase(toUpperCase(ch)) does not always return true. + * + * @param ch character to convert to uppercase + * @return uppercase mapping of ch, or ch if uppercase mapping does + * not exist + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toLowerCase(char) + * @see #toTitleCase(char) + */ + public static char toUpperCase(char ch) + { + return (char) (upper[0][readCodePoint((int)ch) >>> 7] + ch); + } + + /** + * Converts a Unicode character into its uppercase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isUpperCase(toUpperCase(ch)) does not always return true. + * + * @param codePoint character to convert to uppercase + * @return uppercase mapping of ch, or ch if uppercase mapping does + * not exist + * @see #isLowerCase(char) + * @see #isUpperCase(char) + * @see #toLowerCase(char) + * @see #toTitleCase(char) + * + * @since 1.5 + */ + public static int toUpperCase(int codePoint) + { + // If the code point is unassigned or in one of the private use areas + // then we delegate the call to the appropriate private static inner class. + int plane = codePoint >>> 16; + if (plane > 2 && plane < 14) + return UnassignedCharacters.toUpperCase(codePoint); + if (plane > 14) + return PrivateUseCharacters.toUpperCase(codePoint); + + // The short value stored in upper[plane] is the signed difference between + // codePoint and its uppercase conversion. + return ((short)upper[plane][readCodePoint(codePoint) >>> 7]) + codePoint; + } + + /** + * Converts a Unicode character into its titlecase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isTitleCase(toTitleCase(ch)) does not always return true. + * + * @param ch character to convert to titlecase + * @return titlecase mapping of ch, or ch if titlecase mapping does + * not exist + * @see #isTitleCase(char) + * @see #toLowerCase(char) + * @see #toUpperCase(char) + */ + public static char toTitleCase(char ch) + { + // As title is short, it doesn't hurt to exhaustively iterate over it. + for (int i = title.length - 2; i >= 0; i -= 2) + if (title[i] == ch) + return title[i + 1]; + return toUpperCase(ch); + } + + /** + * Converts a Unicode character into its titlecase equivalent mapping. + * If a mapping does not exist, then the character passed is returned. + * Note that isTitleCase(toTitleCase(ch)) does not always return true. + * + * @param codePoint character to convert to titlecase + * @return titlecase mapping of ch, or ch if titlecase mapping does + * not exist + * @see #isTitleCase(char) + * @see #toLowerCase(char) + * @see #toUpperCase(char) + * + * @since 1.5 + */ + public static int toTitleCase(int codePoint) + { + // As of Unicode 4.0.0 no characters outside of plane 0 have + // titlecase mappings that are different from their uppercase + // mapping. + if (codePoint < 0x10000) + return (int) toTitleCase((char)codePoint); + return toUpperCase(codePoint); + } + + /** + * Converts a character into a digit of the specified radix. If the radix + * exceeds MIN_RADIX or MAX_RADIX, or if the result of getNumericValue(ch) + * exceeds the radix, or if ch is not a decimal digit or in the case + * insensitive set of 'a'-'z', the result is -1. + *
    + * character argument boundary = [Nd]|U+0041-U+005A|U+0061-U+007A + * |U+FF21-U+FF3A|U+FF41-U+FF5A + * + * @param ch character to convert into a digit + * @param radix radix in which ch is a digit + * @return digit which ch represents in radix, or -1 not a valid digit + * @see #MIN_RADIX + * @see #MAX_RADIX + * @see #forDigit(int, int) + * @see #isDigit(char) + * @see #getNumericValue(char) + */ + public static int digit(char ch, int radix) + { + if (radix < MIN_RADIX || radix > MAX_RADIX) + return -1; + char attr = readCodePoint((int)ch); + if (((1 << (attr & TYPE_MASK)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << DECIMAL_DIGIT_NUMBER))) != 0) + { + // Signedness doesn't matter; 0xffff vs. -1 are both rejected. + int digit = numValue[0][attr >> 7]; + return (digit < radix) ? digit : -1; + } + return -1; + } + + /** + * Converts a character into a digit of the specified radix. If the radix + * exceeds MIN_RADIX or MAX_RADIX, or if the result of getNumericValue(ch) + * exceeds the radix, or if ch is not a decimal digit or in the case + * insensitive set of 'a'-'z', the result is -1. + *
    + * character argument boundary = [Nd]|U+0041-U+005A|U+0061-U+007A + * |U+FF21-U+FF3A|U+FF41-U+FF5A + * + * @param codePoint character to convert into a digit + * @param radix radix in which ch is a digit + * @return digit which ch represents in radix, or -1 not a valid digit + * @see #MIN_RADIX + * @see #MAX_RADIX + * @see #forDigit(int, int) + * @see #isDigit(char) + * @see #getNumericValue(char) + */ + public static int digit(int codePoint, int radix) + { + if (radix < MIN_RADIX || radix > MAX_RADIX) + return -1; + + // If the code point is unassigned or in one of the private use areas + // then we delegate the call to the appropriate private static inner class. + int plane = codePoint >>> 16; + if (plane > 2 && plane < 14) + return UnassignedCharacters.digit(codePoint, radix); + if (plane > 14) + return PrivateUseCharacters.digit(codePoint, radix); + char attr = readCodePoint(codePoint); + if (((1 << (attr & TYPE_MASK)) + & ((1 << UPPERCASE_LETTER) + | (1 << LOWERCASE_LETTER) + | (1 << DECIMAL_DIGIT_NUMBER))) != 0) + { + // Signedness doesn't matter; 0xffff vs. -1 are both rejected. + int digit = numValue[plane][attr >> 7]; + + // If digit is less than or equal to -3 then the numerical value was + // too large to fit into numValue and is stored in CharData.LARGENUMS. + if (digit <= -3) + digit = CharData.LARGENUMS[-digit - 3]; + return (digit < radix) ? digit : -1; + } + return -1; + } + + /** + * Returns the Unicode numeric value property of a character. For example, + * '\\u216C' (the Roman numeral fifty) returns 50. + * + *

    This method also returns values for the letters A through Z, (not + * specified by Unicode), in these ranges: '\u0041' + * through '\u005A' (uppercase); '\u0061' + * through '\u007A' (lowercase); and '\uFF21' + * through '\uFF3A', '\uFF41' through + * '\uFF5A' (full width variants). + * + *

    If the character lacks a numeric value property, -1 is returned. + * If the character has a numeric value property which is not representable + * as a nonnegative integer, such as a fraction, -2 is returned. + * + * character argument boundary = [Nd]|[Nl]|[No]|U+0041-U+005A|U+0061-U+007A + * |U+FF21-U+FF3A|U+FF41-U+FF5A + * + * @param ch character from which the numeric value property will + * be retrieved + * @return the numeric value property of ch, or -1 if it does not exist, or + * -2 if it is not representable as a nonnegative integer + * @see #forDigit(int, int) + * @see #digit(char, int) + * @see #isDigit(char) + * @since 1.1 + */ + public static int getNumericValue(char ch) + { + // Treat numValue as signed. + return (short) numValue[0][readCodePoint((int)ch) >> 7]; + } + + /** + * Returns the Unicode numeric value property of a character. For example, + * '\\u216C' (the Roman numeral fifty) returns 50. + * + *

    This method also returns values for the letters A through Z, (not + * specified by Unicode), in these ranges: '\u0041' + * through '\u005A' (uppercase); '\u0061' + * through '\u007A' (lowercase); and '\uFF21' + * through '\uFF3A', '\uFF41' through + * '\uFF5A' (full width variants). + * + *

    If the character lacks a numeric value property, -1 is returned. + * If the character has a numeric value property which is not representable + * as a nonnegative integer, such as a fraction, -2 is returned. + * + * character argument boundary = [Nd]|[Nl]|[No]|U+0041-U+005A|U+0061-U+007A + * |U+FF21-U+FF3A|U+FF41-U+FF5A + * + * @param codePoint character from which the numeric value property will + * be retrieved + * @return the numeric value property of ch, or -1 if it does not exist, or + * -2 if it is not representable as a nonnegative integer + * @see #forDigit(int, int) + * @see #digit(char, int) + * @see #isDigit(char) + * @since 1.5 + */ + public static int getNumericValue(int codePoint) + { + // If the code point is unassigned or in one of the private use areas + // then we delegate the call to the appropriate private static inner class. + int plane = codePoint >>> 16; + if (plane > 2 && plane < 14) + return UnassignedCharacters.getNumericValue(codePoint); + if (plane > 14) + return PrivateUseCharacters.getNumericValue(codePoint); + + // If the value N found in numValue[plane] is less than or equal to -3 + // then the numeric value was too big to fit into 16 bits and is + // stored in CharData.LARGENUMS at offset (-N - 3). + short num = (short)numValue[plane][readCodePoint(codePoint) >> 7]; + if (num <= -3) + return CharData.LARGENUMS[-num - 3]; + return num; + } + + /** + * Determines if a character is a ISO-LATIN-1 space. This is only the five + * characters '\t', '\n', '\f', + * '\r', and ' '. + *
    + * Java space = U+0020|U+0009|U+000A|U+000C|U+000D + * + * @param ch character to test + * @return true if ch is a space, else false + * @deprecated Replaced by {@link #isWhitespace(char)} + * @see #isSpaceChar(char) + * @see #isWhitespace(char) + */ + public static boolean isSpace(char ch) + { + // Performing the subtraction up front alleviates need to compare longs. + return ch-- <= ' ' && ((1 << ch) + & ((1 << (' ' - 1)) + | (1 << ('\t' - 1)) + | (1 << ('\n' - 1)) + | (1 << ('\r' - 1)) + | (1 << ('\f' - 1)))) != 0; + } + + /** + * Determines if a character is a Unicode space character. This includes + * SPACE_SEPARATOR, LINE_SEPARATOR, and PARAGRAPH_SEPARATOR. + *
    + * Unicode space = [Zs]|[Zp]|[Zl] + * + * @param ch character to test + * @return true if ch is a Unicode space, else false + * @see #isWhitespace(char) + * @since 1.1 + */ + public static boolean isSpaceChar(char ch) + { + return isSpaceChar((int)ch); + } + + /** + * Determines if a character is a Unicode space character. This includes + * SPACE_SEPARATOR, LINE_SEPARATOR, and PARAGRAPH_SEPARATOR. + *
    + * Unicode space = [Zs]|[Zp]|[Zl] + * + * @param codePoint character to test + * @return true if ch is a Unicode space, else false + * @see #isWhitespace(char) + * @since 1.5 + */ + public static boolean isSpaceChar(int codePoint) + { + return ((1 << getType(codePoint)) + & ((1 << SPACE_SEPARATOR) + | (1 << LINE_SEPARATOR) + | (1 << PARAGRAPH_SEPARATOR))) != 0; + } + + /** + * Determines if a character is Java whitespace. This includes Unicode + * space characters (SPACE_SEPARATOR, LINE_SEPARATOR, and + * PARAGRAPH_SEPARATOR) except the non-breaking spaces + * ('\u00A0', '\u2007', and '\u202F'); + * and these characters: '\u0009', '\u000A', + * '\u000B', '\u000C', '\u000D', + * '\u001C', '\u001D', '\u001E', + * and '\u001F'. + *
    + * Java whitespace = ([Zs] not Nb)|[Zl]|[Zp]|U+0009-U+000D|U+001C-U+001F + * + * @param ch character to test + * @return true if ch is Java whitespace, else false + * @see #isSpaceChar(char) + * @since 1.1 + */ + public static boolean isWhitespace(char ch) + { + return isWhitespace((int) ch); + } + + /** + * Determines if a character is Java whitespace. This includes Unicode + * space characters (SPACE_SEPARATOR, LINE_SEPARATOR, and + * PARAGRAPH_SEPARATOR) except the non-breaking spaces + * ('\u00A0', '\u2007', and '\u202F'); + * and these characters: '\u0009', '\u000A', + * '\u000B', '\u000C', '\u000D', + * '\u001C', '\u001D', '\u001E', + * and '\u001F'. + *
    + * Java whitespace = ([Zs] not Nb)|[Zl]|[Zp]|U+0009-U+000D|U+001C-U+001F + * + * @param codePoint character to test + * @return true if ch is Java whitespace, else false + * @see #isSpaceChar(char) + * @since 1.5 + */ + public static boolean isWhitespace(int codePoint) + { + int plane = codePoint >>> 16; + if (plane > 2 && plane < 14) + return UnassignedCharacters.isWhiteSpace(codePoint); + if (plane > 14) + return PrivateUseCharacters.isWhiteSpace(codePoint); + + int attr = readCodePoint(codePoint); + return ((((1 << (attr & TYPE_MASK)) + & ((1 << SPACE_SEPARATOR) + | (1 << LINE_SEPARATOR) + | (1 << PARAGRAPH_SEPARATOR))) != 0) + && (attr & NO_BREAK_MASK) == 0) + || (codePoint <= '\u001F' && ((1 << codePoint) + & ((1 << '\t') + | (1 << '\n') + | (1 << '\u000B') + | (1 << '\u000C') + | (1 << '\r') + | (1 << '\u001C') + | (1 << '\u001D') + | (1 << '\u001E') + | (1 << '\u001F'))) != 0); + } + + /** + * Determines if a character has the ISO Control property. + *
    + * ISO Control = [Cc] + * + * @param ch character to test + * @return true if ch is an ISO Control character, else false + * @see #isSpaceChar(char) + * @see #isWhitespace(char) + * @since 1.1 + */ + public static boolean isISOControl(char ch) + { + return isISOControl((int)ch); + } + + /** + * Determines if the character is an ISO Control character. This is true + * if the code point is in the range [0, 0x001F] or if it is in the range + * [0x007F, 0x009F]. + * @param codePoint the character to check + * @return true if the character is in one of the above ranges + * + * @since 1.5 + */ + public static boolean isISOControl(int codePoint) + { + if ((codePoint >= 0 && codePoint <= 0x001F) + || (codePoint >= 0x007F && codePoint <= 0x009F)) + return true; + return false; + } + + /** + * Returns the Unicode general category property of a character. + * + * @param ch character from which the general category property will + * be retrieved + * @return the character category property of ch as an integer + * @see #UNASSIGNED + * @see #UPPERCASE_LETTER + * @see #LOWERCASE_LETTER + * @see #TITLECASE_LETTER + * @see #MODIFIER_LETTER + * @see #OTHER_LETTER + * @see #NON_SPACING_MARK + * @see #ENCLOSING_MARK + * @see #COMBINING_SPACING_MARK + * @see #DECIMAL_DIGIT_NUMBER + * @see #LETTER_NUMBER + * @see #OTHER_NUMBER + * @see #SPACE_SEPARATOR + * @see #LINE_SEPARATOR + * @see #PARAGRAPH_SEPARATOR + * @see #CONTROL + * @see #FORMAT + * @see #PRIVATE_USE + * @see #SURROGATE + * @see #DASH_PUNCTUATION + * @see #START_PUNCTUATION + * @see #END_PUNCTUATION + * @see #CONNECTOR_PUNCTUATION + * @see #OTHER_PUNCTUATION + * @see #MATH_SYMBOL + * @see #CURRENCY_SYMBOL + * @see #MODIFIER_SYMBOL + * @see #INITIAL_QUOTE_PUNCTUATION + * @see #FINAL_QUOTE_PUNCTUATION + * @since 1.1 + */ + public static int getType(char ch) + { + return getType((int)ch); + } + + /** + * Returns the Unicode general category property of a character. + * + * @param codePoint character from which the general category property will + * be retrieved + * @return the character category property of ch as an integer + * @see #UNASSIGNED + * @see #UPPERCASE_LETTER + * @see #LOWERCASE_LETTER + * @see #TITLECASE_LETTER + * @see #MODIFIER_LETTER + * @see #OTHER_LETTER + * @see #NON_SPACING_MARK + * @see #ENCLOSING_MARK + * @see #COMBINING_SPACING_MARK + * @see #DECIMAL_DIGIT_NUMBER + * @see #LETTER_NUMBER + * @see #OTHER_NUMBER + * @see #SPACE_SEPARATOR + * @see #LINE_SEPARATOR + * @see #PARAGRAPH_SEPARATOR + * @see #CONTROL + * @see #FORMAT + * @see #PRIVATE_USE + * @see #SURROGATE + * @see #DASH_PUNCTUATION + * @see #START_PUNCTUATION + * @see #END_PUNCTUATION + * @see #CONNECTOR_PUNCTUATION + * @see #OTHER_PUNCTUATION + * @see #MATH_SYMBOL + * @see #CURRENCY_SYMBOL + * @see #MODIFIER_SYMBOL + * @see #INITIAL_QUOTE_PUNCTUATION + * @see #FINAL_QUOTE_PUNCTUATION + * + * @since 1.5 + */ + public static int getType(int codePoint) + { + // If the codePoint is unassigned or in one of the private use areas + // then we delegate the call to the appropriate private static inner class. + int plane = codePoint >>> 16; + if (plane > 2 && plane < 14) + return UnassignedCharacters.getType(codePoint); + if (plane > 14) + return PrivateUseCharacters.getType(codePoint); + + return readCodePoint(codePoint) & TYPE_MASK; + } + + /** + * Converts a digit into a character which represents that digit + * in a specified radix. If the radix exceeds MIN_RADIX or MAX_RADIX, + * or the digit exceeds the radix, then the null character '\0' + * is returned. Otherwise the return value is in '0'-'9' and 'a'-'z'. + *
    + * return value boundary = U+0030-U+0039|U+0061-U+007A + * + * @param digit digit to be converted into a character + * @param radix radix of digit + * @return character representing digit in radix, or '\0' + * @see #MIN_RADIX + * @see #MAX_RADIX + * @see #digit(char, int) + */ + public static char forDigit(int digit, int radix) + { + if (radix < MIN_RADIX || radix > MAX_RADIX + || digit < 0 || digit >= radix) + return '\0'; + return Number.digits[digit]; + } + + /** + * Returns the Unicode directionality property of the character. This + * is used in the visual ordering of text. + * + * @param ch the character to look up + * @return the directionality constant, or DIRECTIONALITY_UNDEFINED + * @see #DIRECTIONALITY_UNDEFINED + * @see #DIRECTIONALITY_LEFT_TO_RIGHT + * @see #DIRECTIONALITY_RIGHT_TO_LEFT + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC + * @see #DIRECTIONALITY_EUROPEAN_NUMBER + * @see #DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR + * @see #DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR + * @see #DIRECTIONALITY_ARABIC_NUMBER + * @see #DIRECTIONALITY_COMMON_NUMBER_SEPARATOR + * @see #DIRECTIONALITY_NONSPACING_MARK + * @see #DIRECTIONALITY_BOUNDARY_NEUTRAL + * @see #DIRECTIONALITY_PARAGRAPH_SEPARATOR + * @see #DIRECTIONALITY_SEGMENT_SEPARATOR + * @see #DIRECTIONALITY_WHITESPACE + * @see #DIRECTIONALITY_OTHER_NEUTRALS + * @see #DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING + * @see #DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE + * @see #DIRECTIONALITY_POP_DIRECTIONAL_FORMAT + * @since 1.4 + */ + public static byte getDirectionality(char ch) + { + // The result will correctly be signed. + return getDirectionality((int)ch); + } + + + /** + * Returns the Unicode directionality property of the character. This + * is used in the visual ordering of text. + * + * @param codePoint the character to look up + * @return the directionality constant, or DIRECTIONALITY_UNDEFINED + * @see #DIRECTIONALITY_UNDEFINED + * @see #DIRECTIONALITY_LEFT_TO_RIGHT + * @see #DIRECTIONALITY_RIGHT_TO_LEFT + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC + * @see #DIRECTIONALITY_EUROPEAN_NUMBER + * @see #DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR + * @see #DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR + * @see #DIRECTIONALITY_ARABIC_NUMBER + * @see #DIRECTIONALITY_COMMON_NUMBER_SEPARATOR + * @see #DIRECTIONALITY_NONSPACING_MARK + * @see #DIRECTIONALITY_BOUNDARY_NEUTRAL + * @see #DIRECTIONALITY_PARAGRAPH_SEPARATOR + * @see #DIRECTIONALITY_SEGMENT_SEPARATOR + * @see #DIRECTIONALITY_WHITESPACE + * @see #DIRECTIONALITY_OTHER_NEUTRALS + * @see #DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING + * @see #DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING + * @see #DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE + * @see #DIRECTIONALITY_POP_DIRECTIONAL_FORMAT + * @since 1.5 + */ + public static byte getDirectionality(int codePoint) + { + // If the code point is unassigned or in one of the private use areas + // then we delegate the call to the appropriate private static inner class. + int plane = codePoint >>> 16; + if (plane > 2 && plane < 14) + return UnassignedCharacters.getDirectionality(codePoint); + if (plane > 14) + return PrivateUseCharacters.getDirectionality(codePoint); + + // The result will correctly be signed. + return (byte) (direction[plane][readCodePoint(codePoint) >> 7] >> 2); + } + + /** + * Determines whether the character is mirrored according to Unicode. For + * example, \u0028 (LEFT PARENTHESIS) appears as '(' in + * left-to-right text, but ')' in right-to-left text. + * + * @param ch the character to look up + * @return true if the character is mirrored + * @since 1.4 + */ + public static boolean isMirrored(char ch) + { + return (readCodePoint((int)ch) & MIRROR_MASK) != 0; + } + + /** + * Determines whether the character is mirrored according to Unicode. For + * example, \u0028 (LEFT PARENTHESIS) appears as '(' in + * left-to-right text, but ')' in right-to-left text. + * + * @param codePoint the character to look up + * @return true if the character is mirrored + * @since 1.5 + */ + public static boolean isMirrored(int codePoint) + { + // If the code point is unassigned or part of one of the private use areas + // then we delegate the call to the appropriate private static inner class. + int plane = codePoint >>> 16; + if (plane > 2 && plane < 14) + return UnassignedCharacters.isMirrored(codePoint); + if (plane > 14) + return PrivateUseCharacters.isMirrored(codePoint); + + return (readCodePoint(codePoint) & MIRROR_MASK) != 0; + } + + /** + * Compares another Character to this Character, numerically. + * + * @param anotherCharacter Character to compare with this Character + * @return a negative integer if this Character is less than + * anotherCharacter, zero if this Character is equal, and + * a positive integer if this Character is greater + * @throws NullPointerException if anotherCharacter is null + * @since 1.2 + */ + public int compareTo(Character anotherCharacter) + { + return value - anotherCharacter.value; + } + + /** + * Returns an Character object wrapping the value. + * In contrast to the Character constructor, this method + * will cache some values. It is used by boxing conversion. + * + * @param val the value to wrap + * @return the Character + * + * @since 1.5 + */ + public static Character valueOf(char val) + { + if (val > MAX_CACHE) + return new Character(val); + else + return charCache[val - MIN_VALUE]; + } + + /** + * Reverse the bytes in val. + * @since 1.5 + */ + public static char reverseBytes(char val) + { + return (char) (((val >> 8) & 0xff) | ((val << 8) & 0xff00)); + } + + /** + * Converts a unicode code point to a UTF-16 representation of that + * code point. + * + * @param codePoint the unicode code point + * + * @return the UTF-16 representation of that code point + * + * @throws IllegalArgumentException if the code point is not a valid + * unicode code point + * + * @since 1.5 + */ + public static char[] toChars(int codePoint) + { + if (!isValidCodePoint(codePoint)) + throw new IllegalArgumentException("Illegal Unicode code point : " + + codePoint); + char[] result = new char[charCount(codePoint)]; + int ignore = toChars(codePoint, result, 0); + return result; + } + + /** + * Converts a unicode code point to its UTF-16 representation. + * + * @param codePoint the unicode code point + * @param dst the target char array + * @param dstIndex the start index for the target + * + * @return number of characters written to dst + * + * @throws IllegalArgumentException if codePoint is not a + * valid unicode code point + * @throws NullPointerException if dst is null + * @throws IndexOutOfBoundsException if dstIndex is not valid + * in dst or if the UTF-16 representation does not + * fit into dst + * + * @since 1.5 + */ + public static int toChars(int codePoint, char[] dst, int dstIndex) + { + if (!isValidCodePoint(codePoint)) + { + throw new IllegalArgumentException("not a valid code point: " + + codePoint); + } + + int result; + if (isSupplementaryCodePoint(codePoint)) + { + // Write second char first to cause IndexOutOfBoundsException + // immediately. + final int cp2 = codePoint - 0x10000; + dst[dstIndex + 1] = (char) ((cp2 % 0x400) + (int) MIN_LOW_SURROGATE); + dst[dstIndex] = (char) ((cp2 / 0x400) + (int) MIN_HIGH_SURROGATE); + result = 2; + } + else + { + dst[dstIndex] = (char) codePoint; + result = 1; + } + return result; + } + + /** + * Return number of 16-bit characters required to represent the given + * code point. + * + * @param codePoint a unicode code point + * + * @return 2 if codePoint >= 0x10000, 1 otherwise. + * + * @since 1.5 + */ + public static int charCount(int codePoint) + { + return + (codePoint >= MIN_SUPPLEMENTARY_CODE_POINT) + ? 2 + : 1; + } + + /** + * Determines whether the specified code point is + * in the range 0x10000 .. 0x10FFFF, i.e. the character is within the Unicode + * supplementary character range. + * + * @param codePoint a Unicode code point + * + * @return true if code point is in supplementary range + * + * @since 1.5 + */ + public static boolean isSupplementaryCodePoint(int codePoint) + { + return codePoint >= MIN_SUPPLEMENTARY_CODE_POINT + && codePoint <= MAX_CODE_POINT; + } + + /** + * Determines whether the specified code point is + * in the range 0x0000 .. 0x10FFFF, i.e. it is a valid Unicode code point. + * + * @param codePoint a Unicode code point + * + * @return true if code point is valid + * + * @since 1.5 + */ + public static boolean isValidCodePoint(int codePoint) + { + return codePoint >= MIN_CODE_POINT && codePoint <= MAX_CODE_POINT; + } + + /** + * Return true if the given character is a high surrogate. + * @param ch the character + * @return true if the character is a high surrogate character + * + * @since 1.5 + */ + public static boolean isHighSurrogate(char ch) + { + return ch >= MIN_HIGH_SURROGATE && ch <= MAX_HIGH_SURROGATE; + } + + /** + * Return true if the given character is a low surrogate. + * @param ch the character + * @return true if the character is a low surrogate character + * + * @since 1.5 + */ + public static boolean isLowSurrogate(char ch) + { + return ch >= MIN_LOW_SURROGATE && ch <= MAX_LOW_SURROGATE; + } + + /** + * Return true if the given characters compose a surrogate pair. + * This is true if the first character is a high surrogate and the + * second character is a low surrogate. + * @param ch1 the first character + * @param ch2 the first character + * @return true if the characters compose a surrogate pair + * + * @since 1.5 + */ + public static boolean isSurrogatePair(char ch1, char ch2) + { + return isHighSurrogate(ch1) && isLowSurrogate(ch2); + } + + /** + * Given a valid surrogate pair, this returns the corresponding + * code point. + * @param high the high character of the pair + * @param low the low character of the pair + * @return the corresponding code point + * + * @since 1.5 + */ + public static int toCodePoint(char high, char low) + { + return ((high - MIN_HIGH_SURROGATE) * 0x400) + + (low - MIN_LOW_SURROGATE) + 0x10000; + } + + /** + * Get the code point at the specified index in the CharSequence. + * This is like CharSequence#charAt(int), but if the character is + * the start of a surrogate pair, and there is a following + * character, and this character completes the pair, then the + * corresponding supplementary code point is returned. Otherwise, + * the character at the index is returned. + * + * @param sequence the CharSequence + * @param index the index of the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public static int codePointAt(CharSequence sequence, int index) + { + int len = sequence.length(); + if (index < 0 || index >= len) + throw new IndexOutOfBoundsException(); + char high = sequence.charAt(index); + if (! isHighSurrogate(high) || ++index >= len) + return high; + char low = sequence.charAt(index); + if (! isLowSurrogate(low)) + return high; + return toCodePoint(high, low); + } + + /** + * Get the code point at the specified index in the CharSequence. + * If the character is the start of a surrogate pair, and there is a + * following character, and this character completes the pair, then + * the corresponding supplementary code point is returned. + * Otherwise, the character at the index is returned. + * + * @param chars the character array in which to look + * @param index the index of the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public static int codePointAt(char[] chars, int index) + { + return codePointAt(chars, index, chars.length); + } + + /** + * Get the code point at the specified index in the CharSequence. + * If the character is the start of a surrogate pair, and there is a + * following character within the specified range, and this + * character completes the pair, then the corresponding + * supplementary code point is returned. Otherwise, the character + * at the index is returned. + * + * @param chars the character array in which to look + * @param index the index of the codepoint to get, starting at 0 + * @param limit the limit past which characters should not be examined + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= + * limit, or if limit is negative or >= the length of the array + * @since 1.5 + */ + public static int codePointAt(char[] chars, int index, int limit) + { + if (index < 0 || index >= limit || limit < 0 || limit > chars.length) + throw new IndexOutOfBoundsException(); + char high = chars[index]; + if (! isHighSurrogate(high) || ++index >= limit) + return high; + char low = chars[index]; + if (! isLowSurrogate(low)) + return high; + return toCodePoint(high, low); + } + + /** + * Get the code point before the specified index. This is like + * #codePointAt(char[], int), but checks the characters at + * index-1 and index-2 to see if they form + * a supplementary code point. If they do not, the character at + * index-1 is returned. + * + * @param chars the character array + * @param index the index just past the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public static int codePointBefore(char[] chars, int index) + { + return codePointBefore(chars, index, 1); + } + + /** + * Get the code point before the specified index. This is like + * #codePointAt(char[], int), but checks the characters at + * index-1 and index-2 to see if they form + * a supplementary code point. If they do not, the character at + * index-1 is returned. The start parameter is used to + * limit the range of the array which may be examined. + * + * @param chars the character array + * @param index the index just past the codepoint to get, starting at 0 + * @param start the index before which characters should not be examined + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is > start or > + * the length of the array, or if limit is negative or >= the + * length of the array + * @since 1.5 + */ + public static int codePointBefore(char[] chars, int index, int start) + { + if (index < start || index > chars.length + || start < 0 || start >= chars.length) + throw new IndexOutOfBoundsException(); + --index; + char low = chars[index]; + if (! isLowSurrogate(low) || --index < start) + return low; + char high = chars[index]; + if (! isHighSurrogate(high)) + return low; + return toCodePoint(high, low); + } + + /** + * Get the code point before the specified index. This is like + * #codePointAt(CharSequence, int), but checks the characters at + * index-1 and index-2 to see if they form + * a supplementary code point. If they do not, the character at + * index-1 is returned. + * + * @param sequence the CharSequence + * @param index the index just past the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public static int codePointBefore(CharSequence sequence, int index) + { + int len = sequence.length(); + if (index < 1 || index > len) + throw new IndexOutOfBoundsException(); + --index; + char low = sequence.charAt(index); + if (! isLowSurrogate(low) || --index < 0) + return low; + char high = sequence.charAt(index); + if (! isHighSurrogate(high)) + return low; + return toCodePoint(high, low); + } +} // class Character diff --git a/libjava/classpath/java/lang/Class.java b/libjava/classpath/java/lang/Class.java new file mode 100644 index 000000000..1caee0147 --- /dev/null +++ b/libjava/classpath/java/lang/Class.java @@ -0,0 +1,1799 @@ +/* Class.java -- Representation of a Java class. + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.classpath.VMStackWalker; +import gnu.java.lang.reflect.ClassSignatureParser; + +import java.io.InputStream; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.net.URL; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; + + +/** + * A Class represents a Java type. There will never be multiple Class + * objects with identical names and ClassLoaders. Primitive types, array + * types, and void also have a Class object. + * + *

    Arrays with identical type and number of dimensions share the same class. + * The array class ClassLoader is the same as the ClassLoader of the element + * type of the array (which can be null to indicate the bootstrap classloader). + * The name of an array class is [<signature format>;. + *

    For example, + * String[]'s class is [Ljava.lang.String;. boolean, byte, + * short, char, int, long, float and double have the "type name" of + * Z,B,S,C,I,J,F,D for the purposes of array classes. If it's a + * multidimensioned array, the same principle applies: + * int[][][] == [[[I. + * + *

    There is no public constructor - Class objects are obtained only through + * the virtual machine, as defined in ClassLoaders. + * + * @serialData Class objects serialize specially: + * TC_CLASS ClassDescriptor. For more serialization information, + * see {@link ObjectStreamClass}. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.0 + * @see ClassLoader + */ +public final class Class + implements Serializable, Type, AnnotatedElement, GenericDeclaration +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3206093459760846163L; + + /** + * Flag indicating a synthetic member. + * Note that this duplicates a constant in Modifier. + */ + private static final int SYNTHETIC = 0x1000; + + /** + * Flag indiciating an annotation class. + */ + private static final int ANNOTATION = 0x2000; + + /** + * Flag indicating an enum constant or an enum class. + * Note that this duplicates a constant in Modifier. + */ + private static final int ENUM = 0x4000; + + /** The class signers. */ + private Object[] signers = null; + /** The class protection domain. */ + private final transient ProtectionDomain pd; + + /* We use an inner class, so that Class doesn't have a static initializer */ + private static final class StaticData + { + static final ProtectionDomain unknownProtectionDomain; + + static + { + Permissions permissions = new Permissions(); + permissions.add(new AllPermission()); + unknownProtectionDomain = new ProtectionDomain(null, permissions); + } + } + + final transient Object vmdata; + + /** newInstance() caches the default constructor */ + private transient Constructor constructor; + + /** + * Class is non-instantiable from Java code; only the VM can create + * instances of this class. + */ + Class(Object vmdata) + { + this(vmdata, null); + } + + Class(Object vmdata, ProtectionDomain pd) + { + this.vmdata = vmdata; + // If the VM didn't supply a protection domain and the class is an array, + // we "inherit" the protection domain from the component type class. This + // saves the VM from having to worry about protection domains for array + // classes. + if (pd == null && isArray()) + this.pd = getComponentType().pd; + else + this.pd = pd; + } + + /** + * Use the classloader of the current class to load, link, and initialize + * a class. This is equivalent to your code calling + * Class.forName(name, true, getClass().getClassLoader()). + * + * @param name the name of the class to find + * @return the Class object representing the class + * @throws ClassNotFoundException if the class was not found by the + * classloader + * @throws LinkageError if linking the class fails + * @throws ExceptionInInitializerError if the class loads, but an exception + * occurs during initialization + */ + public static Class forName(String name) throws ClassNotFoundException + { + return VMClass.forName(name, true, VMStackWalker.getCallingClassLoader()); + } + + /** + * Use the specified classloader to load and link a class. If the loader + * is null, this uses the bootstrap class loader (provide the security + * check succeeds). Unfortunately, this method cannot be used to obtain + * the Class objects for primitive types or for void, you have to use + * the fields in the appropriate java.lang wrapper classes. + * + *

    Calls classloader.loadclass(name, initialize). + * + * @param name the name of the class to find + * @param initialize whether or not to initialize the class at this time + * @param classloader the classloader to use to find the class; null means + * to use the bootstrap class loader + * + * @return the class object for the given class + * + * @throws ClassNotFoundException if the class was not found by the + * classloader + * @throws LinkageError if linking the class fails + * @throws ExceptionInInitializerError if the class loads, but an exception + * occurs during initialization + * @throws SecurityException if the classloader argument + * is null and the caller does not have the + * RuntimePermission("getClassLoader") permission + * @see ClassLoader + * @since 1.2 + */ + public static Class forName(String name, boolean initialize, + ClassLoader classloader) + throws ClassNotFoundException + { + if (classloader == null) + { + // Check if we may access the bootstrap classloader + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + // Get the calling classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + } + return (Class) VMClass.forName(name, initialize, classloader); + } + + /** + * Get all the public member classes and interfaces declared in this + * class or inherited from superclasses. This returns an array of length + * 0 if there are no member classes, including for primitive types. A + * security check may be performed, with + * checkMemberAccess(this, Member.PUBLIC) as well as + * checkPackageAccess both having to succeed. + * + * @return all public member classes in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Class[] getClasses() + { + memberAccessCheck(Member.PUBLIC); + return internalGetClasses(); + } + + /** + * Like getClasses() but without the security checks. + */ + private Class[] internalGetClasses() + { + ArrayList list = new ArrayList(); + list.addAll(Arrays.asList(getDeclaredClasses(true))); + Class superClass = getSuperclass(); + if (superClass != null) + list.addAll(Arrays.asList(superClass.internalGetClasses())); + return list.toArray(new Class[list.size()]); + } + + /** + * Get the ClassLoader that loaded this class. If the class was loaded + * by the bootstrap classloader, this method will return null. + * If there is a security manager, and the caller's class loader is not + * an ancestor of the requested one, a security check of + * RuntimePermission("getClassLoader") + * must first succeed. Primitive types and void return null. + * + * @return the ClassLoader that loaded this class + * @throws SecurityException if the security check fails + * @see ClassLoader + * @see RuntimePermission + */ + public ClassLoader getClassLoader() + { + if (isPrimitive()) + return null; + + ClassLoader loader = VMClass.getClassLoader(this); + // Check if we may get the classloader + SecurityManager sm = SecurityManager.current; + if (loader != null && sm != null) + { + // Get the calling classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null && !cl.isAncestorOf(loader)) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + return loader; + } + + /** + * If this is an array, get the Class representing the type of array. + * Examples: "[[Ljava.lang.String;" would return "[Ljava.lang.String;", and + * calling getComponentType on that would give "java.lang.String". If + * this is not an array, returns null. + * + * @return the array type of this class, or null + * @see Array + * @since 1.1 + */ + public Class getComponentType() + { + return VMClass.getComponentType (this); + } + + /** + * Get a public constructor declared in this class. If the constructor takes + * no argument, an array of zero elements and null are equivalent for the + * types argument. A security check may be performed, with + * checkMemberAccess(this, Member.PUBLIC) as well as + * checkPackageAccess both having to succeed. + * + * @param types the type of each parameter + * @return the constructor + * @throws NoSuchMethodException if the constructor does not exist + * @throws SecurityException if the security check fails + * @see #getConstructors() + * @since 1.1 + */ + public Constructor getConstructor(Class... types) + throws NoSuchMethodException + { + memberAccessCheck(Member.PUBLIC); + Constructor[] constructors = getDeclaredConstructors(true); + for (int i = 0; i < constructors.length; i++) + { + Constructor constructor = constructors[i]; + if (matchParameters(types, constructor.getParameterTypes())) + return constructor; + } + throw new NoSuchMethodException(); + } + + /** + * Get all the public constructors of this class. This returns an array of + * length 0 if there are no constructors, including for primitive types, + * arrays, and interfaces. It does, however, include the default + * constructor if one was supplied by the compiler. A security check may + * be performed, with checkMemberAccess(this, Member.PUBLIC) + * as well as checkPackageAccess both having to succeed. + * + * @return all public constructors in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Constructor[] getConstructors() + { + memberAccessCheck(Member.PUBLIC); + return getDeclaredConstructors(true); + } + + /** + * Get a constructor declared in this class. If the constructor takes no + * argument, an array of zero elements and null are equivalent for the + * types argument. A security check may be performed, with + * checkMemberAccess(this, Member.DECLARED) as well as + * checkPackageAccess both having to succeed. + * + * @param types the type of each parameter + * @return the constructor + * @throws NoSuchMethodException if the constructor does not exist + * @throws SecurityException if the security check fails + * @see #getDeclaredConstructors() + * @since 1.1 + */ + public Constructor getDeclaredConstructor(Class... types) + throws NoSuchMethodException + { + memberAccessCheck(Member.DECLARED); + Constructor[] constructors = getDeclaredConstructors(false); + for (int i = 0; i < constructors.length; i++) + { + Constructor constructor = constructors[i]; + if (matchParameters(types, constructor.getParameterTypes())) + return constructor; + } + throw new NoSuchMethodException(); + } + + /** + * Get all the declared member classes and interfaces in this class, but + * not those inherited from superclasses. This returns an array of length + * 0 if there are no member classes, including for primitive types. A + * security check may be performed, with + * checkMemberAccess(this, Member.DECLARED) as well as + * checkPackageAccess both having to succeed. + * + * @return all declared member classes in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Class[] getDeclaredClasses() + { + memberAccessCheck(Member.DECLARED); + return getDeclaredClasses(false); + } + + Class[] getDeclaredClasses (boolean publicOnly) + { + return VMClass.getDeclaredClasses (this, publicOnly); + } + + /** + * Get all the declared constructors of this class. This returns an array of + * length 0 if there are no constructors, including for primitive types, + * arrays, and interfaces. It does, however, include the default + * constructor if one was supplied by the compiler. A security check may + * be performed, with checkMemberAccess(this, Member.DECLARED) + * as well as checkPackageAccess both having to succeed. + * + * @return all constructors in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Constructor[] getDeclaredConstructors() + { + memberAccessCheck(Member.DECLARED); + return getDeclaredConstructors(false); + } + + Constructor[] getDeclaredConstructors (boolean publicOnly) + { + return VMClass.getDeclaredConstructors (this, publicOnly); + } + + /** + * Get a field declared in this class, where name is its simple name. The + * implicit length field of arrays is not available. A security check may + * be performed, with checkMemberAccess(this, Member.DECLARED) + * as well as checkPackageAccess both having to succeed. + * + * @param name the name of the field + * @return the field + * @throws NoSuchFieldException if the field does not exist + * @throws SecurityException if the security check fails + * @see #getDeclaredFields() + * @since 1.1 + */ + public Field getDeclaredField(String name) throws NoSuchFieldException + { + memberAccessCheck(Member.DECLARED); + Field[] fields = getDeclaredFields(false); + for (int i = 0; i < fields.length; i++) + { + if (fields[i].getName().equals(name)) + return fields[i]; + } + throw new NoSuchFieldException(); + } + + /** + * Get all the declared fields in this class, but not those inherited from + * superclasses. This returns an array of length 0 if there are no fields, + * including for primitive types. This does not return the implicit length + * field of arrays. A security check may be performed, with + * checkMemberAccess(this, Member.DECLARED) as well as + * checkPackageAccess both having to succeed. + * + * @return all declared fields in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Field[] getDeclaredFields() + { + memberAccessCheck(Member.DECLARED); + return getDeclaredFields(false); + } + + Field[] getDeclaredFields (boolean publicOnly) + { + return VMClass.getDeclaredFields (this, publicOnly); + } + + /** + * Get a method declared in this class, where name is its simple name. The + * implicit methods of Object are not available from arrays or interfaces. + * Constructors (named "<init>" in the class file) and class initializers + * (name "<clinit>") are not available. The Virtual Machine allows + * multiple methods with the same signature but differing return types; in + * such a case the most specific return types are favored, then the final + * choice is arbitrary. If the method takes no argument, an array of zero + * elements and null are equivalent for the types argument. A security + * check may be performed, with + * checkMemberAccess(this, Member.DECLARED) as well as + * checkPackageAccess both having to succeed. + * + * @param methodName the name of the method + * @param types the type of each parameter + * @return the method + * @throws NoSuchMethodException if the method does not exist + * @throws SecurityException if the security check fails + * @see #getDeclaredMethods() + * @since 1.1 + */ + public Method getDeclaredMethod(String methodName, Class... types) + throws NoSuchMethodException + { + memberAccessCheck(Member.DECLARED); + Method match = matchMethod(getDeclaredMethods(false), methodName, types); + if (match == null) + throw new NoSuchMethodException(methodName); + return match; + } + + /** + * Get all the declared methods in this class, but not those inherited from + * superclasses. This returns an array of length 0 if there are no methods, + * including for primitive types. This does include the implicit methods of + * arrays and interfaces which mirror methods of Object, nor does it + * include constructors or the class initialization methods. The Virtual + * Machine allows multiple methods with the same signature but differing + * return types; all such methods are in the returned array. A security + * check may be performed, with + * checkMemberAccess(this, Member.DECLARED) as well as + * checkPackageAccess both having to succeed. + * + * @return all declared methods in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Method[] getDeclaredMethods() + { + memberAccessCheck(Member.DECLARED); + return getDeclaredMethods(false); + } + + Method[] getDeclaredMethods (boolean publicOnly) + { + return VMClass.getDeclaredMethods (this, publicOnly); + } + + /** + * If this is a nested or inner class, return the class that declared it. + * If not, return null. + * + * @return the declaring class of this class + * @since 1.1 + */ + public Class getDeclaringClass() + { + return VMClass.getDeclaringClass (this); + } + + /** + * Get a public field declared or inherited in this class, where name is + * its simple name. If the class contains multiple accessible fields by + * that name, an arbitrary one is returned. The implicit length field of + * arrays is not available. A security check may be performed, with + * checkMemberAccess(this, Member.PUBLIC) as well as + * checkPackageAccess both having to succeed. + * + * @param fieldName the name of the field + * @return the field + * @throws NoSuchFieldException if the field does not exist + * @throws SecurityException if the security check fails + * @see #getFields() + * @since 1.1 + */ + public Field getField(String fieldName) + throws NoSuchFieldException + { + memberAccessCheck(Member.PUBLIC); + Field field = internalGetField(fieldName); + if (field == null) + throw new NoSuchFieldException(fieldName); + return field; + } + + /** + * Get all the public fields declared in this class or inherited from + * superclasses. This returns an array of length 0 if there are no fields, + * including for primitive types. This does not return the implicit length + * field of arrays. A security check may be performed, with + * checkMemberAccess(this, Member.PUBLIC) as well as + * checkPackageAccess both having to succeed. + * + * @return all public fields in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Field[] getFields() + { + memberAccessCheck(Member.PUBLIC); + return internalGetFields(); + } + + /** + * Like getFields() but without the security checks. + */ + private Field[] internalGetFields() + { + LinkedHashSet set = new LinkedHashSet(); + set.addAll(Arrays.asList(getDeclaredFields(true))); + Class[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + set.addAll(Arrays.asList(interfaces[i].internalGetFields())); + Class superClass = getSuperclass(); + if (superClass != null) + set.addAll(Arrays.asList(superClass.internalGetFields())); + return set.toArray(new Field[set.size()]); + } + + /** + * Returns the Package in which this class is defined + * Returns null when this information is not available from the + * classloader of this class. + * + * @return the package for this class, if it is available + * @since 1.2 + */ + public Package getPackage() + { + ClassLoader cl = getClassLoader(); + if (cl != null) + return cl.getPackage(getPackagePortion(getName())); + else + return VMClassLoader.getPackage(getPackagePortion(getName())); + } + + /** + * Get the interfaces this class directly implements, in the + * order that they were declared. This returns an empty array, not null, + * for Object, primitives, void, and classes or interfaces with no direct + * superinterface. Array types return Cloneable and Serializable. + * + * @return the interfaces this class directly implements + */ + public Class[] getInterfaces() + { + return VMClass.getInterfaces (this); + } + + private static final class MethodKey + { + private String name; + private Class[] params; + private Class returnType; + private int hash; + + MethodKey(Method m) + { + name = m.getName(); + params = m.getParameterTypes(); + returnType = m.getReturnType(); + hash = name.hashCode() ^ returnType.hashCode(); + for(int i = 0; i < params.length; i++) + { + hash ^= params[i].hashCode(); + } + } + + public boolean equals(Object o) + { + if (o instanceof MethodKey) + { + MethodKey m = (MethodKey) o; + if (m.name.equals(name) && m.params.length == params.length + && m.returnType == returnType) + { + for (int i = 0; i < params.length; i++) + { + if (m.params[i] != params[i]) + return false; + } + return true; + } + } + return false; + } + + public int hashCode() + { + return hash; + } + } + + /** + * Get a public method declared or inherited in this class, where name is + * its simple name. The implicit methods of Object are not available from + * interfaces. Constructors (named "<init>" in the class file) and class + * initializers (name "<clinit>") are not available. The Virtual + * Machine allows multiple methods with the same signature but differing + * return types, and the class can inherit multiple methods of the same + * return type; in such a case the most specific return types are favored, + * then the final choice is arbitrary. If the method takes no argument, an + * array of zero elements and null are equivalent for the types argument. + * A security check may be performed, with + * checkMemberAccess(this, Member.PUBLIC) as well as + * checkPackageAccess both having to succeed. + * + * @param methodName the name of the method + * @param types the type of each parameter + * @return the method + * @throws NoSuchMethodException if the method does not exist + * @throws SecurityException if the security check fails + * @see #getMethods() + * @since 1.1 + */ + public Method getMethod(String methodName, Class... types) + throws NoSuchMethodException + { + memberAccessCheck(Member.PUBLIC); + Method method = internalGetMethod(methodName, types); + if (method == null) + throw new NoSuchMethodException(methodName); + return method; + } + + /** + * Like getMethod(String,Class[]) but without the security + * checks and returns null instead of throwing NoSuchMethodException. + */ + private Method internalGetMethod(String methodName, Class[] args) + { + Method match = matchMethod(getDeclaredMethods(true), methodName, args); + if (match != null) + return match; + Class superClass = getSuperclass(); + if (superClass != null) + { + match = superClass.internalGetMethod(methodName, args); + if(match != null) + return match; + } + Class[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + { + match = interfaces[i].internalGetMethod(methodName, args); + if (match != null) + return match; + } + return null; + } + + /** + * Find the best matching method in list according to + * the definition of ``best matching'' used by getMethod() + * + *

    + * Returns the method if any, otherwise null. + * + * @param list List of methods to search + * @param name Name of method + * @param args Method parameter types + * @see #getMethod(String, Class[]) + */ + private static Method matchMethod(Method[] list, String name, Class[] args) + { + Method match = null; + for (int i = 0; i < list.length; i++) + { + Method method = list[i]; + if (!method.getName().equals(name)) + continue; + if (!matchParameters(args, method.getParameterTypes())) + continue; + if (match == null + || match.getReturnType().isAssignableFrom(method.getReturnType())) + match = method; + } + return match; + } + + /** + * Check for an exact match between parameter type lists. + * Either list may be null to mean a list of + * length zero. + */ + private static boolean matchParameters(Class[] types1, Class[] types2) + { + if (types1 == null) + return types2 == null || types2.length == 0; + if (types2 == null) + return types1 == null || types1.length == 0; + if (types1.length != types2.length) + return false; + for (int i = 0; i < types1.length; i++) + { + if (types1[i] != types2[i]) + return false; + } + return true; + } + + /** + * Get all the public methods declared in this class or inherited from + * superclasses. This returns an array of length 0 if there are no methods, + * including for primitive types. This does not include the implicit + * methods of interfaces which mirror methods of Object, nor does it + * include constructors or the class initialization methods. The Virtual + * Machine allows multiple methods with the same signature but differing + * return types; all such methods are in the returned array. A security + * check may be performed, with + * checkMemberAccess(this, Member.PUBLIC) as well as + * checkPackageAccess both having to succeed. + * + * @return all public methods in this class + * @throws SecurityException if the security check fails + * @since 1.1 + */ + public Method[] getMethods() + { + memberAccessCheck(Member.PUBLIC); + // NOTE the API docs claim that no methods are returned for arrays, + // but Sun's implementation *does* return the public methods of Object + // (as would be expected), so we follow their implementation instead + // of their documentation. + return internalGetMethods(); + } + + /** + * Like getMethods() but without the security checks. + */ + private Method[] internalGetMethods() + { + HashMap map = new HashMap(); + Method[] methods; + Class[] interfaces = getInterfaces(); + for(int i = 0; i < interfaces.length; i++) + { + methods = interfaces[i].internalGetMethods(); + for(int j = 0; j < methods.length; j++) + { + map.put(new MethodKey(methods[j]), methods[j]); + } + } + Class superClass = getSuperclass(); + if(superClass != null) + { + methods = superClass.internalGetMethods(); + for(int i = 0; i < methods.length; i++) + { + map.put(new MethodKey(methods[i]), methods[i]); + } + } + methods = getDeclaredMethods(true); + for(int i = 0; i < methods.length; i++) + { + map.put(new MethodKey(methods[i]), methods[i]); + } + return map.values().toArray(new Method[map.size()]); + } + + /** + * Get the modifiers of this class. These can be decoded using Modifier, + * and is limited to one of public, protected, or private, and any of + * final, static, abstract, or interface. An array class has the same + * public, protected, or private modifier as its component type, and is + * marked final but not an interface. Primitive types and void are marked + * public and final, but not an interface. + * + * @return the modifiers of this class + * @see Modifier + * @since 1.1 + */ + public int getModifiers() + { + int mod = VMClass.getModifiers (this, false); + return (mod & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE | + Modifier.FINAL | Modifier.STATIC | Modifier.ABSTRACT | + Modifier.INTERFACE)); + } + + /** + * Get the name of this class, separated by dots for package separators. + * If the class represents a primitive type, or void, then the + * name of the type as it appears in the Java programming language + * is returned. For instance, Byte.TYPE.getName() + * returns "byte". + * + * Arrays are specially encoded as shown on this table. + *

    +   * array type          [element type
    +   *                     (note that the element type is encoded per
    +   *                      this table)
    +   * boolean             Z
    +   * byte                B
    +   * char                C
    +   * short               S
    +   * int                 I
    +   * long                J
    +   * float               F
    +   * double              D
    +   * void                V
    +   * class or interface, alone: <dotted name>
    +   * class or interface, as element type: L<dotted name>;
    +   * 
    + * + * @return the name of this class + */ + public String getName() + { + return VMClass.getName (this); + } + + /** + * Get a resource URL using this class's package using the + * getClassLoader().getResource() method. If this class was loaded using + * the system classloader, ClassLoader.getSystemResource() is used instead. + * + *

    If the name you supply is absolute (it starts with a /), + * then the leading / is removed and it is passed on to + * getResource(). If it is relative, the package name is prepended, and + * .'s are replaced with /. + * + *

    The URL returned is system- and classloader-dependent, and could + * change across implementations. + * + * @param resourceName the name of the resource, generally a path + * @return the URL to the resource + * @throws NullPointerException if name is null + * @since 1.1 + */ + public URL getResource(String resourceName) + { + String name = resourcePath(resourceName); + ClassLoader loader = getClassLoader(); + if (loader == null) + return ClassLoader.getSystemResource(name); + return loader.getResource(name); + } + + /** + * Get a resource using this class's package using the + * getClassLoader().getResourceAsStream() method. If this class was loaded + * using the system classloader, ClassLoader.getSystemResource() is used + * instead. + * + *

    If the name you supply is absolute (it starts with a /), + * then the leading / is removed and it is passed on to + * getResource(). If it is relative, the package name is prepended, and + * .'s are replaced with /. + * + *

    The URL returned is system- and classloader-dependent, and could + * change across implementations. + * + * @param resourceName the name of the resource, generally a path + * @return an InputStream with the contents of the resource in it, or null + * @throws NullPointerException if name is null + * @since 1.1 + */ + public InputStream getResourceAsStream(String resourceName) + { + String name = resourcePath(resourceName); + ClassLoader loader = getClassLoader(); + if (loader == null) + return ClassLoader.getSystemResourceAsStream(name); + return loader.getResourceAsStream(name); + } + + private String resourcePath(String resourceName) + { + if (resourceName.length() > 0) + { + if (resourceName.charAt(0) != '/') + { + String pkg = getPackagePortion(getName()); + if (pkg.length() > 0) + resourceName = pkg.replace('.','/') + '/' + resourceName; + } + else + { + resourceName = resourceName.substring(1); + } + } + return resourceName; + } + + /** + * Get the signers of this class. This returns null if there are no signers, + * such as for primitive types or void. + * + * @return the signers of this class + * @since 1.1 + */ + public Object[] getSigners() + { + return signers == null ? null : (Object[]) signers.clone (); + } + + /** + * Set the signers of this class. + * + * @param signers the signers of this class + */ + void setSigners(Object[] signers) + { + this.signers = signers; + } + + /** + * Get the direct superclass of this class. If this is an interface, + * Object, a primitive type, or void, it will return null. If this is an + * array type, it will return Object. + * + * @return the direct superclass of this class + */ + public Class getSuperclass() + { + return VMClass.getSuperclass (this); + } + + /** + * Return whether this class is an array type. + * + * @return whether this class is an array type + * @since 1.1 + */ + public boolean isArray() + { + return VMClass.isArray (this); + } + + /** + * Discover whether an instance of the Class parameter would be an + * instance of this Class as well. Think of doing + * isInstance(c.newInstance()) or even + * c.newInstance() instanceof (this class). While this + * checks widening conversions for objects, it must be exact for primitive + * types. + * + * @param c the class to check + * @return whether an instance of c would be an instance of this class + * as well + * @throws NullPointerException if c is null + * @since 1.1 + */ + public boolean isAssignableFrom(Class c) + { + return VMClass.isAssignableFrom (this, c); + } + + /** + * Discover whether an Object is an instance of this Class. Think of it + * as almost like o instanceof (this class). + * + * @param o the Object to check + * @return whether o is an instance of this class + * @since 1.1 + */ + public boolean isInstance(Object o) + { + return VMClass.isInstance (this, o); + } + + /** + * Check whether this class is an interface or not. Array types are not + * interfaces. + * + * @return whether this class is an interface or not + */ + public boolean isInterface() + { + return VMClass.isInterface (this); + } + + /** + * Return whether this class is a primitive type. A primitive type class + * is a class representing a kind of "placeholder" for the various + * primitive types, or void. You can access the various primitive type + * classes through java.lang.Boolean.TYPE, java.lang.Integer.TYPE, etc., + * or through boolean.class, int.class, etc. + * + * @return whether this class is a primitive type + * @see Boolean#TYPE + * @see Byte#TYPE + * @see Character#TYPE + * @see Short#TYPE + * @see Integer#TYPE + * @see Long#TYPE + * @see Float#TYPE + * @see Double#TYPE + * @see Void#TYPE + * @since 1.1 + */ + public boolean isPrimitive() + { + return VMClass.isPrimitive (this); + } + + /** + * Get a new instance of this class by calling the no-argument constructor. + * The class is initialized if it has not been already. A security check + * may be performed, with checkMemberAccess(this, Member.PUBLIC) + * as well as checkPackageAccess both having to succeed. + * + * @return a new instance of this class + * @throws InstantiationException if there is not a no-arg constructor + * for this class, including interfaces, abstract classes, arrays, + * primitive types, and void; or if an exception occurred during + * the constructor + * @throws IllegalAccessException if you are not allowed to access the + * no-arg constructor because of scoping reasons + * @throws SecurityException if the security check fails + * @throws ExceptionInInitializerError if class initialization caused by + * this call fails with an exception + */ + public T newInstance() + throws InstantiationException, IllegalAccessException + { + memberAccessCheck(Member.PUBLIC); + Constructor constructor; + synchronized(this) + { + constructor = this.constructor; + } + if (constructor == null) + { + Constructor[] constructors = getDeclaredConstructors(false); + for (int i = 0; i < constructors.length; i++) + { + if (constructors[i].getParameterTypes().length == 0) + { + constructor = constructors[i]; + break; + } + } + if (constructor == null) + throw new InstantiationException(getName()); + if (!Modifier.isPublic(constructor.getModifiers()) + || !Modifier.isPublic(VMClass.getModifiers(this, true))) + { + setAccessible(constructor); + } + synchronized(this) + { + if (this.constructor == null) + this.constructor = constructor; + } + } + int modifiers = constructor.getModifiers(); + if (!Modifier.isPublic(modifiers) + || !Modifier.isPublic(VMClass.getModifiers(this, true))) + { + Class caller = VMStackWalker.getCallingClass(); + if (caller != null && + caller != this && + (Modifier.isPrivate(modifiers) + || getClassLoader() != caller.getClassLoader() + || !getPackagePortion(getName()) + .equals(getPackagePortion(caller.getName())))) + throw new IllegalAccessException(getName() + + " has an inaccessible constructor"); + } + try + { + return constructor.newInstance(); + } + catch (InvocationTargetException e) + { + VMClass.throwException(e.getTargetException()); + throw (InternalError) new InternalError + ("VMClass.throwException returned").initCause(e); + } + } + + /** + * Returns the protection domain of this class. If the classloader did not + * record the protection domain when creating this class the unknown + * protection domain is returned which has a null code source + * and all permissions. A security check may be performed, with + * RuntimePermission("getProtectionDomain"). + * + * @return the protection domain + * @throws SecurityException if the security manager exists and the caller + * does not have RuntimePermission("getProtectionDomain"). + * @see RuntimePermission + * @since 1.2 + */ + public ProtectionDomain getProtectionDomain() + { + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkPermission(new RuntimePermission("getProtectionDomain")); + + return pd == null ? StaticData.unknownProtectionDomain : pd; + } + + /** + * Return the human-readable form of this Object. For an object, this + * is either "interface " or "class " followed by getName(), + * for primitive types and void it is just getName(). + * + * @return the human-readable form of this Object + */ + public String toString() + { + if (isPrimitive()) + return getName(); + return (isInterface() ? "interface " : "class ") + getName(); + } + + /** + * Returns the desired assertion status of this class, if it were to be + * initialized at this moment. The class assertion status, if set, is + * returned; the backup is the default package status; then if there is + * a class loader, that default is returned; and finally the system default + * is returned. This method seldom needs calling in user code, but exists + * for compilers to implement the assert statement. Note that there is no + * guarantee that the result of this method matches the class's actual + * assertion status. + * + * @return the desired assertion status + * @see ClassLoader#setClassAssertionStatus(String, boolean) + * @see ClassLoader#setPackageAssertionStatus(String, boolean) + * @see ClassLoader#setDefaultAssertionStatus(boolean) + * @since 1.4 + */ + public boolean desiredAssertionStatus() + { + ClassLoader c = getClassLoader(); + Object status; + if (c == null) + return VMClassLoader.defaultAssertionStatus(); + if (c.classAssertionStatus != null) + synchronized (c) + { + status = c.classAssertionStatus.get(getName()); + if (status != null) + return status.equals(Boolean.TRUE); + } + else + { + status = ClassLoader.StaticData. + systemClassAssertionStatus.get(getName()); + if (status != null) + return status.equals(Boolean.TRUE); + } + if (c.packageAssertionStatus != null) + synchronized (c) + { + String name = getPackagePortion(getName()); + if ("".equals(name)) + status = c.packageAssertionStatus.get(null); + else + do + { + status = c.packageAssertionStatus.get(name); + name = getPackagePortion(name); + } + while (! "".equals(name) && status == null); + if (status != null) + return status.equals(Boolean.TRUE); + } + else + { + String name = getPackagePortion(getName()); + if ("".equals(name)) + status = ClassLoader.StaticData. + systemPackageAssertionStatus.get(null); + else + do + { + status = ClassLoader.StaticData. + systemPackageAssertionStatus.get(name); + name = getPackagePortion(name); + } + while (! "".equals(name) && status == null); + if (status != null) + return status.equals(Boolean.TRUE); + } + return c.defaultAssertionStatus; + } + + /** + *

    + * Casts this class to represent a subclass of the specified class. + * This method is useful for `narrowing' the type of a class so that + * the class object, and instances of that class, can match the contract + * of a more restrictive method. For example, if this class has the + * static type of Class<Object>, and a dynamic type of + * Class<Rectangle>, then, assuming Shape is + * a superclass of Rectangle, this method can be used on + * this class with the parameter, Class<Shape>, to retain + * the same instance but with the type + * Class<? extends Shape>. + *

    + *

    + * If this class can be converted to an instance which is parameterised + * over a subtype of the supplied type, U, then this method + * returns an appropriately cast reference to this object. Otherwise, + * a ClassCastException is thrown. + *

    + * + * @param klass the class object, the parameterized type (U) of + * which should be a superclass of the parameterized type of + * this instance. + * @return a reference to this object, appropriately cast. + * @throws ClassCastException if this class can not be converted to one + * which represents a subclass of the specified + * type, U. + * @since 1.5 + */ + public Class asSubclass(Class klass) + { + if (! klass.isAssignableFrom(this)) + throw new ClassCastException(); + return (Class) this; + } + + /** + * Returns the specified object, cast to this Class' type. + * + * @param obj the object to cast + * @throws ClassCastException if obj is not an instance of this class + * @since 1.5 + */ + public T cast(Object obj) + { + if (obj != null && ! isInstance(obj)) + throw new ClassCastException(); + return (T) obj; + } + + /** + * Like getField(String) but without the security checks and + * returns null instead of throwing NoSuchFieldException. + */ + private Field internalGetField(String name) + { + Field[] fields = getDeclaredFields(true); + for (int i = 0; i < fields.length; i++) + { + Field field = fields[i]; + if (field.getName().equals(name)) + return field; + } + Class[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + { + Field field = interfaces[i].internalGetField(name); + if(field != null) + return field; + } + Class superClass = getSuperclass(); + if (superClass != null) + return superClass.internalGetField(name); + return null; + } + + /** + * Strip the last portion of the name (after the last dot). + * + * @param name the name to get package of + * @return the package name, or "" if no package + */ + private static String getPackagePortion(String name) + { + int lastInd = name.lastIndexOf('.'); + if (lastInd == -1) + return ""; + return name.substring(0, lastInd); + } + + /** + * Perform security checks common to all of the methods that + * get members of this Class. + */ + private void memberAccessCheck(int which) + { + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + sm.checkMemberAccess(this, which); + Package pkg = getPackage(); + if (pkg != null) + sm.checkPackageAccess(pkg.getName()); + } + } + + /** + * Returns the enumeration constants of this class, or + * null if this class is not an Enum. + * + * @return an array of Enum constants + * associated with this class, or null if this + * class is not an enum. + * @since 1.5 + */ + public T[] getEnumConstants() + { + if (isEnum()) + { + try + { + Method m = getMethod("values"); + setAccessible(m); + return (T[]) m.invoke(null); + } + catch (NoSuchMethodException exception) + { + throw new Error("Enum lacks values() method"); + } + catch (IllegalAccessException exception) + { + throw new Error("Unable to access Enum class"); + } + catch (InvocationTargetException exception) + { + throw new + RuntimeException("The values method threw an exception", + exception); + } + } + else + { + return null; + } + } + + /** + * Returns true if this class is an Enum. + * + * @return true if this is an enumeration class. + * @since 1.5 + */ + public boolean isEnum() + { + int mod = VMClass.getModifiers (this, true); + return (mod & ENUM) != 0; + } + + /** + * Returns true if this class is a synthetic class, generated by + * the compiler. + * + * @return true if this is a synthetic class. + * @since 1.5 + */ + public boolean isSynthetic() + { + int mod = VMClass.getModifiers (this, true); + return (mod & SYNTHETIC) != 0; + } + + /** + * Returns true if this class is an Annotation. + * + * @return true if this is an annotation class. + * @since 1.5 + */ + public boolean isAnnotation() + { + int mod = VMClass.getModifiers (this, true); + return (mod & ANNOTATION) != 0; + } + + /** + * Returns the simple name for this class, as used in the source + * code. For normal classes, this is the content returned by + * getName() which follows the last ".". Anonymous + * classes have no name, and so the result of calling this method is + * "". The simple name of an array consists of the simple name of + * its component type, followed by "[]". Thus, an array with the + * component type of an anonymous class has a simple name of simply + * "[]". + * + * @return the simple name for this class. + * @since 1.5 + */ + public String getSimpleName() + { + return VMClass.getSimpleName(this); + } + + /** + * Returns this class' annotation for the specified annotation type, + * or null if no such annotation exists. + * + * @param annotationClass the type of annotation to look for. + * @return this class' annotation for the specified type, or + * null if no such annotation exists. + * @since 1.5 + */ + public A getAnnotation(Class annotationClass) + { + A foundAnnotation = null; + Annotation[] annotations = getAnnotations(); + for (Annotation annotation : annotations) + if (annotation.annotationType() == annotationClass) + foundAnnotation = (A) annotation; + return foundAnnotation; + } + + /** + * Returns all annotations associated with this class. If there are + * no annotations associated with this class, then a zero-length array + * will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * class, and hence no effect on the return value of this method for + * future callers. + * + * @return this class' annotations. + * @since 1.5 + */ + public Annotation[] getAnnotations() + { + HashMap map = new HashMap(); + for (Annotation a : getDeclaredAnnotations()) + map.put((Class) a.annotationType(), a); + for (Class s = getSuperclass(); + s != null; + s = s.getSuperclass()) + { + for (Annotation a : s.getDeclaredAnnotations()) + { + Class k = (Class) a.annotationType(); + if (! map.containsKey(k) && k.isAnnotationPresent(Inherited.class)) + map.put(k, a); + } + } + Collection v = map.values(); + return v.toArray(new Annotation[v.size()]); + } + + /** + *

    + * Returns the canonical name of this class, as defined by section + * 6.7 of the Java language specification. Each package, top-level class, + * top-level interface and primitive type has a canonical name. A member + * class has a canonical name, if its parent class has one. Likewise, + * an array type has a canonical name, if its component type does. + * Local or anonymous classes do not have canonical names. + *

    + *

    + * The canonical name for top-level classes, top-level interfaces and + * primitive types is always the same as the fully-qualified name. + * For array types, the canonical name is the canonical name of its + * component type with `[]' appended. + *

    + *

    + * The canonical name of a member class always refers to the place where + * the class was defined, and is composed of the canonical name of the + * defining class and the simple name of the member class, joined by `.'. + * For example, if a Person class has an inner class, + * M, then both its fully-qualified name and canonical name + * is Person.M. A subclass, Staff, of + * Person refers to the same inner class by the fully-qualified + * name of Staff.M, but its canonical name is still + * Person.M. + *

    + *

    + * Where no canonical name is present, null is returned. + *

    + * + * @return the canonical name of the class, or null if the + * class doesn't have a canonical name. + * @since 1.5 + */ + public String getCanonicalName() + { + return VMClass.getCanonicalName(this); + } + + /** + * Returns all annotations directly defined by this class. If there are + * no annotations associated with this class, then a zero-length array + * will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * class, and hence no effect on the return value of this method for + * future callers. + * + * @return the annotations directly defined by this class. + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() + { + return VMClass.getDeclaredAnnotations(this); + } + + /** + * Returns the class which immediately encloses this class. If this class + * is a top-level class, this method returns null. + * + * @return the immediate enclosing class, or null if this is + * a top-level class. + * @since 1.5 + */ + public Class getEnclosingClass() + { + return VMClass.getEnclosingClass(this); + } + + /** + * Returns the constructor which immediately encloses this class. If + * this class is a top-level class, or a local or anonymous class + * immediately enclosed by a type definition, instance initializer + * or static initializer, then null is returned. + * + * @return the immediate enclosing constructor if this class is + * declared within a constructor. Otherwise, null + * is returned. + * @since 1.5 + */ + public Constructor getEnclosingConstructor() + { + return VMClass.getEnclosingConstructor(this); + } + + /** + * Returns the method which immediately encloses this class. If + * this class is a top-level class, or a local or anonymous class + * immediately enclosed by a type definition, instance initializer + * or static initializer, then null is returned. + * + * @return the immediate enclosing method if this class is + * declared within a method. Otherwise, null + * is returned. + * @since 1.5 + */ + public Method getEnclosingMethod() + { + return VMClass.getEnclosingMethod(this); + } + + /** + *

    + * Returns an array of Type objects which represent the + * interfaces directly implemented by this class or extended by this + * interface. + *

    + *

    + * If one of the superinterfaces is a parameterized type, then the + * object returned for this interface reflects the actual type + * parameters used in the source code. Type parameters are created + * using the semantics specified by the ParameterizedType + * interface, and only if an instance has not already been created. + *

    + *

    + * The order of the interfaces in the array matches the order in which + * the interfaces are declared. For classes which represent an array, + * an array of two interfaces, Cloneable and + * Serializable, is always returned, with the objects in + * that order. A class representing a primitive type or void always + * returns an array of zero size. + *

    + * + * @return an array of interfaces implemented or extended by this class. + * @throws GenericSignatureFormatError if the generic signature of one + * of the interfaces does not comply with that specified by the Java + * Virtual Machine specification, 3rd edition. + * @throws TypeNotPresentException if any of the superinterfaces refers + * to a non-existant type. + * @throws MalformedParameterizedTypeException if any of the interfaces + * refer to a parameterized type that can not be instantiated for + * some reason. + * @since 1.5 + * @see java.lang.reflect.ParameterizedType + */ + public Type[] getGenericInterfaces() + { + if (isPrimitive()) + return new Type[0]; + + String sig = VMClass.getClassSignature(this); + if (sig == null) + return getInterfaces(); + + ClassSignatureParser p = new ClassSignatureParser(this, sig); + return p.getInterfaceTypes(); + } + + /** + *

    + * Returns a Type object representing the direct superclass, + * whether class, interface, primitive type or void, of this class. + * If this class is an array class, then a class instance representing + * the Object class is returned. If this class is primitive, + * an interface, or a representation of either the Object + * class or void, then null is returned. + *

    + *

    + * If the superclass is a parameterized type, then the + * object returned for this interface reflects the actual type + * parameters used in the source code. Type parameters are created + * using the semantics specified by the ParameterizedType + * interface, and only if an instance has not already been created. + *

    + * + * @return the superclass of this class. + * @throws GenericSignatureFormatError if the generic signature of the + * class does not comply with that specified by the Java + * Virtual Machine specification, 3rd edition. + * @throws TypeNotPresentException if the superclass refers + * to a non-existant type. + * @throws MalformedParameterizedTypeException if the superclass + * refers to a parameterized type that can not be instantiated for + * some reason. + * @since 1.5 + * @see java.lang.reflect.ParameterizedType + */ + public Type getGenericSuperclass() + { + if (isArray()) + return Object.class; + + if (isPrimitive() || isInterface() || this == Object.class) + return null; + + String sig = VMClass.getClassSignature(this); + if (sig == null) + return getSuperclass(); + + ClassSignatureParser p = new ClassSignatureParser(this, sig); + return p.getSuperclassType(); + } + + /** + * Returns an array of TypeVariable objects that represents + * the type variables declared by this class, in declaration order. + * An array of size zero is returned if this class has no type + * variables. + * + * @return the type variables associated with this class. + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public TypeVariable>[] getTypeParameters() + { + String sig = VMClass.getClassSignature(this); + if (sig == null) + return (TypeVariable>[])new TypeVariable[0]; + + ClassSignatureParser p = new ClassSignatureParser(this, sig); + return p.getTypeParameters(); + } + + /** + * Returns true if an annotation for the specified type is associated + * with this class. This is primarily a short-hand for using marker + * annotations. + * + * @param annotationClass the type of annotation to look for. + * @return true if an annotation exists for the specified type. + * @since 1.5 + */ + public boolean isAnnotationPresent(Class + annotationClass) + { + return getAnnotation(annotationClass) != null; + } + + /** + * Returns true if this object represents an anonymous class. + * + * @return true if this object represents an anonymous class. + * @since 1.5 + */ + public boolean isAnonymousClass() + { + return VMClass.isAnonymousClass(this); + } + + /** + * Returns true if this object represents an local class. + * + * @return true if this object represents an local class. + * @since 1.5 + */ + public boolean isLocalClass() + { + return VMClass.isLocalClass(this); + } + + /** + * Returns true if this object represents an member class. + * + * @return true if this object represents an member class. + * @since 1.5 + */ + public boolean isMemberClass() + { + return VMClass.isMemberClass(this); + } + + /** + * Utility method for use by classes in this package. + */ + static void setAccessible(final AccessibleObject obj) + { + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + obj.setAccessible(true); + return null; + } + }); + } +} diff --git a/libjava/classpath/java/lang/ClassCastException.java b/libjava/classpath/java/lang/ClassCastException.java new file mode 100644 index 000000000..c490f42aa --- /dev/null +++ b/libjava/classpath/java/lang/ClassCastException.java @@ -0,0 +1,76 @@ +/* ClassCastException.java -- exception thrown on bad cast + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when an attempt is made to cast an object which is not of the + * appropriate runtime type. For example:
    + *
    + * Object o = new Vector();
    + * String s = (String) o;
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ClassCastException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -9223365651070458532L; + + /** + * Create an exception without a message. + */ + public ClassCastException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ClassCastException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/ClassCircularityError.java b/libjava/classpath/java/lang/ClassCircularityError.java new file mode 100644 index 000000000..ecdfb7aaf --- /dev/null +++ b/libjava/classpath/java/lang/ClassCircularityError.java @@ -0,0 +1,73 @@ +/* ClassCircularityError.java -- thrown when linking circular classes + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A ClassCircularityError is thrown when a circular dependency + * has been detected while initializing a class. This signals binary + * incompatible versions of class files, as the compiler normally catches this. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class ClassCircularityError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 1054362542914539689L; + + /** + * Create an error without a message. + */ + public ClassCircularityError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public ClassCircularityError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/ClassFormatError.java b/libjava/classpath/java/lang/ClassFormatError.java new file mode 100644 index 000000000..7f90f5cd8 --- /dev/null +++ b/libjava/classpath/java/lang/ClassFormatError.java @@ -0,0 +1,72 @@ +/* ClassFormatError.java -- thrown if a class file is invalid + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A ClassFormatError is thrown when a Java Virtual Machine + * unable to read a class file because the file is corrupted or cannot be + * interpreted as a class file. + * + * @author Brian Jones + * @status updated to 1.4 + */ +public class ClassFormatError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -8420114879011949195L; + + /** + * Create an error without a message. + */ + public ClassFormatError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public ClassFormatError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/ClassLoader.java b/libjava/classpath/java/lang/ClassLoader.java new file mode 100644 index 000000000..b7cbb2a1a --- /dev/null +++ b/libjava/classpath/java/lang/ClassLoader.java @@ -0,0 +1,1151 @@ +/* ClassLoader.java -- responsible for loading classes into the VM + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.SystemProperties; +import gnu.classpath.VMStackWalker; +import gnu.java.util.DoubleEnumeration; +import gnu.java.util.EmptyEnumeration; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.ByteBuffer; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * The ClassLoader is a way of customizing the way Java gets its classes + * and loads them into memory. The verifier and other standard Java things + * still run, but the ClassLoader is allowed great flexibility in determining + * where to get the classfiles and when to load and resolve them. For that + * matter, a custom ClassLoader can perform on-the-fly code generation or + * modification! + * + *

    Every classloader has a parent classloader that is consulted before + * the 'child' classloader when classes or resources should be loaded. + * This is done to make sure that classes can be loaded from an hierarchy of + * multiple classloaders and classloaders do not accidentially redefine + * already loaded classes by classloaders higher in the hierarchy. + * + *

    The grandparent of all classloaders is the bootstrap classloader, which + * loads all the standard system classes as implemented by GNU Classpath. The + * other special classloader is the system classloader (also called + * application classloader) that loads all classes from the CLASSPATH + * (java.class.path system property). The system classloader + * is responsible for finding the application classes from the classpath, + * and delegates all requests for the standard library classes to its parent + * the bootstrap classloader. Most programs will load all their classes + * through the system classloaders. + * + *

    The bootstrap classloader in GNU Classpath is implemented as a couple of + * static (native) methods on the package private class + * java.lang.VMClassLoader, the system classloader is an + * anonymous inner class of ClassLoader and a subclass of + * java.net.URLClassLoader. + * + *

    Users of a ClassLoader will normally just use the methods + *

      + *
    • loadClass() to load a class.
    • + *
    • getResource() or getResourceAsStream() + * to access a resource.
    • + *
    • getResources() to get an Enumeration of URLs to all + * the resources provided by the classloader and its parents with the + * same name.
    • + *
    + * + *

    Subclasses should implement the methods + *

      + *
    • findClass() which is called by loadClass() + * when the parent classloader cannot provide a named class.
    • + *
    • findResource() which is called by + * getResource() when the parent classloader cannot provide + * a named resource.
    • + *
    • findResources() which is called by + * getResource() to combine all the resources with the + * same name from the classloader and its parents.
    • + *
    • findLibrary() which is called by + * Runtime.loadLibrary() when a class defined by the + * classloader wants to load a native library.
    • + *
    + * + * @author John Keiser + * @author Mark Wielaard + * @author Eric Blake (ebb9@email.byu.edu) + * @see Class + * @since 1.0 + */ +public abstract class ClassLoader +{ + /** + * All packages defined by this classloader. It is not private in order to + * allow native code (and trusted subclasses) access to this field. + */ + final HashMap definedPackages = new HashMap(); + + /** + * The classloader that is consulted before this classloader. + * If null then the parent is the bootstrap classloader. + */ + private final ClassLoader parent; + + /** + * This is true if this classloader was successfully initialized. + * This flag is needed to avoid a class loader attack: even if the + * security manager rejects an attempt to create a class loader, the + * malicious class could have a finalize method which proceeds to + * define classes. + */ + private final boolean initialized; + + static class StaticData + { + /** + * The System Class Loader (a.k.a. Application Class Loader). The one + * returned by ClassLoader.getSystemClassLoader. + */ + static final ClassLoader systemClassLoader = + VMClassLoader.getSystemClassLoader(); + static + { + // Find out if we have to install a default security manager. Note that + // this is done here because we potentially need the system class loader + // to load the security manager and note also that we don't need the + // security manager until the system class loader is created. + // If the runtime chooses to use a class loader that doesn't have the + // system class loader as its parent, it is responsible for setting + // up a security manager before doing so. + String secman = SystemProperties.getProperty("java.security.manager"); + if (secman != null && SecurityManager.current == null) + { + if (secman.equals("") || secman.equals("default")) + { + SecurityManager.current = new SecurityManager(); + } + else + { + try + { + Class cl = Class.forName(secman, false, StaticData.systemClassLoader); + SecurityManager.current = (SecurityManager)cl.newInstance(); + } + catch (Exception x) + { + throw (InternalError) + new InternalError("Unable to create SecurityManager") + .initCause(x); + } + } + } + } + + /** + * The default protection domain, used when defining a class with a null + * parameter for the domain. + */ + static final ProtectionDomain defaultProtectionDomain; + static + { + CodeSource cs = new CodeSource(null, null); + PermissionCollection perm = Policy.getPolicy().getPermissions(cs); + defaultProtectionDomain = new ProtectionDomain(cs, perm); + } + /** + * The command-line state of the package assertion status overrides. This + * map is never modified, so it does not need to be synchronized. + */ + // Package visible for use by Class. + static final Map systemPackageAssertionStatus + = VMClassLoader.packageAssertionStatus(); + /** + * The command-line state of the class assertion status overrides. This + * map is never modified, so it does not need to be synchronized. + */ + // Package visible for use by Class. + static final Map systemClassAssertionStatus + = VMClassLoader.classAssertionStatus(); + } + + /** + * The desired assertion status of classes loaded by this loader, if not + * overridden by package or class instructions. + */ + // Package visible for use by Class. + boolean defaultAssertionStatus = VMClassLoader.defaultAssertionStatus(); + + /** + * The map of package assertion status overrides, or null if no package + * overrides have been specified yet. The values of the map should be + * Boolean.TRUE or Boolean.FALSE, and the unnamed package is represented + * by the null key. This map must be synchronized on this instance. + */ + // Package visible for use by Class. + Map packageAssertionStatus; + + /** + * The map of class assertion status overrides, or null if no class + * overrides have been specified yet. The values of the map should be + * Boolean.TRUE or Boolean.FALSE. This map must be synchronized on this + * instance. + */ + // Package visible for use by Class. + Map classAssertionStatus; + + /** + * VM private data. + */ + transient Object vmdata; + + /** + * Create a new ClassLoader with as parent the system classloader. There + * may be a security check for checkCreateClassLoader. + * + * @throws SecurityException if the security check fails + */ + protected ClassLoader() throws SecurityException + { + this(StaticData.systemClassLoader); + } + + /** + * Create a new ClassLoader with the specified parent. The parent will + * be consulted when a class or resource is requested through + * loadClass() or getResource(). Only when the + * parent classloader cannot provide the requested class or resource the + * findClass() or findResource() method + * of this classloader will be called. There may be a security check for + * checkCreateClassLoader. + * + * @param parent the classloader's parent, or null for the bootstrap + * classloader + * @throws SecurityException if the security check fails + * @since 1.2 + */ + protected ClassLoader(ClassLoader parent) + { + // May we create a new classloader? + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkCreateClassLoader(); + this.parent = parent; + this.initialized = true; + } + + /** + * Load a class using this ClassLoader or its parent, without resolving + * it. Calls loadClass(name, false). + * + *

    Subclasses should not override this method but should override + * findClass() which is called by this method.

    + * + * @param name the name of the class relative to this ClassLoader + * @return the loaded class + * @throws ClassNotFoundException if the class cannot be found + */ + public Class loadClass(String name) throws ClassNotFoundException + { + return loadClass(name, false); + } + + /** + * Load a class using this ClassLoader or its parent, possibly resolving + * it as well using resolveClass(). It first tries to find + * out if the class has already been loaded through this classloader by + * calling findLoadedClass(). Then it calls + * loadClass() on the parent classloader (or when there is + * no parent it uses the VM bootclassloader). If the class is still + * not loaded it tries to create a new class by calling + * findClass(). Finally when resolve is + * true it also calls resolveClass() on the + * newly loaded class. + * + *

    Subclasses should not override this method but should override + * findClass() which is called by this method.

    + * + * @param name the fully qualified name of the class to load + * @param resolve whether or not to resolve the class + * @return the loaded class + * @throws ClassNotFoundException if the class cannot be found + */ + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + // Have we already loaded this class? + Class c = findLoadedClass(name); + if (c == null) + { + // Can the class be loaded by a parent? + try + { + if (parent == null) + { + c = VMClassLoader.loadClass(name, resolve); + if (c != null) + return c; + } + else + { + return parent.loadClass(name, resolve); + } + } + catch (ClassNotFoundException e) + { + } + // Still not found, we have to do it ourself. + c = findClass(name); + } + if (resolve) + resolveClass(c); + return c; + } + + /** + * Called for every class name that is needed but has not yet been + * defined by this classloader or one of its parents. It is called by + * loadClass() after both findLoadedClass() and + * parent.loadClass() couldn't provide the requested class. + * + *

    The default implementation throws a + * ClassNotFoundException. Subclasses should override this + * method. An implementation of this method in a subclass should get the + * class bytes of the class (if it can find them), if the package of the + * requested class doesn't exist it should define the package and finally + * it should call define the actual class. It does not have to resolve the + * class. It should look something like the following:
    + * + *

    +   * // Get the bytes that describe the requested class
    +   * byte[] classBytes = classLoaderSpecificWayToFindClassBytes(name);
    +   * // Get the package name
    +   * int lastDot = name.lastIndexOf('.');
    +   * if (lastDot != -1)
    +   *   {
    +   *     String packageName = name.substring(0, lastDot);
    +   *     // Look if the package already exists
    +   *     if (getPackage(packageName) == null)
    +   *       {
    +   *         // define the package
    +   *         definePackage(packageName, ...);
    +   *       }
    +   *   }
    +   * // Define and return the class
    +   *  return defineClass(name, classBytes, 0, classBytes.length);
    +   * 
    + * + *

    loadClass() makes sure that the Class + * returned by findClass() will later be returned by + * findLoadedClass() when the same class name is requested. + * + * @param name class name to find (including the package name) + * @return the requested Class + * @throws ClassNotFoundException when the class can not be found + * @since 1.2 + */ + protected Class findClass(String name) throws ClassNotFoundException + { + throw new ClassNotFoundException(name); + } + + /** + * Helper to define a class using a string of bytes. This version is not + * secure. + * + * @param data the data representing the classfile, in classfile format + * @param offset the offset into the data where the classfile starts + * @param len the length of the classfile data in the array + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + * @throws IndexOutOfBoundsException if offset or len is negative, or + * offset + len exceeds data + * @deprecated use {@link #defineClass(String, byte[], int, int)} instead + */ + protected final Class defineClass(byte[] data, int offset, int len) + throws ClassFormatError + { + return defineClass(null, data, offset, len); + } + + /** + * Helper to define a class using a string of bytes without a + * ProtectionDomain. Subclasses should call this method from their + * findClass() implementation. The name should use '.' + * separators, and discard the trailing ".class". The default protection + * domain has the permissions of + * Policy.getPolicy().getPermissions(new CodeSource(null, null)). + * + * @param name the name to give the class, or null if unknown + * @param data the data representing the classfile, in classfile format + * @param offset the offset into the data where the classfile starts + * @param len the length of the classfile data in the array + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + * @throws IndexOutOfBoundsException if offset or len is negative, or + * offset + len exceeds data + * @throws SecurityException if name starts with "java." + * @since 1.1 + */ + protected final Class defineClass(String name, byte[] data, int offset, + int len) throws ClassFormatError + { + return defineClass(name, data, offset, len, null); + } + + /** + * Helper to define a class using a string of bytes. Subclasses should call + * this method from their findClass() implementation. If the + * domain is null, the default of + * Policy.getPolicy().getPermissions(new CodeSource(null, null)) + * is used. Once a class has been defined in a package, all further classes + * in that package must have the same set of certificates or a + * SecurityException is thrown. + * + * @param name the name to give the class. null if unknown + * @param data the data representing the classfile, in classfile format + * @param offset the offset into the data where the classfile starts + * @param len the length of the classfile data in the array + * @param domain the ProtectionDomain to give to the class, null for the + * default protection domain + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + * @throws IndexOutOfBoundsException if offset or len is negative, or + * offset + len exceeds data + * @throws SecurityException if name starts with "java.", or if certificates + * do not match up + * @since 1.2 + */ + protected final synchronized Class defineClass(String name, byte[] data, + int offset, int len, + ProtectionDomain domain) + throws ClassFormatError + { + checkInitialized(); + if (domain == null) + domain = StaticData.defaultProtectionDomain; + + return VMClassLoader.defineClassWithTransformers(this, name, data, offset, + len, domain); + } + + /** + * Helper to define a class using the contents of a byte buffer. If + * the domain is null, the default of + * Policy.getPolicy().getPermissions(new CodeSource(null, + * null)) is used. Once a class has been defined in a + * package, all further classes in that package must have the same + * set of certificates or a SecurityException is thrown. + * + * @param name the name to give the class. null if unknown + * @param buf a byte buffer containing bytes that form a class. + * @param domain the ProtectionDomain to give to the class, null for the + * default protection domain + * @return the class that was defined + * @throws ClassFormatError if data is not in proper classfile format + * @throws NoClassDefFoundError if the supplied name is not the same as + * the one specified by the byte buffer. + * @throws SecurityException if name starts with "java.", or if certificates + * do not match up + * @since 1.5 + */ + protected final Class defineClass(String name, ByteBuffer buf, + ProtectionDomain domain) + throws ClassFormatError + { + byte[] data = new byte[buf.remaining()]; + buf.get(data); + return defineClass(name, data, 0, data.length, domain); + } + + /** + * Links the class, if that has not already been done. Linking basically + * resolves all references to other classes made by this class. + * + * @param c the class to resolve + * @throws NullPointerException if c is null + * @throws LinkageError if linking fails + */ + protected final void resolveClass(Class c) + { + checkInitialized(); + VMClassLoader.resolveClass(c); + } + + /** + * Helper to find a Class using the system classloader, possibly loading it. + * A subclass usually does not need to call this, if it correctly + * overrides findClass(String). + * + * @param name the name of the class to find + * @return the found class + * @throws ClassNotFoundException if the class cannot be found + */ + protected final Class findSystemClass(String name) + throws ClassNotFoundException + { + checkInitialized(); + return Class.forName(name, false, StaticData.systemClassLoader); + } + + /** + * Returns the parent of this classloader. If the parent of this + * classloader is the bootstrap classloader then this method returns + * null. A security check may be performed on + * RuntimePermission("getClassLoader"). + * + * @return the parent ClassLoader + * @throws SecurityException if the security check fails + * @since 1.2 + */ + public final ClassLoader getParent() + { + // Check if we may return the parent classloader. + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null && ! cl.isAncestorOf(this)) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + return parent; + } + + /** + * Helper to set the signers of a class. This should be called after + * defining the class. + * + * @param c the Class to set signers of + * @param signers the signers to set + * @since 1.1 + */ + protected final void setSigners(Class c, Object[] signers) + { + checkInitialized(); + c.setSigners(signers); + } + + /** + * Helper to find an already-loaded class in this ClassLoader. + * + * @param name the name of the class to find + * @return the found Class, or null if it is not found + * @since 1.1 + */ + protected final synchronized Class findLoadedClass(String name) + { + checkInitialized(); + return VMClassLoader.findLoadedClass(this, name); + } + + /** + * Get the URL to a resource using this classloader or one of its parents. + * First tries to get the resource by calling getResource() + * on the parent classloader. If the parent classloader returns null then + * it tries finding the resource by calling findResource() on + * this classloader. The resource name should be separated by '/' for path + * elements. + * + *

    Subclasses should not override this method but should override + * findResource() which is called by this method. + * + * @param name the name of the resource relative to this classloader + * @return the URL to the resource or null when not found + */ + public URL getResource(String name) + { + URL result; + + if (parent == null) + result = VMClassLoader.getResource(name); + else + result = parent.getResource(name); + + if (result == null) + result = findResource(name); + return result; + } + + /** + * Returns an Enumeration of all resources with a given name that can + * be found by this classloader and its parents. Certain classloaders + * (such as the URLClassLoader when given multiple jar files) can have + * multiple resources with the same name that come from multiple locations. + * It can also occur that a parent classloader offers a resource with a + * certain name and the child classloader also offers a resource with that + * same name. getResource() only offers the first resource (of the + * parent) with a given name. This method lists all resources with the + * same name. The name should use '/' as path separators. + * + *

    The Enumeration is created by first calling getResources() + * on the parent classloader and then calling findResources() + * on this classloader.

    + * + * @param name the resource name + * @return an enumaration of all resources found + * @throws IOException if I/O errors occur in the process + * @since 1.2 + * @specnote this was final prior to 1.5 + */ + public Enumeration getResources(String name) throws IOException + { + Enumeration parentResources; + if (parent == null) + parentResources = VMClassLoader.getResources(name); + else + parentResources = parent.getResources(name); + return new DoubleEnumeration(parentResources, findResources(name)); + } + + /** + * Called whenever all locations of a named resource are needed. + * It is called by getResources() after it has called + * parent.getResources(). The results are combined by + * the getResources() method. + * + *

    The default implementation always returns an empty Enumeration. + * Subclasses should override it when they can provide an Enumeration of + * URLs (possibly just one element) to the named resource. + * The first URL of the Enumeration should be the same as the one + * returned by findResource. + * + * @param name the name of the resource to be found + * @return a possibly empty Enumeration of URLs to the named resource + * @throws IOException if I/O errors occur in the process + * @since 1.2 + */ + protected Enumeration findResources(String name) throws IOException + { + return new EmptyEnumeration(); + } + + /** + * Called whenever a resource is needed that could not be provided by + * one of the parents of this classloader. It is called by + * getResource() after parent.getResource() + * couldn't provide the requested resource. + * + *

    The default implementation always returns null. Subclasses should + * override this method when they can provide a way to return a URL + * to a named resource. + * + * @param name the name of the resource to be found + * @return a URL to the named resource or null when not found + * @since 1.2 + */ + protected URL findResource(String name) + { + return null; + } + + /** + * Get the URL to a resource using the system classloader. + * + * @param name the name of the resource relative to the system classloader + * @return the URL to the resource + * @since 1.1 + */ + public static final URL getSystemResource(String name) + { + return StaticData.systemClassLoader.getResource(name); + } + + /** + * Get an Enumeration of URLs to resources with a given name using the + * the system classloader. The enumeration firsts lists the resources with + * the given name that can be found by the bootstrap classloader followed + * by the resources with the given name that can be found on the classpath. + * + * @param name the name of the resource relative to the system classloader + * @return an Enumeration of URLs to the resources + * @throws IOException if I/O errors occur in the process + * @since 1.2 + */ + public static Enumeration getSystemResources(String name) + throws IOException + { + return StaticData.systemClassLoader.getResources(name); + } + + /** + * Get a resource as stream using this classloader or one of its parents. + * First calls getResource() and if that returns a URL to + * the resource then it calls and returns the InputStream given by + * URL.openStream(). + * + *

    Subclasses should not override this method but should override + * findResource() which is called by this method. + * + * @param name the name of the resource relative to this classloader + * @return an InputStream to the resource, or null + * @since 1.1 + */ + public InputStream getResourceAsStream(String name) + { + try + { + URL url = getResource(name); + if (url == null) + return null; + return url.openStream(); + } + catch (IOException e) + { + return null; + } + } + + /** + * Get a resource using the system classloader. + * + * @param name the name of the resource relative to the system classloader + * @return an input stream for the resource, or null + * @since 1.1 + */ + public static final InputStream getSystemResourceAsStream(String name) + { + try + { + URL url = getSystemResource(name); + if (url == null) + return null; + return url.openStream(); + } + catch (IOException e) + { + return null; + } + } + + /** + * Returns the system classloader. The system classloader (also called + * the application classloader) is the classloader that is used to + * load the application classes on the classpath (given by the system + * property java.class.path. This is set as the context + * class loader for a thread. The system property + * java.system.class.loader, if defined, is taken to be the + * name of the class to use as the system class loader, which must have + * a public constructor which takes a ClassLoader as a parent. The parent + * class loader passed in the constructor is the default system class + * loader. + * + *

    Note that this is different from the bootstrap classloader that + * actually loads all the real "system" classes. + * + *

    A security check will be performed for + * RuntimePermission("getClassLoader") if the calling class + * is not a parent of the system class loader. + * + * @return the system class loader + * @throws SecurityException if the security check fails + * @throws IllegalStateException if this is called recursively + * @throws Error if java.system.class.loader fails to load + * @since 1.2 + */ + public static ClassLoader getSystemClassLoader() + { + // Check if we may return the system classloader + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null && cl != StaticData.systemClassLoader) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + + return StaticData.systemClassLoader; + } + + /** + * Defines a new package and creates a Package object. The package should + * be defined before any class in the package is defined with + * defineClass(). The package should not yet be defined + * before in this classloader or in one of its parents (which means that + * getPackage() should return null). All + * parameters except the name of the package may be + * null. + * + *

    Subclasses should call this method from their findClass() + * implementation before calling defineClass() on a Class + * in a not yet defined Package (which can be checked by calling + * getPackage()). + * + * @param name the name of the Package + * @param specTitle the name of the specification + * @param specVendor the name of the specification designer + * @param specVersion the version of this specification + * @param implTitle the name of the implementation + * @param implVendor the vendor that wrote this implementation + * @param implVersion the version of this implementation + * @param sealed if sealed the origin of the package classes + * @return the Package object for the specified package + * @throws IllegalArgumentException if the package name is null or it + * was already defined by this classloader or one of its parents + * @see Package + * @since 1.2 + */ + protected Package definePackage(String name, String specTitle, + String specVendor, String specVersion, + String implTitle, String implVendor, + String implVersion, URL sealed) + { + if (getPackage(name) != null) + throw new IllegalArgumentException("Package " + name + + " already defined"); + Package p = new Package(name, specTitle, specVendor, specVersion, + implTitle, implVendor, implVersion, sealed, this); + synchronized (definedPackages) + { + definedPackages.put(name, p); + } + return p; + } + + /** + * Returns the Package object for the requested package name. It returns + * null when the package is not defined by this classloader or one of its + * parents. + * + * @param name the package name to find + * @return the package, if defined + * @since 1.2 + */ + protected Package getPackage(String name) + { + Package p; + if (parent == null) + p = VMClassLoader.getPackage(name); + else + p = parent.getPackage(name); + + if (p == null) + { + synchronized (definedPackages) + { + p = definedPackages.get(name); + } + } + return p; + } + + /** + * Returns all Package objects defined by this classloader and its parents. + * + * @return an array of all defined packages + * @since 1.2 + */ + protected Package[] getPackages() + { + // Get all our packages. + Package[] packages; + synchronized(definedPackages) + { + packages = new Package[definedPackages.size()]; + definedPackages.values().toArray(packages); + } + + // If we have a parent get all packages defined by our parents. + Package[] parentPackages; + if (parent == null) + parentPackages = VMClassLoader.getPackages(); + else + parentPackages = parent.getPackages(); + + Package[] allPackages = new Package[parentPackages.length + + packages.length]; + System.arraycopy(parentPackages, 0, allPackages, 0, + parentPackages.length); + System.arraycopy(packages, 0, allPackages, parentPackages.length, + packages.length); + return allPackages; + } + + /** + * Called by Runtime.loadLibrary() to get an absolute path + * to a (system specific) library that was requested by a class loaded + * by this classloader. The default implementation returns + * null. It should be implemented by subclasses when they + * have a way to find the absolute path to a library. If this method + * returns null the library is searched for in the default locations + * (the directories listed in the java.library.path system + * property). + * + * @param name the (system specific) name of the requested library + * @return the full pathname to the requested library, or null + * @see Runtime#loadLibrary(String) + * @since 1.2 + */ + protected String findLibrary(String name) + { + return null; + } + + /** + * Set the default assertion status for classes loaded by this classloader, + * used unless overridden by a package or class request. + * + * @param enabled true to set the default to enabled + * @see #setClassAssertionStatus(String, boolean) + * @see #setPackageAssertionStatus(String, boolean) + * @see #clearAssertionStatus() + * @since 1.4 + */ + public void setDefaultAssertionStatus(boolean enabled) + { + defaultAssertionStatus = enabled; + } + + /** + * Set the default assertion status for packages, used unless overridden + * by a class request. This default also covers subpackages, unless they + * are also specified. The unnamed package should use null for the name. + * + * @param name the package (and subpackages) to affect + * @param enabled true to set the default to enabled + * @see #setDefaultAssertionStatus(boolean) + * @see #setClassAssertionStatus(String, boolean) + * @see #clearAssertionStatus() + * @since 1.4 + */ + public synchronized void setPackageAssertionStatus(String name, + boolean enabled) + { + if (packageAssertionStatus == null) + packageAssertionStatus + = new HashMap(StaticData.systemPackageAssertionStatus); + packageAssertionStatus.put(name, Boolean.valueOf(enabled)); + } + + /** + * Set the default assertion status for a class. This only affects the + * status of top-level classes, any other string is harmless. + * + * @param name the class to affect + * @param enabled true to set the default to enabled + * @throws NullPointerException if name is null + * @see #setDefaultAssertionStatus(boolean) + * @see #setPackageAssertionStatus(String, boolean) + * @see #clearAssertionStatus() + * @since 1.4 + */ + public synchronized void setClassAssertionStatus(String name, + boolean enabled) + { + if (classAssertionStatus == null) + classAssertionStatus + = new HashMap(StaticData.systemClassAssertionStatus); + // The toString() hack catches null, as required. + classAssertionStatus.put(name.toString(), Boolean.valueOf(enabled)); + } + + /** + * Resets the default assertion status of this classloader, its packages + * and classes, all to false. This allows overriding defaults inherited + * from the command line. + * + * @see #setDefaultAssertionStatus(boolean) + * @see #setClassAssertionStatus(String, boolean) + * @see #setPackageAssertionStatus(String, boolean) + * @since 1.4 + */ + public synchronized void clearAssertionStatus() + { + defaultAssertionStatus = false; + packageAssertionStatus = null; + classAssertionStatus = null; + } + + /** + * Return true if this loader is either the specified class loader + * or an ancestor thereof. + * @param loader the class loader to check + */ + final boolean isAncestorOf(ClassLoader loader) + { + while (loader != null) + { + if (this == loader) + return true; + loader = loader.parent; + } + return false; + } + + private static URL[] getExtClassLoaderUrls() + { + String classpath = SystemProperties.getProperty("java.ext.dirs", ""); + StringTokenizer tok = new StringTokenizer(classpath, File.pathSeparator); + ArrayList list = new ArrayList(); + while (tok.hasMoreTokens()) + { + try + { + File f = new File(tok.nextToken()); + File[] files = f.listFiles(); + if (files != null) + for (int i = 0; i < files.length; i++) + list.add(files[i].toURL()); + } + catch(Exception x) + { + } + } + URL[] urls = new URL[list.size()]; + list.toArray(urls); + return urls; + } + + private static void addFileURL(ArrayList list, String file) + { + try + { + list.add(new File(file).toURL()); + } + catch(java.net.MalformedURLException x) + { + } + } + + private static URL[] getSystemClassLoaderUrls() + { + String classpath = SystemProperties.getProperty("java.class.path", "."); + StringTokenizer tok = new StringTokenizer(classpath, File.pathSeparator, true); + ArrayList list = new ArrayList(); + while (tok.hasMoreTokens()) + { + String s = tok.nextToken(); + if (s.equals(File.pathSeparator)) + addFileURL(list, "."); + else + { + addFileURL(list, s); + if (tok.hasMoreTokens()) + { + // Skip the separator. + tok.nextToken(); + // If the classpath ended with a separator, + // append the current directory. + if (!tok.hasMoreTokens()) + addFileURL(list, "."); + } + } + } + URL[] urls = new URL[list.size()]; + list.toArray(urls); + return urls; + } + + static ClassLoader defaultGetSystemClassLoader() + { + return createAuxiliarySystemClassLoader( + createSystemClassLoader(getSystemClassLoaderUrls(), + createExtClassLoader(getExtClassLoaderUrls(), null))); + } + + static ClassLoader createExtClassLoader(URL[] urls, ClassLoader parent) + { + if (urls.length > 0) + return new URLClassLoader(urls, parent); + else + return parent; + } + + static ClassLoader createSystemClassLoader(URL[] urls, ClassLoader parent) + { + return + new URLClassLoader(urls, parent) + { + protected synchronized Class loadClass(String name, + boolean resolve) + throws ClassNotFoundException + { + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + int lastDot = name.lastIndexOf('.'); + if (lastDot != -1) + sm.checkPackageAccess(name.substring(0, lastDot)); + } + return super.loadClass(name, resolve); + } + }; + } + + static ClassLoader createAuxiliarySystemClassLoader(ClassLoader parent) + { + String loader = SystemProperties.getProperty("java.system.class.loader", null); + if (loader == null) + { + return parent; + } + try + { + Constructor c = Class.forName(loader, false, parent) + .getConstructor(new Class[] { ClassLoader.class }); + return (ClassLoader)c.newInstance(new Object[] { parent }); + } + catch (Exception e) + { + System.err.println("Requested system classloader " + loader + " failed."); + throw (Error) + new Error("Requested system classloader " + loader + " failed.") + .initCause(e); + } + } + + /** + * Before doing anything "dangerous" please call this method to make sure + * this class loader instance was properly constructed (and not obtained + * by exploiting the finalizer attack) + * @see #initialized + */ + private void checkInitialized() + { + if (! initialized) + throw new SecurityException("attempt to use uninitialized class loader"); + } + +} diff --git a/libjava/classpath/java/lang/ClassNotFoundException.java b/libjava/classpath/java/lang/ClassNotFoundException.java new file mode 100644 index 000000000..142bc5d03 --- /dev/null +++ b/libjava/classpath/java/lang/ClassNotFoundException.java @@ -0,0 +1,126 @@ +/* ClassNotFoundException.java -- thrown when class definition cannot be found + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a class is requested by reflection, but the class definition + * cannot be found. This exception is often chained from another Throwable. + * + * @author Brian Jones + * @author Eric Blake (ebb9@email.byu.edu) + * @see Class#forName(String) + * @see ClassLoader#findSystemClass(String) + * @see ClassLoader#loadClass(String, boolean) + * @status updated to 1.4 + */ +public class ClassNotFoundException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 9176873029745254542L; + + /** + * The cause of this exception (duplicates the one stored in Throwable). + * + * @serial the exception cause + * @since 1.2 + */ + private final Throwable ex; + + /** + * Create an exception without a message. Note that this initializes the + * cause to null. + */ + public ClassNotFoundException() + { + this(null); + } + + /** + * Create an exception with a message. Note that this initializes the + * cause to null. + * + * @param s the message + */ + public ClassNotFoundException(String s) + { + super(s); + ex = null; + } + + /** + * Create an exception with a message and chain it to the exception + * which occurred while loading the class. + * + * @param s the message + * @param ex the chained exception + * @since 1.2 + */ + public ClassNotFoundException(String s, Throwable ex) + { + super(s, ex); + this.ex = ex; + } + + /** + * Returns the exception which occurred while loading the class, + * otherwise returns null. This is a legacy method; the preferred choice + * now is {@link Throwable#getCause()}. + * + * @return the cause of this exception + * @since 1.2 + */ + public Throwable getException() + { + return ex; + } + + /** + * Returns the exception which occurred while loading the class, + * otherwise returns null. + * + * @return the cause of this exception + * @since 1.4 + */ + public Throwable getCause() + { + return ex; + } +} diff --git a/libjava/classpath/java/lang/CloneNotSupportedException.java b/libjava/classpath/java/lang/CloneNotSupportedException.java new file mode 100644 index 000000000..9d10cf389 --- /dev/null +++ b/libjava/classpath/java/lang/CloneNotSupportedException.java @@ -0,0 +1,92 @@ +/* CloneNotSupportedException.java -- thrown when an object cannot be cloned + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown to indicate an object should not or could not be cloned. This + * includes the case when {@link Object#clone()} is called on an object + * which does not implement the {@link Cloneable} interface. For example:
    + *

    + * void m() throws CloneNotSupportedException
    + * {
    + *   clone();
    + * }
    + * 
    + * + *

    Notice that calling clone() on an array will never produce + * this exception, as the VM will always succeed in copying the array, or + * cause an OutOfMemoryError first. For example:
    + *

    + * void m(int[] array)
    + * {
    + *   int[] copy = (int[]) array.clone();
    + * }
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Cloneable + * @see Object#clone() + * @status updated to 1.4 + */ +public class CloneNotSupportedException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 5195511250079656443L; + + /** + * Create an exception without a message. + */ + public CloneNotSupportedException() + { + } + + /** + * Create an exception with a message. + * + * @param s the error message + */ + public CloneNotSupportedException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Cloneable.java b/libjava/classpath/java/lang/Cloneable.java new file mode 100644 index 000000000..10f20ce3b --- /dev/null +++ b/libjava/classpath/java/lang/Cloneable.java @@ -0,0 +1,78 @@ +/* Cloneable.java -- Interface for marking objects cloneable by Object.clone() + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * This interface should be implemented by classes wishing to + * support of override Object.clone(). The default + * behaviour of clone() performs a shallow copy, but + * subclasses often change this to perform a deep copy. Therefore, + * it is a good idea to document how deep your clone will go. + * If clone() is called on an object which does not + * implement this interface, a CloneNotSupportedException + * will be thrown. + * + *

    This interface is simply a tagging interface; it carries no + * requirements on methods to implement. However, it is typical for + * a Cloneable class to implement at least equals, + * hashCode, and clone, sometimes + * increasing the accessibility of clone to be public. The typical + * implementation of clone invokes super.clone() + * rather than a constructor, but this is not a requirement. + * + *

    If an object that implement Cloneable should not be cloned, + * simply override the clone method to throw a + * CloneNotSupportedException. + * + *

    All array types implement Cloneable, and have a public + * clone method that will never fail with a + * CloneNotSupportedException. + * + * @author Paul Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @author Warren Levy (warrenl@cygnus.com) + * @see Object#clone() + * @see CloneNotSupportedException + * @since 1.0 + * @status updated to 1.4 + */ +public interface Cloneable +{ + // Tagging interface only. +} diff --git a/libjava/classpath/java/lang/Comparable.java b/libjava/classpath/java/lang/Comparable.java new file mode 100644 index 000000000..d4ca63ad8 --- /dev/null +++ b/libjava/classpath/java/lang/Comparable.java @@ -0,0 +1,98 @@ +/* Comparable.java -- Interface for comparaing objects to obtain an ordering + Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Interface for objects that can be ordering among other objects. The + * ordering can be total, such that two objects only compare equal + * if they are also equal by the equals method, or partial such + * that this is not necessarily true. For example, a case-sensitive + * dictionary order comparison of Strings is total, but if it is + * case-insensitive it is partial, because "abc" and "ABC" compare as + * equal even though "abc".equals("ABC") returns false. However, if you use + * a partial ordering, it is a good idea to document your class as + * "inconsistent with equals", because the behavior of your class in a + * SortedMap will be different than in a HashMap. + * + *

    Lists, arrays, and sets of objects that implement this interface can + * be sorted automatically, without the need for an explicit + * {@link java.util.Comparator}. Note that e1.compareTo(null) + * should throw an Exception; as should comparison between incompatible + * classes. + * + * @author Geoff Berry + * @author Warren Levy (warrenl@cygnus.com) + * @see java.util.Comparator + * @see java.util.Collections#sort(java.util.List) + * @see java.util.Arrays#sort(Object[]) + * @see java.util.SortedSet + * @see java.util.SortedMap + * @see java.util.TreeSet + * @see java.util.TreeMap + * @since 1.2 + * @status updated to 1.5 + */ +public interface Comparable +{ + /** + * Compares this object with another, and returns a numerical result based + * on the comparison. If the result is negative, this object sorts less + * than the other; if 0, the two are equal, and if positive, this object + * sorts greater than the other. To translate this into boolean, simply + * perform o1.compareTo(o2) <op> 0, where op + * is one of <, <=, =, !=, >, or >=. + * + *

    You must make sure that the comparison is mutual, ie. + * sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) (where sgn() is + * defined as -1, 0, or 1 based on the sign). This includes throwing an + * exception in either direction if the two are not comparable; hence, + * compareTo(null) should always throw an Exception. + * + *

    You should also ensure transitivity, in two forms: + * x.compareTo(y) > 0 && y.compareTo(z) > 0 implies + * x.compareTo(z) > 0; and x.compareTo(y) == 0 + * implies x.compareTo(z) == y.compareTo(z). + * + * @param o the object to be compared + * @return an integer describing the comparison + * @throws NullPointerException if o is null + * @throws ClassCastException if o cannot be compared + */ + int compareTo(T o); +} diff --git a/libjava/classpath/java/lang/Compiler.java b/libjava/classpath/java/lang/Compiler.java new file mode 100644 index 000000000..0d990e938 --- /dev/null +++ b/libjava/classpath/java/lang/Compiler.java @@ -0,0 +1,127 @@ +/* Compiler.java -- placeholder for Java-to-native runtime compilers + Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * The Compiler class is a placeholder for a JIT compiler + * implementation, and does nothing unless there is such a compiler. + * + *

    The system property java.compiler may contain the name + * of a library to load with System.loadLibrary when the + * virtual machine first starts. If so, and loading the library succeeds, + * then a function by the name of java_lang_Compiler_start() + * in that library is called. + * + *

    Note that a VM might not have implemented any of this. + * + * @author Tom Tromey (tromey@cygnus.com) + * @see System#getProperty(String) + * @see System#getProperty(String, String) + * @see System#loadLibrary(String) + * @since JDK 1.0 + * @status updated to 1.4 + */ +public final class Compiler +{ + /** + * Don't allow new `Compiler's to be made. + */ + private Compiler() + { + } + + /** + * Compile the class named by oneClass. + * + * @param oneClass the class to compile + * @return false if no compiler is available or + * compilation failed, true if compilation succeeded + * @throws NullPointerException if oneClass is null + */ + public static boolean compileClass(Class oneClass) + { + return VMCompiler.compileClass(oneClass); + } + + /** + * Compile the classes whose name matches classNames. + * + * @param classNames the name of classes to compile + * @return false if no compiler is available or + * compilation failed, true if compilation succeeded + * @throws NullPointerException if classNames is null + */ + public static boolean compileClasses(String classNames) + { + return VMCompiler.compileClasses(classNames); + } + + /** + * This method examines the argument and performs an operation + * according to the compilers documentation. No specific operation + * is required. + * + * @param arg a compiler-specific argument + * @return a compiler-specific value, including null + * @throws NullPointerException if the compiler doesn't like a null arg + */ + public static Object command(Object arg) + { + return VMCompiler.command(arg); + } + + /** + * Calling Compiler.enable() will cause the compiler + * to resume operation if it was previously disabled; provided that a + * compiler even exists. + */ + public static void enable() + { + VMCompiler.enable(); + } + + /** + * Calling Compiler.disable() will cause the compiler + * to be suspended; provided that a compiler even exists. + */ + public static void disable() + { + VMCompiler.disable(); + } +} diff --git a/libjava/classpath/java/lang/Deprecated.java b/libjava/classpath/java/lang/Deprecated.java new file mode 100644 index 000000000..a52abdb4e --- /dev/null +++ b/libjava/classpath/java/lang/Deprecated.java @@ -0,0 +1,56 @@ +/* Deprecated - Annotation to mark elements as deprecated + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * This annotation is used as a marker to indicate that the annotated + * declaration is deprecated and should not be used in new code. + * This replaces the old "@deprecated" javadoc tag. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +@Documented @Retention(RUNTIME) +public @interface Deprecated +{ +} diff --git a/libjava/classpath/java/lang/Double.java b/libjava/classpath/java/lang/Double.java new file mode 100644 index 000000000..3ae1b0111 --- /dev/null +++ b/libjava/classpath/java/lang/Double.java @@ -0,0 +1,625 @@ +/* Double.java -- object wrapper for double + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.java.lang.CPStringBuilder; + +/** + * Instances of class Double represent primitive + * double values. + * + * Additionally, this class provides various helper functions and variables + * related to doubles. + * + * @author Paul Fisher + * @author Andrew Haley (aph@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.0 + * @status partly updated to 1.5 + */ +public final class Double extends Number implements Comparable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -9172774392245257468L; + + /** + * The maximum positive value a double may represent + * is 1.7976931348623157e+308. + */ + public static final double MAX_VALUE = 1.7976931348623157e+308; + + /** + * The minimum positive value a double may represent + * is 5e-324. + */ + public static final double MIN_VALUE = 5e-324; + + /** + * The value of a double representation -1.0/0.0, negative + * infinity. + */ + public static final double NEGATIVE_INFINITY = -1.0 / 0.0; + + /** + * The value of a double representing 1.0/0.0, positive infinity. + */ + public static final double POSITIVE_INFINITY = 1.0 / 0.0; + + /** + * All IEEE 754 values of NaN have the same value in Java. + */ + public static final double NaN = 0.0 / 0.0; + + /** + * The number of bits needed to represent a double. + * @since 1.5 + */ + public static final int SIZE = 64; + + /** + * The primitive type double is represented by this + * Class object. + * @since 1.1 + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass('D'); + + /** + * Cache representation of 0 + */ + private static final Double ZERO = new Double(0.0d); + + /** + * Cache representation of 1 + */ + private static final Double ONE = new Double(1.0d); + + /** + * The immutable value of this Double. + * + * @serial the wrapped double + */ + private final double value; + + /** + * Create a Double from the primitive double + * specified. + * + * @param value the double argument + */ + public Double(double value) + { + this.value = value; + } + + /** + * Create a Double from the specified String. + * This method calls Double.parseDouble(). + * + * @param s the String to convert + * @throws NumberFormatException if s cannot be parsed as a + * double + * @throws NullPointerException if s is null + * @see #parseDouble(String) + */ + public Double(String s) + { + value = parseDouble(s); + } + + /** + * Convert the double to a String. + * Floating-point string representation is fairly complex: here is a + * rundown of the possible values. "[-]" indicates that a + * negative sign will be printed if the value (or exponent) is negative. + * "<number>" means a string of digits ('0' to '9'). + * "<digit>" means a single digit ('0' to '9').
    + * + * + * + * + * + * + * + * + * + * + *
    Value of DoubleString Representation
    [+-] 0 [-]0.0
    Between [+-] 10-3 and 107, exclusive[-]number.number
    Other numeric value[-]<digit>.<number> + * E[-]<number>
    [+-] infinity [-]Infinity
    NaN NaN
    + * + * Yes, negative zero is a possible value. Note that there is + * always a . and at least one digit printed after + * it: even if the number is 3, it will be printed as 3.0. + * After the ".", all digits will be printed except trailing zeros. The + * result is rounded to the shortest decimal number which will parse back + * to the same double. + * + *

    To create other output formats, use {@link java.text.NumberFormat}. + * + * @XXX specify where we are not in accord with the spec. + * + * @param d the double to convert + * @return the String representing the double + */ + public static String toString(double d) + { + return VMDouble.toString(d, false); + } + + /** + * Convert a double value to a hexadecimal string. This converts as + * follows: + *

      + *
    • A NaN value is converted to the string "NaN". + *
    • Positive infinity is converted to the string "Infinity". + *
    • Negative infinity is converted to the string "-Infinity". + *
    • For all other values, the first character of the result is '-' + * if the value is negative. This is followed by '0x1.' if the + * value is normal, and '0x0.' if the value is denormal. This is + * then followed by a (lower-case) hexadecimal representation of the + * mantissa, with leading zeros as required for denormal values. + * The next character is a 'p', and this is followed by a decimal + * representation of the unbiased exponent. + *
    + * @param d the double value + * @return the hexadecimal string representation + * @since 1.5 + */ + public static String toHexString(double d) + { + if (isNaN(d)) + return "NaN"; + if (isInfinite(d)) + return d < 0 ? "-Infinity" : "Infinity"; + + long bits = doubleToLongBits(d); + CPStringBuilder result = new CPStringBuilder(); + + if (bits < 0) + result.append('-'); + result.append("0x"); + + final int mantissaBits = 52; + final int exponentBits = 11; + long mantMask = (1L << mantissaBits) - 1; + long mantissa = bits & mantMask; + long expMask = (1L << exponentBits) - 1; + long exponent = (bits >>> mantissaBits) & expMask; + + result.append(exponent == 0 ? '0' : '1'); + result.append('.'); + result.append(Long.toHexString(mantissa)); + if (exponent == 0 && mantissa != 0) + { + // Treat denormal specially by inserting '0's to make + // the length come out right. The constants here are + // to account for things like the '0x'. + int offset = 4 + ((bits < 0) ? 1 : 0); + // The silly +3 is here to keep the code the same between + // the Float and Double cases. In Float the value is + // not a multiple of 4. + int desiredLength = offset + (mantissaBits + 3) / 4; + while (result.length() < desiredLength) + result.insert(offset, '0'); + } + result.append('p'); + if (exponent == 0 && mantissa == 0) + { + // Zero, so do nothing special. + } + else + { + // Apply bias. + boolean denormal = exponent == 0; + exponent -= (1 << (exponentBits - 1)) - 1; + // Handle denormal. + if (denormal) + ++exponent; + } + + result.append(Long.toString(exponent)); + return result.toString(); + } + + /** + * Returns a Double object wrapping the value. + * In contrast to the Double constructor, this method + * may cache some values. It is used by boxing conversion. + * + * @param val the value to wrap + * @return the Double + * @since 1.5 + */ + public static Double valueOf(double val) + { + if ((val == 0.0) && (doubleToRawLongBits(val) == 0L)) + return ZERO; + else if (val == 1.0) + return ONE; + else + return new Double(val); + } + + /** + * Create a new Double object using the String. + * + * @param s the String to convert + * @return the new Double + * @throws NumberFormatException if s cannot be parsed as a + * double + * @throws NullPointerException if s is null. + * @see #parseDouble(String) + */ + public static Double valueOf(String s) + { + return valueOf(parseDouble(s)); + } + + /** + * Parse the specified String as a double. The + * extended BNF grammar is as follows:
    + *
    +   * DecodableString:
    +   *      ( [ - | + ] NaN )
    +   *    | ( [ - | + ] Infinity )
    +   *    | ( [ - | + ] FloatingPoint
    +   *              [ f | F | d
    +   *                | D] )
    +   * FloatingPoint:
    +   *      ( { Digit }+ [ . { Digit } ]
    +   *              [ Exponent ] )
    +   *    | ( . { Digit }+ [ Exponent ] )
    +   * Exponent:
    +   *      ( ( e | E )
    +   *              [ - | + ] { Digit }+ )
    +   * Digit: '0' through '9'
    +   * 
    + * + *

    NaN and infinity are special cases, to allow parsing of the output + * of toString. Otherwise, the result is determined by calculating + * n * 10exponent to infinite precision, then rounding + * to the nearest double. Remember that many numbers cannot be precisely + * represented in floating point. In case of overflow, infinity is used, + * and in case of underflow, signed zero is used. Unlike Integer.parseInt, + * this does not accept Unicode digits outside the ASCII range. + * + *

    If an unexpected character is found in the String, a + * NumberFormatException will be thrown. Leading and trailing + * 'whitespace' is ignored via String.trim(), but spaces + * internal to the actual number are not allowed. + * + *

    To parse numbers according to another format, consider using + * {@link java.text.NumberFormat}. + * + * @XXX specify where/how we are not in accord with the spec. + * + * @param str the String to convert + * @return the double value of s + * @throws NumberFormatException if s cannot be parsed as a + * double + * @throws NullPointerException if s is null + * @see #MIN_VALUE + * @see #MAX_VALUE + * @see #POSITIVE_INFINITY + * @see #NEGATIVE_INFINITY + * @since 1.2 + */ + public static double parseDouble(String str) + { + return VMDouble.parseDouble(str); + } + + /** + * Return true if the double has the same + * value as NaN, otherwise return false. + * + * @param v the double to compare + * @return whether the argument is NaN. + */ + public static boolean isNaN(double v) + { + // This works since NaN != NaN is the only reflexive inequality + // comparison which returns true. + return v != v; + } + + /** + * Return true if the double has a value + * equal to either NEGATIVE_INFINITY or + * POSITIVE_INFINITY, otherwise return false. + * + * @param v the double to compare + * @return whether the argument is (-/+) infinity. + */ + public static boolean isInfinite(double v) + { + return v == POSITIVE_INFINITY || v == NEGATIVE_INFINITY; + } + + /** + * Return true if the value of this Double + * is the same as NaN, otherwise return false. + * + * @return whether this Double is NaN + */ + public boolean isNaN() + { + return isNaN(value); + } + + /** + * Return true if the value of this Double + * is the same as NEGATIVE_INFINITY or + * POSITIVE_INFINITY, otherwise return false. + * + * @return whether this Double is (-/+) infinity + */ + public boolean isInfinite() + { + return isInfinite(value); + } + + /** + * Convert the double value of this Double + * to a String. This method calls + * Double.toString(double) to do its dirty work. + * + * @return the String representation + * @see #toString(double) + */ + public String toString() + { + return toString(value); + } + + /** + * Return the value of this Double as a byte. + * + * @return the byte value + * @since 1.1 + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this Double as a short. + * + * @return the short value + * @since 1.1 + */ + public short shortValue() + { + return (short) value; + } + + /** + * Return the value of this Double as an int. + * + * @return the int value + */ + public int intValue() + { + return (int) value; + } + + /** + * Return the value of this Double as a long. + * + * @return the long value + */ + public long longValue() + { + return (long) value; + } + + /** + * Return the value of this Double as a float. + * + * @return the float value + */ + public float floatValue() + { + return (float) value; + } + + /** + * Return the value of this Double. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Return a hashcode representing this Object. Double's hash + * code is calculated by:
    + * long v = Double.doubleToLongBits(doubleValue());
    + * int hash = (int)(v^(v>>32))
    . + * + * @return this Object's hash code + * @see #doubleToLongBits(double) + */ + public int hashCode() + { + long v = doubleToLongBits(value); + return (int) (v ^ (v >>> 32)); + } + + /** + * Returns true if obj is an instance of + * Double and represents the same double value. Unlike comparing + * two doubles with ==, this treats two instances of + * Double.NaN as equal, but treats 0.0 and + * -0.0 as unequal. + * + *

    Note that d1.equals(d2) is identical to + * doubleToLongBits(d1.doubleValue()) == + * doubleToLongBits(d2.doubleValue()). + * + * @param obj the object to compare + * @return whether the objects are semantically equal + */ + public boolean equals(Object obj) + { + if (obj instanceof Double) + { + double d = ((Double) obj).value; + return (doubleToRawLongBits(value) == doubleToRawLongBits(d)) || + (isNaN(value) && isNaN(d)); + } + return false; + } + + /** + * Convert the double to the IEEE 754 floating-point "double format" bit + * layout. Bit 63 (the most significant) is the sign bit, bits 62-52 + * (masked by 0x7ff0000000000000L) represent the exponent, and bits 51-0 + * (masked by 0x000fffffffffffffL) are the mantissa. This function + * collapses all versions of NaN to 0x7ff8000000000000L. The result of this + * function can be used as the argument to + * Double.longBitsToDouble(long) to obtain the original + * double value. + * + * @param value the double to convert + * @return the bits of the double + * @see #longBitsToDouble(long) + */ + public static long doubleToLongBits(double value) + { + if (isNaN(value)) + return 0x7ff8000000000000L; + else + return VMDouble.doubleToRawLongBits(value); + } + + /** + * Convert the double to the IEEE 754 floating-point "double format" bit + * layout. Bit 63 (the most significant) is the sign bit, bits 62-52 + * (masked by 0x7ff0000000000000L) represent the exponent, and bits 51-0 + * (masked by 0x000fffffffffffffL) are the mantissa. This function + * leaves NaN alone, rather than collapsing to a canonical value. The + * result of this function can be used as the argument to + * Double.longBitsToDouble(long) to obtain the original + * double value. + * + * @param value the double to convert + * @return the bits of the double + * @see #longBitsToDouble(long) + */ + public static long doubleToRawLongBits(double value) + { + return VMDouble.doubleToRawLongBits(value); + } + + /** + * Convert the argument in IEEE 754 floating-point "double format" bit + * layout to the corresponding float. Bit 63 (the most significant) is the + * sign bit, bits 62-52 (masked by 0x7ff0000000000000L) represent the + * exponent, and bits 51-0 (masked by 0x000fffffffffffffL) are the mantissa. + * This function leaves NaN alone, so that you can recover the bit pattern + * with Double.doubleToRawLongBits(double). + * + * @param bits the bits to convert + * @return the double represented by the bits + * @see #doubleToLongBits(double) + * @see #doubleToRawLongBits(double) + */ + public static double longBitsToDouble(long bits) + { + return VMDouble.longBitsToDouble(bits); + } + + /** + * Compare two Doubles numerically by comparing their double + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. However, this special + * cases NaN and signed zero as follows: NaN is considered greater than + * all other doubles, including POSITIVE_INFINITY, and positive + * zero is considered greater than negative zero. + * + * @param d the Double to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Double d) + { + return compare(value, d.value); + } + + /** + * Behaves like new Double(x).compareTo(new Double(y)); in + * other words this compares two doubles, special casing NaN and zero, + * without the overhead of objects. + * + * @param x the first double to compare + * @param y the second double to compare + * @return the comparison + * @since 1.4 + */ + public static int compare(double x, double y) + { + // handle the easy cases: + if (x < y) + return -1; + if (x > y) + return 1; + + // handle equality respecting that 0.0 != -0.0 (hence not using x == y): + long lx = doubleToRawLongBits(x); + long ly = doubleToRawLongBits(y); + if (lx == ly) + return 0; + + // handle NaNs: + if (x != x) + return (y != y) ? 0 : 1; + else if (y != y) + return -1; + + // handle +/- 0.0 + return (lx < ly) ? -1 : 1; + } +} diff --git a/libjava/classpath/java/lang/Enum.java b/libjava/classpath/java/lang/Enum.java new file mode 100644 index 000000000..46d28243f --- /dev/null +++ b/libjava/classpath/java/lang/Enum.java @@ -0,0 +1,237 @@ +/* Enum.java - Base class for all enums + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.Serializable; +import java.lang.reflect.Field; + +/** + * This class represents a Java enumeration. All enumerations are + * subclasses of this class. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public abstract class Enum> + implements Comparable, Serializable +{ + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = -4300926546619394005L; + + /** + * The name of this enum constant. + */ + final String name; + + /** + * The number of this enum constant. Each constant is given a number + * which matches the order in which it was declared, starting with zero. + */ + final int ordinal; + + /** + * This constructor is used by the compiler to create enumeration constants. + * + * @param name the name of the enumeration constant. + * @param ordinal the number of the enumeration constant, based on the + * declaration order of the constants and starting from zero. + */ + protected Enum(String name, int ordinal) + { + this.name = name; + this.ordinal = ordinal; + } + + /** + * Returns an Enum for a enum class given a description string of + * the enum constant. + * + * @exception NullPointerException when etype or s are null. + * @exception IllegalArgumentException when there is no value s in + * the enum etype. + */ + public static > S valueOf(Class etype, String s) + { + if (etype == null || s == null) + throw new NullPointerException(); + + try + { + // XXX We should not use getDeclaredField, because it does + // an unnecessary security check. + Field f = etype.getDeclaredField(s); + if (! f.isEnumConstant()) + throw new IllegalArgumentException(s); + Class.setAccessible(f); + @SuppressWarnings("unchecked") + S val = (S) f.get(null); + return val; + } + catch (NoSuchFieldException exception) + { + throw new IllegalArgumentException(s); + } + catch (IllegalAccessException exception) + { + throw new Error("Unable to access Enum class"); + } + } + + /** + * Returns true if this enumeration is equivalent to the supplied object, + * o. Only one instance of an enumeration constant exists, + * so the comparison is simply done using ==. + * + * @param o the object to compare to this. + * @return true if this == o. + */ + public final boolean equals(Object o) + { + // Enum constants are singular, so we need only compare `=='. + return this == o; + } + + /** + * Returns the hash code of this constant. This is simply the ordinal. + * + * @return the hash code of this enumeration constant. + */ + public final int hashCode() + { + return ordinal; + } + + /** + * Returns a textual representation of this enumeration constant. + * By default, this is simply the declared name of the constant, but + * specific enumeration types may provide an implementation more suited + * to the data being stored. + * + * @return a textual representation of this constant. + */ + public String toString() + { + return name; + } + + /** + * Returns an integer which represents the relative ordering of this + * enumeration constant. Enumeration constants are ordered by their + * ordinals, which represents their declaration order. So, comparing + * two identical constants yields zero, while one declared prior to + * this returns a positive integer and one declared after yields a + * negative integer. + * + * @param e the enumeration constant to compare. + * @return a negative integer if e.ordinal < this.ordinal, + * zero if e.ordinal == this.ordinal and a positive + * integer if e.ordinal > this.ordinal. + * @throws ClassCastException if e is not an enumeration + * constant of the same class. + */ + public final int compareTo(T e) + { + if (getDeclaringClass() != e.getDeclaringClass()) + throw new ClassCastException(); + return ordinal - e.ordinal; + } + + /** + * Cloning of enumeration constants is prevented, to maintain their + * singleton status. + * + * @return the cloned object. + * @throws CloneNotSupportedException as enumeration constants can't be + * cloned. + */ + protected final Object clone() throws CloneNotSupportedException + { + throw new CloneNotSupportedException("can't clone an enum constant"); + } + + /** + * Returns the name of this enumeration constant. + * + * @return the name of the constant. + */ + public final String name() + { + return name; + } + + /** + * Returns the number of this enumeration constant, which represents + * the order in which it was originally declared, starting from zero. + * + * @return the number of this constant. + */ + public final int ordinal() + { + return ordinal; + } + + /** + * Returns the type of this enumeration constant. This is the class + * corresponding to the declaration of the enumeration. + * + * @return the type of this enumeration constant. + */ + public final Class getDeclaringClass() + { + Class k = getClass(); + // We might be in an anonymous subclass of the enum class, so go + // up one more level. + if (k.getSuperclass() != Enum.class) + k = k.getSuperclass(); + return k; + } + + /** + * Enumerations can not have finalization methods. + * + * @since 1.6 + */ + protected final void finalize() + { + } + +} diff --git a/libjava/classpath/java/lang/EnumConstantNotPresentException.java b/libjava/classpath/java/lang/EnumConstantNotPresentException.java new file mode 100644 index 000000000..f597318db --- /dev/null +++ b/libjava/classpath/java/lang/EnumConstantNotPresentException.java @@ -0,0 +1,96 @@ +/* EnumConstantNotPresentException.java -- thrown when enum constant + not available + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An exception of this type is thrown when a symbolic reference is + * made to an enum constant which does not exist. + * + * @author Tom Tromey (tromey@redhat.com) + * @since 1.5 + */ +public class EnumConstantNotPresentException extends RuntimeException +{ + private static final long serialVersionUID = -6046998521960521108L; + + /** + * The enum's type. Note that the name is fixed by the + * serialization spec. + */ + private Class enumType; + + /** + * The name of the missing enum constant. Note that the name is + * fixed by the serialization spec. + */ + private String constantName; + + /** + * Create a new EnumConstantNotPresentException with the indicated + * enum type and enum constant name. + * @param theEnum the enum's class + * @param name the name of the missing enum constant + */ + public EnumConstantNotPresentException(Class theEnum, + String name) + { + super("enum " + theEnum + " is missing the constant " + name); + enumType = theEnum; + constantName = name; + } + + /** + * Return the name of the missing constant. + * @return the name of the missing constant + */ + public String constantName() + { + return constantName; + } + + /** + * Return the enum type which is missing a constant. + * @return the enum type which is missing a constant + */ + public Class enumType() + { + return enumType; + } +} diff --git a/libjava/classpath/java/lang/Error.java b/libjava/classpath/java/lang/Error.java new file mode 100644 index 000000000..f66c7548b --- /dev/null +++ b/libjava/classpath/java/lang/Error.java @@ -0,0 +1,107 @@ +/* Error.java -- Indication of fatal abnormal conditions + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Applications should not try to catch errors since they indicate + * abnormal conditions. An abnormal condition is something which should not + * occur, or which should not be recovered from. This latter category + * includes ThreadDeath and AssertionError. + * + *

    A method is not required to declare any subclass of Error in + * its throws clause which might be thrown but not caught while + * executing the method. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Error extends Throwable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4980196508277280342L; + + /** + * Create an error without a message. The cause remains uninitialized. + * + * @see #initCause(Throwable) + */ + public Error() + { + } + + /** + * Create an error with a message. The cause remains uninitialized. + * + * @param s the message string + * @see #initCause(Throwable) + */ + public Error(String s) + { + super(s); + } + + /** + * Create an error with a message and a cause. + * + * @param s the message string + * @param cause the cause of this error + * @since 1.4 + */ + public Error(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create an error with a given cause, and a message of + * cause == null ? null : cause.toString(). + * + * @param cause the cause of this error + * @since 1.4 + */ + public Error(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/lang/Exception.java b/libjava/classpath/java/lang/Exception.java new file mode 100644 index 000000000..42f7c640d --- /dev/null +++ b/libjava/classpath/java/lang/Exception.java @@ -0,0 +1,104 @@ +/* Exception.java -- generic exception thrown to indicate an exceptional + condition has occurred. + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * The root class of all exceptions worth catching in a program. This + * includes the special category of RuntimeException, which + * does not need to be declared in a throws clause. Exceptions can be used + * to represent almost any exceptional behavior, such as programming errors, + * mouse movements, keyboard clicking, etc. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class Exception extends Throwable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -3387516993124229948L; + + /** + * Create an exception without a message. The cause remains uninitialized. + * + * @see #initCause(Throwable) + */ + public Exception() + { + } + + /** + * Create an exception with a message. The cause remains uninitialized. + * + * @param s the message + * @see #initCause(Throwable) + */ + public Exception(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message string + * @param cause the cause of this error + * @since 1.4 + */ + public Exception(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create an exception with a given cause, and a message of + * cause == null ? null : cause.toString(). + * + * @param cause the cause of this exception + * @since 1.4 + */ + public Exception(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/lang/ExceptionInInitializerError.java b/libjava/classpath/java/lang/ExceptionInInitializerError.java new file mode 100644 index 000000000..1e5809580 --- /dev/null +++ b/libjava/classpath/java/lang/ExceptionInInitializerError.java @@ -0,0 +1,123 @@ +/* ExceptionInInitializerError.java -- thrown when class initialization fails + with an uncaught exception + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An ExceptionInInitializerError is thrown when an uncaught + * exception has occurred in a static initializer or the initializer for a + * static variable. In general, this wraps only RuntimeExceptions, since the + * compiler does not allow a checked exception to be uncaught in an + * initializer. This exception only occurs during reflection, when a class + * is initialized as part of another action. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class ExceptionInInitializerError extends LinkageError +{ + /** + * Compatible with JDK 1.1+. + */ + static final long serialVersionUID = 1521711792217232256L; + + /** + * The cause of this exception (duplicates the one stored in Throwable). + * + * @serial the exception cause + */ + private final Throwable exception; + + /** + * Create an error without a message. The cause is initialized as null. + */ + public ExceptionInInitializerError() + { + this((String) null); + } + + /** + * Create an error with a message. The cause is initialized as null. + * + * @param s the message + */ + public ExceptionInInitializerError(String s) + { + super(s); + exception = null; + } + + /** + * Creates an error an saves a reference to the Throwable + * object. The message string is null. + * + * @param t the exception thrown + */ + public ExceptionInInitializerError(Throwable t) + { + super(null); + initCause(t); + exception = t; + } + + /** + * Return the exception that caused this error to be created. This is a + * legacy method; the preferred choice now is {@link Throwable#getCause()}. + * + * @return the cause, or null if unknown + */ + public Throwable getException() + { + return exception; + } + + /** + * Return the exception that cause this error to be created. + * + * @return the cause, or null if unknown + * @since 1.4 + */ + public Throwable getCause() + { + return exception; + } +} diff --git a/libjava/classpath/java/lang/Float.java b/libjava/classpath/java/lang/Float.java new file mode 100644 index 000000000..a4a766ec4 --- /dev/null +++ b/libjava/classpath/java/lang/Float.java @@ -0,0 +1,633 @@ +/* Float.java -- object wrapper for float + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.java.lang.CPStringBuilder; + +/** + * Instances of class Float represent primitive + * float values. + * + * Additionally, this class provides various helper functions and variables + * related to floats. + * + * @author Paul Fisher + * @author Andrew Haley (aph@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.0 + * @status partly updated to 1.5 + */ +public final class Float extends Number implements Comparable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2671257302660747028L; + + /** + * The maximum positive value a double may represent + * is 3.4028235e+38f. + */ + public static final float MAX_VALUE = 3.4028235e+38f; + + /** + * The minimum positive value a float may represent + * is 1.4e-45. + */ + public static final float MIN_VALUE = 1.4e-45f; + + /** + * The value of a float representation -1.0/0.0, negative infinity. + */ + public static final float NEGATIVE_INFINITY = -1.0f / 0.0f; + + /** + * The value of a float representation 1.0/0.0, positive infinity. + */ + public static final float POSITIVE_INFINITY = 1.0f / 0.0f; + + /** + * All IEEE 754 values of NaN have the same value in Java. + */ + public static final float NaN = 0.0f / 0.0f; + + /** + * The primitive type float is represented by this + * Class object. + * @since 1.1 + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass('F'); + + /** + * The number of bits needed to represent a float. + * @since 1.5 + */ + public static final int SIZE = 32; + + /** + * Cache representation of 0 + */ + private static final Float ZERO = new Float(0.0f); + + /** + * Cache representation of 1 + */ + private static final Float ONE = new Float(1.0f); + + /** + * The immutable value of this Float. + * + * @serial the wrapped float + */ + private final float value; + + /** + * Create a Float from the primitive float + * specified. + * + * @param value the float argument + */ + public Float(float value) + { + this.value = value; + } + + /** + * Create a Float from the primitive double + * specified. + * + * @param value the double argument + */ + public Float(double value) + { + this.value = (float) value; + } + + /** + * Create a Float from the specified String. + * This method calls Float.parseFloat(). + * + * @param s the String to convert + * @throws NumberFormatException if s cannot be parsed as a + * float + * @throws NullPointerException if s is null + * @see #parseFloat(String) + */ + public Float(String s) + { + value = parseFloat(s); + } + + /** + * Convert the float to a String. + * Floating-point string representation is fairly complex: here is a + * rundown of the possible values. "[-]" indicates that a + * negative sign will be printed if the value (or exponent) is negative. + * "<number>" means a string of digits ('0' to '9'). + * "<digit>" means a single digit ('0' to '9').
    + * + * + * + * + * + * + * + * + * + * + *
    Value of FloatString Representation
    [+-] 0 [-]0.0
    Between [+-] 10-3 and 107, exclusive[-]number.number
    Other numeric value[-]<digit>.<number> + * E[-]<number>
    [+-] infinity [-]Infinity
    NaN NaN
    + * + * Yes, negative zero is a possible value. Note that there is + * always a . and at least one digit printed after + * it: even if the number is 3, it will be printed as 3.0. + * After the ".", all digits will be printed except trailing zeros. The + * result is rounded to the shortest decimal number which will parse back + * to the same float. + * + *

    To create other output formats, use {@link java.text.NumberFormat}. + * + * @XXX specify where we are not in accord with the spec. + * + * @param f the float to convert + * @return the String representing the float + */ + public static String toString(float f) + { + return VMFloat.toString(f); + } + + /** + * Convert a float value to a hexadecimal string. This converts as + * follows: + *

      + *
    • A NaN value is converted to the string "NaN". + *
    • Positive infinity is converted to the string "Infinity". + *
    • Negative infinity is converted to the string "-Infinity". + *
    • For all other values, the first character of the result is '-' + * if the value is negative. This is followed by '0x1.' if the + * value is normal, and '0x0.' if the value is denormal. This is + * then followed by a (lower-case) hexadecimal representation of the + * mantissa, with leading zeros as required for denormal values. + * The next character is a 'p', and this is followed by a decimal + * representation of the unbiased exponent. + *
    + * @param f the float value + * @return the hexadecimal string representation + * @since 1.5 + */ + public static String toHexString(float f) + { + if (isNaN(f)) + return "NaN"; + if (isInfinite(f)) + return f < 0 ? "-Infinity" : "Infinity"; + + int bits = floatToIntBits(f); + CPStringBuilder result = new CPStringBuilder(); + + if (bits < 0) + result.append('-'); + result.append("0x"); + + final int mantissaBits = 23; + final int exponentBits = 8; + int mantMask = (1 << mantissaBits) - 1; + int mantissa = bits & mantMask; + int expMask = (1 << exponentBits) - 1; + int exponent = (bits >>> mantissaBits) & expMask; + + result.append(exponent == 0 ? '0' : '1'); + result.append('.'); + // For Float only, we have to adjust the mantissa. + mantissa <<= 1; + result.append(Integer.toHexString(mantissa)); + if (exponent == 0 && mantissa != 0) + { + // Treat denormal specially by inserting '0's to make + // the length come out right. The constants here are + // to account for things like the '0x'. + int offset = 4 + ((bits < 0) ? 1 : 0); + // The silly +3 is here to keep the code the same between + // the Float and Double cases. In Float the value is + // not a multiple of 4. + int desiredLength = offset + (mantissaBits + 3) / 4; + while (result.length() < desiredLength) + result.insert(offset, '0'); + } + result.append('p'); + if (exponent == 0 && mantissa == 0) + { + // Zero, so do nothing special. + } + else + { + // Apply bias. + boolean denormal = exponent == 0; + exponent -= (1 << (exponentBits - 1)) - 1; + // Handle denormal. + if (denormal) + ++exponent; + } + + result.append(Integer.toString(exponent)); + return result.toString(); + } + + /** + * Creates a new Float object using the String. + * + * @param s the String to convert + * @return the new Float + * @throws NumberFormatException if s cannot be parsed as a + * float + * @throws NullPointerException if s is null + * @see #parseFloat(String) + */ + public static Float valueOf(String s) + { + return valueOf(parseFloat(s)); + } + + /** + * Returns a Float object wrapping the value. + * In contrast to the Float constructor, this method + * may cache some values. It is used by boxing conversion. + * + * @param val the value to wrap + * @return the Float + * @since 1.5 + */ + public static Float valueOf(float val) + { + if ((val == 0.0) && (floatToRawIntBits(val) == 0)) + return ZERO; + else if (val == 1.0) + return ONE; + else + return new Float(val); + } + + /** + * Parse the specified String as a float. The + * extended BNF grammar is as follows:
    + *
    +   * DecodableString:
    +   *      ( [ - | + ] NaN )
    +   *    | ( [ - | + ] Infinity )
    +   *    | ( [ - | + ] FloatingPoint
    +   *              [ f | F | d
    +   *                | D] )
    +   * FloatingPoint:
    +   *      ( { Digit }+ [ . { Digit } ]
    +   *              [ Exponent ] )
    +   *    | ( . { Digit }+ [ Exponent ] )
    +   * Exponent:
    +   *      ( ( e | E )
    +   *              [ - | + ] { Digit }+ )
    +   * Digit: '0' through '9'
    +   * 
    + * + *

    NaN and infinity are special cases, to allow parsing of the output + * of toString. Otherwise, the result is determined by calculating + * n * 10exponent to infinite precision, then rounding + * to the nearest float. Remember that many numbers cannot be precisely + * represented in floating point. In case of overflow, infinity is used, + * and in case of underflow, signed zero is used. Unlike Integer.parseInt, + * this does not accept Unicode digits outside the ASCII range. + * + *

    If an unexpected character is found in the String, a + * NumberFormatException will be thrown. Leading and trailing + * 'whitespace' is ignored via String.trim(), but spaces + * internal to the actual number are not allowed. + * + *

    To parse numbers according to another format, consider using + * {@link java.text.NumberFormat}. + * + * @XXX specify where/how we are not in accord with the spec. + * + * @param str the String to convert + * @return the float value of s + * @throws NumberFormatException if str cannot be parsed as a + * float + * @throws NullPointerException if str is null + * @see #MIN_VALUE + * @see #MAX_VALUE + * @see #POSITIVE_INFINITY + * @see #NEGATIVE_INFINITY + * @since 1.2 + */ + public static float parseFloat(String str) + { + return VMFloat.parseFloat(str); + } + + /** + * Return true if the float has the same + * value as NaN, otherwise return false. + * + * @param v the float to compare + * @return whether the argument is NaN + */ + public static boolean isNaN(float v) + { + // This works since NaN != NaN is the only reflexive inequality + // comparison which returns true. + return v != v; + } + + /** + * Return true if the float has a value + * equal to either NEGATIVE_INFINITY or + * POSITIVE_INFINITY, otherwise return false. + * + * @param v the float to compare + * @return whether the argument is (-/+) infinity + */ + public static boolean isInfinite(float v) + { + return v == POSITIVE_INFINITY || v == NEGATIVE_INFINITY; + } + + /** + * Return true if the value of this Float + * is the same as NaN, otherwise return false. + * + * @return whether this Float is NaN + */ + public boolean isNaN() + { + return isNaN(value); + } + + /** + * Return true if the value of this Float + * is the same as NEGATIVE_INFINITY or + * POSITIVE_INFINITY, otherwise return false. + * + * @return whether this Float is (-/+) infinity + */ + public boolean isInfinite() + { + return isInfinite(value); + } + + /** + * Convert the float value of this Float + * to a String. This method calls + * Float.toString(float) to do its dirty work. + * + * @return the String representation + * @see #toString(float) + */ + public String toString() + { + return toString(value); + } + + /** + * Return the value of this Float as a byte. + * + * @return the byte value + * @since 1.1 + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this Float as a short. + * + * @return the short value + * @since 1.1 + */ + public short shortValue() + { + return (short) value; + } + + /** + * Return the value of this Integer as an int. + * + * @return the int value + */ + public int intValue() + { + return (int) value; + } + + /** + * Return the value of this Integer as a long. + * + * @return the long value + */ + public long longValue() + { + return (long) value; + } + + /** + * Return the value of this Float. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this Float as a double + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Return a hashcode representing this Object. Float's hash + * code is calculated by calling floatToIntBits(floatValue()). + * + * @return this Object's hash code + * @see #floatToIntBits(float) + */ + public int hashCode() + { + return floatToIntBits(value); + } + + /** + * Returns true if obj is an instance of + * Float and represents the same float value. Unlike comparing + * two floats with ==, this treats two instances of + * Float.NaN as equal, but treats 0.0 and + * -0.0 as unequal. + * + *

    Note that f1.equals(f2) is identical to + * floatToIntBits(f1.floatValue()) == + * floatToIntBits(f2.floatValue()). + * + * @param obj the object to compare + * @return whether the objects are semantically equal + */ + public boolean equals(Object obj) + { + if (obj instanceof Float) + { + float f = ((Float) obj).value; + return (floatToRawIntBits(value) == floatToRawIntBits(f)) || + (isNaN(value) && isNaN(f)); + } + return false; + } + + /** + * Convert the float to the IEEE 754 floating-point "single format" bit + * layout. Bit 31 (the most significant) is the sign bit, bits 30-23 + * (masked by 0x7f800000) represent the exponent, and bits 22-0 + * (masked by 0x007fffff) are the mantissa. This function collapses all + * versions of NaN to 0x7fc00000. The result of this function can be used + * as the argument to Float.intBitsToFloat(int) to obtain the + * original float value. + * + * @param value the float to convert + * @return the bits of the float + * @see #intBitsToFloat(int) + */ + public static int floatToIntBits(float value) + { + if (isNaN(value)) + return 0x7fc00000; + else + return VMFloat.floatToRawIntBits(value); + } + + /** + * Convert the float to the IEEE 754 floating-point "single format" bit + * layout. Bit 31 (the most significant) is the sign bit, bits 30-23 + * (masked by 0x7f800000) represent the exponent, and bits 22-0 + * (masked by 0x007fffff) are the mantissa. This function leaves NaN alone, + * rather than collapsing to a canonical value. The result of this function + * can be used as the argument to Float.intBitsToFloat(int) to + * obtain the original float value. + * + * @param value the float to convert + * @return the bits of the float + * @see #intBitsToFloat(int) + */ + public static int floatToRawIntBits(float value) + { + return VMFloat.floatToRawIntBits(value); + } + + /** + * Convert the argument in IEEE 754 floating-point "single format" bit + * layout to the corresponding float. Bit 31 (the most significant) is the + * sign bit, bits 30-23 (masked by 0x7f800000) represent the exponent, and + * bits 22-0 (masked by 0x007fffff) are the mantissa. This function leaves + * NaN alone, so that you can recover the bit pattern with + * Float.floatToRawIntBits(float). + * + * @param bits the bits to convert + * @return the float represented by the bits + * @see #floatToIntBits(float) + * @see #floatToRawIntBits(float) + */ + public static float intBitsToFloat(int bits) + { + return VMFloat.intBitsToFloat(bits); + } + + /** + * Compare two Floats numerically by comparing their float + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. However, this special + * cases NaN and signed zero as follows: NaN is considered greater than + * all other floats, including POSITIVE_INFINITY, and positive + * zero is considered greater than negative zero. + * + * @param f the Float to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Float f) + { + return compare(value, f.value); + } + + /** + * Behaves like new Float(x).compareTo(new Float(y)); in + * other words this compares two floats, special casing NaN and zero, + * without the overhead of objects. + * + * @param x the first float to compare + * @param y the second float to compare + * @return the comparison + * @since 1.4 + */ + public static int compare(float x, float y) + { + // handle the easy cases: + if (x < y) + return -1; + if (x > y) + return 1; + + // handle equality respecting that 0.0 != -0.0 (hence not using x == y): + int ix = floatToRawIntBits(x); + int iy = floatToRawIntBits(y); + if (ix == iy) + return 0; + + // handle NaNs: + if (x != x) + return (y != y) ? 0 : 1; + else if (y != y) + return -1; + + // handle +/- 0.0 + return (ix < iy) ? -1 : 1; + } +} diff --git a/libjava/classpath/java/lang/IllegalAccessError.java b/libjava/classpath/java/lang/IllegalAccessError.java new file mode 100644 index 000000000..e4821606a --- /dev/null +++ b/libjava/classpath/java/lang/IllegalAccessError.java @@ -0,0 +1,76 @@ +/* IllegalAccessError.java -- thrown when linking to an inaccessible member + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An IllegalAccessError is thrown when an attempt is made to + * call a method, or access or modify a field that the application does not + * have access to. Because this error is usually caught by a compiler, + * the error only occurs at runtime when the definition of a class has + * changed in a way that is incompatible with the previously compiled + * application. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class IllegalAccessError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -8988904074992417891L; + + /** + * Create an error without a message. + */ + public IllegalAccessError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public IllegalAccessError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IllegalAccessException.java b/libjava/classpath/java/lang/IllegalAccessException.java new file mode 100644 index 000000000..a352c8b1b --- /dev/null +++ b/libjava/classpath/java/lang/IllegalAccessException.java @@ -0,0 +1,99 @@ +/* IllegalAccessException.java -- thrown on attempt to reflect on + inaccessible data + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Thrown whenever a reflective method tries to do something that the + * compiler would not allow. For example, using reflection to set a private + * variable that belongs to a class in another package is bad. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @see Class#newInstance() + * @see Field#set(Object, Object) + * @see Field#setBoolean(Object, boolean) + * @see Field#setByte(Object, byte) + * @see Field#setShort(Object, short) + * @see Field#setChar(Object, char) + * @see Field#setInt(Object, int) + * @see Field#setLong(Object, long) + * @see Field#setFloat(Object, float) + * @see Field#setDouble(Object, double) + * @see Field#get(Object) + * @see Field#getBoolean(Object) + * @see Field#getByte(Object) + * @see Field#getShort(Object) + * @see Field#getChar(Object) + * @see Field#getInt(Object) + * @see Field#getLong(Object) + * @see Field#getFloat(Object) + * @see Field#getDouble(Object) + * @see Method#invoke(Object, Object[]) + * @see Constructor#newInstance(Object[]) + * @status updated to 1.4 + */ +public class IllegalAccessException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6616958222490762034L; + + /** + * Create an exception without a message. + */ + public IllegalAccessException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalAccessException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IllegalArgumentException.java b/libjava/classpath/java/lang/IllegalArgumentException.java new file mode 100644 index 000000000..26354a58e --- /dev/null +++ b/libjava/classpath/java/lang/IllegalArgumentException.java @@ -0,0 +1,129 @@ +/* IllegalArgumentException.java -- thrown when a method is passed an + illegal or inappropriate argument + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +/** + * Thrown when a method is passed an illegal or inappropriate argument. For + * example:
    + *

    + * wait(-1);
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @status updated to 1.5 + */ +public class IllegalArgumentException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5365630128856068164L; + + /** + * Create an exception without a message. + */ + public IllegalArgumentException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalArgumentException(String s) + { + super(s); + } + + /** + *

    + * Constructs a IllegalArgumentException using + * the specified error message, which should give further details + * as to the reason for this exception. The specified cause + * Throwable may be used to provide additional history, + * with regards to the root of the problem. It is perfectly valid + * for this to be null, if the cause of the problem is unknown. + *

    + *

    + * Note: the detail message from the cause is not + * automatically incorporated into the resulting detail message of + * this exception. + *

    + * + * @param message the detail message, which should give the reason for + * this exception being thrown. + * @param cause the cause of this exception, or null if the cause + * is unknown. + * @since 1.5 + */ + public IllegalArgumentException(String message, Throwable cause) + { + super(message, cause); + } + + /** + *

    + * Constructs a IllegalArgumentException using + * the specified cause Throwable, which may be used + * to provide additional history, with regards to the root of the + * problem. It is perfectly valid for this to be null, if the + * cause of the problem is unknown. + *

    + *

    + * The detail message is automatically constructed from the detail + * message of the supplied causal exception. If the cause is null, + * then the detail message will also be null. Otherwise, the detail + * message of this exception will be that of the causal exception. + * This makes this constructor very useful for simply wrapping another + * exception. + *

    + * + * @param cause the cause of this exception, or null if the cause + * is unknown. + * @since 1.5 + */ + public IllegalArgumentException(Throwable cause) + { + super(cause); + } + +} diff --git a/libjava/classpath/java/lang/IllegalMonitorStateException.java b/libjava/classpath/java/lang/IllegalMonitorStateException.java new file mode 100644 index 000000000..13b3f952b --- /dev/null +++ b/libjava/classpath/java/lang/IllegalMonitorStateException.java @@ -0,0 +1,78 @@ +/* IllegalMonitorStateException.java -- thrown when trying to wait or + notify a monitor that is not owned + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a thread attempts to wait or notify on a monitor that it + * does not own (ie. it has not synchronized on the object). For example:
    + *
    + * void m() {
    + *   notify();
    + * }
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class IllegalMonitorStateException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3713306369498869069L; + + /** + * Create an exception without a message. + */ + public IllegalMonitorStateException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalMonitorStateException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IllegalStateException.java b/libjava/classpath/java/lang/IllegalStateException.java new file mode 100644 index 000000000..b182f092e --- /dev/null +++ b/libjava/classpath/java/lang/IllegalStateException.java @@ -0,0 +1,134 @@ +/* IllegalStateException.java -- thrown when invoking a method at + an illegal or inappropriate time + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a method is invoked at an illegal or inappropriate time. For + * example:
    + *
    + * void m(Collecion c)
    + * {
    + *   c.iterator().remove();
    + * }
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.1 + * @status updated to 1.5 + */ +public class IllegalStateException extends RuntimeException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1848914673093119416L; + + /** + * Create an exception without a message. + */ + public IllegalStateException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalStateException(String s) + { + super(s); + } + + /** + *

    + * Constructs a IllegalStateException using + * the specified error message, which should give further details + * as to the reason for this exception. The specified cause + * Throwable may be used to provide additional history, + * with regards to the root of the problem. It is perfectly valid + * for this to be null, if the cause of the problem is unknown. + *

    + *

    + * Note: the detail message from the cause is not + * automatically incorporated into the resulting detail message of + * this exception. + *

    + * + * @param message the detail message, which should give the reason for + * this exception being thrown. + * @param cause the cause of this exception, or null if the cause + * is unknown. + * @since 1.5 + */ + public IllegalStateException(String message, Throwable cause) + { + super(message, cause); + } + + /** + *

    + * Constructs a IllegalStateException using + * the specified cause Throwable, which may be used + * to provide additional history, with regards to the root of the + * problem. It is perfectly valid for this to be null, if the + * cause of the problem is unknown. + *

    + *

    + * The detail message is automatically constructed from the detail + * message of the supplied causal exception. If the cause is null, + * then the detail message will also be null. Otherwise, the detail + * message of this exception will be that of the causal exception. + * This makes this constructor very useful for simply wrapping another + * exception. + *

    + * + * @param cause the cause of this exception, or null if the cause + * is unknown. + * @since 1.5 + */ + public IllegalStateException(Throwable cause) + { + super(cause); + } + +} diff --git a/libjava/classpath/java/lang/IllegalThreadStateException.java b/libjava/classpath/java/lang/IllegalThreadStateException.java new file mode 100644 index 000000000..e14385a3e --- /dev/null +++ b/libjava/classpath/java/lang/IllegalThreadStateException.java @@ -0,0 +1,75 @@ +/* IllegalThreadStateException.java -- thrown when trying to manipulate a + Thread when it is not in an appropriate state + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown When trying to manipulate a Thread which is in an inappropriate + * state. Since the documentation suggests that this can happen with + * Thread.suspend or Thread.resume, but these + * two methods are deprecated, this exception is likely very rare. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class IllegalThreadStateException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -7626246362397460174L; + + /** + * Create an exception without a message. + */ + public IllegalThreadStateException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalThreadStateException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IncompatibleClassChangeError.java b/libjava/classpath/java/lang/IncompatibleClassChangeError.java new file mode 100644 index 000000000..637410a90 --- /dev/null +++ b/libjava/classpath/java/lang/IncompatibleClassChangeError.java @@ -0,0 +1,73 @@ +/* IncompatibleClassChangeError.java -- thrown for binary incompatible classes + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An IncompatibleClassChangeError is thrown when the definition + * of a class used by the currently executing method has changed in an + * incompatible way. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class IncompatibleClassChangeError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4914975503642802119L; + + /** + * Create an error without a message. + */ + public IncompatibleClassChangeError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public IncompatibleClassChangeError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/IndexOutOfBoundsException.java b/libjava/classpath/java/lang/IndexOutOfBoundsException.java new file mode 100644 index 000000000..0a00253ae --- /dev/null +++ b/libjava/classpath/java/lang/IndexOutOfBoundsException.java @@ -0,0 +1,75 @@ +/* IndexOutOfBoundsException.java -- thrown for an invalid index + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * This exception can be thrown to indicate an attempt to access an + * index which is out of bounds on objects like String, Array, or Vector. + * Usually any negative integer less than or equal to -1 and positive + * integer greater than or equal to the size of the object is an index + * which would be out of bounds. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class IndexOutOfBoundsException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 234122996006267687L; + + /** + * Create an exception without a message. + */ + public IndexOutOfBoundsException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IndexOutOfBoundsException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/InheritableThreadLocal.java b/libjava/classpath/java/lang/InheritableThreadLocal.java new file mode 100644 index 000000000..07d52b5d0 --- /dev/null +++ b/libjava/classpath/java/lang/InheritableThreadLocal.java @@ -0,0 +1,98 @@ +/* InheritableThreadLocal -- a ThreadLocal which inherits values across threads + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +/** + * A ThreadLocal whose value is inherited by child Threads. The value of the + * InheritableThreadLocal associated with the (parent) Thread is copied to + * the new (child) Thread at the moment of creation. + * + *

    It is possible to make the value associated with the child Thread a + * function of the value that is associated with the parent Thread by + * overriding the childValue() method. The utility of this class + * is in transferring items like User ID or Transaction ID across threads + * automatically. + * + * @author Mark Wielaard (mark@klomp.org) + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see ThreadLocal + * @since 1.2 + * @status updated to 1.4 + */ +public class InheritableThreadLocal extends ThreadLocal +{ + + /** + * Creates a new InheritableThreadLocal that has no values associated + * with it yet. + */ + public InheritableThreadLocal() + { + } + + /** + * Determines the value associated with a newly created child Thread as a + * function of the value associated with the currently executing (parent) + * Thread. The default implementation just returns the parentValue. + * + * @param parentValue the value of this object in the parent thread at + * the moment of creation of the child + * @return the initial value for the child thread + */ + protected T childValue(T parentValue) + { + return parentValue; + } + + /** + * Generates the childValues of all InheritableThreadLocals + * that are in the heritage of the current Thread for the newly created + * childThread. Should be called from the constructor Thread. + * + * @param childThread the newly created thread, to inherit from this thread + * @see Thread#Thread(ThreadGroup, Runnable, String) + */ + static void newChildThread(Thread childThread) + { + // The currentThread is the parent of the new thread. + Thread parentThread = Thread.currentThread(); + childThread.locals.inherit(parentThread.locals); + } +} diff --git a/libjava/classpath/java/lang/InstantiationError.java b/libjava/classpath/java/lang/InstantiationError.java new file mode 100644 index 000000000..dd12b513a --- /dev/null +++ b/libjava/classpath/java/lang/InstantiationError.java @@ -0,0 +1,75 @@ +/* InstantiationError.java -- thrown when the linker cannot create an instance + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An InstantiationError is thrown when an attempt is made to + * create an instance of an abstract class or an interface. Because this + * error is usually caught by a compiler, the error only occurs at runtime + * when the definition of a class has changed in a way that is incompatible + * with the previously compiled application. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class InstantiationError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4885810657349421204L; + + /** + * Create an error without a message. + */ + public InstantiationError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public InstantiationError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/InstantiationException.java b/libjava/classpath/java/lang/InstantiationException.java new file mode 100644 index 000000000..367b14bd2 --- /dev/null +++ b/libjava/classpath/java/lang/InstantiationException.java @@ -0,0 +1,74 @@ +/* InstantiationException.java -- thrown when reflection cannot create an + instance + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when an attempt is made to use reflection to build a + * non-instantiable class (an interface or abstract class). + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @see Class#newInstance() + * @status updated to 1.4 + */ +public class InstantiationException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -8441929162975509110L; + + /** + * Create an exception without a message. + */ + public InstantiationException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public InstantiationException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Integer.java b/libjava/classpath/java/lang/Integer.java new file mode 100644 index 000000000..f379795ea --- /dev/null +++ b/libjava/classpath/java/lang/Integer.java @@ -0,0 +1,841 @@ +/* Integer.java -- object wrapper for int + Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class Integer represent primitive + * int values. + * + * Additionally, this class provides various helper functions and variables + * related to ints. + * + * @author Paul Fisher + * @author John Keiser + * @author Warren Levy + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @author Ian Rogers + * @since 1.0 + * @status updated to 1.5 + */ +public final class Integer extends Number implements Comparable +{ + /** + * Compatible with JDK 1.0.2+. + */ + private static final long serialVersionUID = 1360826667806852920L; + + /** + * The minimum value an int can represent is -2147483648 (or + * -231). + */ + public static final int MIN_VALUE = 0x80000000; + + /** + * The maximum value an int can represent is 2147483647 (or + * 231 - 1). + */ + public static final int MAX_VALUE = 0x7fffffff; + + /** + * The primitive type int is represented by this + * Class object. + * @since 1.1 + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass('I'); + + /** + * The number of bits needed to represent an int. + * @since 1.5 + */ + public static final int SIZE = 32; + + // This caches some Integer values, and is used by boxing + // conversions via valueOf(). We must cache at least -128..127; + // these constants control how much we actually cache. + private static final int MIN_CACHE = -128; + private static final int MAX_CACHE = 127; + private static final Integer[] intCache = new Integer[MAX_CACHE - MIN_CACHE + 1]; + static + { + for (int i=MIN_CACHE; i <= MAX_CACHE; i++) + intCache[i - MIN_CACHE] = new Integer(i); + } + + /** + * The immutable value of this Integer. + * + * @serial the wrapped int + */ + private final int value; + + /** + * Create an Integer object representing the value of the + * int argument. + * + * @param value the value to use + */ + public Integer(int value) + { + this.value = value; + } + + /** + * Create an Integer object representing the value of the + * argument after conversion to an int. + * + * @param s the string to convert + * @throws NumberFormatException if the String does not contain an int + * @see #valueOf(String) + */ + public Integer(String s) + { + value = parseInt(s, 10, false); + } + + /** + * Return the size of a string large enough to hold the given number + * + * @param num the number we want the string length for (must be positive) + * @param radix the radix (base) that will be used for the string + * @return a size sufficient for a string of num + */ + private static int stringSize(int num, int radix) { + int exp; + if (radix < 4) + { + exp = 1; + } + else if (radix < 8) + { + exp = 2; + } + else if (radix < 16) + { + exp = 3; + } + else if (radix < 32) + { + exp = 4; + } + else + { + exp = 5; + } + int size=0; + do + { + num >>>= exp; + size++; + } + while(num != 0); + return size; + } + + /** + * Converts the int to a String using + * the specified radix (base). If the radix exceeds + * Character.MIN_RADIX or Character.MAX_RADIX, 10 + * is used instead. If the result is negative, the leading character is + * '-' ('\\u002D'). The remaining characters come from + * Character.forDigit(digit, radix) ('0'-'9','a'-'z'). + * + * @param num the int to convert to String + * @param radix the radix (base) to use in the conversion + * @return the String representation of the argument + */ + public static String toString(int num, int radix) + { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) + radix = 10; + + // Is the value negative? + boolean isNeg = num < 0; + + // Is the string a single character? + if (!isNeg && num < radix) + return new String(digits, num, 1, true); + + // Compute string size and allocate buffer + // account for a leading '-' if the value is negative + int size; + int i; + char[] buffer; + if (isNeg) + { + num = -num; + + // When the value is MIN_VALUE, it overflows when made positive + if (num < 0) + { + i = size = stringSize(MAX_VALUE, radix) + 2; + buffer = new char[size]; + buffer[--i] = digits[(int) (-(num + radix) % radix)]; + num = -(num / radix); + } + else + { + i = size = stringSize(num, radix) + 1; + buffer = new char[size]; + } + } + else + { + i = size = stringSize(num, radix); + buffer = new char[size]; + } + + do + { + buffer[--i] = digits[num % radix]; + num /= radix; + } + while (num > 0); + + if (isNeg) + buffer[--i] = '-'; + + // Package constructor avoids an array copy. + return new String(buffer, i, size - i, true); + } + + /** + * Converts the int to a String assuming it is + * unsigned in base 16. + * + * @param i the int to convert to String + * @return the String representation of the argument + */ + public static String toHexString(int i) + { + return toUnsignedString(i, 4); + } + + /** + * Converts the int to a String assuming it is + * unsigned in base 8. + * + * @param i the int to convert to String + * @return the String representation of the argument + */ + public static String toOctalString(int i) + { + return toUnsignedString(i, 3); + } + + /** + * Converts the int to a String assuming it is + * unsigned in base 2. + * + * @param i the int to convert to String + * @return the String representation of the argument + */ + public static String toBinaryString(int i) + { + return toUnsignedString(i, 1); + } + + /** + * Converts the int to a String and assumes + * a radix of 10. + * + * @param i the int to convert to String + * @return the String representation of the argument + * @see #toString(int, int) + */ + public static String toString(int i) + { + // This is tricky: in libgcj, String.valueOf(int) is a fast native + // implementation. In Classpath it just calls back to + // Integer.toString(int, int). + return String.valueOf(i); + } + + /** + * Converts the specified String into an int + * using the specified radix (base). The string must not be null + * or empty. It may begin with an optional '-', which will negate the answer, + * provided that there are also valid digits. Each digit is parsed as if by + * Character.digit(d, radix), and must be in the range + * 0 to radix - 1. Finally, the result must be + * within MIN_VALUE to MAX_VALUE, inclusive. + * Unlike Double.parseDouble, you may not have a leading '+'. + * + * @param str the String to convert + * @param radix the radix (base) to use in the conversion + * @return the String argument converted to int + * @throws NumberFormatException if s cannot be parsed as an + * int + */ + public static int parseInt(String str, int radix) + { + return parseInt(str, radix, false); + } + + /** + * Converts the specified String into an int. + * This function assumes a radix of 10. + * + * @param s the String to convert + * @return the int value of s + * @throws NumberFormatException if s cannot be parsed as an + * int + * @see #parseInt(String, int) + */ + public static int parseInt(String s) + { + return parseInt(s, 10, false); + } + + /** + * Creates a new Integer object using the String + * and specified radix (base). + * + * @param s the String to convert + * @param radix the radix (base) to convert with + * @return the new Integer + * @throws NumberFormatException if s cannot be parsed as an + * int + * @see #parseInt(String, int) + */ + public static Integer valueOf(String s, int radix) + { + return valueOf(parseInt(s, radix, false)); + } + + /** + * Creates a new Integer object using the String, + * assuming a radix of 10. + * + * @param s the String to convert + * @return the new Integer + * @throws NumberFormatException if s cannot be parsed as an + * int + * @see #Integer(String) + * @see #parseInt(String) + */ + public static Integer valueOf(String s) + { + return valueOf(parseInt(s, 10, false)); + } + + /** + * Returns an Integer object wrapping the value. + * In contrast to the Integer constructor, this method + * will cache some values. It is used by boxing conversion. + * + * @param val the value to wrap + * @return the Integer + */ + public static Integer valueOf(int val) + { + if (val < MIN_CACHE || val > MAX_CACHE) + return new Integer(val); + else + return intCache[val - MIN_CACHE]; + } + + /** + * Return the value of this Integer as a byte. + * + * @return the byte value + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this Integer as a short. + * + * @return the short value + */ + public short shortValue() + { + return (short) value; + } + + /** + * Return the value of this Integer. + * @return the int value + */ + public int intValue() + { + return value; + } + + /** + * Return the value of this Integer as a long. + * + * @return the long value + */ + public long longValue() + { + return value; + } + + /** + * Return the value of this Integer as a float. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this Integer as a double. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Converts the Integer value to a String and + * assumes a radix of 10. + * + * @return the String representation + */ + public String toString() + { + return String.valueOf(value); + } + + /** + * Return a hashcode representing this Object. Integer's hash + * code is simply its value. + * + * @return this Object's hash code + */ + public int hashCode() + { + return value; + } + + /** + * Returns true if obj is an instance of + * Integer and represents the same int value. + * + * @param obj the object to compare + * @return whether these Objects are semantically equal + */ + public boolean equals(Object obj) + { + return obj instanceof Integer && value == ((Integer) obj).value; + } + + /** + * Get the specified system property as an Integer. The + * decode() method will be used to interpret the value of + * the property. + * + * @param nm the name of the system property + * @return the system property as an Integer, or null if the + * property is not found or cannot be decoded + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Integer getInteger(String nm) + { + return getInteger(nm, null); + } + + /** + * Get the specified system property as an Integer, or use a + * default int value if the property is not found or is not + * decodable. The decode() method will be used to interpret + * the value of the property. + * + * @param nm the name of the system property + * @param val the default value + * @return the value of the system property, or the default + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Integer getInteger(String nm, int val) + { + Integer result = getInteger(nm, null); + return result == null ? valueOf(val) : result; + } + + /** + * Get the specified system property as an Integer, or use a + * default Integer value if the property is not found or is + * not decodable. The decode() method will be used to + * interpret the value of the property. + * + * @param nm the name of the system property + * @param def the default value + * @return the value of the system property, or the default + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Integer getInteger(String nm, Integer def) + { + if (nm == null || "".equals(nm)) + return def; + nm = System.getProperty(nm); + if (nm == null) + return def; + try + { + return decode(nm); + } + catch (NumberFormatException e) + { + return def; + } + } + + /** + * Convert the specified String into an Integer. + * The String may represent decimal, hexadecimal, or + * octal numbers. + * + *

    The extended BNF grammar is as follows:
    + *

    +   * DecodableString:
    +   *      ( [ - ] DecimalNumber )
    +   *    | ( [ - ] ( 0x | 0X
    +   *              | # ) HexDigit { HexDigit } )
    +   *    | ( [ - ] 0 { OctalDigit } )
    +   * DecimalNumber:
    +   *        DecimalDigit except '0' { DecimalDigit }
    +   * DecimalDigit:
    +   *        Character.digit(d, 10) has value 0 to 9
    +   * OctalDigit:
    +   *        Character.digit(d, 8) has value 0 to 7
    +   * DecimalDigit:
    +   *        Character.digit(d, 16) has value 0 to 15
    +   * 
    + * Finally, the value must be in the range MIN_VALUE to + * MAX_VALUE, or an exception is thrown. + * + * @param str the String to interpret + * @return the value of the String as an Integer + * @throws NumberFormatException if s cannot be parsed as a + * int + * @throws NullPointerException if s is null + * @since 1.2 + */ + public static Integer decode(String str) + { + return valueOf(parseInt(str, 10, true)); + } + + /** + * Compare two Integers numerically by comparing their int + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. + * + * @param i the Integer to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Integer i) + { + if (value == i.value) + return 0; + // Returns just -1 or 1 on inequality; doing math might overflow. + return value > i.value ? 1 : -1; + } + + /** + * Return the number of bits set in x. + * @param x value to examine + * @since 1.5 + */ + public static int bitCount(int x) + { + // Successively collapse alternating bit groups into a sum. + x = ((x >> 1) & 0x55555555) + (x & 0x55555555); + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + x = ((x >> 4) & 0x0f0f0f0f) + (x & 0x0f0f0f0f); + x = ((x >> 8) & 0x00ff00ff) + (x & 0x00ff00ff); + return ((x >> 16) & 0x0000ffff) + (x & 0x0000ffff); + } + + /** + * Rotate x to the left by distance bits. + * @param x the value to rotate + * @param distance the number of bits by which to rotate + * @since 1.5 + */ + public static int rotateLeft(int x, int distance) + { + // This trick works because the shift operators implicitly mask + // the shift count. + return (x << distance) | (x >>> - distance); + } + + /** + * Rotate x to the right by distance bits. + * @param x the value to rotate + * @param distance the number of bits by which to rotate + * @since 1.5 + */ + public static int rotateRight(int x, int distance) + { + // This trick works because the shift operators implicitly mask + // the shift count. + return (x << - distance) | (x >>> distance); + } + + /** + * Find the highest set bit in value, and return a new value + * with only that bit set. + * @param value the value to examine + * @since 1.5 + */ + public static int highestOneBit(int value) + { + value |= value >>> 1; + value |= value >>> 2; + value |= value >>> 4; + value |= value >>> 8; + value |= value >>> 16; + return value ^ (value >>> 1); + } + + /** + * Return the number of leading zeros in value. + * @param value the value to examine + * @since 1.5 + */ + public static int numberOfLeadingZeros(int value) + { + value |= value >>> 1; + value |= value >>> 2; + value |= value >>> 4; + value |= value >>> 8; + value |= value >>> 16; + return bitCount(~value); + } + + /** + * Find the lowest set bit in value, and return a new value + * with only that bit set. + * @param value the value to examine + * @since 1.5 + */ + public static int lowestOneBit(int value) + { + // Classic assembly trick. + return value & - value; + } + + /** + * Find the number of trailing zeros in value. + * @param value the value to examine + * @since 1.5 + */ + public static int numberOfTrailingZeros(int value) + { + return bitCount((value & -value) - 1); + } + + /** + * Return 1 if x is positive, -1 if it is negative, and 0 if it is + * zero. + * @param x the value to examine + * @since 1.5 + */ + public static int signum(int x) + { + return (x >> 31) | (-x >>> 31); + + // The LHS propagates the sign bit through every bit in the word; + // if X < 0, every bit is set to 1, else 0. if X > 0, the RHS + // negates x and shifts the resulting 1 in the sign bit to the + // LSB, leaving every other bit 0. + + // Hacker's Delight, Section 2-7 + } + + /** + * Reverse the bytes in val. + * @since 1.5 + */ + public static int reverseBytes(int val) + { + return ( ((val >> 24) & 0xff) + | ((val >> 8) & 0xff00) + | ((val << 8) & 0xff0000) + | ((val << 24) & 0xff000000)); + } + + /** + * Reverse the bits in val. + * @since 1.5 + */ + public static int reverse(int val) + { + // Successively swap alternating bit groups. + val = ((val >> 1) & 0x55555555) + ((val << 1) & ~0x55555555); + val = ((val >> 2) & 0x33333333) + ((val << 2) & ~0x33333333); + val = ((val >> 4) & 0x0f0f0f0f) + ((val << 4) & ~0x0f0f0f0f); + val = ((val >> 8) & 0x00ff00ff) + ((val << 8) & ~0x00ff00ff); + return ((val >> 16) & 0x0000ffff) + ((val << 16) & ~0x0000ffff); + } + + /** + * Helper for converting unsigned numbers to String. + * + * @param num the number + * @param exp log2(digit) (ie. 1, 3, or 4 for binary, oct, hex) + */ + // Package visible for use by Long. + static String toUnsignedString(int num, int exp) + { + // Compute string length + int size = 1; + int copy = num >>> exp; + while (copy != 0) + { + size++; + copy >>>= exp; + } + // Quick path for single character strings + if (size == 1) + return new String(digits, num, 1, true); + + // Encode into buffer + int mask = (1 << exp) - 1; + char[] buffer = new char[size]; + int i = size; + do + { + buffer[--i] = digits[num & mask]; + num >>>= exp; + } + while (num != 0); + + // Package constructor avoids an array copy. + return new String(buffer, i, size - i, true); + } + + /** + * Helper for parsing ints, used by Integer, Short, and Byte. + * + * @param str the string to parse + * @param radix the radix to use, must be 10 if decode is true + * @param decode if called from decode + * @return the parsed int value + * @throws NumberFormatException if there is an error + * @throws NullPointerException if decode is true and str if null + * @see #parseInt(String, int) + * @see #decode(String) + * @see Byte#parseByte(String, int) + * @see Short#parseShort(String, int) + */ + static int parseInt(String str, int radix, boolean decode) + { + if (! decode && str == null) + throw new NumberFormatException(); + int index = 0; + int len = str.length(); + boolean isNeg = false; + if (len == 0) + throw new NumberFormatException("string length is null"); + int ch = str.charAt(index); + if (ch == '-') + { + if (len == 1) + throw new NumberFormatException("pure '-'"); + isNeg = true; + ch = str.charAt(++index); + } + else if (ch == '+') + { + if (len == 1) + throw new NumberFormatException("pure '+'"); + ch = str.charAt(++index); + } + if (decode) + { + if (ch == '0') + { + if (++index == len) + return 0; + if ((str.charAt(index) & ~('x' ^ 'X')) == 'X') + { + radix = 16; + index++; + } + else + radix = 8; + } + else if (ch == '#') + { + radix = 16; + index++; + } + } + if (index == len) + throw new NumberFormatException("non terminated number: " + str); + + int max = MAX_VALUE / radix; + // We can't directly write `max = (MAX_VALUE + 1) / radix'. + // So instead we fake it. + if (isNeg && MAX_VALUE % radix == radix - 1) + ++max; + + int val = 0; + while (index < len) + { + if (val < 0 || val > max) + throw new NumberFormatException("number overflow (pos=" + index + ") : " + str); + + ch = Character.digit(str.charAt(index++), radix); + val = val * radix + ch; + if (ch < 0 || (val < 0 && (! isNeg || val != MIN_VALUE))) + throw new NumberFormatException("invalid character at position " + index + " in " + str); + } + return isNeg ? -val : val; + } +} diff --git a/libjava/classpath/java/lang/InternalError.java b/libjava/classpath/java/lang/InternalError.java new file mode 100644 index 000000000..3a95bbeaf --- /dev/null +++ b/libjava/classpath/java/lang/InternalError.java @@ -0,0 +1,72 @@ +/* InternalError.java -- thrown when the VM encounters an internal error + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An InternalError is thrown when a mystical error has + * occurred in the Java Virtual Machine. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class InternalError extends VirtualMachineError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -9062593416125562365L; + + /** + * Create an error without a message. + */ + public InternalError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public InternalError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/InterruptedException.java b/libjava/classpath/java/lang/InterruptedException.java new file mode 100644 index 000000000..da2173c8b --- /dev/null +++ b/libjava/classpath/java/lang/InterruptedException.java @@ -0,0 +1,80 @@ +/* InterruptedException.java -- thrown when a thread is interrupted + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when a thread interrupts another thread which was previously + * sleeping, waiting, or paused in some other way. See the + * interrupt method of class Thread. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @see Object#wait() + * @see Object#wait(long) + * @see Object#wait(long, int) + * @see Thread#sleep(long) + * @see Thread#interrupt() + * @see Thread#interrupted() + * @status updated to 1.4 + */ +public class InterruptedException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6700697376100628473L; + + /** + * Create an exception without a message. + */ + public InterruptedException() + { + } + + /** + * Create an exception with a message. + * + * + * @param s the message + */ + public InterruptedException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Iterable.java b/libjava/classpath/java/lang/Iterable.java new file mode 100644 index 000000000..ea593e88e --- /dev/null +++ b/libjava/classpath/java/lang/Iterable.java @@ -0,0 +1,60 @@ +/* Iterable.java -- Notes collection over which one may iterate + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +// We only need Iterator, but we import * to support lib/mkcollections.pl +import java.util.*; + +/** + * This interface is used to indicate that a given class can be + * iterated over. The compiler uses this interface to determine which + * classes are suitable targets of the foreach construct. + * + * @author Tom Tromey + * @since 1.5 + */ +public interface Iterable +{ + /** + * Returns an iterator for the collection. + * + * @return an iterator. + */ + Iterator iterator (); +} diff --git a/libjava/classpath/java/lang/LinkageError.java b/libjava/classpath/java/lang/LinkageError.java new file mode 100644 index 000000000..028702081 --- /dev/null +++ b/libjava/classpath/java/lang/LinkageError.java @@ -0,0 +1,74 @@ +/* LinkageError.java -- thrown when classes valid at separate compile times + cannot be linked to each other + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Subclasses of LinkageError are thrown to indicate that two + * classes which were compatible at separate compilation times cannot be + * linked to one another. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class LinkageError extends Error +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3579600108157160122L; + + /** + * Create an error without a message. + */ + public LinkageError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public LinkageError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Long.java b/libjava/classpath/java/lang/Long.java new file mode 100644 index 000000000..e7579d865 --- /dev/null +++ b/libjava/classpath/java/lang/Long.java @@ -0,0 +1,830 @@ +/* Long.java -- object wrapper for long + Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class Long represent primitive + * long values. + * + * Additionally, this class provides various helper functions and variables + * related to longs. + * + * @author Paul Fisher + * @author John Keiser + * @author Warren Levy + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @author Ian Rogers + * @since 1.0 + * @status updated to 1.5 + */ +public final class Long extends Number implements Comparable +{ + /** + * Compatible with JDK 1.0.2+. + */ + private static final long serialVersionUID = 4290774380558885855L; + + /** + * The minimum value a long can represent is + * -9223372036854775808L (or -263). + */ + public static final long MIN_VALUE = 0x8000000000000000L; + + /** + * The maximum value a long can represent is + * 9223372036854775807 (or 263 - 1). + */ + public static final long MAX_VALUE = 0x7fffffffffffffffL; + + /** + * The primitive type long is represented by this + * Class object. + * @since 1.1 + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass ('J'); + + /** + * The number of bits needed to represent a long. + * @since 1.5 + */ + public static final int SIZE = 64; + + // This caches some Long values, and is used by boxing + // conversions via valueOf(). We cache at least -128..127; + // these constants control how much we actually cache. + private static final int MIN_CACHE = -128; + private static final int MAX_CACHE = 127; + private static final Long[] longCache = new Long[MAX_CACHE - MIN_CACHE + 1]; + static + { + for (int i=MIN_CACHE; i <= MAX_CACHE; i++) + longCache[i - MIN_CACHE] = new Long(i); + } + + /** + * The immutable value of this Long. + * + * @serial the wrapped long + */ + private final long value; + + /** + * Create a Long object representing the value of the + * long argument. + * + * @param value the value to use + */ + public Long(long value) + { + this.value = value; + } + + /** + * Create a Long object representing the value of the + * argument after conversion to a long. + * + * @param s the string to convert + * @throws NumberFormatException if the String does not contain a long + * @see #valueOf(String) + */ + public Long(String s) + { + value = parseLong(s, 10, false); + } + + /** + * Return the size of a string large enough to hold the given number + * + * @param num the number we want the string length for (must be positive) + * @param radix the radix (base) that will be used for the string + * @return a size sufficient for a string of num + */ + private static int stringSize(long num, int radix) { + int exp; + if (radix < 4) + { + exp = 1; + } + else if (radix < 8) + { + exp = 2; + } + else if (radix < 16) + { + exp = 3; + } + else if (radix < 32) + { + exp = 4; + } + else + { + exp = 5; + } + int size=0; + do + { + num >>>= exp; + size++; + } + while(num != 0); + return size; + } + + /** + * Converts the long to a String using + * the specified radix (base). If the radix exceeds + * Character.MIN_RADIX or Character.MAX_RADIX, 10 + * is used instead. If the result is negative, the leading character is + * '-' ('\\u002D'). The remaining characters come from + * Character.forDigit(digit, radix) ('0'-'9','a'-'z'). + * + * @param num the long to convert to String + * @param radix the radix (base) to use in the conversion + * @return the String representation of the argument + */ + public static String toString(long num, int radix) + { + if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) + radix = 10; + + // Is the value negative? + boolean isNeg = num < 0; + + // Is the string a single character? + if (!isNeg && num < radix) + return new String(digits, (int)num, 1, true); + + // Compute string size and allocate buffer + // account for a leading '-' if the value is negative + int size; + int i; + char[] buffer; + if (isNeg) + { + num = -num; + + // When the value is MIN_VALUE, it overflows when made positive + if (num < 0) + { + i = size = stringSize(MAX_VALUE, radix) + 2; + buffer = new char[size]; + buffer[--i] = digits[(int) (-(num + radix) % radix)]; + num = -(num / radix); + } + else + { + i = size = stringSize(num, radix) + 1; + buffer = new char[size]; + } + } + else + { + i = size = stringSize(num, radix); + buffer = new char[size]; + } + + do + { + buffer[--i] = digits[(int) (num % radix)]; + num /= radix; + } + while (num > 0); + + if (isNeg) + buffer[--i] = '-'; + + // Package constructor avoids an array copy. + return new String(buffer, i, size - i, true); + } + + /** + * Converts the long to a String assuming it is + * unsigned in base 16. + * + * @param l the long to convert to String + * @return the String representation of the argument + */ + public static String toHexString(long l) + { + return toUnsignedString(l, 4); + } + + /** + * Converts the long to a String assuming it is + * unsigned in base 8. + * + * @param l the long to convert to String + * @return the String representation of the argument + */ + public static String toOctalString(long l) + { + return toUnsignedString(l, 3); + } + + /** + * Converts the long to a String assuming it is + * unsigned in base 2. + * + * @param l the long to convert to String + * @return the String representation of the argument + */ + public static String toBinaryString(long l) + { + return toUnsignedString(l, 1); + } + + /** + * Converts the long to a String and assumes + * a radix of 10. + * + * @param num the long to convert to String + * @return the String representation of the argument + * @see #toString(long, int) + */ + public static String toString(long num) + { + return toString(num, 10); + } + + /** + * Converts the specified String into an int + * using the specified radix (base). The string must not be null + * or empty. It may begin with an optional '-', which will negate the answer, + * provided that there are also valid digits. Each digit is parsed as if by + * Character.digit(d, radix), and must be in the range + * 0 to radix - 1. Finally, the result must be + * within MIN_VALUE to MAX_VALUE, inclusive. + * Unlike Double.parseDouble, you may not have a leading '+'; and 'l' or + * 'L' as the last character is only valid in radices 22 or greater, where + * it is a digit and not a type indicator. + * + * @param str the String to convert + * @param radix the radix (base) to use in the conversion + * @return the String argument converted to long + * @throws NumberFormatException if s cannot be parsed as a + * long + */ + public static long parseLong(String str, int radix) + { + return parseLong(str, radix, false); + } + + /** + * Converts the specified String into a long. + * This function assumes a radix of 10. + * + * @param s the String to convert + * @return the int value of s + * @throws NumberFormatException if s cannot be parsed as a + * long + * @see #parseLong(String, int) + */ + public static long parseLong(String s) + { + return parseLong(s, 10, false); + } + + /** + * Creates a new Long object using the String + * and specified radix (base). + * + * @param s the String to convert + * @param radix the radix (base) to convert with + * @return the new Long + * @throws NumberFormatException if s cannot be parsed as a + * long + * @see #parseLong(String, int) + */ + public static Long valueOf(String s, int radix) + { + return valueOf(parseLong(s, radix, false)); + } + + /** + * Creates a new Long object using the String, + * assuming a radix of 10. + * + * @param s the String to convert + * @return the new Long + * @throws NumberFormatException if s cannot be parsed as a + * long + * @see #Long(String) + * @see #parseLong(String) + */ + public static Long valueOf(String s) + { + return valueOf(parseLong(s, 10, false)); + } + + /** + * Returns a Long object wrapping the value. + * + * @param val the value to wrap + * @return the Long + * @since 1.5 + */ + public static Long valueOf(long val) + { + if (val < MIN_CACHE || val > MAX_CACHE) + return new Long(val); + else + return longCache[((int)val) - MIN_CACHE]; + } + + /** + * Convert the specified String into a Long. + * The String may represent decimal, hexadecimal, or + * octal numbers. + * + *

    The extended BNF grammar is as follows:
    + *

    +   * DecodableString:
    +   *      ( [ - ] DecimalNumber )
    +   *    | ( [ - ] ( 0x | 0X
    +   *              | # ) HexDigit { HexDigit } )
    +   *    | ( [ - ] 0 { OctalDigit } )
    +   * DecimalNumber:
    +   *        DecimalDigit except '0' { DecimalDigit }
    +   * DecimalDigit:
    +   *        Character.digit(d, 10) has value 0 to 9
    +   * OctalDigit:
    +   *        Character.digit(d, 8) has value 0 to 7
    +   * DecimalDigit:
    +   *        Character.digit(d, 16) has value 0 to 15
    +   * 
    + * Finally, the value must be in the range MIN_VALUE to + * MAX_VALUE, or an exception is thrown. Note that you cannot + * use a trailing 'l' or 'L', unlike in Java source code. + * + * @param str the String to interpret + * @return the value of the String as a Long + * @throws NumberFormatException if s cannot be parsed as a + * long + * @throws NullPointerException if s is null + * @since 1.2 + */ + public static Long decode(String str) + { + return valueOf(parseLong(str, 10, true)); + } + + /** + * Return the value of this Long as a byte. + * + * @return the byte value + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this Long as a short. + * + * @return the short value + */ + public short shortValue() + { + return (short) value; + } + + /** + * Return the value of this Long as an int. + * + * @return the int value + */ + public int intValue() + { + return (int) value; + } + + /** + * Return the value of this Long. + * + * @return the long value + */ + public long longValue() + { + return value; + } + + /** + * Return the value of this Long as a float. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this Long as a double. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Converts the Long value to a String and + * assumes a radix of 10. + * + * @return the String representation + */ + public String toString() + { + return toString(value, 10); + } + + /** + * Return a hashcode representing this Object. Long's hash + * code is calculated by (int) (value ^ (value >> 32)). + * + * @return this Object's hash code + */ + public int hashCode() + { + return (int) (value ^ (value >>> 32)); + } + + /** + * Returns true if obj is an instance of + * Long and represents the same long value. + * + * @param obj the object to compare + * @return whether these Objects are semantically equal + */ + public boolean equals(Object obj) + { + return obj instanceof Long && value == ((Long) obj).value; + } + + /** + * Get the specified system property as a Long. The + * decode() method will be used to interpret the value of + * the property. + * + * @param nm the name of the system property + * @return the system property as a Long, or null if the + * property is not found or cannot be decoded + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Long getLong(String nm) + { + return getLong(nm, null); + } + + /** + * Get the specified system property as a Long, or use a + * default long value if the property is not found or is not + * decodable. The decode() method will be used to interpret + * the value of the property. + * + * @param nm the name of the system property + * @param val the default value + * @return the value of the system property, or the default + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Long getLong(String nm, long val) + { + Long result = getLong(nm, null); + return result == null ? valueOf(val) : result; + } + + /** + * Get the specified system property as a Long, or use a + * default Long value if the property is not found or is + * not decodable. The decode() method will be used to + * interpret the value of the property. + * + * @param nm the name of the system property + * @param def the default value + * @return the value of the system property, or the default + * @throws SecurityException if accessing the system property is forbidden + * @see System#getProperty(String) + * @see #decode(String) + */ + public static Long getLong(String nm, Long def) + { + if (nm == null || "".equals(nm)) + return def; + nm = System.getProperty(nm); + if (nm == null) + return def; + try + { + return decode(nm); + } + catch (NumberFormatException e) + { + return def; + } + } + + /** + * Compare two Longs numerically by comparing their long + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. + * + * @param l the Long to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Long l) + { + if (value == l.value) + return 0; + // Returns just -1 or 1 on inequality; doing math might overflow the long. + return value > l.value ? 1 : -1; + } + + /** + * Return the number of bits set in x. + * @param x value to examine + * @since 1.5 + */ + public static int bitCount(long x) + { + // Successively collapse alternating bit groups into a sum. + x = ((x >> 1) & 0x5555555555555555L) + (x & 0x5555555555555555L); + x = ((x >> 2) & 0x3333333333333333L) + (x & 0x3333333333333333L); + int v = (int) ((x >>> 32) + x); + v = ((v >> 4) & 0x0f0f0f0f) + (v & 0x0f0f0f0f); + v = ((v >> 8) & 0x00ff00ff) + (v & 0x00ff00ff); + return ((v >> 16) & 0x0000ffff) + (v & 0x0000ffff); + } + + /** + * Rotate x to the left by distance bits. + * @param x the value to rotate + * @param distance the number of bits by which to rotate + * @since 1.5 + */ + public static long rotateLeft(long x, int distance) + { + // This trick works because the shift operators implicitly mask + // the shift count. + return (x << distance) | (x >>> - distance); + } + + /** + * Rotate x to the right by distance bits. + * @param x the value to rotate + * @param distance the number of bits by which to rotate + * @since 1.5 + */ + public static long rotateRight(long x, int distance) + { + // This trick works because the shift operators implicitly mask + // the shift count. + return (x << - distance) | (x >>> distance); + } + + /** + * Find the highest set bit in value, and return a new value + * with only that bit set. + * @param value the value to examine + * @since 1.5 + */ + public static long highestOneBit(long value) + { + value |= value >>> 1; + value |= value >>> 2; + value |= value >>> 4; + value |= value >>> 8; + value |= value >>> 16; + value |= value >>> 32; + return value ^ (value >>> 1); + } + + /** + * Return the number of leading zeros in value. + * @param value the value to examine + * @since 1.5 + */ + public static int numberOfLeadingZeros(long value) + { + value |= value >>> 1; + value |= value >>> 2; + value |= value >>> 4; + value |= value >>> 8; + value |= value >>> 16; + value |= value >>> 32; + return bitCount(~value); + } + + /** + * Find the lowest set bit in value, and return a new value + * with only that bit set. + * @param value the value to examine + * @since 1.5 + */ + public static long lowestOneBit(long value) + { + // Classic assembly trick. + return value & - value; + } + + /** + * Find the number of trailing zeros in value. + * @param value the value to examine + * @since 1.5 + */ + public static int numberOfTrailingZeros(long value) + { + return bitCount((value & -value) - 1); + } + + /** + * Return 1 if x is positive, -1 if it is negative, and 0 if it is + * zero. + * @param x the value to examine + * @since 1.5 + */ + public static int signum(long x) + { + return (int) ((x >> 63) | (-x >>> 63)); + + // The LHS propagates the sign bit through every bit in the word; + // if X < 0, every bit is set to 1, else 0. if X > 0, the RHS + // negates x and shifts the resulting 1 in the sign bit to the + // LSB, leaving every other bit 0. + + // Hacker's Delight, Section 2-7 + } + + /** + * Reverse the bytes in val. + * @since 1.5 + */ + public static long reverseBytes(long val) + { + int hi = Integer.reverseBytes((int) val); + int lo = Integer.reverseBytes((int) (val >>> 32)); + return (((long) hi) << 32) | lo; + } + + /** + * Reverse the bits in val. + * @since 1.5 + */ + public static long reverse(long val) + { + long hi = Integer.reverse((int) val) & 0xffffffffL; + long lo = Integer.reverse((int) (val >>> 32)) & 0xffffffffL; + return (hi << 32) | lo; + } + + /** + * Helper for converting unsigned numbers to String. + * + * @param num the number + * @param exp log2(digit) (ie. 1, 3, or 4 for binary, oct, hex) + */ + private static String toUnsignedString(long num, int exp) + { + // Compute string length + int size = 1; + long copy = num >>> exp; + while (copy != 0) + { + size++; + copy >>>= exp; + } + // Quick path for single character strings + if (size == 1) + return new String(digits, (int)num, 1, true); + + // Encode into buffer + int mask = (1 << exp) - 1; + char[] buffer = new char[size]; + int i = size; + do + { + buffer[--i] = digits[(int) num & mask]; + num >>>= exp; + } + while (num != 0); + + // Package constructor avoids an array copy. + return new String(buffer, i, size - i, true); + } + + /** + * Helper for parsing longs. + * + * @param str the string to parse + * @param radix the radix to use, must be 10 if decode is true + * @param decode if called from decode + * @return the parsed long value + * @throws NumberFormatException if there is an error + * @throws NullPointerException if decode is true and str is null + * @see #parseLong(String, int) + * @see #decode(String) + */ + private static long parseLong(String str, int radix, boolean decode) + { + if (! decode && str == null) + throw new NumberFormatException(); + int index = 0; + int len = str.length(); + boolean isNeg = false; + if (len == 0) + throw new NumberFormatException(); + int ch = str.charAt(index); + if (ch == '-') + { + if (len == 1) + throw new NumberFormatException(); + isNeg = true; + ch = str.charAt(++index); + } + if (decode) + { + if (ch == '0') + { + if (++index == len) + return 0; + if ((str.charAt(index) & ~('x' ^ 'X')) == 'X') + { + radix = 16; + index++; + } + else + radix = 8; + } + else if (ch == '#') + { + radix = 16; + index++; + } + } + if (index == len) + throw new NumberFormatException(); + + long max = MAX_VALUE / radix; + // We can't directly write `max = (MAX_VALUE + 1) / radix'. + // So instead we fake it. + if (isNeg && MAX_VALUE % radix == radix - 1) + ++max; + + long val = 0; + while (index < len) + { + if (val < 0 || val > max) + throw new NumberFormatException(); + + ch = Character.digit(str.charAt(index++), radix); + val = val * radix + ch; + if (ch < 0 || (val < 0 && (! isNeg || val != MIN_VALUE))) + throw new NumberFormatException(); + } + return isNeg ? -val : val; + } +} diff --git a/libjava/classpath/java/lang/Math.java b/libjava/classpath/java/lang/Math.java new file mode 100644 index 000000000..6cf29b4a0 --- /dev/null +++ b/libjava/classpath/java/lang/Math.java @@ -0,0 +1,1052 @@ +/* java.lang.Math -- common mathematical functions, native allowed (VMMath) + Copyright (C) 1998, 2001, 2002, 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.Configuration; + +import java.util.Random; + +/** + * Helper class containing useful mathematical functions and constants. + *

    + * + * Note that angles are specified in radians. Conversion functions are + * provided for your convenience. + * + * @author Paul Fisher + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.0 + */ +public final class Math +{ + + // FIXME - This is here because we need to load the "javalang" system + // library somewhere late in the bootstrap cycle. We cannot do this + // from VMSystem or VMRuntime since those are used to actually load + // the library. This is mainly here because historically Math was + // late enough in the bootstrap cycle to start using System after it + // was initialized (called from the java.util classes). + static + { + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary("javalang"); + } + } + + /** + * Math is non-instantiable + */ + private Math() + { + } + + /** + * A random number generator, initialized on first use. + */ + private static Random rand; + + /** + * The most accurate approximation to the mathematical constant e: + * 2.718281828459045. Used in natural log and exp. + * + * @see #log(double) + * @see #exp(double) + */ + public static final double E = 2.718281828459045; + + /** + * The most accurate approximation to the mathematical constant pi: + * 3.141592653589793. This is the ratio of a circle's diameter + * to its circumference. + */ + public static final double PI = 3.141592653589793; + + /** + * Take the absolute value of the argument. + * (Absolute value means make it positive.) + *

    + * + * Note that the the largest negative value (Integer.MIN_VALUE) cannot + * be made positive. In this case, because of the rules of negation in + * a computer, MIN_VALUE is what will be returned. + * This is a negative value. You have been warned. + * + * @param i the number to take the absolute value of + * @return the absolute value + * @see Integer#MIN_VALUE + */ + public static int abs(int i) + { + return (i < 0) ? -i : i; + } + + /** + * Take the absolute value of the argument. + * (Absolute value means make it positive.) + *

    + * + * Note that the the largest negative value (Long.MIN_VALUE) cannot + * be made positive. In this case, because of the rules of negation in + * a computer, MIN_VALUE is what will be returned. + * This is a negative value. You have been warned. + * + * @param l the number to take the absolute value of + * @return the absolute value + * @see Long#MIN_VALUE + */ + public static long abs(long l) + { + return (l < 0) ? -l : l; + } + + /** + * Take the absolute value of the argument. + * (Absolute value means make it positive.) + *

    + * + * This is equivalent, but faster than, calling + * Float.intBitsToFloat(0x7fffffff & Float.floatToIntBits(a)). + * + * @param f the number to take the absolute value of + * @return the absolute value + */ + public static float abs(float f) + { + return (f <= 0) ? 0 - f : f; + } + + /** + * Take the absolute value of the argument. + * (Absolute value means make it positive.) + * + * This is equivalent, but faster than, calling + * Double.longBitsToDouble(Double.doubleToLongBits(a) + * << 1) >>> 1);. + * + * @param d the number to take the absolute value of + * @return the absolute value + */ + public static double abs(double d) + { + return (d <= 0) ? 0 - d : d; + } + + /** + * Return whichever argument is smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static int min(int a, int b) + { + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static long min(long a, long b) + { + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, -0 is always smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static float min(float a, float b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; < will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return -(-a - b); + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, -0 is always smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static double min(double a, double b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; < will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return -(-a - b); + return (a < b) ? a : b; + } + + /** + * Return whichever argument is larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static int max(int a, int b) + { + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static long max(long a, long b) + { + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, 0 is always larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static float max(float a, float b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; > will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return a - -b; + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, 0 is always larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static double max(double a, double b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; > will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return a - -b; + return (a > b) ? a : b; + } + + /** + * The trigonometric function sin. The sine of NaN or infinity is + * NaN, and the sine of 0 retains its sign. This is accurate within 1 ulp, + * and is semi-monotonic. + * + * @param a the angle (in radians) + * @return sin(a) + */ + public static double sin(double a) + { + return VMMath.sin(a); + } + + /** + * The trigonometric function cos. The cosine of NaN or infinity is + * NaN. This is accurate within 1 ulp, and is semi-monotonic. + * + * @param a the angle (in radians) + * @return cos(a) + */ + public static double cos(double a) + { + return VMMath.cos(a); + } + + /** + * The trigonometric function tan. The tangent of NaN or infinity + * is NaN, and the tangent of 0 retains its sign. This is accurate within 1 + * ulp, and is semi-monotonic. + * + * @param a the angle (in radians) + * @return tan(a) + */ + public static double tan(double a) + { + return VMMath.tan(a); + } + + /** + * The trigonometric function arcsin. The range of angles returned + * is -pi/2 to pi/2 radians (-90 to 90 degrees). If the argument is NaN or + * its absolute value is beyond 1, the result is NaN; and the arcsine of + * 0 retains its sign. This is accurate within 1 ulp, and is semi-monotonic. + * + * @param a the sin to turn back into an angle + * @return arcsin(a) + */ + public static double asin(double a) + { + return VMMath.asin(a); + } + + /** + * The trigonometric function arccos. The range of angles returned + * is 0 to pi radians (0 to 180 degrees). If the argument is NaN or + * its absolute value is beyond 1, the result is NaN. This is accurate + * within 1 ulp, and is semi-monotonic. + * + * @param a the cos to turn back into an angle + * @return arccos(a) + */ + public static double acos(double a) + { + return VMMath.acos(a); + } + + /** + * The trigonometric function arcsin. The range of angles returned + * is -pi/2 to pi/2 radians (-90 to 90 degrees). If the argument is NaN, the + * result is NaN; and the arctangent of 0 retains its sign. This is accurate + * within 1 ulp, and is semi-monotonic. + * + * @param a the tan to turn back into an angle + * @return arcsin(a) + * @see #atan2(double, double) + */ + public static double atan(double a) + { + return VMMath.atan(a); + } + + /** + * A special version of the trigonometric function arctan, for + * converting rectangular coordinates (x, y) to polar + * (r, theta). This computes the arctangent of x/y in the range + * of -pi to pi radians (-180 to 180 degrees). Special cases:

      + *
    • If either argument is NaN, the result is NaN.
    • + *
    • If the first argument is positive zero and the second argument is + * positive, or the first argument is positive and finite and the second + * argument is positive infinity, then the result is positive zero.
    • + *
    • If the first argument is negative zero and the second argument is + * positive, or the first argument is negative and finite and the second + * argument is positive infinity, then the result is negative zero.
    • + *
    • If the first argument is positive zero and the second argument is + * negative, or the first argument is positive and finite and the second + * argument is negative infinity, then the result is the double value + * closest to pi.
    • + *
    • If the first argument is negative zero and the second argument is + * negative, or the first argument is negative and finite and the second + * argument is negative infinity, then the result is the double value + * closest to -pi.
    • + *
    • If the first argument is positive and the second argument is + * positive zero or negative zero, or the first argument is positive + * infinity and the second argument is finite, then the result is the + * double value closest to pi/2.
    • + *
    • If the first argument is negative and the second argument is + * positive zero or negative zero, or the first argument is negative + * infinity and the second argument is finite, then the result is the + * double value closest to -pi/2.
    • + *
    • If both arguments are positive infinity, then the result is the + * double value closest to pi/4.
    • + *
    • If the first argument is positive infinity and the second argument + * is negative infinity, then the result is the double value closest to + * 3*pi/4.
    • + *
    • If the first argument is negative infinity and the second argument + * is positive infinity, then the result is the double value closest to + * -pi/4.
    • + *
    • If both arguments are negative infinity, then the result is the + * double value closest to -3*pi/4.
    • + * + *

    This is accurate within 2 ulps, and is semi-monotonic. To get r, + * use sqrt(x*x+y*y). + * + * @param y the y position + * @param x the x position + * @return theta in the conversion of (x, y) to (r, theta) + * @see #atan(double) + */ + public static double atan2(double y, double x) + { + return VMMath.atan2(y,x); + } + + /** + * Take ea. The opposite of log(). If the + * argument is NaN, the result is NaN; if the argument is positive infinity, + * the result is positive infinity; and if the argument is negative + * infinity, the result is positive zero. This is accurate within 1 ulp, + * and is semi-monotonic. + * + * @param a the number to raise to the power + * @return the number raised to the power of e + * @see #log(double) + * @see #pow(double, double) + */ + public static double exp(double a) + { + return VMMath.exp(a); + } + + /** + * Take ln(a) (the natural log). The opposite of exp(). If the + * argument is NaN or negative, the result is NaN; if the argument is + * positive infinity, the result is positive infinity; and if the argument + * is either zero, the result is negative infinity. This is accurate within + * 1 ulp, and is semi-monotonic. + * + *

    Note that the way to get logb(a) is to do this: + * ln(a) / ln(b). + * + * @param a the number to take the natural log of + * @return the natural log of a + * @see #exp(double) + */ + public static double log(double a) + { + return VMMath.log(a); + } + + /** + * Take a square root. If the argument is NaN or negative, the result is + * NaN; if the argument is positive infinity, the result is positive + * infinity; and if the result is either zero, the result is the same. + * This is accurate within the limits of doubles. + * + *

    For a cube root, use cbrt. For other roots, use + * pow(a, 1 / rootNumber).

    + * + * @param a the numeric argument + * @return the square root of the argument + * @see #cbrt(double) + * @see #pow(double, double) + */ + public static double sqrt(double a) + { + return VMMath.sqrt(a); + } + + /** + * Raise a number to a power. Special cases:
      + *
    • If the second argument is positive or negative zero, then the result + * is 1.0.
    • + *
    • If the second argument is 1.0, then the result is the same as the + * first argument.
    • + *
    • If the second argument is NaN, then the result is NaN.
    • + *
    • If the first argument is NaN and the second argument is nonzero, + * then the result is NaN.
    • + *
    • If the absolute value of the first argument is greater than 1 and + * the second argument is positive infinity, or the absolute value of the + * first argument is less than 1 and the second argument is negative + * infinity, then the result is positive infinity.
    • + *
    • If the absolute value of the first argument is greater than 1 and + * the second argument is negative infinity, or the absolute value of the + * first argument is less than 1 and the second argument is positive + * infinity, then the result is positive zero.
    • + *
    • If the absolute value of the first argument equals 1 and the second + * argument is infinite, then the result is NaN.
    • + *
    • If the first argument is positive zero and the second argument is + * greater than zero, or the first argument is positive infinity and the + * second argument is less than zero, then the result is positive zero.
    • + *
    • If the first argument is positive zero and the second argument is + * less than zero, or the first argument is positive infinity and the + * second argument is greater than zero, then the result is positive + * infinity.
    • + *
    • If the first argument is negative zero and the second argument is + * greater than zero but not a finite odd integer, or the first argument is + * negative infinity and the second argument is less than zero but not a + * finite odd integer, then the result is positive zero.
    • + *
    • If the first argument is negative zero and the second argument is a + * positive finite odd integer, or the first argument is negative infinity + * and the second argument is a negative finite odd integer, then the result + * is negative zero.
    • + *
    • If the first argument is negative zero and the second argument is + * less than zero but not a finite odd integer, or the first argument is + * negative infinity and the second argument is greater than zero but not a + * finite odd integer, then the result is positive infinity.
    • + *
    • If the first argument is negative zero and the second argument is a + * negative finite odd integer, or the first argument is negative infinity + * and the second argument is a positive finite odd integer, then the result + * is negative infinity.
    • + *
    • If the first argument is less than zero and the second argument is a + * finite even integer, then the result is equal to the result of raising + * the absolute value of the first argument to the power of the second + * argument.
    • + *
    • If the first argument is less than zero and the second argument is a + * finite odd integer, then the result is equal to the negative of the + * result of raising the absolute value of the first argument to the power + * of the second argument.
    • + *
    • If the first argument is finite and less than zero and the second + * argument is finite and not an integer, then the result is NaN.
    • + *
    • If both arguments are integers, then the result is exactly equal to + * the mathematical result of raising the first argument to the power of + * the second argument if that result can in fact be represented exactly as + * a double value.
    • + * + *

    (In the foregoing descriptions, a floating-point value is + * considered to be an integer if and only if it is a fixed point of the + * method {@link #ceil(double)} or, equivalently, a fixed point of the + * method {@link #floor(double)}. A value is a fixed point of a one-argument + * method if and only if the result of applying the method to the value is + * equal to the value.) This is accurate within 1 ulp, and is semi-monotonic. + * + * @param a the number to raise + * @param b the power to raise it to + * @return ab + */ + public static double pow(double a, double b) + { + return VMMath.pow(a,b); + } + + /** + * Get the IEEE 754 floating point remainder on two numbers. This is the + * value of x - y * n, where n is the closest + * double to x / y (ties go to the even n); for a zero + * remainder, the sign is that of x. If either argument is NaN, + * the first argument is infinite, or the second argument is zero, the result + * is NaN; if x is finite but y is infinite, the result is x. This is + * accurate within the limits of doubles. + * + * @param x the dividend (the top half) + * @param y the divisor (the bottom half) + * @return the IEEE 754-defined floating point remainder of x/y + * @see #rint(double) + */ + public static double IEEEremainder(double x, double y) + { + return VMMath.IEEEremainder(x,y); + } + + /** + * Take the nearest integer that is that is greater than or equal to the + * argument. If the argument is NaN, infinite, or zero, the result is the + * same; if the argument is between -1 and 0, the result is negative zero. + * Note that Math.ceil(x) == -Math.floor(-x). + * + * @param a the value to act upon + * @return the nearest integer >= a + */ + public static double ceil(double a) + { + return VMMath.ceil(a); + } + + /** + * Take the nearest integer that is that is less than or equal to the + * argument. If the argument is NaN, infinite, or zero, the result is the + * same. Note that Math.ceil(x) == -Math.floor(-x). + * + * @param a the value to act upon + * @return the nearest integer <= a + */ + public static double floor(double a) + { + return VMMath.floor(a); + } + + /** + * Take the nearest integer to the argument. If it is exactly between + * two integers, the even integer is taken. If the argument is NaN, + * infinite, or zero, the result is the same. + * + * @param a the value to act upon + * @return the nearest integer to a + */ + public static double rint(double a) + { + return VMMath.rint(a); + } + + /** + * Take the nearest integer to the argument. This is equivalent to + * (int) Math.floor(a + 0.5f). If the argument is NaN, the result + * is 0; otherwise if the argument is outside the range of int, the result + * will be Integer.MIN_VALUE or Integer.MAX_VALUE, as appropriate. + * + * @param a the argument to round + * @return the nearest integer to the argument + * @see Integer#MIN_VALUE + * @see Integer#MAX_VALUE + */ + public static int round(float a) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return 0; + return (int) floor(a + 0.5f); + } + + /** + * Take the nearest long to the argument. This is equivalent to + * (long) Math.floor(a + 0.5). If the argument is NaN, the + * result is 0; otherwise if the argument is outside the range of long, the + * result will be Long.MIN_VALUE or Long.MAX_VALUE, as appropriate. + * + * @param a the argument to round + * @return the nearest long to the argument + * @see Long#MIN_VALUE + * @see Long#MAX_VALUE + */ + public static long round(double a) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return 0; + return (long) floor(a + 0.5d); + } + + /** + * Get a random number. This behaves like Random.nextDouble(), seeded by + * System.currentTimeMillis() when first called. In other words, the number + * is from a pseudorandom sequence, and lies in the range [+0.0, 1.0). + * This random sequence is only used by this method, and is threadsafe, + * although you may want your own random number generator if it is shared + * among threads. + * + * @return a random number + * @see Random#nextDouble() + * @see System#currentTimeMillis() + */ + public static synchronized double random() + { + if (rand == null) + rand = new Random(); + return rand.nextDouble(); + } + + /** + * Convert from degrees to radians. The formula for this is + * radians = degrees * (pi/180); however it is not always exact given the + * limitations of floating point numbers. + * + * @param degrees an angle in degrees + * @return the angle in radians + * @since 1.2 + */ + public static double toRadians(double degrees) + { + return (degrees * PI) / 180; + } + + /** + * Convert from radians to degrees. The formula for this is + * degrees = radians * (180/pi); however it is not always exact given the + * limitations of floating point numbers. + * + * @param rads an angle in radians + * @return the angle in degrees + * @since 1.2 + */ + public static double toDegrees(double rads) + { + return (rads * 180) / PI; + } + + /** + *

    + * Take a cube root. If the argument is NaN, an infinity or + * zero, then the original value is returned. The returned result is + * within 1 ulp of the exact result. For a finite value, x, + * the cube root of -x is equal to the negation of the cube root + * of x. + *

    + *

    + * For a square root, use sqrt. For other roots, use + * pow(a, 1 / rootNumber). + *

    + * + * @param a the numeric argument + * @return the cube root of the argument + * @see #sqrt(double) + * @see #pow(double, double) + * @since 1.5 + */ + public static double cbrt(double a) + { + return VMMath.cbrt(a); + } + + /** + *
    + *

    + * If the supplied value is NaN, then the original value is + * returned. For either infinity, positive infinity is returned. + * The hyperbolic cosine of zero is 1.0. + *

    + * + * @param a the numeric argument + * @return the hyperbolic cosine of a. + * @since 1.5 + */ + public static double cosh(double a) + { + return VMMath.cosh(a); + } + + /** + *

    + * Returns ea - 1. For values close to 0, the + * result of expm1(a) + 1 tend to be much closer to the + * exact result than simply exp(x). The result is within + * 1 ulp of the exact result, and results are semi-monotonic. For finite + * inputs, the returned value is greater than or equal to -1.0. Once + * a result enters within half a ulp of this limit, the limit is returned. + *

    + *

    + * For NaN, positive infinity and zero, the original value + * is returned. Negative infinity returns a result of -1.0 (the limit). + *

    + * + * @param a the numeric argument + * @return ea - 1 + * @since 1.5 + */ + public static double expm1(double a) + { + return VMMath.expm1(a); + } + + /** + *

    + * Returns the hypotenuse, a2 + b2, + * without intermediate overflow or underflow. The returned result is + * within 1 ulp of the exact result. If one parameter is held constant, + * then the result in the other parameter is semi-monotonic. + *

    + *

    + * If either of the arguments is an infinity, then the returned result + * is positive infinity. Otherwise, if either argument is NaN, + * then NaN is returned. + *

    + * + * @param a the first parameter. + * @param b the second parameter. + * @return the hypotenuse matching the supplied parameters. + * @since 1.5 + */ + public static double hypot(double a, double b) + { + return VMMath.hypot(a,b); + } + + /** + *

    + * Returns the base 10 logarithm of the supplied value. The returned + * result is within 1 ulp of the exact result, and the results are + * semi-monotonic. + *

    + *

    + * Arguments of either NaN or less than zero return + * NaN. An argument of positive infinity returns positive + * infinity. Negative infinity is returned if either positive or negative + * zero is supplied. Where the argument is the result of + * 10n, then n is returned. + *

    + * + * @param a the numeric argument. + * @return the base 10 logarithm of a. + * @since 1.5 + */ + public static double log10(double a) + { + return VMMath.log10(a); + } + + /** + *

    + * Returns the natural logarithm resulting from the sum of the argument, + * a and 1. For values close to 0, the + * result of log1p(a) tend to be much closer to the + * exact result than simply log(1.0+a). The returned + * result is within 1 ulp of the exact result, and the results are + * semi-monotonic. + *

    + *

    + * Arguments of either NaN or less than -1 return + * NaN. An argument of positive infinity or zero + * returns the original argument. Negative infinity is returned from an + * argument of -1. + *

    + * + * @param a the numeric argument. + * @return the natural logarithm of a + 1. + * @since 1.5 + */ + public static double log1p(double a) + { + return VMMath.log1p(a); + } + + /** + *

    + * Returns the sign of the argument as follows: + *

    + *
      + *
    • If a is greater than zero, the result is 1.0.
    • + *
    • If a is less than zero, the result is -1.0.
    • + *
    • If a is NaN, the result is NaN. + *
    • If a is positive or negative zero, the result is the + * same.
    • + *
    + * + * @param a the numeric argument. + * @return the sign of the argument. + * @since 1.5. + */ + public static double signum(double a) + { + if (Double.isNaN(a)) + return Double.NaN; + if (a > 0) + return 1.0; + if (a < 0) + return -1.0; + return a; + } + + /** + *

    + * Returns the sign of the argument as follows: + *

    + *
      + *
    • If a is greater than zero, the result is 1.0f.
    • + *
    • If a is less than zero, the result is -1.0f.
    • + *
    • If a is NaN, the result is NaN. + *
    • If a is positive or negative zero, the result is the + * same.
    • + *
    + * + * @param a the numeric argument. + * @return the sign of the argument. + * @since 1.5. + */ + public static float signum(float a) + { + if (Float.isNaN(a)) + return Float.NaN; + if (a > 0) + return 1.0f; + if (a < 0) + return -1.0f; + return a; + } + + /** + *

    + * Returns the hyperbolic sine of the given value. For a value, + * x, the hyperbolic sine is (ex - + * e-x)/2 + * with e being Euler's number. The returned + * result is within 2.5 ulps of the exact result. + *

    + *

    + * If the supplied value is NaN, an infinity or a zero, then the + * original value is returned. + *

    + * + * @param a the numeric argument + * @return the hyperbolic sine of a. + * @since 1.5 + */ + public static double sinh(double a) + { + return VMMath.sinh(a); + } + + /** + *

    + * Returns the hyperbolic tangent of the given value. For a value, + * x, the hyperbolic tangent is (ex - + * e-x)/(ex + e-x) + * (i.e. sinh(a)/cosh(a)) + * with e being Euler's number. The returned + * result is within 2.5 ulps of the exact result. The absolute value + * of the exact result is always less than 1. Computed results are thus + * less than or equal to 1 for finite arguments, with results within + * half a ulp of either positive or negative 1 returning the appropriate + * limit value (i.e. as if the argument was an infinity). + *

    + *

    + * If the supplied value is NaN or zero, then the original + * value is returned. Positive infinity returns +1.0 and negative infinity + * returns -1.0. + *

    + * + * @param a the numeric argument + * @return the hyperbolic tangent of a. + * @since 1.5 + */ + public static double tanh(double a) + { + return VMMath.tanh(a); + } + + /** + * Return the ulp for the given double argument. The ulp is the + * difference between the argument and the next larger double. Note + * that the sign of the double argument is ignored, that is, + * ulp(x) == ulp(-x). If the argument is a NaN, then NaN is returned. + * If the argument is an infinity, then +Inf is returned. If the + * argument is zero (either positive or negative), then + * {@link Double#MIN_VALUE} is returned. + * @param d the double whose ulp should be returned + * @return the difference between the argument and the next larger double + * @since 1.5 + */ + public static double ulp(double d) + { + if (Double.isNaN(d)) + return d; + if (Double.isInfinite(d)) + return Double.POSITIVE_INFINITY; + // This handles both +0.0 and -0.0. + if (d == 0.0) + return Double.MIN_VALUE; + long bits = Double.doubleToLongBits(d); + final int mantissaBits = 52; + final int exponentBits = 11; + final long mantMask = (1L << mantissaBits) - 1; + long mantissa = bits & mantMask; + final long expMask = (1L << exponentBits) - 1; + long exponent = (bits >>> mantissaBits) & expMask; + + // Denormal number, so the answer is easy. + if (exponent == 0) + { + long result = (exponent << mantissaBits) | 1L; + return Double.longBitsToDouble(result); + } + + // Conceptually we want to have '1' as the mantissa. Then we would + // shift the mantissa over to make a normal number. If this underflows + // the exponent, we will make a denormal result. + long newExponent = exponent - mantissaBits; + long newMantissa; + if (newExponent > 0) + newMantissa = 0; + else + { + newMantissa = 1L << -(newExponent - 1); + newExponent = 0; + } + return Double.longBitsToDouble((newExponent << mantissaBits) | newMantissa); + } + + /** + * Return the ulp for the given float argument. The ulp is the + * difference between the argument and the next larger float. Note + * that the sign of the float argument is ignored, that is, + * ulp(x) == ulp(-x). If the argument is a NaN, then NaN is returned. + * If the argument is an infinity, then +Inf is returned. If the + * argument is zero (either positive or negative), then + * {@link Float#MIN_VALUE} is returned. + * @param f the float whose ulp should be returned + * @return the difference between the argument and the next larger float + * @since 1.5 + */ + public static float ulp(float f) + { + if (Float.isNaN(f)) + return f; + if (Float.isInfinite(f)) + return Float.POSITIVE_INFINITY; + // This handles both +0.0 and -0.0. + if (f == 0.0) + return Float.MIN_VALUE; + int bits = Float.floatToIntBits(f); + final int mantissaBits = 23; + final int exponentBits = 8; + final int mantMask = (1 << mantissaBits) - 1; + int mantissa = bits & mantMask; + final int expMask = (1 << exponentBits) - 1; + int exponent = (bits >>> mantissaBits) & expMask; + + // Denormal number, so the answer is easy. + if (exponent == 0) + { + int result = (exponent << mantissaBits) | 1; + return Float.intBitsToFloat(result); + } + + // Conceptually we want to have '1' as the mantissa. Then we would + // shift the mantissa over to make a normal number. If this underflows + // the exponent, we will make a denormal result. + int newExponent = exponent - mantissaBits; + int newMantissa; + if (newExponent > 0) + newMantissa = 0; + else + { + newMantissa = 1 << -(newExponent - 1); + newExponent = 0; + } + return Float.intBitsToFloat((newExponent << mantissaBits) | newMantissa); + } +} diff --git a/libjava/classpath/java/lang/NegativeArraySizeException.java b/libjava/classpath/java/lang/NegativeArraySizeException.java new file mode 100644 index 000000000..fcfa52e47 --- /dev/null +++ b/libjava/classpath/java/lang/NegativeArraySizeException.java @@ -0,0 +1,77 @@ +/* NegativeArraySizeException.java -- thrown on attempt to create array + with a negative size + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when an attempt is made to create an array with a negative + * size. For example:
    + *
    + * int i = -1;
    + * int[] array = new int[i];
    + * 
    + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class NegativeArraySizeException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -8960118058596991861L; + + /** + * Create an exception without a message. + */ + public NegativeArraySizeException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NegativeArraySizeException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoClassDefFoundError.java b/libjava/classpath/java/lang/NoClassDefFoundError.java new file mode 100644 index 000000000..55d5f2605 --- /dev/null +++ b/libjava/classpath/java/lang/NoClassDefFoundError.java @@ -0,0 +1,76 @@ +/* NoClassDefFoundError.java -- thrown when a ClassLoader cannot find a class + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A NoClassDefFoundError is thrown when a classloader or the + * Java Virtual Machine tries to load a class and no definition of the class + * can be found. This could happen when using the new expression + * or during a normal method call. The reason this would occur at runtime is + * because the missing class definition existed when the currently executing + * class was compiled, but now that definition cannot be found. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class NoClassDefFoundError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 9095859863287012458L; + + /** + * Create an error without a message. + */ + public NoClassDefFoundError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public NoClassDefFoundError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoSuchFieldError.java b/libjava/classpath/java/lang/NoSuchFieldError.java new file mode 100644 index 000000000..af42e35dc --- /dev/null +++ b/libjava/classpath/java/lang/NoSuchFieldError.java @@ -0,0 +1,74 @@ +/* NoSuchFieldError.java -- thrown when the linker does not find a field + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A NoSuchFieldError is thrown if an application attempts + * to access a field of a class, and that class no longer has that field. + * This is normally detected by the compiler, so it signals that you are + * using binary incompatible class versions. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class NoSuchFieldError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -3456430195886129035L; + + /** + * Create an error without a message. + */ + public NoSuchFieldError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public NoSuchFieldError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoSuchFieldException.java b/libjava/classpath/java/lang/NoSuchFieldException.java new file mode 100644 index 000000000..74d52d137 --- /dev/null +++ b/libjava/classpath/java/lang/NoSuchFieldException.java @@ -0,0 +1,73 @@ +/* NoSuchFieldException.java -- thrown when reflecting a non-existant field + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown to indicate the class does not have the specified field. This is + * caused by a variety of reflection methods, when looking up a field by name. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NoSuchFieldException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -6143714805279938260L; + + /** + * Create an exception without a message. + */ + public NoSuchFieldException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoSuchFieldException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoSuchMethodError.java b/libjava/classpath/java/lang/NoSuchMethodError.java new file mode 100644 index 000000000..2bda776e8 --- /dev/null +++ b/libjava/classpath/java/lang/NoSuchMethodError.java @@ -0,0 +1,74 @@ +/* NoSuchMethodError.java -- thrown when the linker does not find a method + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A NoSuchMethodError is thrown if an application attempts + * to access a method of a class, and that class no longer has that method. + * This is normally detected by the compiler, so it signals that you are + * using binary incompatible class versions. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class NoSuchMethodError extends IncompatibleClassChangeError +{ + /** + * Compatible with JDK 1.0+. + */ + static final long serialVersionUID = -3765521442372831335L; + + /** + * Create an error without a message. + */ + public NoSuchMethodError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public NoSuchMethodError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NoSuchMethodException.java b/libjava/classpath/java/lang/NoSuchMethodException.java new file mode 100644 index 000000000..e423efb79 --- /dev/null +++ b/libjava/classpath/java/lang/NoSuchMethodException.java @@ -0,0 +1,72 @@ +/* NoSuchMethodException.java -- thrown when reflecting a non-existant method + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown to indicate the class does not have the specified method. This is + * caused by a variety of reflection methods, when looking up a method by name. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class NoSuchMethodException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 5034388446362600923L; + + /** + * Create an exception without a message. + */ + public NoSuchMethodException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoSuchMethodException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/NullPointerException.java b/libjava/classpath/java/lang/NullPointerException.java new file mode 100644 index 000000000..29a4ee086 --- /dev/null +++ b/libjava/classpath/java/lang/NullPointerException.java @@ -0,0 +1,82 @@ +/* NullPointerException.java -- thrown when using null instead of an object + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when attempting to use null where an object + * is required. The Virtual Machine automatically throws this exception + * for the following:
      + *
    • Calling an instance method on a null object
    • + *
    • Accessing or modifying a field of a null object
    • + *
    • Taking the array length of a null array
    • + *
    • Accessing or modifying the slots of a null array
    • + *
    • Throwing a null Throwable
    • + *
    • Synchronizing on a null object
    • + *
    + *

    Applications should also throw NullPointerExceptions whenever + * null is an inappropriate parameter to a method. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class NullPointerException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 5162710183389028792L; + + /** + * Create an exception without a message. + */ + public NullPointerException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NullPointerException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Number.java b/libjava/classpath/java/lang/Number.java new file mode 100644 index 000000000..eb81f78c8 --- /dev/null +++ b/libjava/classpath/java/lang/Number.java @@ -0,0 +1,131 @@ +/* Number.java =- abstract superclass of numeric objects + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.Serializable; + +/** + * Number is a generic superclass of all the numeric classes, including + * the wrapper classes {@link Byte}, {@link Short}, {@link Integer}, + * {@link Long}, {@link Float}, and {@link Double}. Also worth mentioning + * are the classes in {@link java.math}. + * + * It provides ways to convert numeric objects to any primitive. + * + * @author Paul Fisher + * @author John Keiser + * @author Warren Levy + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class Number implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8742448824652078965L; + + /** + * Table for calculating digits, used in Character, Long, and Integer. + */ + static final char[] digits = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + }; + + /** + * The basic constructor (often called implicitly). + */ + public Number() + { + } + + /** + * Return the value of this Number as an int. + * + * @return the int value + */ + public abstract int intValue(); + + /** + * Return the value of this Number as a long. + * + * @return the long value + */ + public abstract long longValue(); + + /** + * Return the value of this Number as a float. + * + * @return the float value + */ + public abstract float floatValue(); + + /** + * Return the value of this Number as a float. + * + * @return the double value + */ + public abstract double doubleValue(); + + /** + * Return the value of this Number as a byte. + * + * @return the byte value + * @since 1.1 + */ + public byte byteValue() + { + return (byte) intValue(); + } + + /** + * Return the value of this Number as a short. + * + * @return the short value + * @since 1.1 + */ + public short shortValue() + { + return (short) intValue(); + } +} diff --git a/libjava/classpath/java/lang/NumberFormatException.java b/libjava/classpath/java/lang/NumberFormatException.java new file mode 100644 index 000000000..bf98156d1 --- /dev/null +++ b/libjava/classpath/java/lang/NumberFormatException.java @@ -0,0 +1,73 @@ +/* NumberFormatException.java -- thrown when parsing a bad string as a number + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Can be thrown when attempting to convert a String to + * one of the numeric types, but the operation fails because the string + * has the wrong format. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class NumberFormatException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2848938806368998894L; + + /** + * Create an exception without a message. + */ + public NumberFormatException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NumberFormatException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Object.java b/libjava/classpath/java/lang/Object.java new file mode 100644 index 000000000..a6e700434 --- /dev/null +++ b/libjava/classpath/java/lang/Object.java @@ -0,0 +1,530 @@ +/* java.lang.Object - The universal superclass in Java + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + + +/** + * Object is the ultimate superclass of every class + * (excepting interfaces). When you define a class that + * does not extend any other class, it implicitly extends + * java.lang.Object. Also, an anonymous class based on + * an interface will extend Object. + * + *

    It provides general-purpose methods that every single + * Object, regardless of race, sex or creed, implements. + * All of the public methods may be invoked on arrays or + * interfaces. The protected methods clone + * and finalize are not accessible on arrays + * or interfaces, but all array types have a public version + * of clone which is accessible. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@cygnus.com) + */ +public class Object +{ + // WARNING: Object is a CORE class in the bootstrap cycle. See the comments + // in vm/reference/java/lang/Runtime for implications of this fact. + + // Many JVMs do not allow for static initializers in this class, + // hence we do not use them in the default implementation. + + // Some VM's rely on the order that these methods appear when laying + // out their internal structure. Therefore, do not haphazardly + // rearrange these methods. + + /** + * The basic constructor. Object is special, because it has no + * superclass, so there is no call to super(). + * + * @throws OutOfMemoryError Technically, this constructor never + * throws an OutOfMemoryError, because the memory has + * already been allocated by this point. But as all + * instance creation expressions eventually trace back + * to this constructor, and creating an object allocates + * memory, we list that possibility here. + */ + // This could be implicit, but then javadoc would not document it! + public Object() {} + + /** + * Determine whether this Object is semantically equal + * to another Object. + * + *

    There are some fairly strict requirements on this + * method which subclasses must follow:
    + *

      + *
    • It must be transitive. If a.equals(b) and + * b.equals(c), then a.equals(c) + * must be true as well.
    • + *
    • It must be symmetric. a.equals(b) and + * b.equals(a) must have the same value.
    • + *
    • It must be reflexive. a.equals(a) must + * always be true.
    • + *
    • It must be consistent. Whichever value a.equals(b) + * returns on the first invocation must be the value + * returned on all later invocations.
    • + *
    • a.equals(null) must be false.
    • + *
    • It must be consistent with hashCode(). That is, + * a.equals(b) must imply + * a.hashCode() == b.hashCode(). + * The reverse is not true; two objects that are not + * equal may have the same hashcode, but that has + * the potential to harm hashing performance.
    • + *
    + * + *

    This is typically overridden to throw a {@link ClassCastException} + * if the argument is not comparable to the class performing + * the comparison, but that is not a requirement. It is legal + * for a.equals(b) to be true even though + * a.getClass() != b.getClass(). Also, it + * is typical to never cause a {@link NullPointerException}. + * + *

    In general, the Collections API ({@link java.util}) use the + * equals method rather than the == + * operator to compare objects. However, {@link java.util.IdentityHashMap} + * is an exception to this rule, for its own good reasons. + * + *

    The default implementation returns this == o. + * + * @param obj the Object to compare to + * @return whether this Object is semantically equal to another + * @see #hashCode() + */ + public boolean equals(Object obj) + { + return this == obj; + } + + /** + * Get a value that represents this Object, as uniquely as + * possible within the confines of an int. + * + *

    There are some requirements on this method which + * subclasses must follow:
    + * + *

      + *
    • Semantic equality implies identical hashcodes. In other + * words, if a.equals(b) is true, then + * a.hashCode() == b.hashCode() must be as well. + * However, the reverse is not necessarily true, and two + * objects may have the same hashcode without being equal.
    • + *
    • It must be consistent. Whichever value o.hashCode() + * returns on the first invocation must be the value + * returned on all later invocations as long as the object + * exists. Notice, however, that the result of hashCode may + * change between separate executions of a Virtual Machine, + * because it is not invoked on the same object.
    • + *
    + * + *

    Notice that since hashCode is used in + * {@link java.util.Hashtable} and other hashing classes, + * a poor implementation will degrade the performance of hashing + * (so don't blindly implement it as returning a constant!). Also, + * if calculating the hash is time-consuming, a class may consider + * caching the results. + * + *

    The default implementation returns + * System.identityHashCode(this) + * + * @return the hash code for this Object + * @see #equals(Object) + * @see System#identityHashCode(Object) + */ + public int hashCode() + { + return System.identityHashCode(this); + } + + /** + * Convert this Object to a human-readable String. + * There are no limits placed on how long this String + * should be or what it should contain. We suggest you + * make it as intuitive as possible to be able to place + * it into {@link java.io.PrintStream#println() System.out.println()} + * and such. + * + *

    It is typical, but not required, to ensure that this method + * never completes abruptly with a {@link RuntimeException}. + * + *

    This method will be called when performing string + * concatenation with this object. If the result is + * null, string concatenation will instead + * use "null". + * + *

    The default implementation returns + * getClass().getName() + "@" + + * Integer.toHexString(hashCode()). + * + * @return the String representing this Object, which may be null + * @throws OutOfMemoryError The default implementation creates a new + * String object, therefore it must allocate memory + * @see #getClass() + * @see #hashCode() + * @see Class#getName() + * @see Integer#toHexString(int) + */ + public String toString() + { + return getClass().getName() + '@' + Integer.toHexString(hashCode()); + } + + /** + * Called on an object by the Virtual Machine at most once, + * at some point after the Object is determined unreachable + * but before it is destroyed. You would think that this + * means it eventually is called on every Object, but this is + * not necessarily the case. If execution terminates + * abnormally, garbage collection does not always happen. + * Thus you cannot rely on this method to always work. + * For finer control over garbage collection, use references + * from the {@link java.lang.ref} package. + * + *

    Virtual Machines are free to not call this method if + * they can determine that it does nothing important; for + * example, if your class extends Object and overrides + * finalize to do simply super.finalize(). + * + *

    finalize() will be called by a {@link Thread} that has no + * locks on any Objects, and may be called concurrently. + * There are no guarantees on the order in which multiple + * objects are finalized. This means that finalize() is + * usually unsuited for performing actions that must be + * thread-safe, and that your implementation must be + * use defensive programming if it is to always work. + * + *

    If an Exception is thrown from finalize() during garbage + * collection, it will be patently ignored and the Object will + * still be destroyed. + * + *

    It is allowed, although not typical, for user code to call + * finalize() directly. User invocation does not affect whether + * automatic invocation will occur. It is also permitted, + * although not recommended, for a finalize() method to "revive" + * an object by making it reachable from normal code again. + * + *

    Unlike constructors, finalize() does not get called + * for an object's superclass unless the implementation + * specifically calls super.finalize(). + * + *

    The default implementation does nothing. + * + * @throws Throwable permits a subclass to throw anything in an + * overridden version; but the default throws nothing + * @see System#gc() + * @see System#runFinalizersOnExit(boolean) + * @see java.lang.ref + */ + protected void finalize() throws Throwable + { + } + + /** + * This method may be called to create a new copy of the + * Object. The typical behavior is as follows:
    + *

      + *
    • o == o.clone() is false
    • + *
    • o.getClass() == o.clone().getClass() + * is true
    • + *
    • o.equals(o) is true
    • + *
    + * + *

    However, these are not strict requirements, and may + * be violated if necessary. Of the three requirements, the + * last is the most commonly violated, particularly if the + * subclass does not override {@link #equals(Object)}. + * + *

    If the Object you call clone() on does not implement + * {@link Cloneable} (which is a placeholder interface), then + * a CloneNotSupportedException is thrown. Notice that + * Object does not implement Cloneable; this method exists + * as a convenience for subclasses that do. + * + *

    Object's implementation of clone allocates space for the + * new Object using the correct class, without calling any + * constructors, and then fills in all of the new field values + * with the old field values. Thus, it is a shallow copy. + * However, subclasses are permitted to make a deep copy. + * + *

    All array types implement Cloneable, and override + * this method as follows (it should never fail):
    + *

    +   * public Object clone()
    +   * {
    +   *   try
    +   *     {
    +   *       super.clone();
    +   *     }
    +   *   catch (CloneNotSupportedException e)
    +   *     {
    +   *       throw new InternalError(e.getMessage());
    +   *     }
    +   * }
    +   * 
    + * + * @return a copy of the Object + * @throws CloneNotSupportedException If this Object does not + * implement Cloneable + * @throws OutOfMemoryError Since cloning involves memory allocation, + * even though it may bypass constructors, you might run + * out of memory + * @see Cloneable + */ + protected Object clone() throws CloneNotSupportedException + { + if (this instanceof Cloneable) + return VMObject.clone((Cloneable) this); + throw new CloneNotSupportedException("Object not cloneable"); + } + + /** + * Returns the runtime {@link Class} of this Object. + * + *

    The class object can also be obtained without a runtime + * instance by using the class literal, as in: + * Foo.class. Notice that the class literal + * also works on primitive types, making it useful for + * reflection purposes. + * + * @return the class of this Object + */ + public final Class getClass() + { + return VMObject.getClass(this); + } + + /** + * Wakes up one of the {@link Thread}s that has called + * wait on this Object. Only the owner + * of a lock on this Object may call this method. This lock + * is obtained by a synchronized method or statement. + * + *

    The Thread to wake up is chosen arbitrarily. The + * awakened thread is not guaranteed to be the next thread + * to actually obtain the lock on this object. + * + *

    This thread still holds a lock on the object, so it is + * typical to release the lock by exiting the synchronized + * code, calling wait(), or calling {@link Thread#sleep(long)}, so + * that the newly awakened thread can actually resume. The + * awakened thread will most likely be awakened with an + * {@link InterruptedException}, but that is not guaranteed. + * + * @throws IllegalMonitorStateException if this Thread + * does not own the lock on the Object + * @see #notifyAll() + * @see #wait() + * @see #wait(long) + * @see #wait(long, int) + * @see Thread + */ + public final void notify() throws IllegalMonitorStateException + { + VMObject.notify(this); + } + + /** + * Wakes up all of the {@link Thread}s that have called + * wait on this Object. Only the owner + * of a lock on this Object may call this method. This lock + * is obtained by a synchronized method or statement. + * + *

    There are no guarantees as to which thread will next + * obtain the lock on the object. + * + *

    This thread still holds a lock on the object, so it is + * typical to release the lock by exiting the synchronized + * code, calling wait(), or calling {@link Thread#sleep(long)}, so + * that one of the newly awakened threads can actually resume. + * The resuming thread will most likely be awakened with an + * {@link InterruptedException}, but that is not guaranteed. + * + * @throws IllegalMonitorStateException if this Thread + * does not own the lock on the Object + * @see #notify() + * @see #wait() + * @see #wait(long) + * @see #wait(long, int) + * @see Thread + */ + public final void notifyAll() throws IllegalMonitorStateException + { + VMObject.notifyAll(this); + } + + /** + * Waits indefinitely for notify() or notifyAll() to be + * called on the Object in question. Implementation is + * identical to wait(0). + * + *

    The Thread that calls wait must have a lock on this Object, + * obtained by a synchronized method or statement. + * After calling wait, the thread loses the lock on this + * object until the method completes (abruptly or normally), + * at which time it regains the lock. All locks held on + * other objects remain in force, even though the thread is + * inactive. Therefore, caution must be used to avoid deadlock. + * + *

    While it is typical that this method will complete abruptly + * with an {@link InterruptedException}, it is not guaranteed. So, + * it is typical to call wait inside an infinite loop:
    + * + *

    +   * try
    +   *   {
    +   *     while (true)
    +   *       lock.wait();
    +   *   }
    +   * catch (InterruptedException e)
    +   *   {
    +   *   }
    +   * 
    + * + * @throws IllegalMonitorStateException if this Thread + * does not own a lock on this Object + * @throws InterruptedException if some other Thread + * interrupts this Thread + * @see #notify() + * @see #notifyAll() + * @see #wait(long) + * @see #wait(long, int) + * @see Thread + */ + public final void wait() + throws IllegalMonitorStateException, InterruptedException + { + VMObject.wait(this, 0, 0); + } + + /** + * Waits a specified amount of time (or indefinitely if + * the time specified is 0) for someone to call notify() + * or notifyAll() on this Object, waking up this Thread. + * + *

    The Thread that calls wait must have a lock on this Object, + * obtained by a synchronized method or statement. + * After calling wait, the thread loses the lock on this + * object until the method completes (abruptly or normally), + * at which time it regains the lock. All locks held on + * other objects remain in force, even though the thread is + * inactive. Therefore, caution must be used to avoid deadlock. + * + *

    Usually, this call will complete normally if the time + * expires, or abruptly with {@link InterruptedException} + * if another thread called notify, but neither result + * is guaranteed. + * + *

    The waiting period is only *roughly* the amount of time + * you requested. It cannot be exact because of the overhead + * of the call itself. Most Virtual Machiness treat the + * argument as a lower limit on the time spent waiting, but + * even that is not guaranteed. Besides, some other thread + * may hold the lock on the object when the time expires, so + * the current thread may still have to wait to reobtain the + * lock. + * + * @param ms the minimum number of milliseconds to wait (1000 + * milliseconds = 1 second), or 0 for an indefinite wait + * @throws IllegalArgumentException if ms < 0 + * @throws IllegalMonitorStateException if this Thread + * does not own a lock on this Object + * @throws InterruptedException if some other Thread + * interrupts this Thread + * @see #notify() + * @see #notifyAll() + * @see #wait() + * @see #wait(long, int) + * @see Thread + */ + public final void wait(long ms) + throws IllegalMonitorStateException, InterruptedException + { + wait(ms, 0); + } + + /** + * Waits a specified amount of time (or indefinitely if + * the time specified is 0) for someone to call notify() + * or notifyAll() on this Object, waking up this Thread. + * + *

    The Thread that calls wait must have a lock on this Object, + * obtained by a synchronized method or statement. + * After calling wait, the thread loses the lock on this + * object until the method completes (abruptly or normally), + * at which time it regains the lock. All locks held on + * other objects remain in force, even though the thread is + * inactive. Therefore, caution must be used to avoid deadlock. + * + *

    Usually, this call will complete normally if the time + * expires, or abruptly with {@link InterruptedException} + * if another thread called notify, but neither result + * is guaranteed. + * + *

    The waiting period is nowhere near as precise as + * nanoseconds; considering that even wait(int) is inaccurate, + * how much can you expect? But on supporting + * implementations, this offers somewhat more granularity + * than milliseconds. + * + * @param ms the number of milliseconds to wait (1,000 + * milliseconds = 1 second) + * @param ns the number of nanoseconds to wait over and + * above ms (1,000,000 nanoseconds = 1 millisecond) + * @throws IllegalArgumentException if ms < 0 or ns is not + * in the range 0 to 999,999 + * @throws IllegalMonitorStateException if this Thread + * does not own a lock on this Object + * @throws InterruptedException if some other Thread + * interrupts this Thread + * @see #notify() + * @see #notifyAll() + * @see #wait() + * @see #wait(long) + * @see Thread + */ + public final void wait(long ms, int ns) + throws IllegalMonitorStateException, InterruptedException + { + if (ms < 0 || ns < 0 || ns > 999999) + throw new IllegalArgumentException("argument out of range"); + VMObject.wait(this, ms, ns); + } +} // class Object diff --git a/libjava/classpath/java/lang/OutOfMemoryError.java b/libjava/classpath/java/lang/OutOfMemoryError.java new file mode 100644 index 000000000..66da563a0 --- /dev/null +++ b/libjava/classpath/java/lang/OutOfMemoryError.java @@ -0,0 +1,73 @@ +/* OutOfMemoryError.java -- thrown when a memory allocation fails + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Thrown when the Java Virtual Machine is unable to allocate an object + * because it is out of memory and no more memory could be made available + * by the garbage collector. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class OutOfMemoryError extends VirtualMachineError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8228564086184010517L; + + /** + * Create an error without a message. + */ + public OutOfMemoryError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public OutOfMemoryError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Override.java b/libjava/classpath/java/lang/Override.java new file mode 100644 index 000000000..16581045a --- /dev/null +++ b/libjava/classpath/java/lang/Override.java @@ -0,0 +1,56 @@ +/* Override - Annotation to indicate that a method should be an override + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.RetentionPolicy.SOURCE; +import static java.lang.annotation.ElementType.METHOD; + +/** + * This annotation is used as a marker to indicate that the annotated + * method declaration is intended to override another method in the + * class hierarchy. If this is not the case, the compiler will emit a + * warning. + * + * @since 1.5 + */ +@Retention(SOURCE) @Target(METHOD) +public @interface Override +{ +} diff --git a/libjava/classpath/java/lang/Package.java b/libjava/classpath/java/lang/Package.java new file mode 100644 index 000000000..9220dfd21 --- /dev/null +++ b/libjava/classpath/java/lang/Package.java @@ -0,0 +1,414 @@ +/* Package.java -- information about a package + Copyright (C) 2000, 2001, 2002, 2003, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.classpath.VMStackWalker; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.net.URL; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + + +/** + * Everything you ever wanted to know about a package. This class makes it + * possible to attach specification and implementation information to a + * package as explained in the + * Package Versioning Specification + * section of the + * Product Versioning Specification. + * It also allows packages to be sealed with respect to the originating URL. + * + *

    The most useful method is the isCompatibleWith() method that + * compares a desired version of a specification with the version of the + * specification as implemented by a package. A package is considered + * compatible with another version if the version of the specification is + * equal or higher then the requested version. Version numbers are represented + * as strings of positive numbers separated by dots (e.g. "1.2.0"). + * The first number is called the major number, the second the minor, + * the third the micro, etc. A version is considered higher then another + * version if it has a bigger major number then the another version or when + * the major numbers of the versions are equal if it has a bigger minor number + * then the other version, etc. (If a version has no minor, micro, etc numbers + * then they are considered the be 0.) + * + * @author Mark Wielaard (mark@klomp.org) + * @see ClassLoader#definePackage(String, String, String, String, String, + * String, String, URL) + * @since 1.2 + * @status updated to 1.5 + */ +public class Package + implements AnnotatedElement +{ + /** The name of the Package */ + private final String name; + + /** The name if the implementation */ + private final String implTitle; + + /** The vendor that wrote this implementation */ + private final String implVendor; + + /** The version of this implementation */ + private final String implVersion; + + /** The name of the specification */ + private final String specTitle; + + /** The name of the specification designer */ + private final String specVendor; + + /** The version of this specification */ + private final String specVersion; + + /** If sealed the origin of the package classes, otherwise null */ + private final URL sealed; + + /** The class loader that defined this package */ + private ClassLoader loader; + + /** @deprecated Please use the other constructor that takes the class loader + * that defines the Package. + */ + Package(String name, + String specTitle, String specVendor, String specVersion, + String implTitle, String implVendor, String implVersion, URL sealed) + { + this(name, specTitle, specVendor, specVersion, implTitle, implVendor, + implVersion, sealed, null); + } + + /** + * A package local constructor for the Package class. All parameters except + * the name of the package may be null. + * There are no public constructors defined for Package; this is a package + * local constructor that is used by java.lang.Classloader.definePackage(). + * + * @param name The name of the Package + * @param specTitle The name of the specification + * @param specVendor The name of the specification designer + * @param specVersion The version of this specification + * @param implTitle The name of the implementation + * @param implVendor The vendor that wrote this implementation + * @param implVersion The version of this implementation + * @param sealed If sealed the origin of the package classes + */ + Package(String name, + String specTitle, String specVendor, String specVersion, + String implTitle, String implVendor, String implVersion, URL sealed, + ClassLoader loader) + { + if (name == null) + throw new IllegalArgumentException("null Package name"); + + this.name = name; + this.implTitle = implTitle; + this.implVendor = implVendor; + this.implVersion = implVersion; + this.specTitle = specTitle; + this.specVendor = specVendor; + this.specVersion = specVersion; + this.sealed = sealed; + this.loader = loader; + } + + /** + * Returns the Package name in dot-notation. + * + * @return the non-null package name + */ + public String getName() + { + return name; + } + + /** + * Returns the name of the specification, or null if unknown. + * + * @return the specification title + */ + public String getSpecificationTitle() + { + return specTitle; + } + + /** + * Returns the version of the specification, or null if unknown. + * + * @return the specification version + */ + public String getSpecificationVersion() + { + return specVersion; + } + + /** + * Returns the name of the specification designer, or null if unknown. + * + * @return the specification vendor + */ + public String getSpecificationVendor() + { + return specVendor; + } + + /** + * Returns the name of the implementation, or null if unknown. + * + * @return the implementation title + */ + public String getImplementationTitle() + { + return implTitle; + } + + /** + * Returns the version of this implementation, or null if unknown. + * + * @return the implementation version + */ + public String getImplementationVersion() + { + return implVersion; + } + + /** + * Returns the vendor that wrote this implementation, or null if unknown. + * + * @return the implementation vendor + */ + public String getImplementationVendor() + { + return implVendor; + } + + /** + * Returns true if this Package is sealed. + * + * @return true if the package is sealed + */ + public boolean isSealed() + { + return sealed != null; + } + + /** + * Returns true if this Package is sealed and the origin of the classes is + * the given URL. + * + * @param url the URL to test + * @return true if the package is sealed by this URL + * @throws NullPointerException if url is null + */ + public boolean isSealed(URL url) + { + return url.equals(sealed); + } + + /** + * Checks if the version of the specification is higher or at least as high + * as the desired version. Comparison is done by sequentially comparing + * dotted decimal numbers from the parameter and from + * getSpecificationVersion. + * + * @param version the (minimal) desired version of the specification + * + * @return true if the version is compatible, false otherwise + * + * @throws NumberFormatException if either version string is invalid + * @throws NullPointerException if either version string is null + */ + public boolean isCompatibleWith(String version) + { + StringTokenizer versionTokens = new StringTokenizer(version, "."); + StringTokenizer specTokens = new StringTokenizer(specVersion, "."); + try + { + while (versionTokens.hasMoreElements()) + { + int vers = Integer.parseInt(versionTokens.nextToken()); + int spec = Integer.parseInt(specTokens.nextToken()); + if (spec < vers) + return false; + else if (spec > vers) + return true; + // They must be equal, next Token please! + } + } + catch (NoSuchElementException e) + { + // This must have been thrown by spec.nextToken() so return false. + return false; + } + // They must have been exactly the same version. + // Or the specVersion has more subversions. That is also good. + return true; + } + + /** + * Returns the named package if it is known by the callers class loader. + * It may return null if the package is unknown, when there is no + * information on that particular package available or when the callers + * classloader is null. + * + * @param name the name of the desired package + * @return the package by that name in the current ClassLoader + */ + public static Package getPackage(String name) + { + // Get the caller's classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + return cl != null ? cl.getPackage(name) : VMClassLoader.getPackage(name); + } + + /** + * Returns all the packages that are known to the callers class loader. + * It may return an empty array if the classloader of the caller is null. + * + * @return an array of all known packages + */ + public static Package[] getPackages() + { + // Get the caller's classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + return cl != null ? cl.getPackages() : VMClassLoader.getPackages(); + } + + /** + * Returns the hashCode of the name of this package. + * + * @return the hash code + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * Returns a string representation of this package. It is specified to + * be "package " + getName() + (getSpecificationTitle() == null + * ? "" : ", " + getSpecificationTitle()) + (getSpecificationVersion() + * == null ? "" : ", version " + getSpecificationVersion()). + * + * @return the string representation of the package + */ + public String toString() + { + return ("package " + name + (specTitle == null ? "" : ", " + specTitle) + + (specVersion == null ? "" : ", version " + specVersion)); + } + + /** + * Returns this package's annotation for the specified annotation type, + * or null if no such annotation exists. + * + * @param annotationClass the type of annotation to look for. + * @return this package's annotation for the specified type, or + * null if no such annotation exists. + * @since 1.5 + */ + public A getAnnotation(Class annotationClass) + { + A foundAnnotation = null; + Annotation[] annotations = getAnnotations(); + for (Annotation annotation : annotations) + if (annotation.annotationType() == annotationClass) + foundAnnotation = (A) annotation; + return foundAnnotation; + } + + /** + * Returns all annotations associated with this package. If there are + * no annotations associated with this package, then a zero-length array + * will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * package, and hence no effect on the return value of this method for + * future callers. + * + * @return this package' annotations. + * @since 1.5 + */ + public Annotation[] getAnnotations() + { + /** All a package's annotations are declared within it. */ + return getDeclaredAnnotations(); + } + + /** + * Returns all annotations directly defined by this package. If there are + * no annotations associated with this package, then a zero-length array + * will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * package, and hence no effect on the return value of this method for + * future callers. + * + * @return the annotations directly defined by this package. + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() + { + try + { + Class pkgInfo = Class.forName(name + ".package-info", false, loader); + return pkgInfo.getDeclaredAnnotations(); + } + catch (ClassNotFoundException _) + { + return new Annotation[0]; + } + } + + /** + * Returns true if an annotation for the specified type is associated + * with this package. This is primarily a short-hand for using marker + * annotations. + * + * @param annotationClass the type of annotation to look for. + * @return true if an annotation exists for the specified type. + * @since 1.5 + */ + public boolean isAnnotationPresent(Class + annotationClass) + { + return getAnnotation(annotationClass) != null; + } + +} // class Package diff --git a/libjava/classpath/java/lang/Process.java b/libjava/classpath/java/lang/Process.java new file mode 100644 index 000000000..ccaa3f153 --- /dev/null +++ b/libjava/classpath/java/lang/Process.java @@ -0,0 +1,130 @@ +/* Process.java - Represent spawned system process + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * An instance of a subclass of Process is created by the + * Runtime.exec methods. Methods in Process + * provide a means to send input to a process, obtain the output from a + * subprocess, destroy a subprocess, obtain the exit value from a + * subprocess, and wait for a subprocess to complete. + * + *

    This is dependent on the platform, and some processes (like native + * windowing processes, 16-bit processes in Windows, or shell scripts) may + * be limited in functionality. Because some platforms have limited buffers + * between processes, you may need to provide input and read output to prevent + * the process from blocking, or even deadlocking. + * + *

    Even if all references to this object disapper, the process continues + * to execute to completion. There are no guarantees that the + * subprocess execute asynchronously or concurrently with the process which + * owns this object. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @see Runtime#exec(String[], String[], File) + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class Process +{ + /** + * Empty constructor does nothing. + */ + public Process() + { + } + + /** + * Obtain the output stream that sends data to the subprocess. This is + * the STDIN of the subprocess. When implementing, you should probably + * use a buffered stream. + * + * @return the output stream that pipes to the process input + */ + public abstract OutputStream getOutputStream(); + + /** + * Obtain the input stream that receives data from the subprocess. This is + * the STDOUT of the subprocess. When implementing, you should probably + * use a buffered stream. + * + * @return the input stream that pipes data from the process output + */ + public abstract InputStream getInputStream(); + + /** + * Obtain the input stream that receives data from the subprocess. This is + * the STDERR of the subprocess. When implementing, you should probably + * use a buffered stream. + * + * @return the input stream that pipes data from the process error output + */ + public abstract InputStream getErrorStream(); + + /** + * The thread calling waitFor will block until the subprocess + * has terminated. If the process has already terminated then the method + * immediately returns with the exit value of the subprocess. + * + * @return the subprocess exit value; 0 conventionally denotes success + * @throws InterruptedException if another thread interrupts the blocked one + */ + public abstract int waitFor() throws InterruptedException; + + /** + * When a process terminates there is associated with that termination + * an exit value for the process to indicate why it terminated. A return + * of 0 denotes normal process termination by convention. + * + * @return the exit value of the subprocess + * @throws IllegalThreadStateException if the subprocess has not terminated + */ + public abstract int exitValue(); + + /** + * Kills the subprocess and all of its children forcibly. + */ + public abstract void destroy(); +} // class Process diff --git a/libjava/classpath/java/lang/ProcessBuilder.java b/libjava/classpath/java/lang/ProcessBuilder.java new file mode 100644 index 000000000..0b32edec2 --- /dev/null +++ b/libjava/classpath/java/lang/ProcessBuilder.java @@ -0,0 +1,337 @@ +/* ProcessBuilder.java - Represent spawned system process + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.File; +import java.io.IOException; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + *

    + * This class is used to construct new operating system processes. + * A ProcessBuilder instance basically represent a + * template for a new process. Actual processes are generated from + * this template via use of the start() method, which + * may be invoked multiple times, with each invocation spawning a + * new process with the current attributes of the + * ProcessBuilder object. Each spawned process is + * independent of the ProcessBuilder object, and is + * unaffected by changes in its attributes. + *

    + *

    + * The following attributes define a process: + *

    + *
      + *
    • The working directory; the activities of a + * process begin with the current directory set to this. By default, + * this is the working directory of the current process, as defined + * by the user.dir property.
    • + *
    • The command which invokes the process. This + * usually consists of the name of the program binary followed by an + * arbitrary number of arguments. For example, find -type f + * invokes the find binary with the arguments "-type" and "f". + * The command is provided a list, the elements of which are defined in a + * system dependent manner; the layout is affected by expected operating + * system conventions. A common method is to split the command on each + * space within the string. Thus, find -type f forms a + * three element list. However, in some cases, the expectation is that + * this split is performed by the program itself; thus, the list consists + * of only two elements (the program name and its arguments).
    • + *
    • The environment map, which links environment + * variables to their corresponding values. The initial contents of the map + * are the current environment values i.e. it contains the contents of the + * map returned by System.getenv().
    • + *
    • The redirection flag, which specifies whether + * or not the contents of the error stream should be redirected to standard + * output. By default, this is false, and there are two output streams, one + * for normal data ({@link Process#getOutputStream()}) and one for error data + * ({@link Process#getErrorStream()}). When set to true, the two are merged, + * which simplifies the interleaving of the two streams. Data is read using + * the stream returned by {@link Process#getOutputStream()}, and the + * stream returned by {@link Process#getErrorStream()} throws an immediate + * end-of-file exception.
    • + *
    + *

    + * All checks on attribute validity are delayed until start() + * is called. ProcessBuilder objects are not + * synchronized; the user must provide external synchronization + * where multiple threads may interact with the same + * ProcessBuilder object. + *

    + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Process + * @see System#getenv() + * @since 1.5 + */ +public final class ProcessBuilder +{ + + /** + * The working directory of the process. + */ + private File directory = new File(System.getProperty("user.dir")); + + /** + * The command line syntax for invoking the process. + */ + private List command; + + /** + * The mapping of environment variables to values. + */ + private Map environment = + new System.EnvironmentMap(System.getenv()); + + /** + * A flag indicating whether to redirect the error stream to standard + * output. + */ + private boolean redirect = false; + + /** + * Constructs a new ProcessBuilder with the specified + * command being used to invoke the process. The list is used directly; + * external changes are reflected in the ProcessBuilder. + * + * @param command the name of the program followed by its arguments. + */ + public ProcessBuilder(List command) + { + this.command = command; + } + + /** + * Constructs a new ProcessBuilder with the specified + * command being used to invoke the process. This constructor + * simplifies creating a new ProcessBuilder by + * converting the provided series of constructor arguments into a + * list of command-line arguments. + * + * @param command the name of the program followed by its arguments. + */ + public ProcessBuilder(String... command) + { + this.command = Arrays.asList(command); + } + + /** + * Returns the current command line, used to invoke the process. + * The return value is simply a reference to the list of command + * line arguments used by the ProcessBuilder object; + * any changes made to it will be reflected in the operation of + * the ProcessBuilder. + * + * @return the list of command-line arguments. + */ + public List command() + { + return command; + } + + /** + * Sets the command-line arguments to those specified. The list is + * used directly; external changes are reflected in the + * ProcessBuilder. + * + * @param command the name of the program followed by its arguments. + * @return a reference to this process builder. + */ + public ProcessBuilder command(List command) + { + this.command = command; + return this; + } + + /** + * Sets the command-line arguments to those specified. + * This simplifies modifying the arguments by converting + * the provided series of constructor arguments into a + * list of command-line arguments. + * + * @param command the name of the program followed by its arguments. + * @return a reference to this process builder. + */ + public ProcessBuilder command(String... command) + { + this.command = Arrays.asList(command); + return this; + } + + /** + * Returns the working directory of the process. The + * returned value may be null; this + * indicates that the default behaviour of using the + * working directory of the current process should + * be adopted. + * + * @return the working directory. + */ + public File directory() + { + return directory; + } + + /** + * Sets the working directory to that specified. + * The supplied argument may be null, + * which indicates the default value should be used. + * The default is the working directory of the current + * process. + * + * @param directory the new working directory. + * @return a reference to this process builder. + */ + public ProcessBuilder directory(File directory) + { + this.directory = directory; + return this; + } + + /** + *

    + * Returns the system environment variables of the process. + * If the underlying system does not support environment variables, + * an empty map is returned. + *

    + *

    + * The returned map does not accept queries using + * null keys or values, or those of a type other than + * String. Attempts to pass in a null value will + * throw a NullPointerException. Types other than + * String throw a ClassCastException. + *

    + *

    + * As the returned map is generated using data from the underlying + * platform, it may not comply with the equals() + * and hashCode() contracts. It is also likely that + * the keys of this map will be case-sensitive. + *

    + *

    + * Modification of the map is reliant on the underlying platform; + * some may not allow any changes to the environment variables or + * may prevent certain values being used. Attempts to do so will + * throw an UnsupportedOperationException or + * IllegalArgumentException, respectively. + *

    + *

    + * Use of this method may require a security check for the + * RuntimePermission "getenv.*". + *

    + * + * @return a map of the system environment variables for the process. + * @throws SecurityException if the checkPermission method of + * an installed security manager prevents access to + * the system environment variables. + * @since 1.5 + */ + public Map environment() + { + return environment; + } + + /** + * Returns true if the output stream and error stream of the + * process will be merged to form one composite stream. The + * default return value is false. + * + * @return true if the output stream and error stream are to + * be merged. + */ + public boolean redirectErrorStream() + { + return redirect; + } + + /** + * Sets the error stream redirection flag. If set, the output + * and error streams are merged to form one composite stream. + * + * @param redirect the new value of the redirection flag. + * @return a reference to this process builder. + */ + public ProcessBuilder redirectErrorStream(boolean redirect) + { + this.redirect = redirect; + return this; + } + + /** + *

    + * Starts execution of a new process, based on the attributes of + * this ProcessBuilder object. This is the point + * at which the command-line arguments are checked. The list + * must be non-empty and contain only non-null string objects. + * The other attributes have default values which are used in + * cases where their values are not explicitly specified. + *

    + *

    + * If a security manager is in place, then the + * {@link SecurityManager#checkExec()} method is called to + * ensure that permission is given to execute the process. + *

    + *

    + * The execution of the process is system-dependent. Various + * exceptions may result, due to problems at the operating system + * level. These are all returned as a form of {@link IOException}. + *

    + * + * @return a Process object, representing the spawned + * subprocess. + * @throws IOException if a problem occurs with executing the process + * at the operating system level. + * @throws IndexOutOfBoundsException if the command to execute is + * actually an empty list. + * @throws NullPointerException if the command to execute is null + * or the list contains null elements. + * @throws SecurityException if a security manager exists and prevents + * execution of the subprocess. + */ + public Process start() throws IOException + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExec(command.get(0)); + return VMProcess.exec(command, environment, directory, redirect); + } +} diff --git a/libjava/classpath/java/lang/Readable.java b/libjava/classpath/java/lang/Readable.java new file mode 100644 index 000000000..d8967652b --- /dev/null +++ b/libjava/classpath/java/lang/Readable.java @@ -0,0 +1,72 @@ +/* Readable.java -- A character source + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.IOException; +import java.nio.CharBuffer; +import java.nio.ReadOnlyBufferException; + +/** + * A Readable object is simply a source for Unicode character + * data. On request, a Readable will provide its data in + * a supplied CharBuffer. + * + * @author Tom Tromey + * @author Andrew John Hughes + * @since 1.5 + */ +public interface Readable +{ + + /** + * Adds the character data supplied by this Readable + * to the specified character buffer. This method simply places + * each character into the buffer as supplied, using put(), + * without flipping or rewinding. + * + * @param buf the buffer to place the character data in. + * @return the number of char values placed in the buffer, + * or -1 if no more characters are available. + * @throws IOException if an I/O error occurs. + * @throws NullPointerException if buf is null. + * @throws ReadOnlyBufferException if buf is read only. + */ + int read(CharBuffer buf) + throws IOException; + +} diff --git a/libjava/classpath/java/lang/Runnable.java b/libjava/classpath/java/lang/Runnable.java new file mode 100644 index 000000000..32c52b94a --- /dev/null +++ b/libjava/classpath/java/lang/Runnable.java @@ -0,0 +1,62 @@ +/* Runnable -- interface for a method tied to an Object; often for Threads + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Runnable is an interface you implement to indicate that your class can be + * executed as the main part of a Thread, among other places. When you want + * an entry point to run a piece of code, implement this interface and + * override run. + * + * @author Paul Fisher + * @author Tom Tromey (tromey@cygnus.com) + * @see Thread + * @since 1.0 + * @status updated to 1.4 + */ +public interface Runnable +{ + /** + * This method will be called by whoever wishes to run your class + * implementing Runnable. Note that there are no restrictions on what + * you are allowed to do in the run method, except that you cannot + * throw a checked exception. + */ + void run(); +} diff --git a/libjava/classpath/java/lang/Runtime.java b/libjava/classpath/java/lang/Runtime.java new file mode 100644 index 000000000..3134c2a47 --- /dev/null +++ b/libjava/classpath/java/lang/Runtime.java @@ -0,0 +1,796 @@ +/* Runtime.java -- access to the VM process + Copyright (C) 1998, 2002, 2003, 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.SystemProperties; +import gnu.classpath.VMStackWalker; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * Runtime represents the Virtual Machine. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Jeroen Frijters + */ +// No idea why this class isn't final, since you can't build a subclass! +public class Runtime +{ + /** + * The library path, to search when loading libraries. We can also safely use + * this as a lock for synchronization. + */ + private final String[] libpath; + + /** + * The thread that started the exit sequence. Access to this field must + * be thread-safe; lock on libpath to avoid deadlock with user code. + * runFinalization() may want to look at this to see if ALL + * finalizers should be run, because the virtual machine is about to halt. + */ + private Thread exitSequence; + + /** + * All shutdown hooks. This is initialized lazily, and set to null once all + * shutdown hooks have run. Access to this field must be thread-safe; lock + * on libpath to avoid deadlock with user code. + */ + private Set shutdownHooks; + + /** + * The one and only runtime instance. + */ + private static final Runtime current = new Runtime(); + + /** + * Not instantiable by a user, this should only create one instance. + */ + private Runtime() + { + if (current != null) + throw new InternalError("Attempt to recreate Runtime"); + + // If used by underlying VM this contains the directories where Classpath's own + // native libraries are located. + String bootPath = SystemProperties.getProperty("gnu.classpath.boot.library.path", ""); + + // If properly set by the user this contains the directories where the application's + // native libraries are located. On operating systems where a LD_LIBRARY_PATH environment + // variable is available a VM should preset java.library.path with value of this + // variable. + String path = SystemProperties.getProperty("java.library.path", "."); + String pathSep = SystemProperties.getProperty("path.separator", ":"); + String fileSep = SystemProperties.getProperty("file.separator", "/"); + + StringTokenizer t1 = new StringTokenizer(bootPath, pathSep); + StringTokenizer t2 = new StringTokenizer(path, pathSep); + libpath = new String[t1.countTokens() + t2.countTokens()]; + + int i = 0; + while(t1.hasMoreTokens()) { + String prefix = t1.nextToken(); + if (! prefix.endsWith(fileSep)) + prefix += fileSep; + + libpath[i] = prefix; + i++; + } + + while(t2.hasMoreTokens()) { + String prefix = t2.nextToken(); + if (! prefix.endsWith(fileSep)) + prefix += fileSep; + + libpath[i] = prefix; + i++; + } + } + + /** + * Get the current Runtime object for this JVM. This is necessary to access + * the many instance methods of this class. + * + * @return the current Runtime object + */ + public static Runtime getRuntime() + { + return current; + } + + /** + * Exit the Java runtime. This method will either throw a SecurityException + * or it will never return. The status code is returned to the system; often + * a non-zero status code indicates an abnormal exit. Of course, there is a + * security check, checkExit(status). + * + *

    First, all shutdown hooks are run, in unspecified order, and + * concurrently. Next, if finalization on exit has been enabled, all pending + * finalizers are run. Finally, the system calls halt.

    + * + *

    If this is run a second time after shutdown has already started, there + * are two actions. If shutdown hooks are still executing, it blocks + * indefinitely. Otherwise, if the status is nonzero it halts immediately; + * if it is zero, it blocks indefinitely. This is typically called by + * System.exit.

    + * + * @param status the status to exit with + * @throws SecurityException if permission is denied + * @see #addShutdownHook(Thread) + * @see #runFinalizersOnExit(boolean) + * @see #runFinalization() + * @see #halt(int) + */ + public void exit(int status) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExit(status); + + if (runShutdownHooks()) + halt(status); + + // Someone else already called runShutdownHooks(). + // Make sure we are not/no longer in the shutdownHooks set. + // And wait till the thread that is calling runShutdownHooks() finishes. + synchronized (libpath) + { + if (shutdownHooks != null) + { + shutdownHooks.remove(Thread.currentThread()); + // Interrupt the exit sequence thread, in case it was waiting + // inside a join on our thread. + exitSequence.interrupt(); + // Shutdown hooks are still running, so we clear status to + // make sure we don't halt. + status = 0; + } + } + + // If exit() is called again after the shutdown hooks have run, but + // while finalization for exit is going on and the status is non-zero + // we halt immediately. + if (status != 0) + halt(status); + + while (true) + try + { + exitSequence.join(); + } + catch (InterruptedException e) + { + // Ignore, we've suspended indefinitely to let all shutdown + // hooks complete, and to let any non-zero exits through, because + // this is a duplicate call to exit(0). + } + } + + /** + * On first invocation, run all the shutdown hooks and return true. + * Any subsequent invocations will simply return false. + * Note that it is package accessible so that VMRuntime can call it + * when VM exit is not triggered by a call to Runtime.exit(). + * + * @return was the current thread the first one to call this method? + */ + boolean runShutdownHooks() + { + boolean first = false; + synchronized (libpath) // Synch on libpath, not this, to avoid deadlock. + { + if (exitSequence == null) + { + first = true; + exitSequence = Thread.currentThread(); + if (shutdownHooks != null) + { + Iterator i = shutdownHooks.iterator(); + while (i.hasNext()) // Start all shutdown hooks. + try + { + ((Thread) i.next()).start(); + } + catch (IllegalThreadStateException e) + { + i.remove(); + } + } + } + } + if (first) + { + if (shutdownHooks != null) + { + // Check progress of all shutdown hooks. As a hook completes, + // remove it from the set. If a hook calls exit, it removes + // itself from the set, then waits indefinitely on the + // exitSequence thread. Once the set is empty, set it to null to + // signal all finalizer threads that halt may be called. + while (true) + { + Thread[] hooks; + synchronized (libpath) + { + hooks = new Thread[shutdownHooks.size()]; + shutdownHooks.toArray(hooks); + } + if (hooks.length == 0) + break; + for (int i = 0; i < hooks.length; i++) + { + try + { + synchronized (libpath) + { + if (!shutdownHooks.contains(hooks[i])) + continue; + } + hooks[i].join(); + synchronized (libpath) + { + shutdownHooks.remove(hooks[i]); + } + } + catch (InterruptedException x) + { + // continue waiting on the next thread + } + } + } + synchronized (libpath) + { + shutdownHooks = null; + } + } + // Run finalization on all finalizable objects (even if they are + // still reachable). + VMRuntime.runFinalizationForExit(); + } + return first; + } + + /** + * Register a new shutdown hook. This is invoked when the program exits + * normally (because all non-daemon threads ended, or because + * System.exit was invoked), or when the user terminates + * the virtual machine (such as by typing ^C, or logging off). There is + * a security check to add hooks, + * RuntimePermission("shutdownHooks"). + * + *

    The hook must be an initialized, but unstarted Thread. The threads + * are run concurrently, and started in an arbitrary order; and user + * threads or daemons may still be running. Once shutdown hooks have + * started, they must all complete, or else you must use halt, + * to actually finish the shutdown sequence. Attempts to modify hooks + * after shutdown has started result in IllegalStateExceptions.

    + * + *

    It is imperative that you code shutdown hooks defensively, as you + * do not want to deadlock, and have no idea what other hooks will be + * running concurrently. It is also a good idea to finish quickly, as the + * virtual machine really wants to shut down!

    + * + *

    There are no guarantees that such hooks will run, as there are ways + * to forcibly kill a process. But in such a drastic case, shutdown hooks + * would do little for you in the first place.

    + * + * @param hook an initialized, unstarted Thread + * @throws IllegalArgumentException if the hook is already registered or run + * @throws IllegalStateException if the virtual machine is already in + * the shutdown sequence + * @throws SecurityException if permission is denied + * @since 1.3 + * @see #removeShutdownHook(Thread) + * @see #exit(int) + * @see #halt(int) + */ + public void addShutdownHook(Thread hook) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkPermission(new RuntimePermission("shutdownHooks")); + if (hook.isAlive() || hook.getThreadGroup() == null) + throw new IllegalArgumentException("The hook thread " + hook + " must not have been already run or started"); + synchronized (libpath) + { + if (exitSequence != null) + throw new IllegalStateException("The Virtual Machine is exiting. It is not possible anymore to add any hooks"); + if (shutdownHooks == null) + { + VMRuntime.enableShutdownHooks(); + shutdownHooks = new HashSet(); // Lazy initialization. + } + if (! shutdownHooks.add(hook)) + throw new IllegalArgumentException(hook.toString() + " had already been inserted"); + } + } + + /** + * De-register a shutdown hook. As when you registered it, there is a + * security check to remove hooks, + * RuntimePermission("shutdownHooks"). + * + * @param hook the hook to remove + * @return true if the hook was successfully removed, false if it was not + * registered in the first place + * @throws IllegalStateException if the virtual machine is already in + * the shutdown sequence + * @throws SecurityException if permission is denied + * @since 1.3 + * @see #addShutdownHook(Thread) + * @see #exit(int) + * @see #halt(int) + */ + public boolean removeShutdownHook(Thread hook) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkPermission(new RuntimePermission("shutdownHooks")); + synchronized (libpath) + { + if (exitSequence != null) + throw new IllegalStateException(); + if (shutdownHooks != null) + return shutdownHooks.remove(hook); + } + return false; + } + + /** + * Forcibly terminate the virtual machine. This call never returns. It is + * much more severe than exit, as it bypasses all shutdown + * hooks and initializers. Use caution in calling this! Of course, there is + * a security check, checkExit(status). + * + * @param status the status to exit with + * @throws SecurityException if permission is denied + * @since 1.3 + * @see #exit(int) + * @see #addShutdownHook(Thread) + */ + public void halt(int status) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExit(status); + VMRuntime.exit(status); + } + + /** + * Tell the VM to run the finalize() method on every single Object before + * it exits. Note that the JVM may still exit abnormally and not perform + * this, so you still don't have a guarantee. And besides that, this is + * inherently unsafe in multi-threaded code, as it may result in deadlock + * as multiple threads compete to manipulate objects. This value defaults to + * false. There is a security check, checkExit(0). + * + * @param finalizeOnExit whether to finalize all Objects on exit + * @throws SecurityException if permission is denied + * @see #exit(int) + * @see #gc() + * @since 1.1 + * @deprecated never rely on finalizers to do a clean, thread-safe, + * mop-up from your code + */ + public static void runFinalizersOnExit(boolean finalizeOnExit) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExit(0); + VMRuntime.runFinalizersOnExit(finalizeOnExit); + } + + /** + * Create a new subprocess with the specified command line. Calls + * exec(cmdline, null, null). A security check is performed, + * checkExec. + * + * @param cmdline the command to call + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmdline is null + * @throws IndexOutOfBoundsException if cmdline is "" + */ + public Process exec(String cmdline) throws IOException + { + return exec(cmdline, null, null); + } + + /** + * Create a new subprocess with the specified command line and environment. + * If the environment is null, the process inherits the environment of + * this process. Calls exec(cmdline, env, null). A security + * check is performed, checkExec. + * + * @param cmdline the command to call + * @param env the environment to use, in the format name=value + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmdline is null, or env has null entries + * @throws IndexOutOfBoundsException if cmdline is "" + */ + public Process exec(String cmdline, String[] env) throws IOException + { + return exec(cmdline, env, null); + } + + /** + * Create a new subprocess with the specified command line, environment, and + * working directory. If the environment is null, the process inherits the + * environment of this process. If the directory is null, the process uses + * the current working directory. This splits cmdline into an array, using + * the default StringTokenizer, then calls + * exec(cmdArray, env, dir). A security check is performed, + * checkExec. + * + * @param cmdline the command to call + * @param env the environment to use, in the format name=value + * @param dir the working directory to use + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmdline is null, or env has null entries + * @throws IndexOutOfBoundsException if cmdline is "" + * @since 1.3 + */ + public Process exec(String cmdline, String[] env, File dir) + throws IOException + { + StringTokenizer t = new StringTokenizer(cmdline); + String[] cmd = new String[t.countTokens()]; + for (int i = 0; i < cmd.length; i++) + cmd[i] = t.nextToken(); + return exec(cmd, env, dir); + } + + /** + * Create a new subprocess with the specified command line, already + * tokenized. Calls exec(cmd, null, null). A security check + * is performed, checkExec. + * + * @param cmd the command to call + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmd is null, or has null entries + * @throws IndexOutOfBoundsException if cmd is length 0 + */ + public Process exec(String[] cmd) throws IOException + { + return exec(cmd, null, null); + } + + /** + * Create a new subprocess with the specified command line, already + * tokenized, and specified environment. If the environment is null, the + * process inherits the environment of this process. Calls + * exec(cmd, env, null). A security check is performed, + * checkExec. + * + * @param cmd the command to call + * @param env the environment to use, in the format name=value + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmd is null, or cmd or env has null + * entries + * @throws IndexOutOfBoundsException if cmd is length 0 + */ + public Process exec(String[] cmd, String[] env) throws IOException + { + return exec(cmd, env, null); + } + + /** + * Create a new subprocess with the specified command line, already + * tokenized, and the specified environment and working directory. If the + * environment is null, the process inherits the environment of this + * process. If the directory is null, the process uses the current working + * directory. A security check is performed, checkExec. + * + * @param cmd the command to call + * @param env the environment to use, in the format name=value + * @param dir the working directory to use + * @return the Process object + * @throws SecurityException if permission is denied + * @throws IOException if an I/O error occurs + * @throws NullPointerException if cmd is null, or cmd or env has null + * entries + * @throws IndexOutOfBoundsException if cmd is length 0 + * @since 1.3 + */ + public Process exec(String[] cmd, String[] env, File dir) + throws IOException + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkExec(cmd[0]); + return VMRuntime.exec(cmd, env, dir); + } + + /** + * Returns the number of available processors currently available to the + * virtual machine. This number may change over time; so a multi-processor + * program want to poll this to determine maximal resource usage. + * + * @return the number of processors available, at least 1 + */ + public int availableProcessors() + { + return VMRuntime.availableProcessors(); + } + + /** + * Find out how much memory is still free for allocating Objects on the heap. + * + * @return the number of bytes of free memory for more Objects + */ + public long freeMemory() + { + return VMRuntime.freeMemory(); + } + + /** + * Find out how much memory total is available on the heap for allocating + * Objects. + * + * @return the total number of bytes of memory for Objects + */ + public long totalMemory() + { + return VMRuntime.totalMemory(); + } + + /** + * Returns the maximum amount of memory the virtual machine can attempt to + * use. This may be Long.MAX_VALUE if there is no inherent + * limit (or if you really do have a 8 exabyte memory!). + * + * @return the maximum number of bytes the virtual machine will attempt + * to allocate + */ + public long maxMemory() + { + return VMRuntime.maxMemory(); + } + + /** + * Run the garbage collector. This method is more of a suggestion than + * anything. All this method guarantees is that the garbage collector will + * have "done its best" by the time it returns. Notice that garbage + * collection takes place even without calling this method. + */ + public void gc() + { + VMRuntime.gc(); + } + + /** + * Run finalization on all Objects that are waiting to be finalized. Again, + * a suggestion, though a stronger one than {@link #gc()}. This calls the + * finalize method of all objects waiting to be collected. + * + * @see #finalize() + */ + public void runFinalization() + { + VMRuntime.runFinalization(); + } + + /** + * Tell the VM to trace every bytecode instruction that executes (print out + * a trace of it). No guarantees are made as to where it will be printed, + * and the VM is allowed to ignore this request. + * + * @param on whether to turn instruction tracing on + */ + public void traceInstructions(boolean on) + { + VMRuntime.traceInstructions(on); + } + + /** + * Tell the VM to trace every method call that executes (print out a trace + * of it). No guarantees are made as to where it will be printed, and the + * VM is allowed to ignore this request. + * + * @param on whether to turn method tracing on + */ + public void traceMethodCalls(boolean on) + { + VMRuntime.traceMethodCalls(on); + } + + /** + * Load a native library using the system-dependent filename. This is similar + * to loadLibrary, except the only name mangling done is inserting "_g" + * before the final ".so" if the VM was invoked by the name "java_g". There + * may be a security check, of checkLink. + * + *

    + * The library is loaded using the class loader associated with the + * class associated with the invoking method. + * + * @param filename the file to load + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the library is not found + */ + public void load(String filename) + { + load(filename, VMStackWalker.getCallingClassLoader()); + } + + /** + * Same as load(String) but using the given loader. + * + * @param filename the file to load + * @param loader class loader, or null for the boot loader + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the library is not found + */ + void load(String filename, ClassLoader loader) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkLink(filename); + if (loadLib(filename, loader) == 0) + throw new UnsatisfiedLinkError("Could not load library " + filename); + } + + /** + * Do a security check on the filename and then load the native library. + * + * @param filename the file to load + * @param loader class loader, or null for the boot loader + * @return 0 on failure, nonzero on success + * @throws SecurityException if file read permission is denied + */ + private static int loadLib(String filename, ClassLoader loader) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkRead(filename); + return VMRuntime.nativeLoad(filename, loader); + } + + /** + * Load a native library using a system-independent "short name" for the + * library. It will be transformed to a correct filename in a + * system-dependent manner (for example, in Windows, "mylib" will be turned + * into "mylib.dll"). This is done as follows: if the context that called + * load has a ClassLoader cl, then cl.findLibrary(libpath) is + * used to convert the name. If that result was null, or there was no class + * loader, this searches each directory of the system property + * java.library.path for a file named + * System.mapLibraryName(libname). There may be a security + * check, of checkLink. + * + *

    Note: Besides java.library.path a VM may chose to search + * for native libraries in a path that is specified by the + * gnu.classpath.boot.library.path system property. However + * this is for internal usage or development of GNU Classpath only. + * A Java application must not load a non-system library by changing + * this property otherwise it will break compatibility.

    + * + *

    + * The library is loaded using the class loader associated with the + * class associated with the invoking method. + * + * @param libname the library to load + * + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the library is not found + * + * @see System#mapLibraryName(String) + * @see ClassLoader#findLibrary(String) + */ + public void loadLibrary(String libname) + { + loadLibrary(libname, VMStackWalker.getCallingClassLoader()); + } + + /** + * Same as loadLibrary(String) but using the given loader. + * + * @param libname the library to load + * @param loader class loader, or null for the boot loader + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the library is not found + */ + void loadLibrary(String libname, ClassLoader loader) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe! + if (sm != null) + sm.checkLink(libname); + String filename; + if (loader != null && (filename = loader.findLibrary(libname)) != null) + { + if (loadLib(filename, loader) != 0) + return; + } + else + { + filename = VMRuntime.mapLibraryName(libname); + for (int i = 0; i < libpath.length; i++) + if (loadLib(libpath[i] + filename, loader) != 0) + return; + } + throw new UnsatisfiedLinkError("Native library `" + libname + + "' not found (as file `" + filename + "') in gnu.classpath.boot.library.path and java.library.path"); + } + + /** + * Return a localized version of this InputStream, meaning all characters + * are localized before they come out the other end. + * + * @param in the stream to localize + * @return the localized stream + * @deprecated InputStreamReader is the preferred way to read + * local encodings + * @XXX This implementation does not localize, yet. + */ + public InputStream getLocalizedInputStream(InputStream in) + { + return in; + } + + /** + * Return a localized version of this OutputStream, meaning all characters + * are localized before they are sent to the other end. + * + * @param out the stream to localize + * @return the localized stream + * @deprecated OutputStreamWriter is the preferred way to write + * local encodings + * @XXX This implementation does not localize, yet. + */ + public OutputStream getLocalizedOutputStream(OutputStream out) + { + return out; + } +} // class Runtime diff --git a/libjava/classpath/java/lang/RuntimeException.java b/libjava/classpath/java/lang/RuntimeException.java new file mode 100644 index 000000000..72cf0872b --- /dev/null +++ b/libjava/classpath/java/lang/RuntimeException.java @@ -0,0 +1,102 @@ +/* RuntimeException.java -- root of all unchecked exceptions + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * All exceptions which are subclasses of RuntimeException + * can be thrown at any time during the execution of a Java virtual machine. + * Methods which throw these exceptions are not required to declare them + * in their throws clause. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class RuntimeException extends Exception +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -7034897190745766939L; + + /** + * Create an exception without a message. The cause remains uninitialized. + * + * @see #initCause(Throwable) + */ + public RuntimeException() + { + } + + /** + * Create an exception with a message. The cause remains uninitialized. + * + * @param s the message string + * @see #initCause(Throwable) + */ + public RuntimeException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message string + * @param cause the cause of this exception + * @since 1.4 + */ + public RuntimeException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create an exception with the given cause, and a message of + * cause == null ? null : cause.toString(). + * + * @param cause the cause of this exception + * @since 1.4 + */ + public RuntimeException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/lang/RuntimePermission.java b/libjava/classpath/java/lang/RuntimePermission.java new file mode 100644 index 000000000..2f80b9107 --- /dev/null +++ b/libjava/classpath/java/lang/RuntimePermission.java @@ -0,0 +1,209 @@ +/* RuntimePermission.java -- permission for a secure runtime action + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.security.BasicPermission; +import java.security.Permission; + +/** + * A RuntimePermission contains a permission name, but no + * actions list. This means you either have the permission or you don't. + * + * Permission names have the follow the hierarchial property naming + * convention. In addition, an asterisk may appear at the end of a + * name if following a period or by itself. + * + * + * + * + * + *
    Valid namesInvalid names
    "accessClassInPackage.*","*""**", "*x", "*.a"
    + *
    + * + * The following table provides a list of all the possible RuntimePermission + * permission names with a description of what that permission allows.
    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Permission NamePermission AllowsRisks + *
    createClassLoadercreation of a class loadera class loader can load rogue classes which bypass all security + * permissions
    getClassLoaderretrieval of the class loader for the calling classrogue code could load classes not otherwise available
    setContextClassLoaderallows the setting of the context class loader used by a threadrogue code could change the context class loader needed by system + * threads
    setSecurityManagerallows the application to replace the security managerthe new manager may be less restrictive, so that rogue code can + * bypass existing security checks
    createSecurityManagerallows the application to create a new security managerrogue code can use the new security manager to discover information + * about the execution stack
    exitVMallows the application to halt the virtual machinerogue code can mount a denial-of-service attack by killing the + * virtual machine
    shutdownHooksallows registration and modification of shutdown hooksrogue code can add a hook that interferes with clean + * virtual machine shutdown
    setFactoryallows the application to set the socket factory for socket, + * server socket, stream handler, or RMI socket factory.rogue code can create a rogue network object which mangles or + * intercepts data
    setIOallows the application to set System.out, System.in, and + * System.errrogue code could sniff user input and intercept or mangle + * output
    modifyThreadallows the application to modify any thread in the virtual machine + * using any of the methods stop, resume, + * suspend, setPriority, and + * setName of classs Threadrogue code could adversely modify system or user threads
    stopThreadallows the application to stop any thread it has + * access to in the systemrogue code can stop arbitrary threads
    modifyThreadGroupallows the application to modify thread groups using any of the + * methods destroy, resume, + * setDaemon, setMaxPriority, + * stop, and suspend of the class + * ThreadGrouprogue code can mount a denial-of-service attack by changing run + * priorities
    getProtectionDomainretrieve a class's ProtectionDomainrogue code can gain information about the security policy, to + * prepare a better attack
    readFileDescriptorread a file descriptorrogue code can read sensitive information
    writeFileDescriptorwrite a file descriptorrogue code can write files, including viruses, and can modify the + * virtual machine binary; if not just fill up the disk
    loadLibrary.library namedynamic linking of the named librarynative code can bypass many security checks of pure Java
    accessClassInPackage.package nameaccess to a package via a ClassLoaderrogue code can access classes not normally available
    defineClassInPackage.package namedefine a class inside a given packagerogue code can install rogue classes, including in trusted packages + * like java.security or java.lang
    accessDeclaredMembersaccess declared class members via reflectionrogue code can discover information, invoke methods, or modify fields + * that are not otherwise available
    queuePrintJobinitiate a print jobrogue code could make a hard copy of sensitive information, or + * simply waste paper
    + * + * @author Brian Jones + * @author Eric Blake (ebb9@email.byu.edu) + * @see BasicPermission + * @see Permission + * @see SecurityManager + * @since 1.2 + * @status updated to 1.4 + */ +public final class RuntimePermission extends BasicPermission +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7399184964622342223L; + + /** + * Create a new permission with the specified name. + * + * @param permissionName the name of the granted permission + * @throws NullPointerException if name is null + * @throws IllegalArgumentException thrown if name is empty or invalid + */ + public RuntimePermission(String permissionName) + { + super(permissionName); + } + + /** + * Create a new permission with the specified name. The actions argument + * is ignored, as runtime permissions have no actions. + * + * @param permissionName the name of the granted permission + * @param actions ignored + * @throws NullPointerException if name is null + * @throws IllegalArgumentException thrown if name is empty or invalid + */ + public RuntimePermission(String permissionName, String actions) + { + super(permissionName); + } +} diff --git a/libjava/classpath/java/lang/SecurityException.java b/libjava/classpath/java/lang/SecurityException.java new file mode 100644 index 000000000..f20fbe04f --- /dev/null +++ b/libjava/classpath/java/lang/SecurityException.java @@ -0,0 +1,128 @@ +/* SecurityException.java -- thrown to indicate a security violation + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * The security manager will throw this exception to indicate a security + * violation. This can occur any time an operation is attempted which is + * deemed unsafe by the current security policies. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see SecurityManager + * @status updated to 1.5 + */ +public class SecurityException extends RuntimeException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 6878364983674394167L; + + /** + * Create an exception without a message. + */ + public SecurityException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public SecurityException(String s) + { + super(s); + } + + /** + *

    + * Constructs a SecurityException using + * the specified error message, which should give further details + * as to the reason for this exception. The specified cause + * Throwable may be used to provide additional history, + * with regards to the root of the problem. It is perfectly valid + * for this to be null, if the cause of the problem is unknown. + *

    + *

    + * Note: the detail message from the cause is not + * automatically incorporated into the resulting detail message of + * this exception. + *

    + * + * @param message the detail message, which should give the reason for + * this exception being thrown. + * @param cause the cause of this exception, or null if the cause + * is unknown. + * @since 1.5 + */ + public SecurityException(String message, Throwable cause) + { + super(message, cause); + } + + /** + *

    + * Constructs a SecurityException using + * the specified cause Throwable, which may be used + * to provide additional history, with regards to the root of the + * problem. It is perfectly valid for this to be null, if the + * cause of the problem is unknown. + *

    + *

    + * The detail message is automatically constructed from the detail + * message of the supplied causal exception. If the cause is null, + * then the detail message will also be null. Otherwise, the detail + * message of this exception will be that of the causal exception. + * This makes this constructor very useful for simply wrapping another + * exception. + *

    + * + * @param cause the cause of this exception, or null if the cause + * is unknown. + * @since 1.5 + */ + public SecurityException(Throwable cause) + { + super(cause); + } + +} diff --git a/libjava/classpath/java/lang/SecurityManager.java b/libjava/classpath/java/lang/SecurityManager.java new file mode 100644 index 000000000..d7adc112f --- /dev/null +++ b/libjava/classpath/java/lang/SecurityManager.java @@ -0,0 +1,1087 @@ +/* SecurityManager.java -- security checks for privileged actions + Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.VMStackWalker; + +import java.awt.AWTPermission; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilePermission; +import java.io.RandomAccessFile; +import java.lang.reflect.Member; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketImplFactory; +import java.net.SocketPermission; +import java.net.URL; +import java.net.URLStreamHandlerFactory; +import java.security.AccessControlContext; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.BasicPermission; +import java.security.Permission; +import java.security.Policy; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.security.Security; +import java.security.SecurityPermission; +import java.util.Properties; +import java.util.PropertyPermission; +import java.util.StringTokenizer; + +/** + * SecurityManager is a class you can extend to create your own Java + * security policy. By default, there is no SecurityManager installed in + * 1.1, which means that all things are permitted to all people. The security + * manager, if set, is consulted before doing anything with potentially + * dangerous results, and throws a SecurityException if the + * action is forbidden. + * + *

    A typical check is as follows, just before the dangerous operation:
    + *

    + * SecurityManager sm = System.getSecurityManager();
    + * if (sm != null)
    + *   sm.checkABC(argument, ...);
    + * 
    + * Note that this is thread-safe, by caching the security manager in a local + * variable rather than risking a NullPointerException if the mangager is + * changed between the check for null and before the permission check. + * + *

    The special method checkPermission is a catchall, and + * the default implementation calls + * AccessController.checkPermission. In fact, all the other + * methods default to calling checkPermission. + * + *

    Sometimes, the security check needs to happen from a different context, + * such as when called from a worker thread. In such cases, use + * getSecurityContext to take a snapshot that can be passed + * to the worker thread:
    + *

    + * Object context = null;
    + * SecurityManager sm = System.getSecurityManager();
    + * if (sm != null)
    + *   context = sm.getSecurityContext(); // defaults to an AccessControlContext
    + * // now, in worker thread
    + * if (sm != null)
    + *   sm.checkPermission(permission, context);
    + * 
    + * + *

    Permissions fall into these categories: File, Socket, Net, Security, + * Runtime, Property, AWT, Reflect, and Serializable. Each of these + * permissions have a property naming convention, that follows a hierarchical + * naming convention, to make it easy to grant or deny several permissions + * at once. Some permissions also take a list of permitted actions, such + * as "read" or "write", to fine-tune control even more. The permission + * java.security.AllPermission grants all permissions. + * + *

    The default methods in this class deny all things to all people. You + * must explicitly grant permission for anything you want to be legal when + * subclassing this class. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @see ClassLoader + * @see SecurityException + * @see #checkTopLevelWindow(Object) + * @see System#getSecurityManager() + * @see System#setSecurityManager(SecurityManager) + * @see AccessController + * @see AccessControlContext + * @see AccessControlException + * @see Permission + * @see BasicPermission + * @see java.io.FilePermission + * @see java.net.SocketPermission + * @see java.util.PropertyPermission + * @see RuntimePermission + * @see java.awt.AWTPermission + * @see Policy + * @see SecurityPermission + * @see ProtectionDomain + * @since 1.0 + * @status still missing 1.4 functionality + */ +public class SecurityManager +{ + /** + * The current security manager. This is located here instead of in + * System, to avoid security problems, as well as bootstrap issues. + * Make sure to access it in a thread-safe manner; it is package visible + * to avoid overhead in java.lang. + */ + static volatile SecurityManager current; + + /** + * Tells whether or not the SecurityManager is currently performing a + * security check. + * @deprecated Use {@link #checkPermission(Permission)} instead. + */ + protected boolean inCheck; + + /** + * Construct a new security manager. There may be a security check, of + * RuntimePermission("createSecurityManager"). + * + * @throws SecurityException if permission is denied + */ + public SecurityManager() + { + /* "When there is security manager installed, the security manager + need to check the package access. However, if the security + manager itself uses any unloaded class, it will trigger the + classloading, which causes infinite loop. There is no easy + legal solution. The workaround will be that security manager + can not depend on any unloaded class. In the constructor of + security manager, it must transitively load all classes it + refers to." Sun bug #4242924. */ + + // Load and initialize java.security.Security + java.security.Security.getProvider((String)null); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new RuntimePermission("createSecurityManager")); + } + + /** + * Tells whether or not the SecurityManager is currently performing a + * security check. + * + * @return true if the SecurityManager is in a security check + * @see #inCheck + * @deprecated use {@link #checkPermission(Permission)} instead + */ + public boolean getInCheck() + { + return inCheck; + } + + /** + * Get a list of all the classes currently executing methods on the Java + * stack. getClassContext()[0] is the currently executing method (ie. the + * class that CALLED getClassContext, not SecurityManager). + * + * @return an array of classes on the Java execution stack + */ + protected Class[] getClassContext() + { + Class[] stack1 = VMStackWalker.getClassContext(); + Class[] stack2 = new Class[stack1.length - 1]; + System.arraycopy(stack1, 1, stack2, 0, stack1.length - 1); + return stack2; + } + + /** + * Find the ClassLoader of the first non-system class on the execution + * stack. A non-system class is one whose ClassLoader is not equal to + * {@link ClassLoader#getSystemClassLoader()} or its ancestors. This + * will return null in three cases: + * + *

      + *
    • All methods on the stack are from system classes
    • + *
    • All methods on the stack up to the first "privileged" caller, as + * created by {@link AccessController#doPrivileged(PrivilegedAction)}, + * are from system classes
    • + *
    • A check of java.security.AllPermission succeeds.
    • + *
    + * + * @return the most recent non-system ClassLoader on the execution stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected ClassLoader currentClassLoader() + { + Class cl = currentLoadedClass(); + return cl != null ? cl.getClassLoader() : null; + } + + /** + * Find the first non-system class on the execution stack. A non-system + * class is one whose ClassLoader is not equal to + * {@link ClassLoader#getSystemClassLoader()} or its ancestors. This + * will return null in three cases: + * + *
      + *
    • All methods on the stack are from system classes
    • + *
    • All methods on the stack up to the first "privileged" caller, as + * created by {@link AccessController#doPrivileged(PrivilegedAction)}, + * are from system classes
    • + *
    • A check of java.security.AllPermission succeeds.
    • + *
    + * + * @return the most recent non-system Class on the execution stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected Class currentLoadedClass() + { + int i = classLoaderDepth(); + return i >= 0 ? getClassContext()[i] : null; + } + + /** + * Get the depth of a particular class on the execution stack. + * + * @param className the fully-qualified name to search for + * @return the index of the class on the stack, or -1 + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected int classDepth(String className) + { + Class[] c = getClassContext(); + for (int i = 0; i < c.length; i++) + if (className.equals(c[i].getName())) + return i; + return -1; + } + + /** + * Get the depth on the execution stack of the most recent non-system class. + * A non-system class is one whose ClassLoader is not equal to + * {@link ClassLoader#getSystemClassLoader()} or its ancestors. This + * will return -1 in three cases: + * + *
      + *
    • All methods on the stack are from system classes
    • + *
    • All methods on the stack up to the first "privileged" caller, as + * created by {@link AccessController#doPrivileged(PrivilegedAction)}, + * are from system classes
    • + *
    • A check of java.security.AllPermission succeeds.
    • + *
    + * + * @return the index of the most recent non-system Class on the stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected int classLoaderDepth() + { + try + { + checkPermission(new AllPermission()); + } + catch (SecurityException e) + { + Class[] c = getClassContext(); + for (int i = 0; i < c.length; i++) + if (c[i].getClassLoader() != null) + // XXX Check if c[i] is AccessController, or a system class. + return i; + } + return -1; + } + + /** + * Tell whether the specified class is on the execution stack. + * + * @param className the fully-qualified name of the class to find + * @return whether the specified class is on the execution stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected boolean inClass(String className) + { + return classDepth(className) != -1; + } + + /** + * Tell whether there is a class loaded with an explicit ClassLoader on + * the stack. + * + * @return whether a class with an explicit ClassLoader is on the stack + * @deprecated use {@link #checkPermission(Permission)} instead + */ + protected boolean inClassLoader() + { + return classLoaderDepth() != -1; + } + + /** + * Get an implementation-dependent Object that contains enough information + * about the current environment to be able to perform standard security + * checks later. This is used by trusted methods that need to verify that + * their callers have sufficient access to perform certain operations. + * + *

    Currently the only methods that use this are checkRead() and + * checkConnect(). The default implementation returns an + * AccessControlContext. + * + * @return a security context + * @see #checkConnect(String, int, Object) + * @see #checkRead(String, Object) + * @see AccessControlContext + * @see AccessController#getContext() + */ + public Object getSecurityContext() + { + return AccessController.getContext(); + } + + /** + * Check if the current thread is allowed to perform an operation that + * requires the specified Permission. This defaults to + * AccessController.checkPermission. + * + * @param perm the Permission required + * @throws SecurityException if permission is denied + * @throws NullPointerException if perm is null + * @since 1.2 + */ + public void checkPermission(Permission perm) + { + AccessController.checkPermission(perm); + } + + /** + * Check if the current thread is allowed to perform an operation that + * requires the specified Permission. This is done in a + * context previously returned by getSecurityContext(). The + * default implementation expects context to be an AccessControlContext, + * and it calls AccessControlContext.checkPermission(perm). + * + * @param perm the Permission required + * @param context a security context + * @throws SecurityException if permission is denied, or if context is + * not an AccessControlContext + * @throws NullPointerException if perm is null + * @see #getSecurityContext() + * @see AccessControlContext#checkPermission(Permission) + * @since 1.2 + */ + public void checkPermission(Permission perm, Object context) + { + if (! (context instanceof AccessControlContext)) + throw new SecurityException("Missing context"); + ((AccessControlContext) context).checkPermission(perm); + } + + /** + * Check if the current thread is allowed to create a ClassLoader. This + * method is called from ClassLoader.ClassLoader(), and checks + * RuntimePermission("createClassLoader"). If you override + * this, you should call super.checkCreateClassLoader() rather + * than throwing an exception. + * + * @throws SecurityException if permission is denied + * @see ClassLoader#ClassLoader() + */ + public void checkCreateClassLoader() + { + checkPermission(new RuntimePermission("createClassLoader")); + } + + /** + * Check if the current thread is allowed to modify another Thread. This is + * called by Thread.stop(), suspend(), resume(), interrupt(), destroy(), + * setPriority(), setName(), and setDaemon(). The default implementation + * checks RuntimePermission("modifyThread") on system threads + * (ie. threads in ThreadGroup with a null parent), and returns silently on + * other threads. + * + *

    If you override this, you must do two things. First, call + * super.checkAccess(t), to make sure you are not relaxing + * requirements. Second, if the calling thread has + * RuntimePermission("modifyThread"), return silently, so that + * core classes (the Classpath library!) can modify any thread. + * + * @param thread the other Thread to check + * @throws SecurityException if permission is denied + * @throws NullPointerException if thread is null + * @see Thread#stop() + * @see Thread#suspend() + * @see Thread#resume() + * @see Thread#setPriority(int) + * @see Thread#setName(String) + * @see Thread#setDaemon(boolean) + */ + public void checkAccess(Thread thread) + { + if (thread.getThreadGroup() != null + && thread.getThreadGroup().parent == null) + checkPermission(new RuntimePermission("modifyThread")); + } + + /** + * Check if the current thread is allowed to modify a ThreadGroup. This is + * called by Thread.Thread() (to add a thread to the ThreadGroup), + * ThreadGroup.ThreadGroup() (to add this ThreadGroup to a parent), + * ThreadGroup.stop(), suspend(), resume(), interrupt(), destroy(), + * setDaemon(), and setMaxPriority(). The default implementation + * checks RuntimePermission("modifyThread") on the system group + * (ie. the one with a null parent), and returns silently on other groups. + * + *

    If you override this, you must do two things. First, call + * super.checkAccess(t), to make sure you are not relaxing + * requirements. Second, if the calling thread has + * RuntimePermission("modifyThreadGroup"), return silently, + * so that core classes (the Classpath library!) can modify any thread. + * + * @param g the ThreadGroup to check + * @throws SecurityException if permission is denied + * @throws NullPointerException if g is null + * @see Thread#Thread() + * @see ThreadGroup#ThreadGroup(String) + * @see ThreadGroup#stop() + * @see ThreadGroup#suspend() + * @see ThreadGroup#resume() + * @see ThreadGroup#interrupt() + * @see ThreadGroup#setDaemon(boolean) + * @see ThreadGroup#setMaxPriority(int) + */ + public void checkAccess(ThreadGroup g) + { + if (g.parent == null) + checkPermission(new RuntimePermission("modifyThreadGroup")); + } + + /** + * Check if the current thread is allowed to exit the JVM with the given + * status. This method is called from Runtime.exit() and Runtime.halt(). + * The default implementation checks + * RuntimePermission("exitVM"). If you override this, call + * super.checkExit rather than throwing an exception. + * + * @param status the status to exit with + * @throws SecurityException if permission is denied + * @see Runtime#exit(int) + * @see Runtime#halt(int) + */ + public void checkExit(int status) + { + checkPermission(new RuntimePermission("exitVM")); + } + + /** + * Check if the current thread is allowed to execute the given program. This + * method is called from Runtime.exec(). If the name is an absolute path, + * the default implementation checks + * FilePermission(program, "execute"), otherwise it checks + * FilePermission("<<ALL FILES>>", "execute"). If + * you override this, call super.checkExec rather than + * throwing an exception. + * + * @param program the name of the program to exec + * @throws SecurityException if permission is denied + * @throws NullPointerException if program is null + * @see Runtime#exec(String[], String[], File) + */ + public void checkExec(String program) + { + if (! program.equals(new File(program).getAbsolutePath())) + program = "<>"; + checkPermission(new FilePermission(program, "execute")); + } + + /** + * Check if the current thread is allowed to link in the given native + * library. This method is called from Runtime.load() (and hence, by + * loadLibrary() as well). The default implementation checks + * RuntimePermission("loadLibrary." + filename). If you + * override this, call super.checkLink rather than throwing + * an exception. + * + * @param filename the full name of the library to load + * @throws SecurityException if permission is denied + * @throws NullPointerException if filename is null + * @see Runtime#load(String) + */ + public void checkLink(String filename) + { + // Use the toString() hack to do the null check. + checkPermission(new RuntimePermission("loadLibrary." + + filename.toString())); + } + + /** + * Check if the current thread is allowed to read the given file using the + * FileDescriptor. This method is called from + * FileInputStream.FileInputStream(). The default implementation checks + * RuntimePermission("readFileDescriptor"). If you override + * this, call super.checkRead rather than throwing an + * exception. + * + * @param desc the FileDescriptor representing the file to access + * @throws SecurityException if permission is denied + * @throws NullPointerException if desc is null + * @see FileInputStream#FileInputStream(FileDescriptor) + */ + public void checkRead(FileDescriptor desc) + { + if (desc == null) + throw new NullPointerException(); + checkPermission(new RuntimePermission("readFileDescriptor")); + } + + /** + * Check if the current thread is allowed to read the given file. This + * method is called from FileInputStream.FileInputStream(), + * RandomAccessFile.RandomAccessFile(), File.exists(), canRead(), isFile(), + * isDirectory(), lastModified(), length() and list(). The default + * implementation checks FilePermission(filename, "read"). If + * you override this, call super.checkRead rather than + * throwing an exception. + * + * @param filename the full name of the file to access + * @throws SecurityException if permission is denied + * @throws NullPointerException if filename is null + * @see File + * @see FileInputStream#FileInputStream(String) + * @see RandomAccessFile#RandomAccessFile(String, String) + */ + public void checkRead(String filename) + { + checkPermission(new FilePermission(filename, "read")); + } + + /** + * Check if the current thread is allowed to read the given file. using the + * given security context. The context must be a result of a previous call + * to getSecurityContext(). The default implementation checks + * AccessControlContext.checkPermission(new FilePermission(filename, + * "read")). If you override this, call super.checkRead + * rather than throwing an exception. + * + * @param filename the full name of the file to access + * @param context the context to determine access for + * @throws SecurityException if permission is denied, or if context is + * not an AccessControlContext + * @throws NullPointerException if filename is null + * @see #getSecurityContext() + * @see AccessControlContext#checkPermission(Permission) + */ + public void checkRead(String filename, Object context) + { + if (! (context instanceof AccessControlContext)) + throw new SecurityException("Missing context"); + AccessControlContext ac = (AccessControlContext) context; + ac.checkPermission(new FilePermission(filename, "read")); + } + + /** + * Check if the current thread is allowed to write the given file using the + * FileDescriptor. This method is called from + * FileOutputStream.FileOutputStream(). The default implementation checks + * RuntimePermission("writeFileDescriptor"). If you override + * this, call super.checkWrite rather than throwing an + * exception. + * + * @param desc the FileDescriptor representing the file to access + * @throws SecurityException if permission is denied + * @throws NullPointerException if desc is null + * @see FileOutputStream#FileOutputStream(FileDescriptor) + */ + public void checkWrite(FileDescriptor desc) + { + if (desc == null) + throw new NullPointerException(); + checkPermission(new RuntimePermission("writeFileDescriptor")); + } + + /** + * Check if the current thread is allowed to write the given file. This + * method is called from FileOutputStream.FileOutputStream(), + * RandomAccessFile.RandomAccessFile(), File.canWrite(), mkdir(), and + * renameTo(). The default implementation checks + * FilePermission(filename, "write"). If you override this, + * call super.checkWrite rather than throwing an exception. + * + * @param filename the full name of the file to access + * @throws SecurityException if permission is denied + * @throws NullPointerException if filename is null + * @see File + * @see File#canWrite() + * @see File#mkdir() + * @see File#renameTo(File) + * @see FileOutputStream#FileOutputStream(String) + * @see RandomAccessFile#RandomAccessFile(String, String) + */ + public void checkWrite(String filename) + { + checkPermission(new FilePermission(filename, "write")); + } + + /** + * Check if the current thread is allowed to delete the given file. This + * method is called from File.delete(). The default implementation checks + * FilePermission(filename, "delete"). If you override this, + * call super.checkDelete rather than throwing an exception. + * + * @param filename the full name of the file to delete + * @throws SecurityException if permission is denied + * @throws NullPointerException if filename is null + * @see File#delete() + */ + public void checkDelete(String filename) + { + checkPermission(new FilePermission(filename, "delete")); + } + + /** + * Check if the current thread is allowed to connect to a given host on a + * given port. This method is called from Socket.Socket(). A port number + * of -1 indicates the caller is attempting to determine an IP address, so + * the default implementation checks + * SocketPermission(host, "resolve"). Otherwise, the default + * implementation checks + * SocketPermission(host + ":" + port, "connect"). If you + * override this, call super.checkConnect rather than throwing + * an exception. + * + * @param host the host to connect to + * @param port the port to connect on + * @throws SecurityException if permission is denied + * @throws NullPointerException if host is null + * @see Socket#Socket() + */ + public void checkConnect(String host, int port) + { + if (port == -1) + checkPermission(new SocketPermission(host, "resolve")); + else + // Use the toString() hack to do the null check. + checkPermission(new SocketPermission(host.toString() + ":" + port, + "connect")); + } + + /** + * Check if the current thread is allowed to connect to a given host on a + * given port, using the given security context. The context must be a + * result of a previous call to getSecurityContext. A port + * number of -1 indicates the caller is attempting to determine an IP + * address, so the default implementation checks + * AccessControlContext.checkPermission(new SocketPermission(host, + * "resolve")). Otherwise, the default implementation checks + * AccessControlContext.checkPermission(new SocketPermission(host + * + ":" + port, "connect")). If you override this, call + * super.checkConnect rather than throwing an exception. + * + * @param host the host to connect to + * @param port the port to connect on + * @param context the context to determine access for + * + * @throws SecurityException if permission is denied, or if context is + * not an AccessControlContext + * @throws NullPointerException if host is null + * + * @see #getSecurityContext() + * @see AccessControlContext#checkPermission(Permission) + */ + public void checkConnect(String host, int port, Object context) + { + if (! (context instanceof AccessControlContext)) + throw new SecurityException("Missing context"); + AccessControlContext ac = (AccessControlContext) context; + if (port == -1) + ac.checkPermission(new SocketPermission(host, "resolve")); + else + // Use the toString() hack to do the null check. + ac.checkPermission(new SocketPermission(host.toString() + ":" + port, + "connect")); + } + + /** + * Check if the current thread is allowed to listen to a specific port for + * data. This method is called by ServerSocket.ServerSocket(). The default + * implementation checks + * SocketPermission("localhost:" + (port == 0 ? "1024-" : "" + port), + * "listen"). If you override this, call + * super.checkListen rather than throwing an exception. + * + * @param port the port to listen on + * @throws SecurityException if permission is denied + * @see ServerSocket#ServerSocket(int) + */ + public void checkListen(int port) + { + checkPermission(new SocketPermission("localhost:" + + (port == 0 ? "1024-" : "" +port), + "listen")); + } + + /** + * Check if the current thread is allowed to accept a connection from a + * particular host on a particular port. This method is called by + * ServerSocket.implAccept(). The default implementation checks + * SocketPermission(host + ":" + port, "accept"). If you + * override this, call super.checkAccept rather than throwing + * an exception. + * + * @param host the host which wishes to connect + * @param port the port the connection will be on + * @throws SecurityException if permission is denied + * @throws NullPointerException if host is null + * @see ServerSocket#accept() + */ + public void checkAccept(String host, int port) + { + // Use the toString() hack to do the null check. + checkPermission(new SocketPermission(host.toString() + ":" + port, + "accept")); + } + + /** + * Check if the current thread is allowed to read and write multicast to + * a particular address. The default implementation checks + * SocketPermission(addr.getHostAddress(), "accept,connect"). + * If you override this, call super.checkMulticast rather than + * throwing an exception. + * + * @param addr the address to multicast to + * @throws SecurityException if permission is denied + * @throws NullPointerException if host is null + * @since 1.1 + */ + public void checkMulticast(InetAddress addr) + { + checkPermission(new SocketPermission(addr.getHostAddress(), + "accept,connect")); + } + + /** + *Check if the current thread is allowed to read and write multicast to + * a particular address with a particular ttl (time-to-live) value. The + * default implementation ignores ttl, and checks + * SocketPermission(addr.getHostAddress(), "accept,connect"). + * If you override this, call super.checkMulticast rather than + * throwing an exception. + * + * @param addr the address to multicast to + * @param ttl value in use for multicast send + * @throws SecurityException if permission is denied + * @throws NullPointerException if host is null + * @since 1.1 + * @deprecated use {@link #checkPermission(Permission)} instead + */ + public void checkMulticast(InetAddress addr, byte ttl) + { + checkPermission(new SocketPermission(addr.getHostAddress(), + "accept,connect")); + } + + /** + * Check if the current thread is allowed to read or write all the system + * properties at once. This method is called by System.getProperties() + * and setProperties(). The default implementation checks + * PropertyPermission("*", "read,write"). If you override + * this, call super.checkPropertiesAccess rather than + * throwing an exception. + * + * @throws SecurityException if permission is denied + * @see System#getProperties() + * @see System#setProperties(Properties) + */ + public void checkPropertiesAccess() + { + checkPermission(new PropertyPermission("*", "read,write")); + } + + /** + * Check if the current thread is allowed to read a particular system + * property (writes are checked directly via checkPermission). This method + * is called by System.getProperty() and setProperty(). The default + * implementation checks PropertyPermission(key, "read"). If + * you override this, call super.checkPropertyAccess rather + * than throwing an exception. + * + * @param key the key of the property to check + * + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + * + * @see System#getProperty(String) + */ + public void checkPropertyAccess(String key) + { + checkPermission(new PropertyPermission(key, "read")); + } + + /** + * Check if the current thread is allowed to create a top-level window. If + * it is not, the operation should still go through, but some sort of + * nonremovable warning should be placed on the window to show that it + * is untrusted. This method is called by Window.Window(). The default + * implementation checks + * AWTPermission("showWindowWithoutWarningBanner"), and returns + * true if no exception was thrown. If you override this, use + * return super.checkTopLevelWindow rather than returning + * false. + * + * @param window the window to create + * @return true if there is permission to show the window without warning + * @throws NullPointerException if window is null + * @see java.awt.Window#Window(java.awt.Frame) + */ + public boolean checkTopLevelWindow(Object window) + { + if (window == null) + throw new NullPointerException(); + try + { + checkPermission(new AWTPermission("showWindowWithoutWarningBanner")); + return true; + } + catch (SecurityException e) + { + return false; + } + } + + /** + * Check if the current thread is allowed to create a print job. This + * method is called by Toolkit.getPrintJob(). The default implementation + * checks RuntimePermission("queuePrintJob"). If you override + * this, call super.checkPrintJobAccess rather than throwing + * an exception. + * + * @throws SecurityException if permission is denied + * @see java.awt.Toolkit#getPrintJob(java.awt.Frame, String, Properties) + * @since 1.1 + */ + public void checkPrintJobAccess() + { + checkPermission(new RuntimePermission("queuePrintJob")); + } + + /** + * Check if the current thread is allowed to use the system clipboard. This + * method is called by Toolkit.getSystemClipboard(). The default + * implementation checks AWTPermission("accessClipboard"). If + * you override this, call super.checkSystemClipboardAccess + * rather than throwing an exception. + * + * @throws SecurityException if permission is denied + * @see java.awt.Toolkit#getSystemClipboard() + * @since 1.1 + */ + public void checkSystemClipboardAccess() + { + checkPermission(new AWTPermission("accessClipboard")); + } + + /** + * Check if the current thread is allowed to use the AWT event queue. This + * method is called by Toolkit.getSystemEventQueue(). The default + * implementation checks AWTPermission("accessEventQueue"). + * you override this, call super.checkAwtEventQueueAccess + * rather than throwing an exception. + * + * @throws SecurityException if permission is denied + * @see java.awt.Toolkit#getSystemEventQueue() + * @since 1.1 + */ + public void checkAwtEventQueueAccess() + { + checkPermission(new AWTPermission("accessEventQueue")); + } + + /** + * Check if the current thread is allowed to access the specified package + * at all. This method is called by ClassLoader.loadClass() in user-created + * ClassLoaders. The default implementation gets a list of all restricted + * packages, via Security.getProperty("package.access"). Then, + * if packageName starts with or equals any restricted package, it checks + * RuntimePermission("accessClassInPackage." + packageName). + * If you override this, you should call + * super.checkPackageAccess before doing anything else. + * + * @param packageName the package name to check access to + * @throws SecurityException if permission is denied + * @throws NullPointerException if packageName is null + * @see ClassLoader#loadClass(String, boolean) + * @see Security#getProperty(String) + */ + public void checkPackageAccess(String packageName) + { + checkPackageList(packageName, "package.access", "accessClassInPackage."); + } + + /** + * Check if the current thread is allowed to define a class into the + * specified package. This method is called by ClassLoader.loadClass() in + * user-created ClassLoaders. The default implementation gets a list of all + * restricted packages, via + * Security.getProperty("package.definition"). Then, if + * packageName starts with or equals any restricted package, it checks + * RuntimePermission("defineClassInPackage." + packageName). + * If you override this, you should call + * super.checkPackageDefinition before doing anything else. + * + * @param packageName the package name to check access to + * @throws SecurityException if permission is denied + * @throws NullPointerException if packageName is null + * @see ClassLoader#loadClass(String, boolean) + * @see Security#getProperty(String) + */ + public void checkPackageDefinition(String packageName) + { + checkPackageList(packageName, "package.definition", "defineClassInPackage."); + } + + /** + * Check if the current thread is allowed to set the current socket factory. + * This method is called by Socket.setSocketImplFactory(), + * ServerSocket.setSocketFactory(), and URL.setURLStreamHandlerFactory(). + * The default implementation checks + * RuntimePermission("setFactory"). If you override this, call + * super.checkSetFactory rather than throwing an exception. + * + * @throws SecurityException if permission is denied + * @see Socket#setSocketImplFactory(SocketImplFactory) + * @see ServerSocket#setSocketFactory(SocketImplFactory) + * @see URL#setURLStreamHandlerFactory(URLStreamHandlerFactory) + */ + public void checkSetFactory() + { + checkPermission(new RuntimePermission("setFactory")); + } + + /** + * Check if the current thread is allowed to get certain types of Methods, + * Fields and Constructors from a Class object. This method is called by + * Class.getMethod[s](), Class.getField[s](), Class.getConstructor[s], + * Class.getDeclaredMethod[s](), Class.getDeclaredField[s](), and + * Class.getDeclaredConstructor[s](). The default implementation allows + * PUBLIC access, and access to classes defined by the same classloader as + * the code performing the reflection. Otherwise, it checks + * RuntimePermission("accessDeclaredMembers"). If you override + * this, do not call super.checkMemberAccess, as this would + * mess up the stack depth check that determines the ClassLoader requesting + * the access. + * + * @param c the Class to check + * @param memberType either DECLARED or PUBLIC + * @throws SecurityException if permission is denied, including when + * memberType is not DECLARED or PUBLIC + * @throws NullPointerException if c is null + * @see Class + * @see Member#DECLARED + * @see Member#PUBLIC + * @since 1.1 + */ + public void checkMemberAccess(Class c, int memberType) + { + if (c == null) + throw new NullPointerException(); + if (memberType == Member.PUBLIC) + return; + // XXX Allow access to classes created by same classloader before next + // check. + checkPermission(new RuntimePermission("accessDeclaredMembers")); + } + + /** + * Test whether a particular security action may be taken. The default + * implementation checks SecurityPermission(action). If you + * override this, call super.checkSecurityAccess rather than + * throwing an exception. + * + * @param action the desired action to take + * @throws SecurityException if permission is denied + * @throws NullPointerException if action is null + * @throws IllegalArgumentException if action is "" + * @since 1.1 + */ + public void checkSecurityAccess(String action) + { + checkPermission(new SecurityPermission(action)); + } + + /** + * Get the ThreadGroup that a new Thread should belong to by default. Called + * by Thread.Thread(). The default implementation returns the current + * ThreadGroup of the current Thread. Spec Note: it is not + * clear whether the new Thread is guaranteed to pass the + * checkAccessThreadGroup() test when using this ThreadGroup, but I presume + * so. + * + * @return the ThreadGroup to put the new Thread into + * @since 1.1 + */ + public ThreadGroup getThreadGroup() + { + return Thread.currentThread().getThreadGroup(); + } + + /** + * Helper that checks a comma-separated list of restricted packages, from + * Security.getProperty("package.definition"), for the given + * package access permission. If packageName starts with or equals any + * restricted package, it checks + * RuntimePermission(permission + packageName). + * + * @param packageName the package name to check access to + * @param restriction "package.access" or "package.definition" + * @param permission the base permission, including the '.' + * @throws SecurityException if permission is denied + * @throws NullPointerException if packageName is null + * @see #checkPackageAccess(String) + * @see #checkPackageDefinition(String) + */ + void checkPackageList(String packageName, final String restriction, + String permission) + { + if (packageName == null) + throw new NullPointerException(); + + String list = (String)AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + return Security.getProperty(restriction); + } + }); + + if (list == null || list.equals("")) + return; + + String packageNamePlusDot = packageName + "."; + + StringTokenizer st = new StringTokenizer(list, ","); + while (st.hasMoreTokens()) + { + if (packageNamePlusDot.startsWith(st.nextToken())) + { + Permission p = new RuntimePermission(permission + packageName); + checkPermission(p); + return; + } + } + } +} diff --git a/libjava/classpath/java/lang/Short.java b/libjava/classpath/java/lang/Short.java new file mode 100644 index 000000000..ec87f933e --- /dev/null +++ b/libjava/classpath/java/lang/Short.java @@ -0,0 +1,383 @@ +/* Short.java -- object wrapper for short + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * Instances of class Short represent primitive + * short values. + * + * Additionally, this class provides various helper functions and variables + * related to shorts. + * + * @author Paul Fisher + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.1 + * @status updated to 1.5 + */ +public final class Short extends Number implements Comparable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7515723908773894738L; + + /** + * The minimum value a short can represent is -32768 (or + * -215). + */ + public static final short MIN_VALUE = -32768; + + /** + * The minimum value a short can represent is 32767 (or + * 215). + */ + public static final short MAX_VALUE = 32767; + + /** + * The primitive type short is represented by this + * Class object. + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass('S'); + + /** + * The number of bits needed to represent a short. + * @since 1.5 + */ + public static final int SIZE = 16; + + // This caches some Short values, and is used by boxing conversions + // via valueOf(). We must cache at least -128..127; these constants + // control how much we actually cache. + private static final int MIN_CACHE = -128; + private static final int MAX_CACHE = 127; + private static Short[] shortCache = new Short[MAX_CACHE - MIN_CACHE + 1]; + static + { + for (short i=MIN_CACHE; i <= MAX_CACHE; i++) + shortCache[i - MIN_CACHE] = new Short(i); + } + + /** + * The immutable value of this Short. + * + * @serial the wrapped short + */ + private final short value; + + /** + * Create a Short object representing the value of the + * short argument. + * + * @param value the value to use + */ + public Short(short value) + { + this.value = value; + } + + /** + * Create a Short object representing the value of the + * argument after conversion to a short. + * + * @param s the string to convert + * @throws NumberFormatException if the String cannot be parsed + */ + public Short(String s) + { + value = parseShort(s, 10); + } + + /** + * Converts the short to a String and assumes + * a radix of 10. + * + * @param s the short to convert to String + * @return the String representation of the argument + */ + public static String toString(short s) + { + return String.valueOf(s); + } + + /** + * Converts the specified String into a short. + * This function assumes a radix of 10. + * + * @param s the String to convert + * @return the short value of s + * @throws NumberFormatException if s cannot be parsed as a + * short + */ + public static short parseShort(String s) + { + return parseShort(s, 10); + } + + /** + * Converts the specified String into a short + * using the specified radix (base). The string must not be null + * or empty. It may begin with an optional '-', which will negate the answer, + * provided that there are also valid digits. Each digit is parsed as if by + * Character.digit(d, radix), and must be in the range + * 0 to radix - 1. Finally, the result must be + * within MIN_VALUE to MAX_VALUE, inclusive. + * Unlike Double.parseDouble, you may not have a leading '+'. + * + * @param s the String to convert + * @param radix the radix (base) to use in the conversion + * @return the String argument converted to short + * @throws NumberFormatException if s cannot be parsed as a + * short + */ + public static short parseShort(String s, int radix) + { + int i = Integer.parseInt(s, radix, false); + if ((short) i != i) + throw new NumberFormatException(); + return (short) i; + } + + /** + * Creates a new Short object using the String + * and specified radix (base). + * + * @param s the String to convert + * @param radix the radix (base) to convert with + * @return the new Short + * @throws NumberFormatException if s cannot be parsed as a + * short + * @see #parseShort(String, int) + */ + public static Short valueOf(String s, int radix) + { + return valueOf(parseShort(s, radix)); + } + + /** + * Creates a new Short object using the String, + * assuming a radix of 10. + * + * @param s the String to convert + * @return the new Short + * @throws NumberFormatException if s cannot be parsed as a + * short + * @see #Short(String) + * @see #parseShort(String) + */ + public static Short valueOf(String s) + { + return valueOf(parseShort(s, 10)); + } + + /** + * Returns a Short object wrapping the value. + * In contrast to the Short constructor, this method + * will cache some values. It is used by boxing conversion. + * + * @param val the value to wrap + * @return the Short + * @since 1.5 + */ + public static Short valueOf(short val) + { + if (val < MIN_CACHE || val > MAX_CACHE) + return new Short(val); + else + return shortCache[val - MIN_CACHE]; + } + + /** + * Convert the specified String into a Short. + * The String may represent decimal, hexadecimal, or + * octal numbers. + * + *

    The extended BNF grammar is as follows:
    + *

    +   * DecodableString:
    +   *      ( [ - ] DecimalNumber )
    +   *    | ( [ - ] ( 0x | 0X
    +   *              | # ) HexDigit { HexDigit } )
    +   *    | ( [ - ] 0 { OctalDigit } )
    +   * DecimalNumber:
    +   *        DecimalDigit except '0' { DecimalDigit }
    +   * DecimalDigit:
    +   *        Character.digit(d, 10) has value 0 to 9
    +   * OctalDigit:
    +   *        Character.digit(d, 8) has value 0 to 7
    +   * DecimalDigit:
    +   *        Character.digit(d, 16) has value 0 to 15
    +   * 
    + * Finally, the value must be in the range MIN_VALUE to + * MAX_VALUE, or an exception is thrown. + * + * @param s the String to interpret + * @return the value of the String as a Short + * @throws NumberFormatException if s cannot be parsed as a + * short + * @throws NullPointerException if s is null + * @see Integer#decode(String) + */ + public static Short decode(String s) + { + int i = Integer.parseInt(s, 10, true); + if ((short) i != i) + throw new NumberFormatException(); + return valueOf((short) i); + } + + /** + * Return the value of this Short as a byte. + * + * @return the byte value + */ + public byte byteValue() + { + return (byte) value; + } + + /** + * Return the value of this Short. + * + * @return the short value + */ + public short shortValue() + { + return value; + } + + /** + * Return the value of this Short as an int. + * + * @return the int value + */ + public int intValue() + { + return value; + } + + /** + * Return the value of this Short as a long. + * + * @return the long value + */ + public long longValue() + { + return value; + } + + /** + * Return the value of this Short as a float. + * + * @return the float value + */ + public float floatValue() + { + return value; + } + + /** + * Return the value of this Short as a double. + * + * @return the double value + */ + public double doubleValue() + { + return value; + } + + /** + * Converts the Short value to a String and + * assumes a radix of 10. + * + * @return the String representation of this Short + */ + public String toString() + { + return String.valueOf(value); + } + + /** + * Return a hashcode representing this Object. Short's hash + * code is simply its value. + * + * @return this Object's hash code + */ + public int hashCode() + { + return value; + } + + /** + * Returns true if obj is an instance of + * Short and represents the same short value. + * + * @param obj the object to compare + * @return whether these Objects are semantically equal + */ + public boolean equals(Object obj) + { + return obj instanceof Short && value == ((Short) obj).value; + } + + /** + * Compare two Shorts numerically by comparing their short + * values. The result is positive if the first is greater, negative if the + * second is greater, and 0 if the two are equal. + * + * @param s the Short to compare + * @return the comparison + * @since 1.2 + */ + public int compareTo(Short s) + { + return value - s.value; + } + + /** + * Reverse the bytes in val. + * @since 1.5 + */ + public static short reverseBytes(short val) + { + return (short) (((val >> 8) & 0xff) | ((val << 8) & 0xff00)); + } +} diff --git a/libjava/classpath/java/lang/StackOverflowError.java b/libjava/classpath/java/lang/StackOverflowError.java new file mode 100644 index 000000000..5188ddda1 --- /dev/null +++ b/libjava/classpath/java/lang/StackOverflowError.java @@ -0,0 +1,72 @@ +/* StackOverflowError.java -- thrown when the stack depth is exceeded + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A StackOverflowError is thrown when the execution stack + * overflow occurs. This often occurs when a method enters infinit recursion. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class StackOverflowError extends VirtualMachineError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8609175038441759607L; + + /** + * Create an error without a message. + */ + public StackOverflowError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public StackOverflowError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/StackTraceElement.java b/libjava/classpath/java/lang/StackTraceElement.java new file mode 100644 index 000000000..167272dab --- /dev/null +++ b/libjava/classpath/java/lang/StackTraceElement.java @@ -0,0 +1,279 @@ +/* StackTraceElement.java -- One function call or call stack element + Copyright (C) 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import java.io.Serializable; + +/** + * One function call or stack trace element. Gives information about + * the execution point such as the source file name, the line number, + * the fully qualified class name, the method name and whether this method + * is native, if this information is known. + * + * @author Mark Wielaard (mark@klomp.org) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.5 + */ +public final class StackTraceElement implements Serializable +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 6992337162326171013L; + + /** + * The name of the file, null if unknown. + * + * @serial the source code filename, if known + */ + private final String fileName; + + /** + * The line number in the file, negative if unknown. + * + * @serial the source code line number, if known + */ + private final int lineNumber; + + /** + * The fully qualified class name, null if unknown. + * + * @serial the enclosing class, if known + */ + private final String declaringClass; + + /** + * The method name in the class, null if unknown. + * + * @serial the enclosing method, if known + */ + private final String methodName; + + /** Whether the method is native. */ + private final transient boolean isNative; + + /** + * A package local constructor for the StackTraceElement class, to be + * called by the Virtual Machine as part of Throwable.fillInStackTrace. + * There are no public constructors defined for this class. Creation + * of new elements is implementation specific. + * + * @param fileName the name of the file, null if unknown + * @param lineNumber the line in the file, negative if unknown + * @param className the fully qualified name of the class, null if unknown + * @param methodName the name of the method, null if unknown + * @param isNative true if native, false otherwise + */ + StackTraceElement(String fileName, int lineNumber, String className, + String methodName, boolean isNative) + { + this.fileName = fileName; + this.lineNumber = lineNumber; + this.declaringClass = className; + this.methodName = methodName; + this.isNative = isNative; + } + + /** + * Create a new StackTraceElement representing a given source location. + * + * @param className the fully qualified name of the class + * @param methodName the name of the method + * @param fileName the name of the file, null if unknown + * @param lineNumber the line in the file, negative if unknown, or -2 + * if this method is native + * + * @since 1.5 + */ + public StackTraceElement(String className, String methodName, String fileName, + int lineNumber) + { + this(fileName, lineNumber, className, methodName, lineNumber == -2); + // The public constructor doesn't allow certain values to be null. + if (className == null || methodName == null) + throw new NullPointerException("invalid argument to constructor"); + } + + /** + * Returns the name of the file, or null if unknown. This is usually + * obtained from the SourceFile attribute of the class file + * format, if present. + * + * @return the file name + */ + public String getFileName() + { + return fileName; + } + + /** + * Returns the line number in the file, or a negative number if unknown. + * This is usually obtained from the LineNumberTable attribute + * of the method in the class file format, if present. + * + * @return the line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * Returns the fully qualified class name, or null if unknown. + * + * @return the class name + */ + public String getClassName() + { + return declaringClass; + } + + /** + * Returns the method name in the class, or null if unknown. If the + * execution point is in a constructor, the name is + * <init>; if the execution point is in the class + * initializer, the name is <clinit>. + * + * @return the method name + */ + public String getMethodName() + { + return methodName; + } + + /** + * Returns true if the method is native, or false if it is not or unknown. + * + * @return whether the method is native + */ + public boolean isNativeMethod() + { + return isNative; + } + + /** + * Returns a string representation of this stack trace element. The + * returned String is implementation specific. This implementation + * returns the following String: "[class][.][method]([file][:line])". + * If the fully qualified class name or the method is unknown it is + * omitted including the point seperator. If the source file name is + * unknown it is replaced by "Unknown Source" if the method is not native + * or by "Native Method" if the method is native. If the line number + * is unknown it and the colon are omitted. + * + * @return a string representation of this execution point + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + if (declaringClass != null) + { + sb.append(declaringClass); + if (methodName != null) + sb.append('.'); + } + if (methodName != null) + sb.append(methodName); + sb.append("("); + if (fileName != null) + sb.append(fileName); + else + sb.append(isNative ? "Native Method" : "Unknown Source"); + if (lineNumber >= 0) + sb.append(':').append(lineNumber); + sb.append(')'); + return sb.toString(); + } + + /** + * Returns true if the given object is also a StackTraceElement and all + * attributes, except the native flag, are equal (either the same attribute + * between the two elments are null, or both satisfy Object.equals). + * + * @param o the object to compare + * @return true if the two are equal + */ + public boolean equals(Object o) + { + if (! (o instanceof StackTraceElement)) + return false; + StackTraceElement e = (StackTraceElement) o; + return equals(fileName, e.fileName) + && lineNumber == e.lineNumber + && equals(declaringClass, e.declaringClass) + && equals(methodName, e.methodName); + } + + /** + * Returns the hashCode of this StackTraceElement. This implementation + * computes the hashcode by xor-ing the hashcode of all attributes except + * the native flag. + * + * @return the hashcode + */ + public int hashCode() + { + return hashCode(fileName) ^ lineNumber ^ hashCode(declaringClass) + ^ hashCode(methodName); + } + + /** + * Compare two objects according to Collection semantics. + * + * @param o1 the first object + * @param o2 the second object + * @return o1 == null ? o2 == null : o1.equals(o2) + */ + private static boolean equals(Object o1, Object o2) + { + return o1 == null ? o2 == null : o1.equals(o2); + } + + /** + * Hash an object according to Collection semantics. + * + * @param o the object to hash + * @return o1 == null ? 0 : o1.hashCode() + */ + private static int hashCode(Object o) + { + return o == null ? 0 : o.hashCode(); + } +} diff --git a/libjava/classpath/java/lang/StrictMath.java b/libjava/classpath/java/lang/StrictMath.java new file mode 100644 index 000000000..88f5e5750 --- /dev/null +++ b/libjava/classpath/java/lang/StrictMath.java @@ -0,0 +1,2575 @@ +/* java.lang.StrictMath -- common mathematical functions, strict Java + Copyright (C) 1998, 2001, 2002, 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +/* + * Some of the algorithms in this class are in the public domain, as part + * of fdlibm (freely-distributable math library), available at + * http://www.netlib.org/fdlibm/, and carry the following copyright: + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +package java.lang; + +import gnu.classpath.Configuration; + +import java.util.Random; + +/** + * Helper class containing useful mathematical functions and constants. + * This class mirrors {@link Math}, but is 100% portable, because it uses + * no native methods whatsoever. Also, these algorithms are all accurate + * to less than 1 ulp, and execute in strictfp mode, while + * Math is allowed to vary in its results for some functions. Unfortunately, + * this usually means StrictMath has less efficiency and speed, as Math can + * use native methods. + * + *

    The source of the various algorithms used is the fdlibm library, at:
    + *
    http://www.netlib.org/fdlibm/ + * + * Note that angles are specified in radians. Conversion functions are + * provided for your convenience. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + */ +public final strictfp class StrictMath +{ + /** + * StrictMath is non-instantiable. + */ + private StrictMath() + { + } + + /** + * A random number generator, initialized on first use. + * + * @see #random() + */ + private static Random rand; + + /** + * The most accurate approximation to the mathematical constant e: + * 2.718281828459045. Used in natural log and exp. + * + * @see #log(double) + * @see #exp(double) + */ + public static final double E + = 2.718281828459045; // Long bits 0x4005bf0z8b145769L. + + /** + * The most accurate approximation to the mathematical constant pi: + * 3.141592653589793. This is the ratio of a circle's diameter + * to its circumference. + */ + public static final double PI + = 3.141592653589793; // Long bits 0x400921fb54442d18L. + + /** + * Take the absolute value of the argument. (Absolute value means make + * it positive.) + * + *

    Note that the the largest negative value (Integer.MIN_VALUE) cannot + * be made positive. In this case, because of the rules of negation in + * a computer, MIN_VALUE is what will be returned. + * This is a negative value. You have been warned. + * + * @param i the number to take the absolute value of + * @return the absolute value + * @see Integer#MIN_VALUE + */ + public static int abs(int i) + { + return (i < 0) ? -i : i; + } + + /** + * Take the absolute value of the argument. (Absolute value means make + * it positive.) + * + *

    Note that the the largest negative value (Long.MIN_VALUE) cannot + * be made positive. In this case, because of the rules of negation in + * a computer, MIN_VALUE is what will be returned. + * This is a negative value. You have been warned. + * + * @param l the number to take the absolute value of + * @return the absolute value + * @see Long#MIN_VALUE + */ + public static long abs(long l) + { + return (l < 0) ? -l : l; + } + + /** + * Take the absolute value of the argument. (Absolute value means make + * it positive.) + * + * @param f the number to take the absolute value of + * @return the absolute value + */ + public static float abs(float f) + { + return (f <= 0) ? 0 - f : f; + } + + /** + * Take the absolute value of the argument. (Absolute value means make + * it positive.) + * + * @param d the number to take the absolute value of + * @return the absolute value + */ + public static double abs(double d) + { + return (d <= 0) ? 0 - d : d; + } + + /** + * Return whichever argument is smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static int min(int a, int b) + { + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static long min(long a, long b) + { + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, -0 is always smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static float min(float a, float b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; < will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return -(-a - b); + return (a < b) ? a : b; + } + + /** + * Return whichever argument is smaller. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, -0 is always smaller. + * + * @param a the first number + * @param b a second number + * @return the smaller of the two numbers + */ + public static double min(double a, double b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; < will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return -(-a - b); + return (a < b) ? a : b; + } + + /** + * Return whichever argument is larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static int max(int a, int b) + { + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static long max(long a, long b) + { + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, 0 is always larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static float max(float a, float b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; > will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return a - -b; + return (a > b) ? a : b; + } + + /** + * Return whichever argument is larger. If either argument is NaN, the + * result is NaN, and when comparing 0 and -0, 0 is always larger. + * + * @param a the first number + * @param b a second number + * @return the larger of the two numbers + */ + public static double max(double a, double b) + { + // this check for NaN, from JLS 15.21.1, saves a method call + if (a != a) + return a; + // no need to check if b is NaN; > will work correctly + // recall that -0.0 == 0.0, but [+-]0.0 - [+-]0.0 behaves special + if (a == 0 && b == 0) + return a - -b; + return (a > b) ? a : b; + } + + /** + * The trigonometric function sin. The sine of NaN or infinity is + * NaN, and the sine of 0 retains its sign. + * + * @param a the angle (in radians) + * @return sin(a) + */ + public static double sin(double a) + { + if (a == Double.NEGATIVE_INFINITY || ! (a < Double.POSITIVE_INFINITY)) + return Double.NaN; + + if (abs(a) <= PI / 4) + return sin(a, 0); + + // Argument reduction needed. + double[] y = new double[2]; + int n = remPiOver2(a, y); + switch (n & 3) + { + case 0: + return sin(y[0], y[1]); + case 1: + return cos(y[0], y[1]); + case 2: + return -sin(y[0], y[1]); + default: + return -cos(y[0], y[1]); + } + } + + /** + * The trigonometric function cos. The cosine of NaN or infinity is + * NaN. + * + * @param a the angle (in radians). + * @return cos(a). + */ + public static double cos(double a) + { + if (a == Double.NEGATIVE_INFINITY || ! (a < Double.POSITIVE_INFINITY)) + return Double.NaN; + + if (abs(a) <= PI / 4) + return cos(a, 0); + + // Argument reduction needed. + double[] y = new double[2]; + int n = remPiOver2(a, y); + switch (n & 3) + { + case 0: + return cos(y[0], y[1]); + case 1: + return -sin(y[0], y[1]); + case 2: + return -cos(y[0], y[1]); + default: + return sin(y[0], y[1]); + } + } + + /** + * The trigonometric function tan. The tangent of NaN or infinity + * is NaN, and the tangent of 0 retains its sign. + * + * @param a the angle (in radians) + * @return tan(a) + */ + public static double tan(double a) + { + if (a == Double.NEGATIVE_INFINITY || ! (a < Double.POSITIVE_INFINITY)) + return Double.NaN; + + if (abs(a) <= PI / 4) + return tan(a, 0, false); + + // Argument reduction needed. + double[] y = new double[2]; + int n = remPiOver2(a, y); + return tan(y[0], y[1], (n & 1) == 1); + } + + /** + * The trigonometric function arcsin. The range of angles returned + * is -pi/2 to pi/2 radians (-90 to 90 degrees). If the argument is NaN or + * its absolute value is beyond 1, the result is NaN; and the arcsine of + * 0 retains its sign. + * + * @param x the sin to turn back into an angle + * @return arcsin(x) + */ + public static double asin(double x) + { + boolean negative = x < 0; + if (negative) + x = -x; + if (! (x <= 1)) + return Double.NaN; + if (x == 1) + return negative ? -PI / 2 : PI / 2; + if (x < 0.5) + { + if (x < 1 / TWO_27) + return negative ? -x : x; + double t = x * x; + double p = t * (PS0 + t * (PS1 + t * (PS2 + t * (PS3 + t + * (PS4 + t * PS5))))); + double q = 1 + t * (QS1 + t * (QS2 + t * (QS3 + t * QS4))); + return negative ? -x - x * (p / q) : x + x * (p / q); + } + double w = 1 - x; // 1>|x|>=0.5. + double t = w * 0.5; + double p = t * (PS0 + t * (PS1 + t * (PS2 + t * (PS3 + t + * (PS4 + t * PS5))))); + double q = 1 + t * (QS1 + t * (QS2 + t * (QS3 + t * QS4))); + double s = sqrt(t); + if (x >= 0.975) + { + w = p / q; + t = PI / 2 - (2 * (s + s * w) - PI_L / 2); + } + else + { + w = (float) s; + double c = (t - w * w) / (s + w); + p = 2 * s * (p / q) - (PI_L / 2 - 2 * c); + q = PI / 4 - 2 * w; + t = PI / 4 - (p - q); + } + return negative ? -t : t; + } + + /** + * The trigonometric function arccos. The range of angles returned + * is 0 to pi radians (0 to 180 degrees). If the argument is NaN or + * its absolute value is beyond 1, the result is NaN. + * + * @param x the cos to turn back into an angle + * @return arccos(x) + */ + public static double acos(double x) + { + boolean negative = x < 0; + if (negative) + x = -x; + if (! (x <= 1)) + return Double.NaN; + if (x == 1) + return negative ? PI : 0; + if (x < 0.5) + { + if (x < 1 / TWO_57) + return PI / 2; + double z = x * x; + double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z + * (PS4 + z * PS5))))); + double q = 1 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4))); + double r = x - (PI_L / 2 - x * (p / q)); + return negative ? PI / 2 + r : PI / 2 - r; + } + if (negative) // x<=-0.5. + { + double z = (1 + x) * 0.5; + double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z + * (PS4 + z * PS5))))); + double q = 1 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4))); + double s = sqrt(z); + double w = p / q * s - PI_L / 2; + return PI - 2 * (s + w); + } + double z = (1 - x) * 0.5; // x>0.5. + double s = sqrt(z); + double df = (float) s; + double c = (z - df * df) / (s + df); + double p = z * (PS0 + z * (PS1 + z * (PS2 + z * (PS3 + z + * (PS4 + z * PS5))))); + double q = 1 + z * (QS1 + z * (QS2 + z * (QS3 + z * QS4))); + double w = p / q * s + c; + return 2 * (df + w); + } + + /** + * The trigonometric function arcsin. The range of angles returned + * is -pi/2 to pi/2 radians (-90 to 90 degrees). If the argument is NaN, the + * result is NaN; and the arctangent of 0 retains its sign. + * + * @param x the tan to turn back into an angle + * @return arcsin(x) + * @see #atan2(double, double) + */ + public static double atan(double x) + { + double lo; + double hi; + boolean negative = x < 0; + if (negative) + x = -x; + if (x >= TWO_66) + return negative ? -PI / 2 : PI / 2; + if (! (x >= 0.4375)) // |x|<7/16, or NaN. + { + if (! (x >= 1 / TWO_29)) // Small, or NaN. + return negative ? -x : x; + lo = hi = 0; + } + else if (x < 1.1875) + { + if (x < 0.6875) // 7/16<=|x|<11/16. + { + x = (2 * x - 1) / (2 + x); + hi = ATAN_0_5H; + lo = ATAN_0_5L; + } + else // 11/16<=|x|<19/16. + { + x = (x - 1) / (x + 1); + hi = PI / 4; + lo = PI_L / 4; + } + } + else if (x < 2.4375) // 19/16<=|x|<39/16. + { + x = (x - 1.5) / (1 + 1.5 * x); + hi = ATAN_1_5H; + lo = ATAN_1_5L; + } + else // 39/16<=|x|<2**66. + { + x = -1 / x; + hi = PI / 2; + lo = PI_L / 2; + } + + // Break sum from i=0 to 10 ATi*z**(i+1) into odd and even poly. + double z = x * x; + double w = z * z; + double s1 = z * (AT0 + w * (AT2 + w * (AT4 + w * (AT6 + w + * (AT8 + w * AT10))))); + double s2 = w * (AT1 + w * (AT3 + w * (AT5 + w * (AT7 + w * AT9)))); + if (hi == 0) + return negative ? x * (s1 + s2) - x : x - x * (s1 + s2); + z = hi - ((x * (s1 + s2) - lo) - x); + return negative ? -z : z; + } + + /** + * A special version of the trigonometric function arctan, for + * converting rectangular coordinates (x, y) to polar + * (r, theta). This computes the arctangent of x/y in the range + * of -pi to pi radians (-180 to 180 degrees). Special cases:

      + *
    • If either argument is NaN, the result is NaN.
    • + *
    • If the first argument is positive zero and the second argument is + * positive, or the first argument is positive and finite and the second + * argument is positive infinity, then the result is positive zero.
    • + *
    • If the first argument is negative zero and the second argument is + * positive, or the first argument is negative and finite and the second + * argument is positive infinity, then the result is negative zero.
    • + *
    • If the first argument is positive zero and the second argument is + * negative, or the first argument is positive and finite and the second + * argument is negative infinity, then the result is the double value + * closest to pi.
    • + *
    • If the first argument is negative zero and the second argument is + * negative, or the first argument is negative and finite and the second + * argument is negative infinity, then the result is the double value + * closest to -pi.
    • + *
    • If the first argument is positive and the second argument is + * positive zero or negative zero, or the first argument is positive + * infinity and the second argument is finite, then the result is the + * double value closest to pi/2.
    • + *
    • If the first argument is negative and the second argument is + * positive zero or negative zero, or the first argument is negative + * infinity and the second argument is finite, then the result is the + * double value closest to -pi/2.
    • + *
    • If both arguments are positive infinity, then the result is the + * double value closest to pi/4.
    • + *
    • If the first argument is positive infinity and the second argument + * is negative infinity, then the result is the double value closest to + * 3*pi/4.
    • + *
    • If the first argument is negative infinity and the second argument + * is positive infinity, then the result is the double value closest to + * -pi/4.
    • + *
    • If both arguments are negative infinity, then the result is the + * double value closest to -3*pi/4.
    • + * + *

    This returns theta, the angle of the point. To get r, albeit + * slightly inaccurately, use sqrt(x*x+y*y). + * + * @param y the y position + * @param x the x position + * @return theta in the conversion of (x, y) to (r, theta) + * @see #atan(double) + */ + public static double atan2(double y, double x) + { + if (x != x || y != y) + return Double.NaN; + if (x == 1) + return atan(y); + if (x == Double.POSITIVE_INFINITY) + { + if (y == Double.POSITIVE_INFINITY) + return PI / 4; + if (y == Double.NEGATIVE_INFINITY) + return -PI / 4; + return 0 * y; + } + if (x == Double.NEGATIVE_INFINITY) + { + if (y == Double.POSITIVE_INFINITY) + return 3 * PI / 4; + if (y == Double.NEGATIVE_INFINITY) + return -3 * PI / 4; + return (1 / (0 * y) == Double.POSITIVE_INFINITY) ? PI : -PI; + } + if (y == 0) + { + if (1 / (0 * x) == Double.POSITIVE_INFINITY) + return y; + return (1 / y == Double.POSITIVE_INFINITY) ? PI : -PI; + } + if (y == Double.POSITIVE_INFINITY || y == Double.NEGATIVE_INFINITY + || x == 0) + return y < 0 ? -PI / 2 : PI / 2; + + double z = abs(y / x); // Safe to do y/x. + if (z > TWO_60) + z = PI / 2 + 0.5 * PI_L; + else if (x < 0 && z < 1 / TWO_60) + z = 0; + else + z = atan(z); + if (x > 0) + return y > 0 ? z : -z; + return y > 0 ? PI - (z - PI_L) : z - PI_L - PI; + } + + /** + * Returns the hyperbolic sine of x which is defined as + * (exp(x) - exp(-x)) / 2. + * + * Special cases: + *

      + *
    • If the argument is NaN, the result is NaN
    • + *
    • If the argument is positive infinity, the result is positive + * infinity.
    • + *
    • If the argument is negative infinity, the result is negative + * infinity.
    • + *
    • If the argument is zero, the result is zero.
    • + *
    + * + * @param x the argument to sinh + * @return the hyperbolic sine of x + * + * @since 1.5 + */ + public static double sinh(double x) + { + // Method : + // mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2 + // 1. Replace x by |x| (sinh(-x) = -sinh(x)). + // 2. + // E + E/(E+1) + // 0 <= x <= 22 : sinh(x) := --------------, E=expm1(x) + // 2 + // + // 22 <= x <= lnovft : sinh(x) := exp(x)/2 + // lnovft <= x <= ln2ovft: sinh(x) := exp(x/2)/2 * exp(x/2) + // ln2ovft < x : sinh(x) := +inf (overflow) + + double t, w, h; + + long bits; + long h_bits; + long l_bits; + + // handle special cases + if (x != x) + return x; + if (x == Double.POSITIVE_INFINITY) + return Double.POSITIVE_INFINITY; + if (x == Double.NEGATIVE_INFINITY) + return Double.NEGATIVE_INFINITY; + + if (x < 0) + h = - 0.5; + else + h = 0.5; + + bits = Double.doubleToLongBits(x); + h_bits = getHighDWord(bits) & 0x7fffffffL; // ignore sign + l_bits = getLowDWord(bits); + + // |x| in [0, 22], return sign(x) * 0.5 * (E+E/(E+1)) + if (h_bits < 0x40360000L) // |x| < 22 + { + if (h_bits < 0x3e300000L) // |x| < 2^-28 + return x; // for tiny arguments return x + + t = expm1(abs(x)); + + if (h_bits < 0x3ff00000L) + return h * (2.0 * t - t * t / (t + 1.0)); + + return h * (t + t / (t + 1.0)); + } + + // |x| in [22, log(Double.MAX_VALUE)], return 0.5 * exp(|x|) + if (h_bits < 0x40862e42L) + return h * exp(abs(x)); + + // |x| in [log(Double.MAX_VALUE), overflowthreshold] + if ((h_bits < 0x408633ceL) + || ((h_bits == 0x408633ceL) && (l_bits <= 0x8fb9f87dL))) + { + w = exp(0.5 * abs(x)); + t = h * w; + + return t * w; + } + + // |x| > overflowthershold + return h * Double.POSITIVE_INFINITY; + } + + /** + * Returns the hyperbolic cosine of x, which is defined as + * (exp(x) + exp(-x)) / 2. + * + * Special cases: + *
      + *
    • If the argument is NaN, the result is NaN
    • + *
    • If the argument is positive infinity, the result is positive + * infinity.
    • + *
    • If the argument is negative infinity, the result is positive + * infinity.
    • + *
    • If the argument is zero, the result is one.
    • + *
    + * + * @param x the argument to cosh + * @return the hyperbolic cosine of x + * + * @since 1.5 + */ + public static double cosh(double x) + { + // Method : + // mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2 + // 1. Replace x by |x| (cosh(x) = cosh(-x)). + // 2. + // [ exp(x) - 1 ]^2 + // 0 <= x <= ln2/2 : cosh(x) := 1 + ------------------- + // 2*exp(x) + // + // exp(x) + 1/exp(x) + // ln2/2 <= x <= 22 : cosh(x) := ------------------ + // 2 + // 22 <= x <= lnovft : cosh(x) := exp(x)/2 + // lnovft <= x <= ln2ovft: cosh(x) := exp(x/2)/2 * exp(x/2) + // ln2ovft < x : cosh(x) := +inf (overflow) + + double t, w; + long bits; + long hx; + long lx; + + // handle special cases + if (x != x) + return x; + if (x == Double.POSITIVE_INFINITY) + return Double.POSITIVE_INFINITY; + if (x == Double.NEGATIVE_INFINITY) + return Double.POSITIVE_INFINITY; + + bits = Double.doubleToLongBits(x); + hx = getHighDWord(bits) & 0x7fffffffL; // ignore sign + lx = getLowDWord(bits); + + // |x| in [0, 0.5 * ln(2)], return 1 + expm1(|x|)^2 / (2 * exp(|x|)) + if (hx < 0x3fd62e43L) + { + t = expm1(abs(x)); + w = 1.0 + t; + + // for tiny arguments return 1. + if (hx < 0x3c800000L) + return w; + + return 1.0 + (t * t) / (w + w); + } + + // |x| in [0.5 * ln(2), 22], return exp(|x|)/2 + 1 / (2 * exp(|x|)) + if (hx < 0x40360000L) + { + t = exp(abs(x)); + + return 0.5 * t + 0.5 / t; + } + + // |x| in [22, log(Double.MAX_VALUE)], return 0.5 * exp(|x|) + if (hx < 0x40862e42L) + return 0.5 * exp(abs(x)); + + // |x| in [log(Double.MAX_VALUE), overflowthreshold], + // return exp(x/2)/2 * exp(x/2) + if ((hx < 0x408633ceL) + || ((hx == 0x408633ceL) && (lx <= 0x8fb9f87dL))) + { + w = exp(0.5 * abs(x)); + t = 0.5 * w; + + return t * w; + } + + // |x| > overflowthreshold + return Double.POSITIVE_INFINITY; + } + + /** + * Returns the hyperbolic tangent of x, which is defined as + * (exp(x) - exp(-x)) / (exp(x) + exp(-x)), i.e. sinh(x) / cosh(x). + * + Special cases: + *
      + *
    • If the argument is NaN, the result is NaN
    • + *
    • If the argument is positive infinity, the result is 1.
    • + *
    • If the argument is negative infinity, the result is -1.
    • + *
    • If the argument is zero, the result is zero.
    • + *
    + * + * @param x the argument to tanh + * @return the hyperbolic tagent of x + * + * @since 1.5 + */ + public static double tanh(double x) + { + // Method : + // 0. tanh(x) is defined to be (exp(x) - exp(-x)) / (exp(x) + exp(-x)) + // 1. reduce x to non-negative by tanh(-x) = -tanh(x). + // 2. 0 <= x <= 2^-55 : tanh(x) := x * (1.0 + x) + // -t + // 2^-55 < x <= 1 : tanh(x) := -----; t = expm1(-2x) + // t + 2 + // 2 + // 1 <= x <= 22.0 : tanh(x) := 1 - ----- ; t=expm1(2x) + // t + 2 + // 22.0 < x <= INF : tanh(x) := 1. + + double t, z; + + long bits; + long h_bits; + + // handle special cases + if (x != x) + return x; + if (x == Double.POSITIVE_INFINITY) + return 1.0; + if (x == Double.NEGATIVE_INFINITY) + return -1.0; + + bits = Double.doubleToLongBits(x); + h_bits = getHighDWord(bits) & 0x7fffffffL; // ingnore sign + + if (h_bits < 0x40360000L) // |x| < 22 + { + if (h_bits < 0x3c800000L) // |x| < 2^-55 + return x * (1.0 + x); + + if (h_bits >= 0x3ff00000L) // |x| >= 1 + { + t = expm1(2.0 * abs(x)); + z = 1.0 - 2.0 / (t + 2.0); + } + else // |x| < 1 + { + t = expm1(-2.0 * abs(x)); + z = -t / (t + 2.0); + } + } + else // |x| >= 22 + z = 1.0; + + return (x >= 0) ? z : -z; + } + + /** + * Returns the lower two words of a long. This is intended to be + * used like this: + * getLowDWord(Double.doubleToLongBits(x)). + */ + private static long getLowDWord(long x) + { + return x & 0x00000000ffffffffL; + } + + /** + * Returns the higher two words of a long. This is intended to be + * used like this: + * getHighDWord(Double.doubleToLongBits(x)). + */ + private static long getHighDWord(long x) + { + return (x & 0xffffffff00000000L) >> 32; + } + + /** + * Returns a double with the IEEE754 bit pattern given in the lower + * and higher two words lowDWord and highDWord. + */ + private static double buildDouble(long lowDWord, long highDWord) + { + return Double.longBitsToDouble(((highDWord & 0xffffffffL) << 32) + | (lowDWord & 0xffffffffL)); + } + + /** + * Returns the cube root of x. The sign of the cube root + * is equal to the sign of x. + * + * Special cases: + *
      + *
    • If the argument is NaN, the result is NaN
    • + *
    • If the argument is positive infinity, the result is positive + * infinity.
    • + *
    • If the argument is negative infinity, the result is negative + * infinity.
    • + *
    • If the argument is zero, the result is zero with the same + * sign as the argument.
    • + *
    + * + * @param x the number to take the cube root of + * @return the cube root of x + * @see #sqrt(double) + * + * @since 1.5 + */ + public static double cbrt(double x) + { + boolean negative = (x < 0); + double r; + double s; + double t; + double w; + + long bits; + long l; + long h; + + // handle the special cases + if (x != x) + return x; + if (x == Double.POSITIVE_INFINITY) + return Double.POSITIVE_INFINITY; + if (x == Double.NEGATIVE_INFINITY) + return Double.NEGATIVE_INFINITY; + if (x == 0) + return x; + + x = abs(x); + bits = Double.doubleToLongBits(x); + + if (bits < 0x0010000000000000L) // subnormal number + { + t = TWO_54; + t *= x; + + // __HI(t)=__HI(t)/3+B2; + bits = Double.doubleToLongBits(t); + h = getHighDWord(bits); + l = getLowDWord(bits); + + h = h / 3 + CBRT_B2; + + t = buildDouble(l, h); + } + else + { + // __HI(t)=__HI(x)/3+B1; + h = getHighDWord(bits); + l = 0; + + h = h / 3 + CBRT_B1; + t = buildDouble(l, h); + } + + // new cbrt to 23 bits + r = t * t / x; + s = CBRT_C + r * t; + t *= CBRT_G + CBRT_F / (s + CBRT_E + CBRT_D / s); + + // chopped to 20 bits and make it larger than cbrt(x) + bits = Double.doubleToLongBits(t); + h = getHighDWord(bits); + + // __LO(t)=0; + // __HI(t)+=0x00000001; + l = 0; + h += 1; + t = buildDouble(l, h); + + // one step newton iteration to 53 bits with error less than 0.667 ulps + s = t * t; // t * t is exact + r = x / s; + w = t + t; + r = (r - t) / (w + r); // r - t is exact + t = t + t * r; + + return negative ? -t : t; + } + + /** + * Take ea. The opposite of log(). If the + * argument is NaN, the result is NaN; if the argument is positive infinity, + * the result is positive infinity; and if the argument is negative + * infinity, the result is positive zero. + * + * @param x the number to raise to the power + * @return the number raised to the power of e + * @see #log(double) + * @see #pow(double, double) + */ + public static double exp(double x) + { + if (x != x) + return x; + if (x > EXP_LIMIT_H) + return Double.POSITIVE_INFINITY; + if (x < EXP_LIMIT_L) + return 0; + + // Argument reduction. + double hi; + double lo; + int k; + double t = abs(x); + if (t > 0.5 * LN2) + { + if (t < 1.5 * LN2) + { + hi = t - LN2_H; + lo = LN2_L; + k = 1; + } + else + { + k = (int) (INV_LN2 * t + 0.5); + hi = t - k * LN2_H; + lo = k * LN2_L; + } + if (x < 0) + { + hi = -hi; + lo = -lo; + k = -k; + } + x = hi - lo; + } + else if (t < 1 / TWO_28) + return 1; + else + lo = hi = k = 0; + + // Now x is in primary range. + t = x * x; + double c = x - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5)))); + if (k == 0) + return 1 - (x * c / (c - 2) - x); + double y = 1 - (lo - x * c / (2 - c) - hi); + return scale(y, k); + } + + /** + * Returns ex - 1. + * Special cases: + *
      + *
    • If the argument is NaN, the result is NaN.
    • + *
    • If the argument is positive infinity, the result is positive + * infinity
    • + *
    • If the argument is negative infinity, the result is -1.
    • + *
    • If the argument is zero, the result is zero.
    • + *
    + * + * @param x the argument to ex - 1. + * @return e raised to the power x minus one. + * @see #exp(double) + */ + public static double expm1(double x) + { + // Method + // 1. Argument reduction: + // Given x, find r and integer k such that + // + // x = k * ln(2) + r, |r| <= 0.5 * ln(2) + // + // Here a correction term c will be computed to compensate + // the error in r when rounded to a floating-point number. + // + // 2. Approximating expm1(r) by a special rational function on + // the interval [0, 0.5 * ln(2)]: + // Since + // r*(exp(r)+1)/(exp(r)-1) = 2 + r^2/6 - r^4/360 + ... + // we define R1(r*r) by + // r*(exp(r)+1)/(exp(r)-1) = 2 + r^2/6 * R1(r*r) + // That is, + // R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r) + // = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r)) + // = 1 - r^2/60 + r^4/2520 - r^6/100800 + ... + // We use a special Remes algorithm on [0, 0.347] to generate + // a polynomial of degree 5 in r*r to approximate R1. The + // maximum error of this polynomial approximation is bounded + // by 2**-61. In other words, + // R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5 + // where Q1 = -1.6666666666666567384E-2, + // Q2 = 3.9682539681370365873E-4, + // Q3 = -9.9206344733435987357E-6, + // Q4 = 2.5051361420808517002E-7, + // Q5 = -6.2843505682382617102E-9; + // (where z=r*r, and Q1 to Q5 are called EXPM1_Qx in the source) + // with error bounded by + // | 5 | -61 + // | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2 + // | | + // + // expm1(r) = exp(r)-1 is then computed by the following + // specific way which minimize the accumulation rounding error: + // 2 3 + // r r [ 3 - (R1 + R1*r/2) ] + // expm1(r) = r + --- + --- * [--------------------] + // 2 2 [ 6 - r*(3 - R1*r/2) ] + // + // To compensate the error in the argument reduction, we use + // expm1(r+c) = expm1(r) + c + expm1(r)*c + // ~ expm1(r) + c + r*c + // Thus c+r*c will be added in as the correction terms for + // expm1(r+c). Now rearrange the term to avoid optimization + // screw up: + // ( 2 2 ) + // ({ ( r [ R1 - (3 - R1*r/2) ] ) } r ) + // expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- ) + // ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 ) + // ( ) + // + // = r - E + // 3. Scale back to obtain expm1(x): + // From step 1, we have + // expm1(x) = either 2^k*[expm1(r)+1] - 1 + // = or 2^k*[expm1(r) + (1-2^-k)] + // 4. Implementation notes: + // (A). To save one multiplication, we scale the coefficient Qi + // to Qi*2^i, and replace z by (x^2)/2. + // (B). To achieve maximum accuracy, we compute expm1(x) by + // (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf) + // (ii) if k=0, return r-E + // (iii) if k=-1, return 0.5*(r-E)-0.5 + // (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E) + // else return 1.0+2.0*(r-E); + // (v) if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1) + // (vi) if k <= 20, return 2^k((1-2^-k)-(E-r)), else + // (vii) return 2^k(1-((E+2^-k)-r)) + + boolean negative = (x < 0); + double y, hi, lo, c, t, e, hxs, hfx, r1; + int k; + + long bits; + long h_bits; + long l_bits; + + c = 0.0; + y = abs(x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + // handle special cases and large arguments + if (h_bits >= 0x4043687aL) // if |x| >= 56 * ln(2) + { + if (h_bits >= 0x40862e42L) // if |x| >= EXP_LIMIT_H + { + if (h_bits >= 0x7ff00000L) + { + if (((h_bits & 0x000fffffL) | (l_bits & 0xffffffffL)) != 0) + return x; // exp(NaN) = NaN + else + return negative ? -1.0 : x; // exp({+-inf}) = {+inf, -1} + } + + if (x > EXP_LIMIT_H) + return Double.POSITIVE_INFINITY; // overflow + } + + if (negative) // x <= -56 * ln(2) + return -1.0; + } + + // argument reduction + if (h_bits > 0x3fd62e42L) // |x| > 0.5 * ln(2) + { + if (h_bits < 0x3ff0a2b2L) // |x| < 1.5 * ln(2) + { + if (negative) + { + hi = x + LN2_H; + lo = -LN2_L; + k = -1; + } + else + { + hi = x - LN2_H; + lo = LN2_L; + k = 1; + } + } + else + { + k = (int) (INV_LN2 * x + (negative ? - 0.5 : 0.5)); + t = k; + hi = x - t * LN2_H; + lo = t * LN2_L; + } + + x = hi - lo; + c = (hi - x) - lo; + + } + else if (h_bits < 0x3c900000L) // |x| < 2^-54 return x + return x; + else + k = 0; + + // x is now in primary range + hfx = 0.5 * x; + hxs = x * hfx; + r1 = 1.0 + hxs * (EXPM1_Q1 + + hxs * (EXPM1_Q2 + + hxs * (EXPM1_Q3 + + hxs * (EXPM1_Q4 + + hxs * EXPM1_Q5)))); + t = 3.0 - r1 * hfx; + e = hxs * ((r1 - t) / (6.0 - x * t)); + + if (k == 0) + { + return x - (x * e - hxs); // c == 0 + } + else + { + e = x * (e - c) - c; + e -= hxs; + + if (k == -1) + return 0.5 * (x - e) - 0.5; + + if (k == 1) + { + if (x < - 0.25) + return -2.0 * (e - (x + 0.5)); + else + return 1.0 + 2.0 * (x - e); + } + + if (k <= -2 || k > 56) // sufficient to return exp(x) - 1 + { + y = 1.0 - (e - x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + + return y - 1.0; + } + + t = 1.0; + if (k < 20) + { + bits = Double.doubleToLongBits(t); + h_bits = 0x3ff00000L - (0x00200000L >> k); + l_bits = getLowDWord(bits); + + t = buildDouble(l_bits, h_bits); // t = 1 - 2^(-k) + y = t - (e - x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + } + else + { + bits = Double.doubleToLongBits(t); + h_bits = (0x000003ffL - k) << 20; + l_bits = getLowDWord(bits); + + t = buildDouble(l_bits, h_bits); // t = 2^(-k) + + y = x - (e + t); + y += 1.0; + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + } + } + + return y; + } + + /** + * Take ln(a) (the natural log). The opposite of exp(). If the + * argument is NaN or negative, the result is NaN; if the argument is + * positive infinity, the result is positive infinity; and if the argument + * is either zero, the result is negative infinity. + * + *

    Note that the way to get logb(a) is to do this: + * ln(a) / ln(b). + * + * @param x the number to take the natural log of + * @return the natural log of a + * @see #exp(double) + */ + public static double log(double x) + { + if (x == 0) + return Double.NEGATIVE_INFINITY; + if (x < 0) + return Double.NaN; + if (! (x < Double.POSITIVE_INFINITY)) + return x; + + // Normalize x. + long bits = Double.doubleToLongBits(x); + int exp = (int) (bits >> 52); + if (exp == 0) // Subnormal x. + { + x *= TWO_54; + bits = Double.doubleToLongBits(x); + exp = (int) (bits >> 52) - 54; + } + exp -= 1023; // Unbias exponent. + bits = (bits & 0x000fffffffffffffL) | 0x3ff0000000000000L; + x = Double.longBitsToDouble(bits); + if (x >= SQRT_2) + { + x *= 0.5; + exp++; + } + x--; + if (abs(x) < 1 / TWO_20) + { + if (x == 0) + return exp * LN2_H + exp * LN2_L; + double r = x * x * (0.5 - 1 / 3.0 * x); + if (exp == 0) + return x - r; + return exp * LN2_H - ((r - exp * LN2_L) - x); + } + double s = x / (2 + x); + double z = s * s; + double w = z * z; + double t1 = w * (LG2 + w * (LG4 + w * LG6)); + double t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7))); + double r = t2 + t1; + if (bits >= 0x3ff6174a00000000L && bits < 0x3ff6b85200000000L) + { + double h = 0.5 * x * x; // Need more accuracy for x near sqrt(2). + if (exp == 0) + return x - (h - s * (h + r)); + return exp * LN2_H - ((h - (s * (h + r) + exp * LN2_L)) - x); + } + if (exp == 0) + return x - s * (x - r); + return exp * LN2_H - ((s * (x - r) - exp * LN2_L) - x); + } + + /** + * Take a square root. If the argument is NaN or negative, the result is + * NaN; if the argument is positive infinity, the result is positive + * infinity; and if the result is either zero, the result is the same. + * + *

    For other roots, use pow(x, 1/rootNumber). + * + * @param x the numeric argument + * @return the square root of the argument + * @see #pow(double, double) + */ + public static double sqrt(double x) + { + if (x < 0) + return Double.NaN; + if (x == 0 || ! (x < Double.POSITIVE_INFINITY)) + return x; + + // Normalize x. + long bits = Double.doubleToLongBits(x); + int exp = (int) (bits >> 52); + if (exp == 0) // Subnormal x. + { + x *= TWO_54; + bits = Double.doubleToLongBits(x); + exp = (int) (bits >> 52) - 54; + } + exp -= 1023; // Unbias exponent. + bits = (bits & 0x000fffffffffffffL) | 0x0010000000000000L; + if ((exp & 1) == 1) // Odd exp, double x to make it even. + bits <<= 1; + exp >>= 1; + + // Generate sqrt(x) bit by bit. + bits <<= 1; + long q = 0; + long s = 0; + long r = 0x0020000000000000L; // Move r right to left. + while (r != 0) + { + long t = s + r; + if (t <= bits) + { + s = t + r; + bits -= t; + q += r; + } + bits <<= 1; + r >>= 1; + } + + // Use floating add to round correctly. + if (bits != 0) + q += q & 1; + return Double.longBitsToDouble((q >> 1) + ((exp + 1022L) << 52)); + } + + /** + * Raise a number to a power. Special cases:

      + *
    • If the second argument is positive or negative zero, then the result + * is 1.0.
    • + *
    • If the second argument is 1.0, then the result is the same as the + * first argument.
    • + *
    • If the second argument is NaN, then the result is NaN.
    • + *
    • If the first argument is NaN and the second argument is nonzero, + * then the result is NaN.
    • + *
    • If the absolute value of the first argument is greater than 1 and + * the second argument is positive infinity, or the absolute value of the + * first argument is less than 1 and the second argument is negative + * infinity, then the result is positive infinity.
    • + *
    • If the absolute value of the first argument is greater than 1 and + * the second argument is negative infinity, or the absolute value of the + * first argument is less than 1 and the second argument is positive + * infinity, then the result is positive zero.
    • + *
    • If the absolute value of the first argument equals 1 and the second + * argument is infinite, then the result is NaN.
    • + *
    • If the first argument is positive zero and the second argument is + * greater than zero, or the first argument is positive infinity and the + * second argument is less than zero, then the result is positive zero.
    • + *
    • If the first argument is positive zero and the second argument is + * less than zero, or the first argument is positive infinity and the + * second argument is greater than zero, then the result is positive + * infinity.
    • + *
    • If the first argument is negative zero and the second argument is + * greater than zero but not a finite odd integer, or the first argument is + * negative infinity and the second argument is less than zero but not a + * finite odd integer, then the result is positive zero.
    • + *
    • If the first argument is negative zero and the second argument is a + * positive finite odd integer, or the first argument is negative infinity + * and the second argument is a negative finite odd integer, then the result + * is negative zero.
    • + *
    • If the first argument is negative zero and the second argument is + * less than zero but not a finite odd integer, or the first argument is + * negative infinity and the second argument is greater than zero but not a + * finite odd integer, then the result is positive infinity.
    • + *
    • If the first argument is negative zero and the second argument is a + * negative finite odd integer, or the first argument is negative infinity + * and the second argument is a positive finite odd integer, then the result + * is negative infinity.
    • + *
    • If the first argument is less than zero and the second argument is a + * finite even integer, then the result is equal to the result of raising + * the absolute value of the first argument to the power of the second + * argument.
    • + *
    • If the first argument is less than zero and the second argument is a + * finite odd integer, then the result is equal to the negative of the + * result of raising the absolute value of the first argument to the power + * of the second argument.
    • + *
    • If the first argument is finite and less than zero and the second + * argument is finite and not an integer, then the result is NaN.
    • + *
    • If both arguments are integers, then the result is exactly equal to + * the mathematical result of raising the first argument to the power of + * the second argument if that result can in fact be represented exactly as + * a double value.
    • + * + *

    (In the foregoing descriptions, a floating-point value is + * considered to be an integer if and only if it is a fixed point of the + * method {@link #ceil(double)} or, equivalently, a fixed point of the + * method {@link #floor(double)}. A value is a fixed point of a one-argument + * method if and only if the result of applying the method to the value is + * equal to the value.) + * + * @param x the number to raise + * @param y the power to raise it to + * @return xy + */ + public static double pow(double x, double y) + { + // Special cases first. + if (y == 0) + return 1; + if (y == 1) + return x; + if (y == -1) + return 1 / x; + if (x != x || y != y) + return Double.NaN; + + // When x < 0, yisint tells if y is not an integer (0), even(1), + // or odd (2). + int yisint = 0; + if (x < 0 && floor(y) == y) + yisint = (y % 2 == 0) ? 2 : 1; + double ax = abs(x); + double ay = abs(y); + + // More special cases, of y. + if (ay == Double.POSITIVE_INFINITY) + { + if (ax == 1) + return Double.NaN; + if (ax > 1) + return y > 0 ? y : 0; + return y < 0 ? -y : 0; + } + if (y == 2) + return x * x; + if (y == 0.5) + return sqrt(x); + + // More special cases, of x. + if (x == 0 || ax == Double.POSITIVE_INFINITY || ax == 1) + { + if (y < 0) + ax = 1 / ax; + if (x < 0) + { + if (x == -1 && yisint == 0) + ax = Double.NaN; + else if (yisint == 1) + ax = -ax; + } + return ax; + } + if (x < 0 && yisint == 0) + return Double.NaN; + + // Now we can start! + double t; + double t1; + double t2; + double u; + double v; + double w; + if (ay > TWO_31) + { + if (ay > TWO_64) // Automatic over/underflow. + return ((ax < 1) ? y < 0 : y > 0) ? Double.POSITIVE_INFINITY : 0; + // Over/underflow if x is not close to one. + if (ax < 0.9999995231628418) + return y < 0 ? Double.POSITIVE_INFINITY : 0; + if (ax >= 1.0000009536743164) + return y > 0 ? Double.POSITIVE_INFINITY : 0; + // Now |1-x| is <= 2**-20, sufficient to compute + // log(x) by x-x^2/2+x^3/3-x^4/4. + t = x - 1; + w = t * t * (0.5 - t * (1 / 3.0 - t * 0.25)); + u = INV_LN2_H * t; + v = t * INV_LN2_L - w * INV_LN2; + t1 = (float) (u + v); + t2 = v - (t1 - u); + } + else + { + long bits = Double.doubleToLongBits(ax); + int exp = (int) (bits >> 52); + if (exp == 0) // Subnormal x. + { + ax *= TWO_54; + bits = Double.doubleToLongBits(ax); + exp = (int) (bits >> 52) - 54; + } + exp -= 1023; // Unbias exponent. + ax = Double.longBitsToDouble((bits & 0x000fffffffffffffL) + | 0x3ff0000000000000L); + boolean k; + if (ax < SQRT_1_5) // |x|= 1024) // Detect overflow. + { + if (z > 1024 || p_l + OVT > z - p_h) + return negative ? Double.NEGATIVE_INFINITY + : Double.POSITIVE_INFINITY; + } + else if (z <= -1075) // Detect underflow. + { + if (z < -1075 || p_l <= z - p_h) + return negative ? -0.0 : 0; + } + + // Compute 2**(p_h+p_l). + int n = round((float) z); + p_h -= n; + t = (float) (p_l + p_h); + u = t * LN2_H; + v = (p_l - (t - p_h)) * LN2 + t * LN2_L; + z = u + v; + w = v - (z - u); + t = z * z; + t1 = z - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5)))); + double r = (z * t1) / (t1 - 2) - (w + z * w); + z = scale(1 - (r - z), n); + return negative ? -z : z; + } + + /** + * Get the IEEE 754 floating point remainder on two numbers. This is the + * value of x - y * n, where n is the closest + * double to x / y (ties go to the even n); for a zero + * remainder, the sign is that of x. If either argument is NaN, + * the first argument is infinite, or the second argument is zero, the result + * is NaN; if x is finite but y is infinite, the result is x. + * + * @param x the dividend (the top half) + * @param y the divisor (the bottom half) + * @return the IEEE 754-defined floating point remainder of x/y + * @see #rint(double) + */ + public static double IEEEremainder(double x, double y) + { + // Purge off exception values. + if (x == Double.NEGATIVE_INFINITY || ! (x < Double.POSITIVE_INFINITY) + || y == 0 || y != y) + return Double.NaN; + + boolean negative = x < 0; + x = abs(x); + y = abs(y); + if (x == y || x == 0) + return 0 * x; // Get correct sign. + + // Achieve x < 2y, then take first shot at remainder. + if (y < TWO_1023) + x %= y + y; + + // Now adjust x to get correct precision. + if (y < 4 / TWO_1023) + { + if (x + x > y) + { + x -= y; + if (x + x >= y) + x -= y; + } + } + else + { + y *= 0.5; + if (x > y) + { + x -= y; + if (x >= y) + x -= y; + } + } + return negative ? -x : x; + } + + /** + * Take the nearest integer that is that is greater than or equal to the + * argument. If the argument is NaN, infinite, or zero, the result is the + * same; if the argument is between -1 and 0, the result is negative zero. + * Note that Math.ceil(x) == -Math.floor(-x). + * + * @param a the value to act upon + * @return the nearest integer >= a + */ + public static double ceil(double a) + { + return -floor(-a); + } + + /** + * Take the nearest integer that is that is less than or equal to the + * argument. If the argument is NaN, infinite, or zero, the result is the + * same. Note that Math.ceil(x) == -Math.floor(-x). + * + * @param a the value to act upon + * @return the nearest integer <= a + */ + public static double floor(double a) + { + double x = abs(a); + if (! (x < TWO_52) || (long) a == a) + return a; // No fraction bits; includes NaN and infinity. + if (x < 1) + return a >= 0 ? 0 * a : -1; // Worry about signed zero. + return a < 0 ? (long) a - 1.0 : (long) a; // Cast to long truncates. + } + + /** + * Take the nearest integer to the argument. If it is exactly between + * two integers, the even integer is taken. If the argument is NaN, + * infinite, or zero, the result is the same. + * + * @param a the value to act upon + * @return the nearest integer to a + */ + public static double rint(double a) + { + double x = abs(a); + if (! (x < TWO_52)) + return a; // No fraction bits; includes NaN and infinity. + if (x <= 0.5) + return 0 * a; // Worry about signed zero. + if (x % 2 <= 0.5) + return (long) a; // Catch round down to even. + return (long) (a + (a < 0 ? -0.5 : 0.5)); // Cast to long truncates. + } + + /** + * Take the nearest integer to the argument. This is equivalent to + * (int) Math.floor(f + 0.5f). If the argument is NaN, the + * result is 0; otherwise if the argument is outside the range of int, the + * result will be Integer.MIN_VALUE or Integer.MAX_VALUE, as appropriate. + * + * @param f the argument to round + * @return the nearest integer to the argument + * @see Integer#MIN_VALUE + * @see Integer#MAX_VALUE + */ + public static int round(float f) + { + return (int) floor(f + 0.5f); + } + + /** + * Take the nearest long to the argument. This is equivalent to + * (long) Math.floor(d + 0.5). If the argument is NaN, the + * result is 0; otherwise if the argument is outside the range of long, the + * result will be Long.MIN_VALUE or Long.MAX_VALUE, as appropriate. + * + * @param d the argument to round + * @return the nearest long to the argument + * @see Long#MIN_VALUE + * @see Long#MAX_VALUE + */ + public static long round(double d) + { + return (long) floor(d + 0.5); + } + + /** + * Get a random number. This behaves like Random.nextDouble(), seeded by + * System.currentTimeMillis() when first called. In other words, the number + * is from a pseudorandom sequence, and lies in the range [+0.0, 1.0). + * This random sequence is only used by this method, and is threadsafe, + * although you may want your own random number generator if it is shared + * among threads. + * + * @return a random number + * @see Random#nextDouble() + * @see System#currentTimeMillis() + */ + public static synchronized double random() + { + if (rand == null) + rand = new Random(); + return rand.nextDouble(); + } + + /** + * Convert from degrees to radians. The formula for this is + * radians = degrees * (pi/180); however it is not always exact given the + * limitations of floating point numbers. + * + * @param degrees an angle in degrees + * @return the angle in radians + */ + public static double toRadians(double degrees) + { + return (degrees * PI) / 180; + } + + /** + * Convert from radians to degrees. The formula for this is + * degrees = radians * (180/pi); however it is not always exact given the + * limitations of floating point numbers. + * + * @param rads an angle in radians + * @return the angle in degrees + */ + public static double toDegrees(double rads) + { + return (rads * 180) / PI; + } + + /** + * Constants for scaling and comparing doubles by powers of 2. The compiler + * must automatically inline constructs like (1/TWO_54), so we don't list + * negative powers of two here. + */ + private static final double + TWO_16 = 0x10000, // Long bits 0x40f0000000000000L. + TWO_20 = 0x100000, // Long bits 0x4130000000000000L. + TWO_24 = 0x1000000, // Long bits 0x4170000000000000L. + TWO_27 = 0x8000000, // Long bits 0x41a0000000000000L. + TWO_28 = 0x10000000, // Long bits 0x41b0000000000000L. + TWO_29 = 0x20000000, // Long bits 0x41c0000000000000L. + TWO_31 = 0x80000000L, // Long bits 0x41e0000000000000L. + TWO_49 = 0x2000000000000L, // Long bits 0x4300000000000000L. + TWO_52 = 0x10000000000000L, // Long bits 0x4330000000000000L. + TWO_54 = 0x40000000000000L, // Long bits 0x4350000000000000L. + TWO_57 = 0x200000000000000L, // Long bits 0x4380000000000000L. + TWO_60 = 0x1000000000000000L, // Long bits 0x43b0000000000000L. + TWO_64 = 1.8446744073709552e19, // Long bits 0x43f0000000000000L. + TWO_66 = 7.378697629483821e19, // Long bits 0x4410000000000000L. + TWO_1023 = 8.98846567431158e307; // Long bits 0x7fe0000000000000L. + + /** + * Super precision for 2/pi in 24-bit chunks, for use in + * {@link #remPiOver2(double, double[])}. + */ + private static final int TWO_OVER_PI[] = { + 0xa2f983, 0x6e4e44, 0x1529fc, 0x2757d1, 0xf534dd, 0xc0db62, + 0x95993c, 0x439041, 0xfe5163, 0xabdebb, 0xc561b7, 0x246e3a, + 0x424dd2, 0xe00649, 0x2eea09, 0xd1921c, 0xfe1deb, 0x1cb129, + 0xa73ee8, 0x8235f5, 0x2ebb44, 0x84e99c, 0x7026b4, 0x5f7e41, + 0x3991d6, 0x398353, 0x39f49c, 0x845f8b, 0xbdf928, 0x3b1ff8, + 0x97ffde, 0x05980f, 0xef2f11, 0x8b5a0a, 0x6d1f6d, 0x367ecf, + 0x27cb09, 0xb74f46, 0x3f669e, 0x5fea2d, 0x7527ba, 0xc7ebe5, + 0xf17b3d, 0x0739f7, 0x8a5292, 0xea6bfb, 0x5fb11f, 0x8d5d08, + 0x560330, 0x46fc7b, 0x6babf0, 0xcfbc20, 0x9af436, 0x1da9e3, + 0x91615e, 0xe61b08, 0x659985, 0x5f14a0, 0x68408d, 0xffd880, + 0x4d7327, 0x310606, 0x1556ca, 0x73a8c9, 0x60e27b, 0xc08c6b, + }; + + /** + * Super precision for pi/2 in 24-bit chunks, for use in + * {@link #remPiOver2(double, double[])}. + */ + private static final double PI_OVER_TWO[] = { + 1.570796251296997, // Long bits 0x3ff921fb40000000L. + 7.549789415861596e-8, // Long bits 0x3e74442d00000000L. + 5.390302529957765e-15, // Long bits 0x3cf8469880000000L. + 3.282003415807913e-22, // Long bits 0x3b78cc5160000000L. + 1.270655753080676e-29, // Long bits 0x39f01b8380000000L. + 1.2293330898111133e-36, // Long bits 0x387a252040000000L. + 2.7337005381646456e-44, // Long bits 0x36e3822280000000L. + 2.1674168387780482e-51, // Long bits 0x3569f31d00000000L. + }; + + /** + * More constants related to pi, used in + * {@link #remPiOver2(double, double[])} and elsewhere. + */ + private static final double + PI_L = 1.2246467991473532e-16, // Long bits 0x3ca1a62633145c07L. + PIO2_1 = 1.5707963267341256, // Long bits 0x3ff921fb54400000L. + PIO2_1L = 6.077100506506192e-11, // Long bits 0x3dd0b4611a626331L. + PIO2_2 = 6.077100506303966e-11, // Long bits 0x3dd0b4611a600000L. + PIO2_2L = 2.0222662487959506e-21, // Long bits 0x3ba3198a2e037073L. + PIO2_3 = 2.0222662487111665e-21, // Long bits 0x3ba3198a2e000000L. + PIO2_3L = 8.4784276603689e-32; // Long bits 0x397b839a252049c1L. + + /** + * Natural log and square root constants, for calculation of + * {@link #exp(double)}, {@link #log(double)} and + * {@link #pow(double, double)}. CP is 2/(3*ln(2)). + */ + private static final double + SQRT_1_5 = 1.224744871391589, // Long bits 0x3ff3988e1409212eL. + SQRT_2 = 1.4142135623730951, // Long bits 0x3ff6a09e667f3bcdL. + SQRT_3 = 1.7320508075688772, // Long bits 0x3ffbb67ae8584caaL. + EXP_LIMIT_H = 709.782712893384, // Long bits 0x40862e42fefa39efL. + EXP_LIMIT_L = -745.1332191019411, // Long bits 0xc0874910d52d3051L. + CP = 0.9617966939259756, // Long bits 0x3feec709dc3a03fdL. + CP_H = 0.9617967009544373, // Long bits 0x3feec709e0000000L. + CP_L = -7.028461650952758e-9, // Long bits 0xbe3e2fe0145b01f5L. + LN2 = 0.6931471805599453, // Long bits 0x3fe62e42fefa39efL. + LN2_H = 0.6931471803691238, // Long bits 0x3fe62e42fee00000L. + LN2_L = 1.9082149292705877e-10, // Long bits 0x3dea39ef35793c76L. + INV_LN2 = 1.4426950408889634, // Long bits 0x3ff71547652b82feL. + INV_LN2_H = 1.4426950216293335, // Long bits 0x3ff7154760000000L. + INV_LN2_L = 1.9259629911266175e-8; // Long bits 0x3e54ae0bf85ddf44L. + + /** + * Constants for computing {@link #log(double)}. + */ + private static final double + LG1 = 0.6666666666666735, // Long bits 0x3fe5555555555593L. + LG2 = 0.3999999999940942, // Long bits 0x3fd999999997fa04L. + LG3 = 0.2857142874366239, // Long bits 0x3fd2492494229359L. + LG4 = 0.22222198432149784, // Long bits 0x3fcc71c51d8e78afL. + LG5 = 0.1818357216161805, // Long bits 0x3fc7466496cb03deL. + LG6 = 0.15313837699209373, // Long bits 0x3fc39a09d078c69fL. + LG7 = 0.14798198605116586; // Long bits 0x3fc2f112df3e5244L. + + /** + * Constants for computing {@link #pow(double, double)}. L and P are + * coefficients for series; OVT is -(1024-log2(ovfl+.5ulp)); and DP is ???. + * The P coefficients also calculate {@link #exp(double)}. + */ + private static final double + L1 = 0.5999999999999946, // Long bits 0x3fe3333333333303L. + L2 = 0.4285714285785502, // Long bits 0x3fdb6db6db6fabffL. + L3 = 0.33333332981837743, // Long bits 0x3fd55555518f264dL. + L4 = 0.272728123808534, // Long bits 0x3fd17460a91d4101L. + L5 = 0.23066074577556175, // Long bits 0x3fcd864a93c9db65L. + L6 = 0.20697501780033842, // Long bits 0x3fca7e284a454eefL. + P1 = 0.16666666666666602, // Long bits 0x3fc555555555553eL. + P2 = -2.7777777777015593e-3, // Long bits 0xbf66c16c16bebd93L. + P3 = 6.613756321437934e-5, // Long bits 0x3f11566aaf25de2cL. + P4 = -1.6533902205465252e-6, // Long bits 0xbebbbd41c5d26bf1L. + P5 = 4.1381367970572385e-8, // Long bits 0x3e66376972bea4d0L. + DP_H = 0.5849624872207642, // Long bits 0x3fe2b80340000000L. + DP_L = 1.350039202129749e-8, // Long bits 0x3e4cfdeb43cfd006L. + OVT = 8.008566259537294e-17; // Long bits 0x3c971547652b82feL. + + /** + * Coefficients for computing {@link #sin(double)}. + */ + private static final double + S1 = -0.16666666666666632, // Long bits 0xbfc5555555555549L. + S2 = 8.33333333332249e-3, // Long bits 0x3f8111111110f8a6L. + S3 = -1.984126982985795e-4, // Long bits 0xbf2a01a019c161d5L. + S4 = 2.7557313707070068e-6, // Long bits 0x3ec71de357b1fe7dL. + S5 = -2.5050760253406863e-8, // Long bits 0xbe5ae5e68a2b9cebL. + S6 = 1.58969099521155e-10; // Long bits 0x3de5d93a5acfd57cL. + + /** + * Coefficients for computing {@link #cos(double)}. + */ + private static final double + C1 = 0.0416666666666666, // Long bits 0x3fa555555555554cL. + C2 = -1.388888888887411e-3, // Long bits 0xbf56c16c16c15177L. + C3 = 2.480158728947673e-5, // Long bits 0x3efa01a019cb1590L. + C4 = -2.7557314351390663e-7, // Long bits 0xbe927e4f809c52adL. + C5 = 2.087572321298175e-9, // Long bits 0x3e21ee9ebdb4b1c4L. + C6 = -1.1359647557788195e-11; // Long bits 0xbda8fae9be8838d4L. + + /** + * Coefficients for computing {@link #tan(double)}. + */ + private static final double + T0 = 0.3333333333333341, // Long bits 0x3fd5555555555563L. + T1 = 0.13333333333320124, // Long bits 0x3fc111111110fe7aL. + T2 = 0.05396825397622605, // Long bits 0x3faba1ba1bb341feL. + T3 = 0.021869488294859542, // Long bits 0x3f9664f48406d637L. + T4 = 8.8632398235993e-3, // Long bits 0x3f8226e3e96e8493L. + T5 = 3.5920791075913124e-3, // Long bits 0x3f6d6d22c9560328L. + T6 = 1.4562094543252903e-3, // Long bits 0x3f57dbc8fee08315L. + T7 = 5.880412408202641e-4, // Long bits 0x3f4344d8f2f26501L. + T8 = 2.464631348184699e-4, // Long bits 0x3f3026f71a8d1068L. + T9 = 7.817944429395571e-5, // Long bits 0x3f147e88a03792a6L. + T10 = 7.140724913826082e-5, // Long bits 0x3f12b80f32f0a7e9L. + T11 = -1.8558637485527546e-5, // Long bits 0xbef375cbdb605373L. + T12 = 2.590730518636337e-5; // Long bits 0x3efb2a7074bf7ad4L. + + /** + * Coefficients for computing {@link #asin(double)} and + * {@link #acos(double)}. + */ + private static final double + PS0 = 0.16666666666666666, // Long bits 0x3fc5555555555555L. + PS1 = -0.3255658186224009, // Long bits 0xbfd4d61203eb6f7dL. + PS2 = 0.20121253213486293, // Long bits 0x3fc9c1550e884455L. + PS3 = -0.04005553450067941, // Long bits 0xbfa48228b5688f3bL. + PS4 = 7.915349942898145e-4, // Long bits 0x3f49efe07501b288L. + PS5 = 3.479331075960212e-5, // Long bits 0x3f023de10dfdf709L. + QS1 = -2.403394911734414, // Long bits 0xc0033a271c8a2d4bL. + QS2 = 2.0209457602335057, // Long bits 0x40002ae59c598ac8L. + QS3 = -0.6882839716054533, // Long bits 0xbfe6066c1b8d0159L. + QS4 = 0.07703815055590194; // Long bits 0x3fb3b8c5b12e9282L. + + /** + * Coefficients for computing {@link #atan(double)}. + */ + private static final double + ATAN_0_5H = 0.4636476090008061, // Long bits 0x3fddac670561bb4fL. + ATAN_0_5L = 2.2698777452961687e-17, // Long bits 0x3c7a2b7f222f65e2L. + ATAN_1_5H = 0.982793723247329, // Long bits 0x3fef730bd281f69bL. + ATAN_1_5L = 1.3903311031230998e-17, // Long bits 0x3c7007887af0cbbdL. + AT0 = 0.3333333333333293, // Long bits 0x3fd555555555550dL. + AT1 = -0.19999999999876483, // Long bits 0xbfc999999998ebc4L. + AT2 = 0.14285714272503466, // Long bits 0x3fc24924920083ffL. + AT3 = -0.11111110405462356, // Long bits 0xbfbc71c6fe231671L. + AT4 = 0.09090887133436507, // Long bits 0x3fb745cdc54c206eL. + AT5 = -0.0769187620504483, // Long bits 0xbfb3b0f2af749a6dL. + AT6 = 0.06661073137387531, // Long bits 0x3fb10d66a0d03d51L. + AT7 = -0.058335701337905735, // Long bits 0xbfadde2d52defd9aL. + AT8 = 0.049768779946159324, // Long bits 0x3fa97b4b24760debL. + AT9 = -0.036531572744216916, // Long bits 0xbfa2b4442c6a6c2fL. + AT10 = 0.016285820115365782; // Long bits 0x3f90ad3ae322da11L. + + /** + * Constants for computing {@link #cbrt(double)}. + */ + private static final int + CBRT_B1 = 715094163, // B1 = (682-0.03306235651)*2**20 + CBRT_B2 = 696219795; // B2 = (664-0.03306235651)*2**20 + + /** + * Constants for computing {@link #cbrt(double)}. + */ + private static final double + CBRT_C = 5.42857142857142815906e-01, // Long bits 0x3fe15f15f15f15f1L + CBRT_D = -7.05306122448979611050e-01, // Long bits 0xbfe691de2532c834L + CBRT_E = 1.41428571428571436819e+00, // Long bits 0x3ff6a0ea0ea0ea0fL + CBRT_F = 1.60714285714285720630e+00, // Long bits 0x3ff9b6db6db6db6eL + CBRT_G = 3.57142857142857150787e-01; // Long bits 0x3fd6db6db6db6db7L + + /** + * Constants for computing {@link #expm1(double)} + */ + private static final double + EXPM1_Q1 = -3.33333333333331316428e-02, // Long bits 0xbfa11111111110f4L + EXPM1_Q2 = 1.58730158725481460165e-03, // Long bits 0x3f5a01a019fe5585L + EXPM1_Q3 = -7.93650757867487942473e-05, // Long bits 0xbf14ce199eaadbb7L + EXPM1_Q4 = 4.00821782732936239552e-06, // Long bits 0x3ed0cfca86e65239L + EXPM1_Q5 = -2.01099218183624371326e-07; // Long bits 0xbe8afdb76e09c32dL + + /** + * Helper function for reducing an angle to a multiple of pi/2 within + * [-pi/4, pi/4]. + * + * @param x the angle; not infinity or NaN, and outside pi/4 + * @param y an array of 2 doubles modified to hold the remander x % pi/2 + * @return the quadrant of the result, mod 4: 0: [-pi/4, pi/4], + * 1: [pi/4, 3*pi/4], 2: [3*pi/4, 5*pi/4], 3: [-3*pi/4, -pi/4] + */ + private static int remPiOver2(double x, double[] y) + { + boolean negative = x < 0; + x = abs(x); + double z; + int n; + if (Configuration.DEBUG && (x <= PI / 4 || x != x + || x == Double.POSITIVE_INFINITY)) + throw new InternalError("Assertion failure"); + if (x < 3 * PI / 4) // If |x| is small. + { + z = x - PIO2_1; + if ((float) x != (float) (PI / 2)) // 33+53 bit pi is good enough. + { + y[0] = z - PIO2_1L; + y[1] = z - y[0] - PIO2_1L; + } + else // Near pi/2, use 33+33+53 bit pi. + { + z -= PIO2_2; + y[0] = z - PIO2_2L; + y[1] = z - y[0] - PIO2_2L; + } + n = 1; + } + else if (x <= TWO_20 * PI / 2) // Medium size. + { + n = (int) (2 / PI * x + 0.5); + z = x - n * PIO2_1; + double w = n * PIO2_1L; // First round good to 85 bits. + y[0] = z - w; + if (n >= 32 || (float) x == (float) (w)) + { + if (x / y[0] >= TWO_16) // Second iteration, good to 118 bits. + { + double t = z; + w = n * PIO2_2; + z = t - w; + w = n * PIO2_2L - (t - z - w); + y[0] = z - w; + if (x / y[0] >= TWO_49) // Third iteration, 151 bits accuracy. + { + t = z; + w = n * PIO2_3; + z = t - w; + w = n * PIO2_3L - (t - z - w); + y[0] = z - w; + } + } + } + y[1] = z - y[0] - w; + } + else + { + // All other (large) arguments. + int e0 = (int) (Double.doubleToLongBits(x) >> 52) - 1046; + z = scale(x, -e0); // e0 = ilogb(z) - 23. + double[] tx = new double[3]; + for (int i = 0; i < 2; i++) + { + tx[i] = (int) z; + z = (z - tx[i]) * TWO_24; + } + tx[2] = z; + int nx = 2; + while (tx[nx] == 0) + nx--; + n = remPiOver2(tx, y, e0, nx); + } + if (negative) + { + y[0] = -y[0]; + y[1] = -y[1]; + return -n; + } + return n; + } + + /** + * Helper function for reducing an angle to a multiple of pi/2 within + * [-pi/4, pi/4]. + * + * @param x the positive angle, broken into 24-bit chunks + * @param y an array of 2 doubles modified to hold the remander x % pi/2 + * @param e0 the exponent of x[0] + * @param nx the last index used in x + * @return the quadrant of the result, mod 4: 0: [-pi/4, pi/4], + * 1: [pi/4, 3*pi/4], 2: [3*pi/4, 5*pi/4], 3: [-3*pi/4, -pi/4] + */ + private static int remPiOver2(double[] x, double[] y, int e0, int nx) + { + int i; + int ih; + int n; + double fw; + double z; + int[] iq = new int[20]; + double[] f = new double[20]; + double[] q = new double[20]; + boolean recompute = false; + + // Initialize jk, jz, jv, q0; note that 3>q0. + int jk = 4; + int jz = jk; + int jv = max((e0 - 3) / 24, 0); + int q0 = e0 - 24 * (jv + 1); + + // Set up f[0] to f[nx+jk] where f[nx+jk] = TWO_OVER_PI[jv+jk]. + int j = jv - nx; + int m = nx + jk; + for (i = 0; i <= m; i++, j++) + f[i] = (j < 0) ? 0 : TWO_OVER_PI[j]; + + // Compute q[0],q[1],...q[jk]. + for (i = 0; i <= jk; i++) + { + for (j = 0, fw = 0; j <= nx; j++) + fw += x[j] * f[nx + i - j]; + q[i] = fw; + } + + do + { + // Distill q[] into iq[] reversingly. + for (i = 0, j = jz, z = q[jz]; j > 0; i++, j--) + { + fw = (int) (1 / TWO_24 * z); + iq[i] = (int) (z - TWO_24 * fw); + z = q[j - 1] + fw; + } + + // Compute n. + z = scale(z, q0); + z -= 8 * floor(z * 0.125); // Trim off integer >= 8. + n = (int) z; + z -= n; + ih = 0; + if (q0 > 0) // Need iq[jz-1] to determine n. + { + i = iq[jz - 1] >> (24 - q0); + n += i; + iq[jz - 1] -= i << (24 - q0); + ih = iq[jz - 1] >> (23 - q0); + } + else if (q0 == 0) + ih = iq[jz - 1] >> 23; + else if (z >= 0.5) + ih = 2; + + if (ih > 0) // If q > 0.5. + { + n += 1; + int carry = 0; + for (i = 0; i < jz; i++) // Compute 1-q. + { + j = iq[i]; + if (carry == 0) + { + if (j != 0) + { + carry = 1; + iq[i] = 0x1000000 - j; + } + } + else + iq[i] = 0xffffff - j; + } + switch (q0) + { + case 1: // Rare case: chance is 1 in 12 for non-default. + iq[jz - 1] &= 0x7fffff; + break; + case 2: + iq[jz - 1] &= 0x3fffff; + } + if (ih == 2) + { + z = 1 - z; + if (carry != 0) + z -= scale(1, q0); + } + } + + // Check if recomputation is needed. + if (z == 0) + { + j = 0; + for (i = jz - 1; i >= jk; i--) + j |= iq[i]; + if (j == 0) // Need recomputation. + { + int k; // k = no. of terms needed. + for (k = 1; iq[jk - k] == 0; k++) + ; + + for (i = jz + 1; i <= jz + k; i++) // Add q[jz+1] to q[jz+k]. + { + f[nx + i] = TWO_OVER_PI[jv + i]; + for (j = 0, fw = 0; j <= nx; j++) + fw += x[j] * f[nx + i - j]; + q[i] = fw; + } + jz += k; + recompute = true; + } + } + } + while (recompute); + + // Chop off zero terms. + if (z == 0) + { + jz--; + q0 -= 24; + while (iq[jz] == 0) + { + jz--; + q0 -= 24; + } + } + else // Break z into 24-bit if necessary. + { + z = scale(z, -q0); + if (z >= TWO_24) + { + fw = (int) (1 / TWO_24 * z); + iq[jz] = (int) (z - TWO_24 * fw); + jz++; + q0 += 24; + iq[jz] = (int) fw; + } + else + iq[jz] = (int) z; + } + + // Convert integer "bit" chunk to floating-point value. + fw = scale(1, q0); + for (i = jz; i >= 0; i--) + { + q[i] = fw * iq[i]; + fw *= 1 / TWO_24; + } + + // Compute PI_OVER_TWO[0,...,jk]*q[jz,...,0]. + double[] fq = new double[20]; + for (i = jz; i >= 0; i--) + { + fw = 0; + for (int k = 0; k <= jk && k <= jz - i; k++) + fw += PI_OVER_TWO[k] * q[i + k]; + fq[jz - i] = fw; + } + + // Compress fq[] into y[]. + fw = 0; + for (i = jz; i >= 0; i--) + fw += fq[i]; + y[0] = (ih == 0) ? fw : -fw; + fw = fq[0] - fw; + for (i = 1; i <= jz; i++) + fw += fq[i]; + y[1] = (ih == 0) ? fw : -fw; + return n; + } + + /** + * Helper method for scaling a double by a power of 2. + * + * @param x the double + * @param n the scale; |n| < 2048 + * @return x * 2**n + */ + private static double scale(double x, int n) + { + if (Configuration.DEBUG && abs(n) >= 2048) + throw new InternalError("Assertion failure"); + if (x == 0 || x == Double.NEGATIVE_INFINITY + || ! (x < Double.POSITIVE_INFINITY) || n == 0) + return x; + long bits = Double.doubleToLongBits(x); + int exp = (int) (bits >> 52) & 0x7ff; + if (exp == 0) // Subnormal x. + { + x *= TWO_54; + exp = ((int) (Double.doubleToLongBits(x) >> 52) & 0x7ff) - 54; + } + exp += n; + if (exp > 0x7fe) // Overflow. + return Double.POSITIVE_INFINITY * x; + if (exp > 0) // Normal. + return Double.longBitsToDouble((bits & 0x800fffffffffffffL) + | ((long) exp << 52)); + if (exp <= -54) + return 0 * x; // Underflow. + exp += 54; // Subnormal result. + x = Double.longBitsToDouble((bits & 0x800fffffffffffffL) + | ((long) exp << 52)); + return x * (1 / TWO_54); + } + + /** + * Helper trig function; computes sin in range [-pi/4, pi/4]. + * + * @param x angle within about pi/4 + * @param y tail of x, created by remPiOver2 + * @return sin(x+y) + */ + private static double sin(double x, double y) + { + if (Configuration.DEBUG && abs(x + y) > 0.7854) + throw new InternalError("Assertion failure"); + if (abs(x) < 1 / TWO_27) + return x; // If |x| ~< 2**-27, already know answer. + + double z = x * x; + double v = z * x; + double r = S2 + z * (S3 + z * (S4 + z * (S5 + z * S6))); + if (y == 0) + return x + v * (S1 + z * r); + return x - ((z * (0.5 * y - v * r) - y) - v * S1); + } + + /** + * Helper trig function; computes cos in range [-pi/4, pi/4]. + * + * @param x angle within about pi/4 + * @param y tail of x, created by remPiOver2 + * @return cos(x+y) + */ + private static double cos(double x, double y) + { + if (Configuration.DEBUG && abs(x + y) > 0.7854) + throw new InternalError("Assertion failure"); + x = abs(x); + if (x < 1 / TWO_27) + return 1; // If |x| ~< 2**-27, already know answer. + + double z = x * x; + double r = z * (C1 + z * (C2 + z * (C3 + z * (C4 + z * (C5 + z * C6))))); + + if (x < 0.3) + return 1 - (0.5 * z - (z * r - x * y)); + + double qx = (x > 0.78125) ? 0.28125 : (x * 0.25); + return 1 - qx - ((0.5 * z - qx) - (z * r - x * y)); + } + + /** + * Helper trig function; computes tan in range [-pi/4, pi/4]. + * + * @param x angle within about pi/4 + * @param y tail of x, created by remPiOver2 + * @param invert true iff -1/tan should be returned instead + * @return tan(x+y) + */ + private static double tan(double x, double y, boolean invert) + { + // PI/2 is irrational, so no double is a perfect multiple of it. + if (Configuration.DEBUG && (abs(x + y) > 0.7854 || (x == 0 && invert))) + throw new InternalError("Assertion failure"); + boolean negative = x < 0; + if (negative) + { + x = -x; + y = -y; + } + if (x < 1 / TWO_28) // If |x| ~< 2**-28, already know answer. + return (negative ? -1 : 1) * (invert ? -1 / x : x); + + double z; + double w; + boolean large = x >= 0.6744; + if (large) + { + z = PI / 4 - x; + w = PI_L / 4 - y; + x = z + w; + y = 0; + } + z = x * x; + w = z * z; + // Break x**5*(T1+x**2*T2+...) into + // x**5(T1+x**4*T3+...+x**20*T11) + // + x**5(x**2*(T2+x**4*T4+...+x**22*T12)). + double r = T1 + w * (T3 + w * (T5 + w * (T7 + w * (T9 + w * T11)))); + double v = z * (T2 + w * (T4 + w * (T6 + w * (T8 + w * (T10 + w * T12))))); + double s = z * x; + r = y + z * (s * (r + v) + y); + r += T0 * s; + w = x + r; + if (large) + { + v = invert ? -1 : 1; + return (negative ? -1 : 1) * (v - 2 * (x - (w * w / (w + v) - r))); + } + if (! invert) + return w; + + // Compute -1.0/(x+r) accurately. + z = (float) w; + v = r - (z - x); + double a = -1 / w; + double t = (float) a; + return t + a * (1 + t * z + t * v); + } + + /** + *

    + * Returns the sign of the argument as follows: + *

    + *
      + *
    • If a is greater than zero, the result is 1.0.
    • + *
    • If a is less than zero, the result is -1.0.
    • + *
    • If a is NaN, the result is NaN. + *
    • If a is positive or negative zero, the result is the + * same.
    • + *
    + * + * @param a the numeric argument. + * @return the sign of the argument. + * @since 1.5. + */ + public static double signum(double a) + { + // There's no difference. + return Math.signum(a); + } + + /** + *

    + * Returns the sign of the argument as follows: + *

    + *
      + *
    • If a is greater than zero, the result is 1.0f.
    • + *
    • If a is less than zero, the result is -1.0f.
    • + *
    • If a is NaN, the result is NaN. + *
    • If a is positive or negative zero, the result is the + * same.
    • + *
    + * + * @param a the numeric argument. + * @return the sign of the argument. + * @since 1.5. + */ + public static float signum(float a) + { + // There's no difference. + return Math.signum(a); + } + + /** + * Return the ulp for the given double argument. The ulp is the + * difference between the argument and the next larger double. Note + * that the sign of the double argument is ignored, that is, + * ulp(x) == ulp(-x). If the argument is a NaN, then NaN is returned. + * If the argument is an infinity, then +Inf is returned. If the + * argument is zero (either positive or negative), then + * {@link Double#MIN_VALUE} is returned. + * @param d the double whose ulp should be returned + * @return the difference between the argument and the next larger double + * @since 1.5 + */ + public static double ulp(double d) + { + // There's no difference. + return Math.ulp(d); + } + + /** + * Return the ulp for the given float argument. The ulp is the + * difference between the argument and the next larger float. Note + * that the sign of the float argument is ignored, that is, + * ulp(x) == ulp(-x). If the argument is a NaN, then NaN is returned. + * If the argument is an infinity, then +Inf is returned. If the + * argument is zero (either positive or negative), then + * {@link Float#MIN_VALUE} is returned. + * @param f the float whose ulp should be returned + * @return the difference between the argument and the next larger float + * @since 1.5 + */ + public static float ulp(float f) + { + // There's no difference. + return Math.ulp(f); + } +} diff --git a/libjava/classpath/java/lang/String.java b/libjava/classpath/java/lang/String.java new file mode 100644 index 000000000..45c0daff6 --- /dev/null +++ b/libjava/classpath/java/lang/String.java @@ -0,0 +1,2200 @@ +/* String.java -- immutable character sequences; the object of string literals + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.java.lang.CharData; +import gnu.java.lang.CPStringBuilder; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.text.Collator; +import java.util.Comparator; +import java.util.Formatter; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * Strings represent an immutable set of characters. All String literals + * are instances of this class, and two string literals with the same contents + * refer to the same String object. + * + *

    This class also includes a number of methods for manipulating the + * contents of strings (of course, creating a new object if there are any + * changes, as String is immutable). Case mapping relies on Unicode 3.0.0 + * standards, where some character sequences have a different number of + * characters in the uppercase version than the lower case. + * + *

    Strings are special, in that they are the only object with an overloaded + * operator. When you use '+' with at least one String argument, both + * arguments have String conversion performed on them, and another String (not + * guaranteed to be unique) results. + * + *

    String is special-cased when doing data serialization - rather than + * listing the fields of this class, a String object is converted to a string + * literal in the object stream. + * + * @author Paul N. Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @author Per Bothner (bothner@cygnus.com) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.0 + * @status updated to 1.4; but could use better data sharing via offset field + */ +public final class String + implements Serializable, Comparable, CharSequence +{ + // WARNING: String is a CORE class in the bootstrap cycle. See the comments + // in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * This is probably not necessary because this class is special cased already + * but it will avoid showing up as a discrepancy when comparing SUIDs. + */ + private static final long serialVersionUID = -6849794470754667710L; + + /** + * Stores unicode multi-character uppercase expansion table. + * @see #toUpperCase(Locale) + * @see CharData#UPPER_EXPAND + */ + private static final char[] upperExpand + = zeroBasedStringValue(CharData.UPPER_EXPAND); + + /** + * Stores unicode multi-character uppercase special casing table. + * @see #upperCaseExpansion(char) + * @see CharData#UPPER_SPECIAL + */ + private static final char[] upperSpecial + = zeroBasedStringValue(CharData.UPPER_SPECIAL); + + /** + * Characters which make up the String. + * Package access is granted for use by StringBuffer. + */ + final char[] value; + + /** + * Holds the number of characters in value. This number is generally + * the same as value.length, but can be smaller because substrings and + * StringBuffers can share arrays. Package visible for use by trusted code. + */ + final int count; + + /** + * Caches the result of hashCode(). If this value is zero, the hashcode + * is considered uncached (even if 0 is the correct hash value). + */ + private int cachedHashCode; + + /** + * Holds the starting position for characters in value[]. Since + * substring()'s are common, the use of offset allows the operation + * to perform in O(1). Package access is granted for use by StringBuffer. + */ + final int offset; + + /** + * An implementation for {@link #CASE_INSENSITIVE_ORDER}. + * This must be {@link Serializable}. The class name is dictated by + * compatibility with Sun's JDK. + */ + private static final class CaseInsensitiveComparator + implements Comparator, Serializable + { + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 8575799808933029326L; + + /** + * The default private constructor generates unnecessary overhead. + */ + CaseInsensitiveComparator() {} + + /** + * Compares to Strings, using + * String.compareToIgnoreCase(String). + * + * @param o1 the first string + * @param o2 the second string + * @return < 0, 0, or > 0 depending on the case-insensitive + * comparison of the two strings. + * @throws NullPointerException if either argument is null + * @throws ClassCastException if either argument is not a String + * @see #compareToIgnoreCase(String) + */ + public int compare(String o1, String o2) + { + return o1.compareToIgnoreCase(o2); + } + } // class CaseInsensitiveComparator + + /** + * A Comparator that uses String.compareToIgnoreCase(String). + * This comparator is {@link Serializable}. Note that it ignores Locale, + * for that, you want a Collator. + * + * @see Collator#compare(String, String) + * @since 1.2 + */ + public static final Comparator CASE_INSENSITIVE_ORDER + = new CaseInsensitiveComparator(); + + /** + * Creates an empty String (length 0). Unless you really need a new object, + * consider using "" instead. + */ + public String() + { + value = "".value; + offset = 0; + count = 0; + } + + /** + * Copies the contents of a String to a new String. Since Strings are + * immutable, only a shallow copy is performed. + * + * @param str String to copy + * @throws NullPointerException if value is null + */ + public String(String str) + { + value = str.value; + offset = str.offset; + count = str.count; + cachedHashCode = str.cachedHashCode; + } + + /** + * Creates a new String using the character sequence of the char array. + * Subsequent changes to data do not affect the String. + * + * @param data char array to copy + * @throws NullPointerException if data is null + */ + public String(char[] data) + { + this(data, 0, data.length, false); + } + + /** + * Creates a new String using the character sequence of a subarray of + * characters. The string starts at offset, and copies count chars. + * Subsequent changes to data do not affect the String. + * + * @param data char array to copy + * @param offset position (base 0) to start copying out of data + * @param count the number of characters from data to copy + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if (offset < 0 || count < 0 + * || offset + count < 0 (overflow) + * || offset + count > data.length) + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public String(char[] data, int offset, int count) + { + this(data, offset, count, false); + } + + /** + * Creates a new String using an 8-bit array of integer values, starting at + * an offset, and copying up to the count. Each character c, using + * corresponding byte b, is created in the new String as if by performing: + * + *

    +   * c = (char) (((hibyte & 0xff) << 8) | (b & 0xff))
    +   * 
    + * + * @param ascii array of integer values + * @param hibyte top byte of each Unicode character + * @param offset position (base 0) to start copying out of ascii + * @param count the number of characters from ascii to copy + * @throws NullPointerException if ascii is null + * @throws IndexOutOfBoundsException if (offset < 0 || count < 0 + * || offset + count < 0 (overflow) + * || offset + count > ascii.length) + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #String(byte[]) + * @see #String(byte[], String) + * @see #String(byte[], int, int) + * @see #String(byte[], int, int, String) + * @deprecated use {@link #String(byte[], int, int, String)} to perform + * correct encoding + */ + public String(byte[] ascii, int hibyte, int offset, int count) + { + if (offset < 0) + throw new StringIndexOutOfBoundsException("offset: " + offset); + if (count < 0) + throw new StringIndexOutOfBoundsException("count: " + count); + // equivalent to: offset + count < 0 || offset + count > ascii.length + if (ascii.length - offset < count) + throw new StringIndexOutOfBoundsException("offset + count: " + + (offset + count)); + value = new char[count]; + this.offset = 0; + this.count = count; + hibyte <<= 8; + offset += count; + while (--count >= 0) + value[count] = (char) (hibyte | (ascii[--offset] & 0xff)); + } + + /** + * Creates a new String using an 8-bit array of integer values. Each + * character c, using corresponding byte b, is created in the new String + * as if by performing: + * + *
    +   * c = (char) (((hibyte & 0xff) << 8) | (b & 0xff))
    +   * 
    + * + * @param ascii array of integer values + * @param hibyte top byte of each Unicode character + * @throws NullPointerException if ascii is null + * @see #String(byte[]) + * @see #String(byte[], String) + * @see #String(byte[], int, int) + * @see #String(byte[], int, int, String) + * @see #String(byte[], int, int, int) + * @deprecated use {@link #String(byte[], String)} to perform + * correct encoding + */ + public String(byte[] ascii, int hibyte) + { + this(ascii, hibyte, 0, ascii.length); + } + + /** + * Creates a new String using the portion of the byte array starting at the + * offset and ending at offset + count. Uses the specified encoding type + * to decode the byte array, so the resulting string may be longer or + * shorter than the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. The behavior is not specified if + * the decoder encounters invalid characters; this implementation throws + * an Error. + * + * @param data byte array to copy + * @param offset the offset to start at + * @param count the number of bytes in the array to use + * @param encoding the name of the encoding to use + * @throws NullPointerException if data or encoding is null + * @throws IndexOutOfBoundsException if offset or count is incorrect + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @throws UnsupportedEncodingException if encoding is not found + * @throws Error if the decoding fails + * @since 1.1 + */ + public String(byte[] data, int offset, int count, final String encoding) + throws UnsupportedEncodingException + { + this(data, offset, count, stringToCharset(encoding)); + } + + /** + * Wrapper method to convert exceptions resulting from + * the selection of a {@link java.nio.charset.Charset} based on + * a String. + * + * @throws UnsupportedEncodingException if encoding is not found + */ + private static final Charset stringToCharset(final String encoding) + throws UnsupportedEncodingException + { + try + { + return Charset.forName(encoding); + } + catch(IllegalCharsetNameException e) + { + throw new UnsupportedEncodingException("Encoding: "+encoding+ + " not found."); + } + catch(UnsupportedCharsetException e) + { + throw new UnsupportedEncodingException("Encoding: "+encoding+ + " not found."); + } + } + + /** + * Creates a new String using the portion of the byte array starting at the + * offset and ending at offset + count. Uses the specified encoding type + * to decode the byte array, so the resulting string may be longer or + * shorter than the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. Malformed input and unmappable + * character sequences are replaced with the default replacement string + * provided by the {@link java.nio.charset.Charset}. + * + * @param data byte array to copy + * @param offset the offset to start at + * @param count the number of bytes in the array to use + * @param encoding the encoding to use + * @throws NullPointerException if data or encoding is null + * @throws IndexOutOfBoundsException if offset or count is incorrect + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @since 1.6 + */ + public String(byte[] data, int offset, int count, Charset encoding) + { + if (offset < 0) + throw new StringIndexOutOfBoundsException("offset: " + offset); + if (count < 0) + throw new StringIndexOutOfBoundsException("count: " + count); + // equivalent to: offset + count < 0 || offset + count > data.length + if (data.length - offset < count) + throw new StringIndexOutOfBoundsException("offset + count: " + + (offset + count)); + try + { + CharsetDecoder csd = encoding.newDecoder(); + csd.onMalformedInput(CodingErrorAction.REPLACE); + csd.onUnmappableCharacter(CodingErrorAction.REPLACE); + CharBuffer cbuf = csd.decode(ByteBuffer.wrap(data, offset, count)); + if(cbuf.hasArray()) + { + value = cbuf.array(); + this.offset = cbuf.position(); + this.count = cbuf.remaining(); + } else { + // Doubt this will happen. But just in case. + value = new char[cbuf.remaining()]; + cbuf.get(value); + this.offset = 0; + this.count = value.length; + } + } + catch(CharacterCodingException e) + { + // This shouldn't ever happen. + throw (InternalError) new InternalError().initCause(e); + } + } + + /** + * Creates a new String using the byte array. Uses the specified encoding + * type to decode the byte array, so the resulting string may be longer or + * shorter than the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. The behavior is not specified if + * the decoder encounters invalid characters; this implementation throws + * an Error. + * + * @param data byte array to copy + * @param encoding the name of the encoding to use + * @throws NullPointerException if data or encoding is null + * @throws UnsupportedEncodingException if encoding is not found + * @throws Error if the decoding fails + * @see #String(byte[], int, int, String) + * @since 1.1 + */ + public String(byte[] data, String encoding) + throws UnsupportedEncodingException + { + this(data, 0, data.length, encoding); + } + + /** + * Creates a new String using the byte array. Uses the specified encoding + * type to decode the byte array, so the resulting string may be longer or + * shorter than the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. Malformed input and unmappable + * character sequences are replaced with the default replacement string + * provided by the {@link java.nio.charset.Charset}. + * + * @param data byte array to copy + * @param encoding the name of the encoding to use + * @throws NullPointerException if data or encoding is null + * @see #String(byte[], int, int, java.nio.Charset) + * @since 1.6 + */ + public String(byte[] data, Charset encoding) + { + this(data, 0, data.length, encoding); + } + + /** + * Creates a new String using the portion of the byte array starting at the + * offset and ending at offset + count. Uses the encoding of the platform's + * default charset, so the resulting string may be longer or shorter than + * the byte array. For more decoding control, use + * {@link java.nio.charset.CharsetDecoder}. The behavior is not specified + * if the decoder encounters invalid characters; this implementation throws + * an Error. + * + * @param data byte array to copy + * @param offset the offset to start at + * @param count the number of bytes in the array to use + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if offset or count is incorrect + * @throws Error if the decoding fails + * @see #String(byte[], int, int, String) + * @since 1.1 + */ + public String(byte[] data, int offset, int count) + { + if (offset < 0) + throw new StringIndexOutOfBoundsException("offset: " + offset); + if (count < 0) + throw new StringIndexOutOfBoundsException("count: " + count); + // equivalent to: offset + count < 0 || offset + count > data.length + if (data.length - offset < count) + throw new StringIndexOutOfBoundsException("offset + count: " + + (offset + count)); + int o, c; + char[] v; + String encoding; + try + { + encoding = System.getProperty("file.encoding"); + CharsetDecoder csd = Charset.forName(encoding).newDecoder(); + csd.onMalformedInput(CodingErrorAction.REPLACE); + csd.onUnmappableCharacter(CodingErrorAction.REPLACE); + CharBuffer cbuf = csd.decode(ByteBuffer.wrap(data, offset, count)); + if(cbuf.hasArray()) + { + v = cbuf.array(); + o = cbuf.position(); + c = cbuf.remaining(); + } else { + // Doubt this will happen. But just in case. + v = new char[cbuf.remaining()]; + cbuf.get(v); + o = 0; + c = v.length; + } + } catch(Exception ex){ + // If anything goes wrong (System property not set, + // NIO provider not available, etc) + // Default to the 'safe' encoding ISO8859_1 + v = new char[count]; + o = 0; + c = count; + for (int i=0;i data.length + if (data.length - offset < count) + throw new StringIndexOutOfBoundsException("offset + count: " + + (offset + count)); + if (dont_copy) + { + value = data; + this.offset = offset; + } + else + { + value = new char[count]; + VMSystem.arraycopy(data, offset, value, 0, count); + this.offset = 0; + } + this.count = count; + } + + /** + * Creates a new String containing the characters represented in the + * given subarray of Unicode code points. + * @param codePoints the entire array of code points + * @param offset the start of the subarray + * @param count the length of the subarray + * + * @throws IllegalArgumentException if an invalid code point is found + * in the codePoints array + * @throws IndexOutOfBoundsException if offset is negative or offset + count + * is greater than the length of the array. + */ + public String(int[] codePoints, int offset, int count) + { + // FIXME: This implementation appears to give correct internal + // representation of the String because: + // - length() is correct + // - getting a char[] from toCharArray() and testing + // Character.codePointAt() on all the characters in that array gives + // the appropriate results + // however printing the String gives incorrect results. This may be + // due to printing method errors (such as incorrectly looping through + // the String one char at a time rather than one "character" at a time. + + if (offset < 0) + throw new IndexOutOfBoundsException(); + int end = offset + count; + int pos = 0; + // This creates a char array that is long enough for all of the code + // points to represent supplementary characters. This is more than likely + // a waste of storage, so we use it only temporarily and then copy the + // used portion into the value array. + char[] temp = new char[2 * codePoints.length]; + for (int i = offset; i < end; i++) + { + pos += Character.toChars(codePoints[i], temp, pos); + } + this.count = pos; + this.value = new char[pos]; + System.arraycopy(temp, 0, value, 0, pos); + this.offset = 0; + } + + /** + * Returns the number of characters contained in this String. + * + * @return the length of this String + */ + public int length() + { + return count; + } + + /** + * Returns the character located at the specified index within this String. + * + * @param index position of character to return (base 0) + * @return character located at position index + * @throws IndexOutOfBoundsException if index < 0 || index >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public char charAt(int index) + { + if (index < 0 || index >= count) + throw new StringIndexOutOfBoundsException(index); + return value[offset + index]; + } + + /** + * Get the code point at the specified index. This is like #charAt(int), + * but if the character is the start of a surrogate pair, and the + * following character completes the pair, then the corresponding + * supplementary code point is returned. + * @param index the index of the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public synchronized int codePointAt(int index) + { + // Use the CharSequence overload as we get better range checking + // this way. + return Character.codePointAt(this, index); + } + + /** + * Get the code point before the specified index. This is like + * #codePointAt(int), but checks the characters at index-1 and + * index-2 to see if they form a supplementary code point. + * @param index the index just past the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @since 1.5 + */ + public synchronized int codePointBefore(int index) + { + // Use the CharSequence overload as we get better range checking + // this way. + return Character.codePointBefore(this, index); + } + + /** + * Copies characters from this String starting at a specified start index, + * ending at a specified stop index, to a character array starting at + * a specified destination begin index. + * + * @param srcBegin index to begin copying characters from this String + * @param srcEnd index after the last character to be copied from this String + * @param dst character array which this String is copied into + * @param dstBegin index to start writing characters into dst + * @throws NullPointerException if dst is null + * @throws IndexOutOfBoundsException if any indices are out of bounds + * (while unspecified, source problems cause a + * StringIndexOutOfBoundsException, and dst problems cause an + * ArrayIndexOutOfBoundsException) + */ + public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) + { + if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count) + throw new StringIndexOutOfBoundsException(); + VMSystem.arraycopy(value, srcBegin + offset, + dst, dstBegin, srcEnd - srcBegin); + } + + /** + * Copies the low byte of each character from this String starting at a + * specified start index, ending at a specified stop index, to a byte array + * starting at a specified destination begin index. + * + * @param srcBegin index to being copying characters from this String + * @param srcEnd index after the last character to be copied from this String + * @param dst byte array which each low byte of this String is copied into + * @param dstBegin index to start writing characters into dst + * @throws NullPointerException if dst is null and copy length is non-zero + * @throws IndexOutOfBoundsException if any indices are out of bounds + * (while unspecified, source problems cause a + * StringIndexOutOfBoundsException, and dst problems cause an + * ArrayIndexOutOfBoundsException) + * @see #getBytes() + * @see #getBytes(String) + * @deprecated use {@link #getBytes()}, which uses a char to byte encoder + */ + public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) + { + if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count) + throw new StringIndexOutOfBoundsException(); + int i = srcEnd - srcBegin; + srcBegin += offset; + while (--i >= 0) + dst[dstBegin++] = (byte) value[srcBegin++]; + } + + /** + * Converts the Unicode characters in this String to a byte array. Uses the + * specified encoding method, so the result may be longer or shorter than + * the String. For more encoding control, use + * {@link java.nio.charset.CharsetEncoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. Unsupported characters get + * replaced by an encoding specific byte. + * + * @param enc encoding name + * @return the resulting byte array + * @throws NullPointerException if enc is null + * @throws UnsupportedEncodingException if encoding is not supported + * @since 1.1 + */ + public byte[] getBytes(final String enc) + throws UnsupportedEncodingException + { + return getBytes(stringToCharset(enc)); + } + + /** + * Converts the Unicode characters in this String to a byte array. Uses the + * specified encoding method, so the result may be longer or shorter than + * the String. For more encoding control, use + * {@link java.nio.charset.CharsetEncoder}, and for valid character sets, + * see {@link java.nio.charset.Charset}. Unsupported characters get + * replaced by the {@link java.nio.charset.Charset}'s default replacement. + * + * @param enc encoding name + * @return the resulting byte array + * @throws NullPointerException if enc is null + * @since 1.6 + */ + public byte[] getBytes(Charset enc) + { + try + { + CharsetEncoder cse = enc.newEncoder(); + cse.onMalformedInput(CodingErrorAction.REPLACE); + cse.onUnmappableCharacter(CodingErrorAction.REPLACE); + ByteBuffer bbuf = cse.encode(CharBuffer.wrap(value, offset, count)); + if(bbuf.hasArray()) + return bbuf.array(); + + // Doubt this will happen. But just in case. + byte[] bytes = new byte[bbuf.remaining()]; + bbuf.get(bytes); + return bytes; + } + catch(CharacterCodingException e) + { + // This shouldn't ever happen. + throw (InternalError) new InternalError().initCause(e); + } + } + + /** + * Converts the Unicode characters in this String to a byte array. Uses the + * encoding of the platform's default charset, so the result may be longer + * or shorter than the String. For more encoding control, use + * {@link java.nio.charset.CharsetEncoder}. Unsupported characters get + * replaced by an encoding specific byte. + * + * @return the resulting byte array, or null on a problem + * @since 1.1 + */ + public byte[] getBytes() + { + try + { + return getBytes(System.getProperty("file.encoding")); + } catch(Exception e) { + // XXX - Throw an error here? + // For now, default to the 'safe' encoding. + byte[] bytes = new byte[count]; + for(int i=0;i= 0) + if (value[x++] != str2.value[y++]) + return false; + return true; + } + + /** + * Compares the given StringBuffer to this String. This is true if the + * StringBuffer has the same content as this String at this moment. + * + * @param buffer the StringBuffer to compare to + * @return true if StringBuffer has the same character sequence + * @throws NullPointerException if the given StringBuffer is null + * @since 1.4 + */ + public boolean contentEquals(StringBuffer buffer) + { + synchronized (buffer) + { + if (count != buffer.count) + return false; + if (value == buffer.value) + return true; // Possible if shared. + int i = count; + int x = offset + count; + while (--i >= 0) + if (value[--x] != buffer.value[i]) + return false; + return true; + } + } + + /** + * Compares the given CharSequence to this String. This is true if + * the CharSequence has the same content as this String at this + * moment. + * + * @param seq the CharSequence to compare to + * @return true if CharSequence has the same character sequence + * @throws NullPointerException if the given CharSequence is null + * @since 1.5 + */ + public boolean contentEquals(CharSequence seq) + { + if (seq.length() != count) + return false; + for (int i = 0; i < count; ++i) + if (value[offset + i] != seq.charAt(i)) + return false; + return true; + } + + /** + * Compares a String to this String, ignoring case. This does not handle + * multi-character capitalization exceptions; instead the comparison is + * made on a character-by-character basis, and is true if:
      + *
    • c1 == c2
    • + *
    • Character.toUpperCase(c1) + * == Character.toUpperCase(c2)
    • + *
    • Character.toLowerCase(c1) + * == Character.toLowerCase(c2)
    • + *
    + * + * @param anotherString String to compare to this String + * @return true if anotherString is equal, ignoring case + * @see #equals(Object) + * @see Character#toUpperCase(char) + * @see Character#toLowerCase(char) + */ + public boolean equalsIgnoreCase(String anotherString) + { + if (anotherString == null || count != anotherString.count) + return false; + int i = count; + int x = offset; + int y = anotherString.offset; + while (--i >= 0) + { + char c1 = value[x++]; + char c2 = anotherString.value[y++]; + // Note that checking c1 != c2 is redundant, but avoids method calls. + if (c1 != c2 + && Character.toUpperCase(c1) != Character.toUpperCase(c2) + && Character.toLowerCase(c1) != Character.toLowerCase(c2)) + return false; + } + return true; + } + + /** + * Compares this String and another String (case sensitive, + * lexicographically). The result is less than 0 if this string sorts + * before the other, 0 if they are equal, and greater than 0 otherwise. + * After any common starting sequence is skipped, the result is + * this.charAt(k) - anotherString.charAt(k) if both strings + * have characters remaining, or + * this.length() - anotherString.length() if one string is + * a subsequence of the other. + * + * @param anotherString the String to compare against + * @return the comparison + * @throws NullPointerException if anotherString is null + */ + public int compareTo(String anotherString) + { + int i = Math.min(count, anotherString.count); + int x = offset; + int y = anotherString.offset; + while (--i >= 0) + { + int result = value[x++] - anotherString.value[y++]; + if (result != 0) + return result; + } + return count - anotherString.count; + } + + /** + * Compares this String and another String (case insensitive). This + * comparison is similar to equalsIgnoreCase, in that it ignores + * locale and multi-characater capitalization, and compares characters + * after performing + * Character.toLowerCase(Character.toUpperCase(c)) on each + * character of the string. This is unsatisfactory for locale-based + * comparison, in which case you should use {@link java.text.Collator}. + * + * @param str the string to compare against + * @return the comparison + * @see Collator#compare(String, String) + * @since 1.2 + */ + public int compareToIgnoreCase(String str) + { + int i = Math.min(count, str.count); + int x = offset; + int y = str.offset; + while (--i >= 0) + { + int result = Character.toLowerCase(Character.toUpperCase(value[x++])) + - Character.toLowerCase(Character.toUpperCase(str.value[y++])); + if (result != 0) + return result; + } + return count - str.count; + } + + /** + * Predicate which determines if this String matches another String + * starting at a specified offset for each String and continuing + * for a specified length. Indices out of bounds are harmless, and give + * a false result. + * + * @param toffset index to start comparison at for this String + * @param other String to compare region to this String + * @param ooffset index to start comparison at for other + * @param len number of characters to compare + * @return true if regions match (case sensitive) + * @throws NullPointerException if other is null + */ + public boolean regionMatches(int toffset, String other, int ooffset, int len) + { + return regionMatches(false, toffset, other, ooffset, len); + } + + /** + * Predicate which determines if this String matches another String + * starting at a specified offset for each String and continuing + * for a specified length, optionally ignoring case. Indices out of bounds + * are harmless, and give a false result. Case comparisons are based on + * Character.toLowerCase() and + * Character.toUpperCase(), not on multi-character + * capitalization expansions. + * + * @param ignoreCase true if case should be ignored in comparision + * @param toffset index to start comparison at for this String + * @param other String to compare region to this String + * @param ooffset index to start comparison at for other + * @param len number of characters to compare + * @return true if regions match, false otherwise + * @throws NullPointerException if other is null + */ + public boolean regionMatches(boolean ignoreCase, int toffset, + String other, int ooffset, int len) + { + if (toffset < 0 || ooffset < 0 || toffset + len > count + || ooffset + len > other.count) + return false; + toffset += offset; + ooffset += other.offset; + while (--len >= 0) + { + char c1 = value[toffset++]; + char c2 = other.value[ooffset++]; + // Note that checking c1 != c2 is redundant when ignoreCase is true, + // but it avoids method calls. + if (c1 != c2 + && (! ignoreCase + || (Character.toLowerCase(c1) != Character.toLowerCase(c2) + && (Character.toUpperCase(c1) + != Character.toUpperCase(c2))))) + return false; + } + return true; + } + + /** + * Predicate which determines if this String contains the given prefix, + * beginning comparison at toffset. The result is false if toffset is + * negative or greater than this.length(), otherwise it is the same as + * this.substring(toffset).startsWith(prefix). + * + * @param prefix String to compare + * @param toffset offset for this String where comparison starts + * @return true if this String starts with prefix + * @throws NullPointerException if prefix is null + * @see #regionMatches(boolean, int, String, int, int) + */ + public boolean startsWith(String prefix, int toffset) + { + return regionMatches(false, toffset, prefix, 0, prefix.count); + } + + /** + * Predicate which determines if this String starts with a given prefix. + * If the prefix is an empty String, true is returned. + * + * @param prefix String to compare + * @return true if this String starts with the prefix + * @throws NullPointerException if prefix is null + * @see #startsWith(String, int) + */ + public boolean startsWith(String prefix) + { + return regionMatches(false, 0, prefix, 0, prefix.count); + } + + /** + * Predicate which determines if this String ends with a given suffix. + * If the suffix is an empty String, true is returned. + * + * @param suffix String to compare + * @return true if this String ends with the suffix + * @throws NullPointerException if suffix is null + * @see #regionMatches(boolean, int, String, int, int) + */ + public boolean endsWith(String suffix) + { + return regionMatches(false, count - suffix.count, suffix, 0, suffix.count); + } + + /** + * Computes the hashcode for this String. This is done with int arithmetic, + * where ** represents exponentiation, by this formula:
    + * s[0]*31**(n-1) + s[1]*31**(n-2) + ... + s[n-1]. + * + * @return hashcode value of this String + */ + public int hashCode() + { + if (cachedHashCode != 0) + return cachedHashCode; + + // Compute the hash code using a local variable to be reentrant. + int hashCode = 0; + int limit = count + offset; + for (int i = offset; i < limit; i++) + hashCode = hashCode * 31 + value[i]; + return cachedHashCode = hashCode; + } + + /** + * Finds the first instance of a character in this String. + * + * @param ch character to find + * @return location (base 0) of the character, or -1 if not found + */ + public int indexOf(int ch) + { + return indexOf(ch, 0); + } + + /** + * Finds the first instance of a character in this String, starting at + * a given index. If starting index is less than 0, the search + * starts at the beginning of this String. If the starting index + * is greater than the length of this String, -1 is returned. + * + * @param ch character to find + * @param fromIndex index to start the search + * @return location (base 0) of the character, or -1 if not found + */ + public int indexOf(int ch, int fromIndex) + { + if ((char) ch != ch) + return -1; + if (fromIndex < 0) + fromIndex = 0; + int i = fromIndex + offset; + for ( ; fromIndex < count; fromIndex++) + if (value[i++] == ch) + return fromIndex; + return -1; + } + + /** + * Finds the last instance of a character in this String. + * + * @param ch character to find + * @return location (base 0) of the character, or -1 if not found + */ + public int lastIndexOf(int ch) + { + return lastIndexOf(ch, count - 1); + } + + /** + * Finds the last instance of a character in this String, starting at + * a given index. If starting index is greater than the maximum valid + * index, then the search begins at the end of this String. If the + * starting index is less than zero, -1 is returned. + * + * @param ch character to find + * @param fromIndex index to start the search + * @return location (base 0) of the character, or -1 if not found + */ + public int lastIndexOf(int ch, int fromIndex) + { + if ((char) ch != ch) + return -1; + if (fromIndex >= count) + fromIndex = count - 1; + int i = fromIndex + offset; + for ( ; fromIndex >= 0; fromIndex--) + if (value[i--] == ch) + return fromIndex; + return -1; + } + + /** + * Finds the first instance of a String in this String. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int indexOf(String str) + { + return indexOf(str, 0); + } + + /** + * Finds the first instance of a String in this String, starting at + * a given index. If starting index is less than 0, the search + * starts at the beginning of this String. If the starting index + * is greater than the length of this String, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int indexOf(String str, int fromIndex) + { + if (fromIndex < 0) + fromIndex = 0; + int limit = count - str.count; + for ( ; fromIndex <= limit; fromIndex++) + if (regionMatches(fromIndex, str, 0, str.count)) + return fromIndex; + return -1; + } + + /** + * Finds the last instance of a String in this String. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int lastIndexOf(String str) + { + return lastIndexOf(str, count - str.count); + } + + /** + * Finds the last instance of a String in this String, starting at + * a given index. If starting index is greater than the maximum valid + * index, then the search begins at the end of this String. If the + * starting index is less than zero, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + */ + public int lastIndexOf(String str, int fromIndex) + { + fromIndex = Math.min(fromIndex, count - str.count); + for ( ; fromIndex >= 0; fromIndex--) + if (regionMatches(fromIndex, str, 0, str.count)) + return fromIndex; + return -1; + } + + /** + * Creates a substring of this String, starting at a specified index + * and ending at the end of this String. + * + * @param begin index to start substring (base 0) + * @return new String which is a substring of this String + * @throws IndexOutOfBoundsException if begin < 0 || begin > length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public String substring(int begin) + { + return substring(begin, count); + } + + /** + * Creates a substring of this String, starting at a specified index + * and ending at one character before a specified index. + * + * @param beginIndex index to start substring (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this String + * @throws IndexOutOfBoundsException if begin < 0 || end > length() + * || begin > end (while unspecified, this is a + * StringIndexOutOfBoundsException) + */ + public String substring(int beginIndex, int endIndex) + { + if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) + throw new StringIndexOutOfBoundsException(); + if (beginIndex == 0 && endIndex == count) + return this; + int len = endIndex - beginIndex; + // Package constructor avoids an array copy. + return new String(value, beginIndex + offset, len, + (len << 2) >= value.length); + } + + /** + * Creates a substring of this String, starting at a specified index + * and ending at one character before a specified index. This behaves like + * substring(begin, end). + * + * @param begin index to start substring (inclusive, base 0) + * @param end index to end at (exclusive) + * @return new String which is a substring of this String + * @throws IndexOutOfBoundsException if begin < 0 || end > length() + * || begin > end + * @since 1.4 + */ + public CharSequence subSequence(int begin, int end) + { + return substring(begin, end); + } + + /** + * Concatenates a String to this String. This results in a new string unless + * one of the two originals is "". + * + * @param str String to append to this String + * @return newly concatenated String + * @throws NullPointerException if str is null + */ + public String concat(String str) + { + if (str.count == 0) + return this; + if (count == 0) + return str; + char[] newStr = new char[count + str.count]; + VMSystem.arraycopy(value, offset, newStr, 0, count); + VMSystem.arraycopy(str.value, str.offset, newStr, count, str.count); + // Package constructor avoids an array copy. + return new String(newStr, 0, newStr.length, true); + } + + /** + * Replaces every instance of a character in this String with a new + * character. If no replacements occur, this is returned. + * + * @param oldChar the old character to replace + * @param newChar the new character + * @return new String with all instances of oldChar replaced with newChar + */ + public String replace(char oldChar, char newChar) + { + if (oldChar == newChar) + return this; + int i = count; + int x = offset - 1; + while (--i >= 0) + if (value[++x] == oldChar) + break; + if (i < 0) + return this; + char[] newStr = toCharArray(); + newStr[x - offset] = newChar; + while (--i >= 0) + if (value[++x] == oldChar) + newStr[x - offset] = newChar; + // Package constructor avoids an array copy. + return new String(newStr, 0, count, true); + } + + /** + * Test if this String matches a regular expression. This is shorthand for + * {@link Pattern}.matches(regex, this). + * + * @param regex the pattern to match + * @return true if the pattern matches + * @throws NullPointerException if regex is null + * @throws PatternSyntaxException if regex is invalid + * @see Pattern#matches(String, CharSequence) + * @since 1.4 + */ + public boolean matches(String regex) + { + return Pattern.matches(regex, this); + } + + /** + * Replaces the first substring match of the regular expression with a + * given replacement. This is shorthand for {@link Pattern} + * .compile(regex).matcher(this).replaceFirst(replacement). + * + * @param regex the pattern to match + * @param replacement the replacement string + * @return the modified string + * @throws NullPointerException if regex or replacement is null + * @throws PatternSyntaxException if regex is invalid + * @see #replaceAll(String, String) + * @see Pattern#compile(String) + * @see Pattern#matcher(CharSequence) + * @see Matcher#replaceFirst(String) + * @since 1.4 + */ + public String replaceFirst(String regex, String replacement) + { + return Pattern.compile(regex).matcher(this).replaceFirst(replacement); + } + + /** + * Replaces all matching substrings of the regular expression with a + * given replacement. This is shorthand for {@link Pattern} + * .compile(regex).matcher(this).replaceAll(replacement). + * + * @param regex the pattern to match + * @param replacement the replacement string + * @return the modified string + * @throws NullPointerException if regex or replacement is null + * @throws PatternSyntaxException if regex is invalid + * @see #replaceFirst(String, String) + * @see Pattern#compile(String) + * @see Pattern#matcher(CharSequence) + * @see Matcher#replaceAll(String) + * @since 1.4 + */ + public String replaceAll(String regex, String replacement) + { + return Pattern.compile(regex).matcher(this).replaceAll(replacement); + } + + /** + * Split this string around the matches of a regular expression. Each + * element of the returned array is the largest block of characters not + * terminated by the regular expression, in the order the matches are found. + * + *

    The limit affects the length of the array. If it is positive, the + * array will contain at most n elements (n - 1 pattern matches). If + * negative, the array length is unlimited, but there can be trailing empty + * entries. if 0, the array length is unlimited, and trailing empty entries + * are discarded. + * + *

    For example, splitting "boo:and:foo" yields:
    + * + * + * + * + * + * + * + * + *
    Regex Limit Result
    ":" 2 { "boo", "and:foo" }
    ":" t { "boo", "and", "foo" }
    ":" -2 { "boo", "and", "foo" }
    "o" 5 { "b", "", ":and:f", "", "" }
    "o" -2 { "b", "", ":and:f", "", "" }
    "o" 0 { "b", "", ":and:f" }
    + * + *

    This is shorthand for + * {@link Pattern}.compile(regex).split(this, limit). + * + * @param regex the pattern to match + * @param limit the limit threshold + * @return the array of split strings + * @throws NullPointerException if regex or replacement is null + * @throws PatternSyntaxException if regex is invalid + * @see Pattern#compile(String) + * @see Pattern#split(CharSequence, int) + * @since 1.4 + */ + public String[] split(String regex, int limit) + { + return Pattern.compile(regex).split(this, limit); + } + + /** + * Split this string around the matches of a regular expression. Each + * element of the returned array is the largest block of characters not + * terminated by the regular expression, in the order the matches are found. + * The array length is unlimited, and trailing empty entries are discarded, + * as though calling split(regex, 0). + * + * @param regex the pattern to match + * @return the array of split strings + * @throws NullPointerException if regex or replacement is null + * @throws PatternSyntaxException if regex is invalid + * @see #split(String, int) + * @see Pattern#compile(String) + * @see Pattern#split(CharSequence, int) + * @since 1.4 + */ + public String[] split(String regex) + { + return Pattern.compile(regex).split(this, 0); + } + + /** + * Convert string to lower case for a Turkish locale that requires special + * handling of '\u0049' + */ + private String toLowerCaseTurkish() + { + // First, see if the current string is already lower case. + int i = count; + int x = offset - 1; + while (--i >= 0) + { + char ch = value[++x]; + if ((ch == '\u0049') || ch != Character.toLowerCase(ch)) + break; + } + if (i < 0) + return this; + + // Now we perform the conversion. Fortunately, there are no multi-character + // lowercase expansions in Unicode 3.0.0. + char[] newStr = new char[count]; + VMSystem.arraycopy(value, offset, newStr, 0, x - offset); + do + { + char ch = value[x]; + // Hardcoded special case. + if (ch != '\u0049') + { + newStr[x - offset] = Character.toLowerCase(ch); + } + else + { + newStr[x - offset] = '\u0131'; + } + x++; + } + while (--i >= 0); + // Package constructor avoids an array copy. + return new String(newStr, 0, count, true); + } + + /** + * Lowercases this String according to a particular locale. This uses + * Unicode's special case mappings, as applied to the given Locale, so the + * resulting string may be a different length. + * + * @param loc locale to use + * @return new lowercased String, or this if no characters were lowercased + * @throws NullPointerException if loc is null + * @see #toUpperCase(Locale) + * @since 1.1 + */ + public String toLowerCase(Locale loc) + { + // First, see if the current string is already lower case. + + // Is loc turkish? String equality test is ok as Locale.language is interned + if ("tr" == loc.getLanguage()) + { + return toLowerCaseTurkish(); + } + else + { + int i = count; + int x = offset - 1; + while (--i >= 0) + { + char ch = value[++x]; + if (ch != Character.toLowerCase(ch)) + break; + } + if (i < 0) + return this; + + // Now we perform the conversion. Fortunately, there are no + // multi-character lowercase expansions in Unicode 3.0.0. + char[] newStr = new char[count]; + VMSystem.arraycopy(value, offset, newStr, 0, x - offset); + do + { + char ch = value[x]; + // Hardcoded special case. + newStr[x - offset] = Character.toLowerCase(ch); + x++; + } + while (--i >= 0); + // Package constructor avoids an array copy. + return new String(newStr, 0, count, true); + } + } + + /** + * Lowercases this String. This uses Unicode's special case mappings, as + * applied to the platform's default Locale, so the resulting string may + * be a different length. + * + * @return new lowercased String, or this if no characters were lowercased + * @see #toLowerCase(Locale) + * @see #toUpperCase() + */ + public String toLowerCase() + { + return toLowerCase(Locale.getDefault()); + } + + /** + * Uppercase this string for a Turkish locale + */ + private String toUpperCaseTurkish() + { + // First, see how many characters we have to grow by, as well as if the + // current string is already upper case. + int expand = 0; + boolean unchanged = true; + int i = count; + int x = i + offset; + while (--i >= 0) + { + char ch = value[--x]; + expand += upperCaseExpansion(ch); + unchanged = (unchanged && expand == 0 + && ch != '\u0069' + && ch == Character.toUpperCase(ch)); + } + if (unchanged) + return this; + + // Now we perform the conversion. + i = count; + if (expand == 0) + { + char[] newStr = new char[count]; + VMSystem.arraycopy(value, offset, newStr, 0, count - (x - offset)); + while (--i >= 0) + { + char ch = value[x]; + // Hardcoded special case. + if (ch != '\u0069') + { + newStr[x - offset] = Character.toUpperCase(ch); + } + else + { + newStr[x - offset] = '\u0130'; + } + x++; + } + // Package constructor avoids an array copy. + return new String(newStr, 0, count, true); + } + + // Expansion is necessary. + char[] newStr = new char[count + expand]; + int j = 0; + while (--i >= 0) + { + char ch = value[x++]; + // Hardcoded special case. + if (ch == '\u0069') + { + newStr[j++] = '\u0130'; + continue; + } + expand = upperCaseExpansion(ch); + if (expand > 0) + { + int index = upperCaseIndex(ch); + while (expand-- >= 0) + newStr[j++] = upperExpand[index++]; + } + else + newStr[j++] = Character.toUpperCase(ch); + } + // Package constructor avoids an array copy. + return new String(newStr, 0, newStr.length, true); + } + + /** + * Uppercases this String according to a particular locale. This uses + * Unicode's special case mappings, as applied to the given Locale, so the + * resulting string may be a different length. + * + * @param loc locale to use + * @return new uppercased String, or this if no characters were uppercased + * @throws NullPointerException if loc is null + * @see #toLowerCase(Locale) + * @since 1.1 + */ + public String toUpperCase(Locale loc) + { + // First, see how many characters we have to grow by, as well as if the + // current string is already upper case. + + // Is loc turkish? String equality test is ok as Locale.language is interned + if ("tr" == loc.getLanguage()) + { + return toUpperCaseTurkish(); + } + else + { + int expand = 0; + boolean unchanged = true; + int i = count; + int x = i + offset; + while (--i >= 0) + { + char ch = value[--x]; + expand += upperCaseExpansion(ch); + unchanged = (unchanged && expand == 0 + && ch == Character.toUpperCase(ch)); + } + if (unchanged) + return this; + + // Now we perform the conversion. + i = count; + if (expand == 0) + { + char[] newStr = new char[count]; + VMSystem.arraycopy(value, offset, newStr, 0, count - (x - offset)); + while (--i >= 0) + { + char ch = value[x]; + newStr[x - offset] = Character.toUpperCase(ch); + x++; + } + // Package constructor avoids an array copy. + return new String(newStr, 0, count, true); + } + + // Expansion is necessary. + char[] newStr = new char[count + expand]; + int j = 0; + while (--i >= 0) + { + char ch = value[x++]; + expand = upperCaseExpansion(ch); + if (expand > 0) + { + int index = upperCaseIndex(ch); + while (expand-- >= 0) + newStr[j++] = upperExpand[index++]; + } + else + newStr[j++] = Character.toUpperCase(ch); + } + // Package constructor avoids an array copy. + return new String(newStr, 0, newStr.length, true); + } + } + /** + * Uppercases this String. This uses Unicode's special case mappings, as + * applied to the platform's default Locale, so the resulting string may + * be a different length. + * + * @return new uppercased String, or this if no characters were uppercased + * @see #toUpperCase(Locale) + * @see #toLowerCase() + */ + public String toUpperCase() + { + return toUpperCase(Locale.getDefault()); + } + + /** + * Trims all characters less than or equal to '\u0020' + * (' ') from the beginning and end of this String. This + * includes many, but not all, ASCII control characters, and all + * {@link Character#isWhitespace(char)}. + * + * @return new trimmed String, or this if nothing trimmed + */ + public String trim() + { + int limit = count + offset; + if (count == 0 || (value[offset] > '\u0020' + && value[limit - 1] > '\u0020')) + return this; + int begin = offset; + do + if (begin == limit) + return ""; + while (value[begin++] <= '\u0020'); + + int end = limit; + while (value[--end] <= '\u0020') + ; + return substring(begin - offset - 1, end - offset + 1); + } + + /** + * Returns this, as it is already a String! + * + * @return this + */ + public String toString() + { + return this; + } + + /** + * Copies the contents of this String into a character array. Subsequent + * changes to the array do not affect the String. + * + * @return character array copying the String + */ + public char[] toCharArray() + { + char[] copy = new char[count]; + VMSystem.arraycopy(value, offset, copy, 0, count); + return copy; + } + + /** + * Returns a String representation of an Object. This is "null" if the + * object is null, otherwise it is obj.toString() (which + * can be null). + * + * @param obj the Object + * @return the string conversion of obj + */ + public static String valueOf(Object obj) + { + return obj == null ? "null" : obj.toString(); + } + + /** + * Returns a String representation of a character array. Subsequent + * changes to the array do not affect the String. + * + * @param data the character array + * @return a String containing the same character sequence as data + * @throws NullPointerException if data is null + * @see #valueOf(char[], int, int) + * @see #String(char[]) + */ + public static String valueOf(char[] data) + { + return valueOf (data, 0, data.length); + } + + /** + * Returns a String representing the character sequence of the char array, + * starting at the specified offset, and copying chars up to the specified + * count. Subsequent changes to the array do not affect the String. + * + * @param data character array + * @param offset position (base 0) to start copying out of data + * @param count the number of characters from data to copy + * @return String containing the chars from data[offset..offset+count] + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if (offset < 0 || count < 0 + * || offset + count > data.length) + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #String(char[], int, int) + */ + public static String valueOf(char[] data, int offset, int count) + { + return new String(data, offset, count, false); + } + + /** + * Returns a String representing the character sequence of the char array, + * starting at the specified offset, and copying chars up to the specified + * count. Subsequent changes to the array do not affect the String. + * + * @param data character array + * @param offset position (base 0) to start copying out of data + * @param count the number of characters from data to copy + * @return String containing the chars from data[offset..offset+count] + * @throws NullPointerException if data is null + * @throws IndexOutOfBoundsException if (offset < 0 || count < 0 + * || offset + count < 0 (overflow) + * || offset + count < 0 (overflow) + * || offset + count > data.length) + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #String(char[], int, int) + */ + public static String copyValueOf(char[] data, int offset, int count) + { + return new String(data, offset, count, false); + } + + /** + * Returns a String representation of a character array. Subsequent + * changes to the array do not affect the String. + * + * @param data the character array + * @return a String containing the same character sequence as data + * @throws NullPointerException if data is null + * @see #copyValueOf(char[], int, int) + * @see #String(char[]) + */ + public static String copyValueOf(char[] data) + { + return copyValueOf (data, 0, data.length); + } + + /** + * Returns a String representing a boolean. + * + * @param b the boolean + * @return "true" if b is true, else "false" + */ + public static String valueOf(boolean b) + { + return b ? "true" : "false"; + } + + /** + * Returns a String representing a character. + * + * @param c the character + * @return String containing the single character c + */ + public static String valueOf(char c) + { + // Package constructor avoids an array copy. + return new String(new char[] { c }, 0, 1, true); + } + + /** + * Returns a String representing an integer. + * + * @param i the integer + * @return String containing the integer in base 10 + * @see Integer#toString(int) + */ + public static String valueOf(int i) + { + // See Integer to understand why we call the two-arg variant. + return Integer.toString(i, 10); + } + + /** + * Returns a String representing a long. + * + * @param l the long + * @return String containing the long in base 10 + * @see Long#toString(long) + */ + public static String valueOf(long l) + { + return Long.toString(l); + } + + /** + * Returns a String representing a float. + * + * @param f the float + * @return String containing the float + * @see Float#toString(float) + */ + public static String valueOf(float f) + { + return Float.toString(f); + } + + /** + * Returns a String representing a double. + * + * @param d the double + * @return String containing the double + * @see Double#toString(double) + */ + public static String valueOf(double d) + { + return Double.toString(d); + } + + + /** @since 1.5 */ + public static String format(Locale locale, String format, Object... args) + { + Formatter f = new Formatter(locale); + return f.format(format, args).toString(); + } + + /** @since 1.5 */ + public static String format(String format, Object... args) + { + return format(Locale.getDefault(), format, args); + } + + /** + * If two Strings are considered equal, by the equals() method, + * then intern() will return the same String instance. ie. + * if (s1.equals(s2)) then (s1.intern() == s2.intern()). + * All string literals and string-valued constant expressions + * are already interned. + * + * @return the interned String + */ + public String intern() + { + return VMString.intern(this); + } + + /** + * Return the number of code points between two indices in the + * String. An unpaired surrogate counts as a + * code point for this purpose. Characters outside the indicated + * range are not examined, even if the range ends in the middle of a + * surrogate pair. + * + * @param start the starting index + * @param end one past the ending index + * @return the number of code points + * @since 1.5 + */ + public synchronized int codePointCount(int start, int end) + { + if (start < 0 || end > count || start > end) + throw new StringIndexOutOfBoundsException(); + + start += offset; + end += offset; + int count = 0; + while (start < end) + { + char base = value[start]; + if (base < Character.MIN_HIGH_SURROGATE + || base > Character.MAX_HIGH_SURROGATE + || start == end + || start == count + || value[start + 1] < Character.MIN_LOW_SURROGATE + || value[start + 1] > Character.MAX_LOW_SURROGATE) + { + // Nothing. + } + else + { + // Surrogate pair. + ++start; + } + ++start; + ++count; + } + return count; + } + + /** + * Helper function used to detect which characters have a multi-character + * uppercase expansion. Note that this is only used in locations which + * track one-to-many capitalization (java.lang.Character does not do this). + * As of Unicode 3.0.0, the result is limited in the range 0 to 2, as the + * longest uppercase expansion is three characters (a growth of 2 from the + * lowercase character). + * + * @param ch the char to check + * @return the number of characters to add when converting to uppercase + * @see CharData#DIRECTION + * @see CharData#UPPER_SPECIAL + * @see #toUpperCase(Locale) + */ + private static int upperCaseExpansion(char ch) + { + return Character.direction[0][Character.readCodePoint((int)ch) >> 7] & 3; + } + + /** + * Helper function used to locate the offset in upperExpand given a + * character with a multi-character expansion. The binary search is + * optimized under the assumption that this method will only be called on + * characters which exist in upperSpecial. + * + * @param ch the char to check + * @return the index where its expansion begins + * @see CharData#UPPER_SPECIAL + * @see CharData#UPPER_EXPAND + * @see #toUpperCase(Locale) + */ + private static int upperCaseIndex(char ch) + { + // Simple binary search for the correct character. + int low = 0; + int hi = upperSpecial.length - 2; + int mid = ((low + hi) >> 2) << 1; + char c = upperSpecial[mid]; + while (ch != c) + { + if (ch < c) + hi = mid - 2; + else + low = mid + 2; + mid = ((low + hi) >> 2) << 1; + c = upperSpecial[mid]; + } + return upperSpecial[mid + 1]; + } + + /** + * Returns the value array of the given string if it is zero based or a + * copy of it that is zero based (stripping offset and making length equal + * to count). Used for accessing the char[]s of gnu.java.lang.CharData. + * Package private for use in Character. + */ + static char[] zeroBasedStringValue(String s) + { + char[] value; + + if (s.offset == 0 && s.count == s.value.length) + value = s.value; + else + { + int count = s.count; + value = new char[count]; + VMSystem.arraycopy(s.value, s.offset, value, 0, count); + } + + return value; + } + + /** + * Returns true iff this String contains the sequence of Characters + * described in s. + * @param s the CharSequence + * @return true iff this String contains s + * + * @since 1.5 + */ + public boolean contains (CharSequence s) + { + return this.indexOf(s.toString()) != -1; + } + + /** + * Returns a string that is this string with all instances of the sequence + * represented by target replaced by the sequence in + * replacement. + * @param target the sequence to be replaced + * @param replacement the sequence used as the replacement + * @return the string constructed as above + */ + public String replace (CharSequence target, CharSequence replacement) + { + String targetString = target.toString(); + String replaceString = replacement.toString(); + int targetLength = target.length(); + int replaceLength = replacement.length(); + + int startPos = this.indexOf(targetString); + CPStringBuilder result = new CPStringBuilder(this); + while (startPos != -1) + { + // Replace the target with the replacement + result.replace(startPos, startPos + targetLength, replaceString); + + // Search for a new occurrence of the target + startPos = result.indexOf(targetString, startPos + replaceLength); + } + return result.toString(); + } + + /** + * Return the index into this String that is offset from the given index by + * codePointOffset code points. + * @param index the index at which to start + * @param codePointOffset the number of code points to offset + * @return the index into this String that is codePointOffset + * code points offset from index. + * + * @throws IndexOutOfBoundsException if index is negative or larger than the + * length of this string. + * @throws IndexOutOfBoundsException if codePointOffset is positive and the + * substring starting with index has fewer than codePointOffset code points. + * @throws IndexOutOfBoundsException if codePointOffset is negative and the + * substring ending with index has fewer than (-codePointOffset) code points. + * @since 1.5 + */ + public int offsetByCodePoints(int index, int codePointOffset) + { + if (index < 0 || index > count) + throw new IndexOutOfBoundsException(); + + return Character.offsetByCodePoints(value, offset, count, offset + index, + codePointOffset); + } + + /** + * Returns true if, and only if, {@link #length()} + * is 0. + * + * @return true if the length of the string is zero. + * @since 1.6 + */ + public boolean isEmpty() + { + return count == 0; + } + +} diff --git a/libjava/classpath/java/lang/StringBuffer.java b/libjava/classpath/java/lang/StringBuffer.java new file mode 100644 index 000000000..81a52f6d4 --- /dev/null +++ b/libjava/classpath/java/lang/StringBuffer.java @@ -0,0 +1,976 @@ +/* StringBuffer.java -- Growable strings + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.Serializable; + +/** + * StringBuffer represents a changeable String. + * It provides the operations required to modify the + * StringBuffer, including insert, replace, delete, append, + * and reverse. It is thread-safe; meaning that all modifications to a buffer + * are in synchronized methods. + * + *

    StringBuffers are variable-length in nature, so even if + * you initialize them to a certain size, they can still grow larger than + * that. Capacity indicates the number of characters the + * StringBuffer can have in it before it has to grow (growing + * the char array is an expensive operation involving new). + * + *

    Incidentally, compilers often implement the String operator "+" + * by using a StringBuffer operation:
    + * a + b
    + * is the same as
    + * new StringBuffer().append(a).append(b).toString(). + * + *

    Classpath's StringBuffer is capable of sharing memory with Strings for + * efficiency. This will help when a StringBuffer is converted to a String + * and the StringBuffer is not changed after that (quite common when performing + * string concatenation). + * + * @author Paul Fisher + * @author John Keiser + * @author Tom Tromey + * @author Eric Blake (ebb9@email.byu.edu) + * @see String + * @since 1.0 + * @status updated to 1.4 + */ +public final class StringBuffer + extends AbstractStringBuffer + implements Serializable, CharSequence, Appendable +{ + // Implementation note: if you change this class, you usually will + // want to change StringBuilder as well. + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3388685877147921107L; + + /** + * True if the buffer is shared with another object (StringBuffer or + * String); this means the buffer must be copied before writing to it again. + * Note that this has permissions set this way so that String can get the + * value. + * + * @serial whether the buffer is shared + */ + boolean shared; + + /** + * Create a new StringBuffer with default capacity 16. + */ + public StringBuffer() + { + super(); + } + + /** + * Create an empty StringBuffer with the specified initial + * capacity. + * + * @param capacity the initial capacity + * @throws NegativeArraySizeException if capacity is negative + */ + public StringBuffer(int capacity) + { + super(capacity); + } + + /** + * Create a new StringBuffer with the characters in the + * specified String. Initial capacity will be the size of the + * String plus 16. + * + * @param str the String to convert + * @throws NullPointerException if str is null + */ + public StringBuffer(String str) + { + // Unfortunately, because the size is 16 larger, we cannot share. + super(str); + } + + /** + * Create a new StringBuffer with the characters in the + * specified CharSequence. Initial capacity will be the + * length of the sequence plus 16; if the sequence reports a length + * less than or equal to 0, then the initial capacity will be 16. + * + * @param seq the initializing CharSequence + * @throws NullPointerException if str is null + * @since 1.5 + */ + public StringBuffer(CharSequence seq) + { + super(seq); + } + + /** + * Get the length of the String this StringBuffer + * would create. Not to be confused with the capacity of the + * StringBuffer. + * + * @return the length of this StringBuffer + * @see #capacity() + * @see #setLength(int) + */ + public synchronized int length() + { + return count; + } + + /** + * Get the total number of characters this StringBuffer can + * support before it must be grown. Not to be confused with length. + * + * @return the capacity of this StringBuffer + * @see #length() + * @see #ensureCapacity(int) + */ + public synchronized int capacity() + { + return value.length; + } + + /** + * Increase the capacity of this StringBuffer. This will + * ensure that an expensive growing operation will not occur until + * minimumCapacity is reached. The buffer is grown to the + * larger of minimumCapacity and + * capacity() * 2 + 2, if it is not already large enough. + * + * @param minimumCapacity the new capacity + * @see #capacity() + */ + public synchronized void ensureCapacity(int minimumCapacity) + { + ensureCapacity_unsynchronized(minimumCapacity); + } + + /** + * Set the length of this StringBuffer. If the new length is greater than + * the current length, all the new characters are set to '\0'. If the new + * length is less than the current length, the first newLength + * characters of the old array will be preserved, and the remaining + * characters are truncated. + * + * @param newLength the new length + * @throws IndexOutOfBoundsException if the new length is negative + * (while unspecified, this is a StringIndexOutOfBoundsException) + * @see #length() + */ + public synchronized void setLength(int newLength) + { + super.setLength(newLength); + } + + /** + * Get the character at the specified index. + * + * @param index the index of the character to get, starting at 0 + * @return the character at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public synchronized char charAt(int index) + { + return super.charAt(index); + } + + /** + * Get the code point at the specified index. This is like #charAt(int), + * but if the character is the start of a surrogate pair, and the + * following character completes the pair, then the corresponding + * supplementary code point is returned. + * @param index the index of the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public synchronized int codePointAt(int index) + { + return super.codePointAt(index); + } + + /** + * Get the code point before the specified index. This is like + * #codePointAt(int), but checks the characters at index-1 and + * index-2 to see if they form a supplementary code point. + * @param index the index just past the codepoint to get, starting at 0 + * @return the codepoint at the specified index + * @throws IndexOutOfBoundsException if index is negative or >= length() + * @since 1.5 + */ + public synchronized int codePointBefore(int index) + { + return super.codePointBefore(index); + } + + /** + * Get the specified array of characters. srcOffset - srcEnd + * characters will be copied into the array you pass in. + * + * @param srcOffset the index to start copying from (inclusive) + * @param srcEnd the index to stop copying from (exclusive) + * @param dst the array to copy into + * @param dstOffset the index to start copying into + * @throws NullPointerException if dst is null + * @throws IndexOutOfBoundsException if any source or target indices are + * out of range (while unspecified, source problems cause a + * StringIndexOutOfBoundsException, and dest problems cause an + * ArrayIndexOutOfBoundsException) + * @see System#arraycopy(Object, int, Object, int, int) + */ + public synchronized void getChars(int srcOffset, int srcEnd, + char[] dst, int dstOffset) + { + super.getChars(srcOffset, srcEnd, dst, dstOffset); + } + + /** + * Set the character at the specified index. + * + * @param index the index of the character to set starting at 0 + * @param ch the value to set that character to + * @throws IndexOutOfBoundsException if index is negative or >= length() + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public synchronized void setCharAt(int index, char ch) + { + super.setCharAt(index, ch); + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param obj the Object to convert and append + * @return this StringBuffer + * @see String#valueOf(Object) + * @see #append(String) + */ + public synchronized StringBuffer append(Object obj) + { + super.append(obj); + return this; + } + + /** + * Append the String to this StringBuffer. If + * str is null, the String "null" is appended. + * + * @param str the String to append + * @return this StringBuffer + */ + public synchronized StringBuffer append(String str) + { + super.append(str); + return this; + } + + /** + * Append the StringBuffer value of the argument to this + * StringBuffer. This behaves the same as + * append((Object) stringBuffer), except it is more efficient. + * + * @param stringBuffer the StringBuffer to convert and append + * @return this StringBuffer + * @see #append(Object) + * @since 1.4 + */ + public synchronized StringBuffer append(StringBuffer stringBuffer) + { + super.append(stringBuffer); + return this; + } + + /** + * Append the char array to this StringBuffer. + * This is similar (but more efficient) than + * append(new String(data)), except in the case of null. + * + * @param data the char[] to append + * @return this StringBuffer + * @throws NullPointerException if str is null + * @see #append(char[], int, int) + */ + public synchronized StringBuffer append(char[] data) + { + super.append(data, 0, data.length); + return this; + } + + /** + * Append part of the char array to this + * StringBuffer. This is similar (but more efficient) than + * append(new String(data, offset, count)), except in the case + * of null. + * + * @param data the char[] to append + * @param offset the start location in str + * @param count the number of characters to get from str + * @return this StringBuffer + * @throws NullPointerException if str is null + * @throws IndexOutOfBoundsException if offset or count is out of range + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public synchronized StringBuffer append(char[] data, int offset, int count) + { + super.append(data, offset, count); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param bool the boolean to convert and append + * @return this StringBuffer + * @see String#valueOf(boolean) + */ + public synchronized StringBuffer append(boolean bool) + { + super.append(bool); + return this; + } + + /** + * Append the char to this StringBuffer. + * + * @param ch the char to append + * @return this StringBuffer + */ + public synchronized StringBuffer append(char ch) + { + super.append(ch); + return this; + } + + /** + * Append the characters in the CharSequence to this + * buffer. + * + * @param seq the CharSequence providing the characters + * @return this StringBuffer + * @since 1.5 + */ + public synchronized StringBuffer append(CharSequence seq) + { + super.append(seq, 0, seq.length()); + return this; + } + + /** + * Append some characters from the CharSequence to this + * buffer. If the argument is null, the four characters "null" are + * appended. + * + * @param seq the CharSequence providing the characters + * @param start the starting index + * @param end one past the final index + * @return this StringBuffer + * @since 1.5 + */ + public synchronized StringBuffer append(CharSequence seq, int start, int end) + { + super.append(seq, start, end); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param inum the int to convert and append + * @return this StringBuffer + * @see String#valueOf(int) + */ + // This is native in libgcj, for efficiency. + public synchronized StringBuffer append(int inum) + { + super.append(inum); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param lnum the long to convert and append + * @return this StringBuffer + * @see String#valueOf(long) + */ + public synchronized StringBuffer append(long lnum) + { + super.append(lnum); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param fnum the float to convert and append + * @return this StringBuffer + * @see String#valueOf(float) + */ + public synchronized StringBuffer append(float fnum) + { + super.append(fnum); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param dnum the double to convert and append + * @return this StringBuffer + * @see String#valueOf(double) + */ + public synchronized StringBuffer append(double dnum) + { + super.append(dnum); + return this; + } + + /** + * Append the code point to this StringBuffer. + * This is like #append(char), but will append two characters + * if a supplementary code point is given. + * + * @param code the code point to append + * @return this StringBuffer + * @see Character#toChars(int, char[], int) + * @since 1.5 + */ + public synchronized StringBuffer appendCodePoint(int code) + { + super.appendCodePoint(code); + return this; + } + + /** + * Delete characters from this StringBuffer. + * delete(10, 12) will delete 10 and 11, but not 12. It is + * harmless for end to be larger than length(). + * + * @param start the first character to delete + * @param end the index after the last character to delete + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + * @since 1.2 + */ + public synchronized StringBuffer delete(int start, int end) + { + // This will unshare if required. + super.delete(start, end); + return this; + } + + /** + * Delete a character from this StringBuffer. + * + * @param index the index of the character to delete + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if index is out of bounds + * @since 1.2 + */ + public synchronized StringBuffer deleteCharAt(int index) + { + super.deleteCharAt(index); + return this; + } + + /** + * Replace characters between index start (inclusive) and + * end (exclusive) with str. If end + * is larger than the size of this StringBuffer, all characters after + * start are replaced. + * + * @param start the beginning index of characters to delete (inclusive) + * @param end the ending index of characters to delete (exclusive) + * @param str the new String to insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + * @throws NullPointerException if str is null + * @since 1.2 + */ + public synchronized StringBuffer replace(int start, int end, String str) + { + super.replace(start, end, str); + return this; + } + + /** + * Creates a substring of this StringBuffer, starting at a specified index + * and ending at the end of this StringBuffer. + * + * @param beginIndex index to start substring (base 0) + * @return new String which is a substring of this StringBuffer + * @throws StringIndexOutOfBoundsException if beginIndex is out of bounds + * @see #substring(int, int) + * @since 1.2 + */ + public String substring(int beginIndex) + { + return substring(beginIndex, count); + } + + /** + * Creates a substring of this StringBuffer, starting at a specified index + * and ending at one character before a specified index. This is implemented + * the same as substring(beginIndex, endIndex), to satisfy + * the CharSequence interface. + * + * @param beginIndex index to start at (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this StringBuffer + * @throws IndexOutOfBoundsException if beginIndex or endIndex is out of + * bounds + * @see #substring(int, int) + * @since 1.4 + */ + public CharSequence subSequence(int beginIndex, int endIndex) + { + return substring(beginIndex, endIndex); + } + + /** + * Creates a substring of this StringBuffer, starting at a specified index + * and ending at one character before a specified index. + * + * @param beginIndex index to start at (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this StringBuffer + * @throws StringIndexOutOfBoundsException if beginIndex or endIndex is out + * of bounds + * @since 1.2 + */ + public synchronized String substring(int beginIndex, int endIndex) + { + int len = endIndex - beginIndex; + if (beginIndex < 0 || endIndex > count || endIndex < beginIndex) + throw new StringIndexOutOfBoundsException(); + if (len == 0) + return ""; + // Don't copy unless substring is smaller than 1/4 of the buffer. + boolean share_buffer = ((len << 2) >= value.length); + if (share_buffer) + this.shared = true; + // Package constructor avoids an array copy. + return new String(value, beginIndex, len, share_buffer); + } + + /** + * Insert a subarray of the char[] argument into this + * StringBuffer. + * + * @param offset the place to insert in this buffer + * @param str the char[] to insert + * @param str_offset the index in str to start inserting from + * @param len the number of characters to insert + * @return this StringBuffer + * @throws NullPointerException if str is null + * @throws StringIndexOutOfBoundsException if any index is out of bounds + * @since 1.2 + */ + public synchronized StringBuffer insert(int offset, + char[] str, int str_offset, int len) + { + super.insert(offset, str, str_offset, len); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param obj the Object to convert and insert + * @return this StringBuffer + * @exception StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(Object) + */ + public synchronized StringBuffer insert(int offset, Object obj) + { + super.insert(offset, obj); + return this; + } + + /** + * Insert the String argument into this + * StringBuffer. If str is null, the String "null" is used + * instead. + * + * @param offset the place to insert in this buffer + * @param str the String to insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public synchronized StringBuffer insert(int offset, String str) + { + super.insert(offset, str); + return this; + } + + /** + * Insert the CharSequence argument into this + * StringBuffer. If the sequence is null, the String + * "null" is used instead. + * + * @param offset the place to insert in this buffer + * @param sequence the CharSequence to insert + * @return this StringBuffer + * @throws IndexOutOfBoundsException if offset is out of bounds + * @since 1.5 + */ + public synchronized StringBuffer insert(int offset, CharSequence sequence) + { + super.insert(offset, sequence); + return this; + } + + /** + * Insert a subsequence of the CharSequence argument into this + * StringBuffer. If the sequence is null, the String + * "null" is used instead. + * + * @param offset the place to insert in this buffer + * @param sequence the CharSequence to insert + * @param start the starting index of the subsequence + * @param end one past the ending index of the subsequence + * @return this StringBuffer + * @throws IndexOutOfBoundsException if offset, start, + * or end are out of bounds + * @since 1.5 + */ + public synchronized StringBuffer insert(int offset, CharSequence sequence, + int start, int end) + { + super.insert(offset, sequence, start, end); + return this; + } + + /** + * Insert the char[] argument into this + * StringBuffer. + * + * @param offset the place to insert in this buffer + * @param data the char[] to insert + * @return this StringBuffer + * @throws NullPointerException if data is null + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see #insert(int, char[], int, int) + */ + public synchronized StringBuffer insert(int offset, char[] data) + { + super.insert(offset, data, 0, data.length); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param bool the boolean to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(boolean) + */ + public synchronized StringBuffer insert(int offset, boolean bool) + { + super.insert(offset, bool); + return this; + } + + /** + * Insert the char argument into this StringBuffer. + * + * @param offset the place to insert in this buffer + * @param ch the char to insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public synchronized StringBuffer insert(int offset, char ch) + { + super.insert(offset, ch); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param inum the int to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(int) + */ + public synchronized StringBuffer insert(int offset, int inum) + { + super.insert(offset, inum); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param lnum the long to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(long) + */ + public synchronized StringBuffer insert(int offset, long lnum) + { + super.insert(offset, lnum); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param fnum the float to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(float) + */ + public synchronized StringBuffer insert(int offset, float fnum) + { + super.insert(offset, fnum); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuffer. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param dnum the double to convert and insert + * @return this StringBuffer + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(double) + */ + public synchronized StringBuffer insert(int offset, double dnum) + { + super.insert(offset, dnum); + return this; + } + + /** + * Finds the first instance of a substring in this StringBuffer. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @see #indexOf(String, int) + * @since 1.4 + */ + public synchronized int indexOf(String str) + { + return super.indexOf(str, 0); + } + + /** + * Finds the first instance of a String in this StringBuffer, starting at + * a given index. If starting index is less than 0, the search starts at + * the beginning of this String. If the starting index is greater than the + * length of this String, or the substring is not found, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @since 1.4 + */ + public synchronized int indexOf(String str, int fromIndex) + { + return super.indexOf(str, fromIndex); + } + + /** + * Finds the last instance of a substring in this StringBuffer. + * + * @param str String to find + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @see #lastIndexOf(String, int) + * @since 1.4 + */ + public synchronized int lastIndexOf(String str) + { + return super.lastIndexOf(str, count - str.count); + } + + /** + * Finds the last instance of a String in this StringBuffer, starting at a + * given index. If starting index is greater than the maximum valid index, + * then the search begins at the end of this String. If the starting index + * is less than zero, or the substring is not found, -1 is returned. + * + * @param str String to find + * @param fromIndex index to start the search + * @return location (base 0) of the String, or -1 if not found + * @throws NullPointerException if str is null + * @since 1.4 + */ + public synchronized int lastIndexOf(String str, int fromIndex) + { + return super.lastIndexOf(str, fromIndex); + } + + /** + * Reverse the characters in this StringBuffer. The same sequence of + * characters exists, but in the reverse index ordering. + * + * @return this StringBuffer + */ + public synchronized StringBuffer reverse() + { + super.reverse(); + return this; + } + + /** + * Convert this StringBuffer to a String. The + * String is composed of the characters currently in this StringBuffer. Note + * that the result is a copy, and that future modifications to this buffer + * do not affect the String. + * + * @return the characters in this StringBuffer + */ + public String toString() + { + // The string will set this.shared = true. + return new String(this); + } + + /** + * This may reduce the amount of memory used by the StringBuffer, + * by resizing the internal array to remove unused space. However, + * this method is not required to resize, so this behavior cannot + * be relied upon. + * @since 1.5 + */ + public synchronized void trimToSize() + { + super.trimToSize(); + } + + /** + * Return the number of code points between two indices in the + * StringBuffer. An unpaired surrogate counts as a + * code point for this purpose. Characters outside the indicated + * range are not examined, even if the range ends in the middle of a + * surrogate pair. + * + * @param start the starting index + * @param end one past the ending index + * @return the number of code points + * @since 1.5 + */ + public synchronized int codePointCount(int start, int end) + { + return super.codePointCount(start, end); + } + + /** + * Starting at the given index, this counts forward by the indicated + * number of code points, and then returns the resulting index. An + * unpaired surrogate counts as a single code point for this + * purpose. + * + * @param start the starting index + * @param codePoints the number of code points + * @return the resulting index + * @since 1.5 + */ + public synchronized int offsetByCodePoints(int start, int codePoints) + { + return super.offsetByCodePoints(start, codePoints); + } + + /** + * An unsynchronized version of ensureCapacity, used internally to avoid + * the cost of a second lock on the same object. This also has the side + * effect of duplicating the array, if it was shared (to form copy-on-write + * semantics). + * + * @param minimumCapacity the minimum capacity + * @see #ensureCapacity(int) + */ + void ensureCapacity_unsynchronized(int minimumCapacity) + { + if (shared || minimumCapacity > value.length) + { + // We don't want to make a larger vector when `shared' is + // set. If we do, then setLength becomes very inefficient + // when repeatedly reusing a StringBuffer in a loop. + int max = (minimumCapacity > value.length + ? value.length * 2 + 2 + : value.length); + minimumCapacity = (minimumCapacity < max ? max : minimumCapacity); + char[] nb = new char[minimumCapacity]; + VMSystem.arraycopy(value, 0, nb, 0, count); + value = nb; + shared = false; + } + } + +} diff --git a/libjava/classpath/java/lang/StringBuilder.java b/libjava/classpath/java/lang/StringBuilder.java new file mode 100644 index 000000000..83a064ad0 --- /dev/null +++ b/libjava/classpath/java/lang/StringBuilder.java @@ -0,0 +1,706 @@ +/* StringBuilder.java -- Unsynchronized growable strings + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.io.Serializable; + +/** + * StringBuilder represents a changeable String. + * It provides the operations required to modify the + * StringBuilder, including insert, replace, delete, append, + * and reverse. It like StringBuffer, but is not + * synchronized. It is ideal for use when it is known that the + * object will only be used from a single thread. + * + *

    StringBuilders are variable-length in nature, so even if + * you initialize them to a certain size, they can still grow larger than + * that. Capacity indicates the number of characters the + * StringBuilder can have in it before it has to grow (growing + * the char array is an expensive operation involving new). + * + *

    Incidentally, compilers often implement the String operator "+" + * by using a StringBuilder operation:
    + * a + b
    + * is the same as
    + * new StringBuilder().append(a).append(b).toString(). + * + *

    Classpath's StringBuilder is capable of sharing memory with Strings for + * efficiency. This will help when a StringBuilder is converted to a String + * and the StringBuilder is not changed after that (quite common when + * performing string concatenation). + * + * @author Paul Fisher + * @author John Keiser + * @author Tom Tromey + * @author Eric Blake (ebb9@email.byu.edu) + * @see String + * @see StringBuffer + * + * @since 1.5 + */ +public final class StringBuilder + extends AbstractStringBuffer + implements Serializable, CharSequence, Appendable +{ + // Implementation note: if you change this class, you usually will + // want to change StringBuffer as well. + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 4383685877147921099L; + + /** + * Create a new StringBuilder with default capacity 16. + */ + public StringBuilder() + { + super(); + } + + /** + * Create an empty StringBuilder with the specified initial + * capacity. + * + * @param capacity the initial capacity + * @throws NegativeArraySizeException if capacity is negative + */ + public StringBuilder(int capacity) + { + super(capacity); + } + + /** + * Create a new StringBuilder with the characters in the + * specified String. Initial capacity will be the size of the + * String plus 16. + * + * @param str the String to convert + * @throws NullPointerException if str is null + */ + public StringBuilder(String str) + { + super(str); + } + + /** + * Create a new StringBuilder with the characters in the + * specified CharSequence. Initial capacity will be the + * length of the sequence plus 16; if the sequence reports a length + * less than or equal to 0, then the initial capacity will be 16. + * + * @param seq the initializing CharSequence + * @throws NullPointerException if str is null + */ + public StringBuilder(CharSequence seq) + { + super(seq); + } + + /** + * Get the length of the String this StringBuilder + * would create. Not to be confused with the capacity of the + * StringBuilder. + * + * @return the length of this StringBuilder + * @see #capacity() + * @see #setLength(int) + */ + public int length() + { + return count; + } + + /** + * Get the total number of characters this StringBuilder can + * support before it must be grown. Not to be confused with length. + * + * @return the capacity of this StringBuilder + * @see #length() + * @see #ensureCapacity(int) + */ + public int capacity() + { + return value.length; + } + + /** + * Append the String value of the argument to this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param obj the Object to convert and append + * @return this StringBuilder + * @see String#valueOf(Object) + * @see #append(String) + */ + public StringBuilder append(Object obj) + { + super.append(obj); + return this; + } + + /** + * Append the String to this StringBuilder. If + * str is null, the String "null" is appended. + * + * @param str the String to append + * @return this StringBuilder + */ + public StringBuilder append(String str) + { + super.append(str); + return this; + } + + /** + * Append the StringBuilder value of the argument to this + * StringBuilder. This behaves the same as + * append((Object) stringBuffer), except it is more efficient. + * + * @param stringBuffer the StringBuilder to convert and append + * @return this StringBuilder + * @see #append(Object) + */ + public StringBuilder append(StringBuffer stringBuffer) + { + super.append(stringBuffer); + return this; + } + + /** + * Append the char array to this StringBuilder. + * This is similar (but more efficient) than + * append(new String(data)), except in the case of null. + * + * @param data the char[] to append + * @return this StringBuilder + * @throws NullPointerException if str is null + * @see #append(char[], int, int) + */ + public StringBuilder append(char[] data) + { + super.append(data, 0, data.length); + return this; + } + + /** + * Append part of the char array to this + * StringBuilder. This is similar (but more efficient) than + * append(new String(data, offset, count)), except in the case + * of null. + * + * @param data the char[] to append + * @param offset the start location in str + * @param count the number of characters to get from str + * @return this StringBuilder + * @throws NullPointerException if str is null + * @throws IndexOutOfBoundsException if offset or count is out of range + * (while unspecified, this is a StringIndexOutOfBoundsException) + */ + public StringBuilder append(char[] data, int offset, int count) + { + super.append(data, offset, count); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param bool the boolean to convert and append + * @return this StringBuilder + * @see String#valueOf(boolean) + */ + public StringBuilder append(boolean bool) + { + super.append(bool); + return this; + } + + /** + * Append the char to this StringBuilder. + * + * @param ch the char to append + * @return this StringBuilder + */ + public StringBuilder append(char ch) + { + super.append(ch); + return this; + } + + /** + * Append the characters in the CharSequence to this + * buffer. + * + * @param seq the CharSequence providing the characters + * @return this StringBuilder + */ + public StringBuilder append(CharSequence seq) + { + super.append(seq, 0, seq.length()); + return this; + } + + /** + * Append some characters from the CharSequence to this + * buffer. If the argument is null, the four characters "null" are + * appended. + * + * @param seq the CharSequence providing the characters + * @param start the starting index + * @param end one past the final index + * @return this StringBuilder + */ + public StringBuilder append(CharSequence seq, int start, + int end) + { + super.append(seq, start, end); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param inum the int to convert and append + * @return this StringBuilder + * @see String#valueOf(int) + */ + // This is native in libgcj, for efficiency. + public StringBuilder append(int inum) + { + super.append(inum); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param lnum the long to convert and append + * @return this StringBuilder + * @see String#valueOf(long) + */ + public StringBuilder append(long lnum) + { + super.append(lnum); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param fnum the float to convert and append + * @return this StringBuilder + * @see String#valueOf(float) + */ + public StringBuilder append(float fnum) + { + super.append(fnum); + return this; + } + + /** + * Append the String value of the argument to this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param dnum the double to convert and append + * @return this StringBuilder + * @see String#valueOf(double) + */ + public StringBuilder append(double dnum) + { + super.append(dnum); + return this; + } + + /** + * Append the code point to this StringBuilder. + * This is like #append(char), but will append two characters + * if a supplementary code point is given. + * + * @param code the code point to append + * @return this StringBuilder + * @see Character#toChars(int, char[], int) + * @since 1.5 + */ + public StringBuilder appendCodePoint(int code) + { + super.appendCodePoint(code); + return this; + } + + /** + * Delete characters from this StringBuilder. + * delete(10, 12) will delete 10 and 11, but not 12. It is + * harmless for end to be larger than length(). + * + * @param start the first character to delete + * @param end the index after the last character to delete + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + */ + public StringBuilder delete(int start, int end) + { + super.delete(start, end); + return this; + } + + /** + * Delete a character from this StringBuilder. + * + * @param index the index of the character to delete + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if index is out of bounds + */ + public StringBuilder deleteCharAt(int index) + { + super.deleteCharAt(index); + return this; + } + + /** + * Replace characters between index start (inclusive) and + * end (exclusive) with str. If end + * is larger than the size of this StringBuilder, all characters after + * start are replaced. + * + * @param start the beginning index of characters to delete (inclusive) + * @param end the ending index of characters to delete (exclusive) + * @param str the new String to insert + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if start or end are out of bounds + * @throws NullPointerException if str is null + */ + public StringBuilder replace(int start, int end, String str) + { + super.replace(start, end, str); + return this; + } + + /** + * Creates a substring of this StringBuilder, starting at a specified index + * and ending at the end of this StringBuilder. + * + * @param beginIndex index to start substring (base 0) + * @return new String which is a substring of this StringBuilder + * @throws StringIndexOutOfBoundsException if beginIndex is out of bounds + * @see #substring(int, int) + */ + public String substring(int beginIndex) + { + return substring(beginIndex, count); + } + + /** + * Creates a substring of this StringBuilder, starting at a specified index + * and ending at one character before a specified index. This is implemented + * the same as substring(beginIndex, endIndex), to satisfy + * the CharSequence interface. + * + * @param beginIndex index to start at (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this StringBuilder + * @throws IndexOutOfBoundsException if beginIndex or endIndex is out of + * bounds + * @see #substring(int, int) + */ + public CharSequence subSequence(int beginIndex, int endIndex) + { + return substring(beginIndex, endIndex); + } + + /** + * Creates a substring of this StringBuilder, starting at a specified index + * and ending at one character before a specified index. + * + * @param beginIndex index to start at (inclusive, base 0) + * @param endIndex index to end at (exclusive) + * @return new String which is a substring of this StringBuilder + * @throws StringIndexOutOfBoundsException if beginIndex or endIndex is out + * of bounds + */ + public String substring(int beginIndex, int endIndex) + { + int len = endIndex - beginIndex; + if (beginIndex < 0 || endIndex > count || endIndex < beginIndex) + throw new StringIndexOutOfBoundsException(); + if (len == 0) + return ""; + return new String(value, beginIndex, len); + } + + /** + * Insert a subarray of the char[] argument into this + * StringBuilder. + * + * @param offset the place to insert in this buffer + * @param str the char[] to insert + * @param str_offset the index in str to start inserting from + * @param len the number of characters to insert + * @return this StringBuilder + * @throws NullPointerException if str is null + * @throws StringIndexOutOfBoundsException if any index is out of bounds + */ + public StringBuilder insert(int offset, + char[] str, int str_offset, int len) + { + super.insert(offset, str, str_offset, len); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param obj the Object to convert and insert + * @return this StringBuilder + * @exception StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(Object) + */ + public StringBuilder insert(int offset, Object obj) + { + super.insert(offset, obj); + return this; + } + + /** + * Insert the String argument into this + * StringBuilder. If str is null, the String "null" is used + * instead. + * + * @param offset the place to insert in this buffer + * @param str the String to insert + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public StringBuilder insert(int offset, String str) + { + super.insert(offset, str); + return this; + } + + /** + * Insert the CharSequence argument into this + * StringBuilder. If the sequence is null, the String + * "null" is used instead. + * + * @param offset the place to insert in this buffer + * @param sequence the CharSequence to insert + * @return this StringBuilder + * @throws IndexOutOfBoundsException if offset is out of bounds + */ + public StringBuilder insert(int offset, CharSequence sequence) + { + super.insert(offset, sequence); + return this; + } + + /** + * Insert a subsequence of the CharSequence argument into this + * StringBuilder. If the sequence is null, the String + * "null" is used instead. + * + * @param offset the place to insert in this buffer + * @param sequence the CharSequence to insert + * @param start the starting index of the subsequence + * @param end one past the ending index of the subsequence + * @return this StringBuilder + * @throws IndexOutOfBoundsException if offset, start, + * or end are out of bounds + */ + public StringBuilder insert(int offset, CharSequence sequence, + int start, int end) + { + super.insert(offset, sequence, start, end); + return this; + } + + /** + * Insert the char[] argument into this + * StringBuilder. + * + * @param offset the place to insert in this buffer + * @param data the char[] to insert + * @return this StringBuilder + * @throws NullPointerException if data is null + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see #insert(int, char[], int, int) + */ + public StringBuilder insert(int offset, char[] data) + { + super.insert(offset, data); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param bool the boolean to convert and insert + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(boolean) + */ + public StringBuilder insert(int offset, boolean bool) + { + super.insert(offset, bool); + return this; + } + + /** + * Insert the char argument into this StringBuilder. + * + * @param offset the place to insert in this buffer + * @param ch the char to insert + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if offset is out of bounds + */ + public StringBuilder insert(int offset, char ch) + { + super.insert(offset, ch); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param inum the int to convert and insert + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(int) + */ + public StringBuilder insert(int offset, int inum) + { + super.insert(offset, inum); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param lnum the long to convert and insert + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(long) + */ + public StringBuilder insert(int offset, long lnum) + { + super.insert(offset, lnum); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param fnum the float to convert and insert + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(float) + */ + public StringBuilder insert(int offset, float fnum) + { + super.insert(offset, fnum); + return this; + } + + /** + * Insert the String value of the argument into this + * StringBuilder. Uses String.valueOf() to convert + * to String. + * + * @param offset the place to insert in this buffer + * @param dnum the double to convert and insert + * @return this StringBuilder + * @throws StringIndexOutOfBoundsException if offset is out of bounds + * @see String#valueOf(double) + */ + public StringBuilder insert(int offset, double dnum) + { + super.insert(offset, dnum); + return this; + } + + /** + * Reverse the characters in this StringBuilder. The same sequence of + * characters exists, but in the reverse index ordering. + * + * @return this StringBuilder + */ + public StringBuilder reverse() + { + super.reverse(); + return this; + } + + /** + * Convert this StringBuilder to a String. The + * String is composed of the characters currently in this StringBuilder. Note + * that the result is a copy, and that future modifications to this buffer + * do not affect the String. + * + * @return the characters in this StringBuilder + */ + public String toString() + { + return new String(this); + } + +} diff --git a/libjava/classpath/java/lang/StringIndexOutOfBoundsException.java b/libjava/classpath/java/lang/StringIndexOutOfBoundsException.java new file mode 100644 index 000000000..ebc4a04a3 --- /dev/null +++ b/libjava/classpath/java/lang/StringIndexOutOfBoundsException.java @@ -0,0 +1,85 @@ +/* StringIndexOutOfBoundsException.java -- thrown to indicate attempt to + exceed string bounds + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * This exception can be thrown to indicate an attempt to access an index + * which is out of bounds of a String. Any negative integer, and a positive + * integer greater than or equal to the size of the string, is an index + * which would be out of bounds. + * + * @author Brian Jones + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class StringIndexOutOfBoundsException extends IndexOutOfBoundsException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -6762910422159637258L; + + /** + * Create an exception without a message. + */ + public StringIndexOutOfBoundsException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public StringIndexOutOfBoundsException(String s) + { + super(s); + } + + /** + * Create an exception noting the illegal index. + * + * @param index the invalid index + */ + public StringIndexOutOfBoundsException(int index) + { + super("String index out of range: " + index); + } +} diff --git a/libjava/classpath/java/lang/SuppressWarnings.java b/libjava/classpath/java/lang/SuppressWarnings.java new file mode 100644 index 000000000..35b01a851 --- /dev/null +++ b/libjava/classpath/java/lang/SuppressWarnings.java @@ -0,0 +1,69 @@ +/* SuppressWarnings - Annotation to avoid compiler warnings + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.RetentionPolicy.SOURCE; +import static java.lang.annotation.ElementType.*; + +/** + * Tell the compiler that a given warning should be suppressed when it + * pertains to the marked program element and its sub-elements. + * + * Note that warning suppression is additive. For instance if a + * constructor has a warning suppressed, and a local variable in the + * constructor has a different warning suppressed, then the resulting + * set of suppressed warnings for that variable will be both warnings. + * + * @since 1.5 + */ +@Retention(SOURCE) +@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) +public @interface SuppressWarnings +{ + /** + * The list of warnings to suppress. + * + * It is valid to list a name more than once. Unrecognized names + * are not a compile-time error. At the present there is no + * standard for the names to be recognized by compilers; consult + * your compiler's documentation for this information. + */ + String[] value (); +} diff --git a/libjava/classpath/java/lang/System.java b/libjava/classpath/java/lang/System.java new file mode 100644 index 000000000..39d6da229 --- /dev/null +++ b/libjava/classpath/java/lang/System.java @@ -0,0 +1,1120 @@ +/* System.java -- useful methods to interface with the system + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +import gnu.classpath.SystemProperties; +import gnu.classpath.VMStackWalker; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.nio.channels.Channel; +import java.nio.channels.spi.SelectorProvider; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Properties; +import java.util.PropertyPermission; + +/** + * System represents system-wide resources; things that represent the + * general environment. As such, all methods are static. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status still missing 1.4 functionality + */ +public final class System +{ + // WARNING: System is a CORE class in the bootstrap cycle. See the comments + // in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * The standard InputStream. This is assigned at startup and starts its + * life perfectly valid. Although it is marked final, you can change it + * using {@link #setIn(InputStream)} through some hefty VM magic. + * + *

    This corresponds to the C stdin and C++ cin variables, which + * typically input from the keyboard, but may be used to pipe input from + * other processes or files. That should all be transparent to you, + * however. + */ + public static final InputStream in = VMSystem.makeStandardInputStream(); + + /** + * The standard output PrintStream. This is assigned at startup and + * starts its life perfectly valid. Although it is marked final, you can + * change it using {@link #setOut(PrintStream)} through some hefty VM magic. + * + *

    This corresponds to the C stdout and C++ cout variables, which + * typically output normal messages to the screen, but may be used to pipe + * output to other processes or files. That should all be transparent to + * you, however. + */ + public static final PrintStream out = VMSystem.makeStandardOutputStream(); + + /** + * The standard output PrintStream. This is assigned at startup and + * starts its life perfectly valid. Although it is marked final, you can + * change it using {@link #setErr(PrintStream)} through some hefty VM magic. + * + *

    This corresponds to the C stderr and C++ cerr variables, which + * typically output error messages to the screen, but may be used to pipe + * output to other processes or files. That should all be transparent to + * you, however. + */ + public static final PrintStream err = VMSystem.makeStandardErrorStream(); + + /** + * A cached copy of the environment variable map. + */ + private static Map environmentMap; + + /** + * This class is uninstantiable. + */ + private System() + { + } + + /** + * Set {@link #in} to a new InputStream. This uses some VM magic to change + * a "final" variable, so naturally there is a security check, + * RuntimePermission("setIO"). + * + * @param in the new InputStream + * @throws SecurityException if permission is denied + * @since 1.1 + */ + public static void setIn(InputStream in) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("setIO")); + + VMSystem.setIn(in); + } + + /** + * Set {@link #out} to a new PrintStream. This uses some VM magic to change + * a "final" variable, so naturally there is a security check, + * RuntimePermission("setIO"). + * + * @param out the new PrintStream + * @throws SecurityException if permission is denied + * @since 1.1 + */ + public static void setOut(PrintStream out) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("setIO")); + VMSystem.setOut(out); + } + + /** + * Set {@link #err} to a new PrintStream. This uses some VM magic to change + * a "final" variable, so naturally there is a security check, + * RuntimePermission("setIO"). + * + * @param err the new PrintStream + * @throws SecurityException if permission is denied + * @since 1.1 + */ + public static void setErr(PrintStream err) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("setIO")); + VMSystem.setErr(err); + } + + /** + * Set the current SecurityManager. If a security manager already exists, + * then RuntimePermission("setSecurityManager") is checked + * first. Since this permission is denied by the default security manager, + * setting the security manager is often an irreversible action. + * + * Spec Note: Don't ask me, I didn't write it. It looks + * pretty vulnerable; whoever gets to the gate first gets to set the policy. + * There is probably some way to set the original security manager as a + * command line argument to the VM, but I don't know it. + * + * @param sm the new SecurityManager + * @throws SecurityException if permission is denied + */ + public static synchronized void setSecurityManager(SecurityManager sm) + { + // Implementation note: the field lives in SecurityManager because of + // bootstrap initialization issues. This method is synchronized so that + // no other thread changes it to null before this thread makes the change. + if (SecurityManager.current != null) + SecurityManager.current.checkPermission + (new RuntimePermission("setSecurityManager")); + + // java.security.Security's class initialiser loads and parses the + // policy files. If it hasn't been run already it will be run + // during the first permission check. That initialisation will + // fail if a very restrictive security manager is in force, so we + // preload it here. + if (SecurityManager.current == null) + { + try + { + Class.forName("java.security.Security"); + } + catch (ClassNotFoundException e) + { + } + } + + SecurityManager.current = sm; + } + + /** + * Get the current SecurityManager. If the SecurityManager has not been + * set yet, then this method returns null. + * + * @return the current SecurityManager, or null + */ + public static SecurityManager getSecurityManager() + { + return SecurityManager.current; + } + + /** + * Get the current time, measured in the number of milliseconds from the + * beginning of Jan. 1, 1970. This is gathered from the system clock, with + * any attendant incorrectness (it may be timezone dependent). + * + * @return the current time + * @see java.util.Date + */ + public static long currentTimeMillis() + { + return VMSystem.currentTimeMillis(); + } + + /** + *

    + * Returns the current value of a nanosecond-precise system timer. + * The value of the timer is an offset relative to some arbitrary fixed + * time, which may be in the future (making the value negative). This + * method is useful for timing events where nanosecond precision is + * required. This is achieved by calling this method before and after the + * event, and taking the difference betweent the two times: + *

    + *

    + * long startTime = System.nanoTime();
    + * ... event code ...
    + * long endTime = System.nanoTime();
    + * long duration = endTime - startTime;
    + *

    + *

    + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. + *

    + * + * @return the time of a system timer in nanoseconds. + * @since 1.5 + */ + public static long nanoTime() + { + return VMSystem.nanoTime(); + } + + /** + * Copy one array onto another from src[srcStart] ... + * src[srcStart+len-1] to dest[destStart] ... + * dest[destStart+len-1]. First, the arguments are validated: + * neither array may be null, they must be of compatible types, and the + * start and length must fit within both arrays. Then the copying starts, + * and proceeds through increasing slots. If src and dest are the same + * array, this will appear to copy the data to a temporary location first. + * An ArrayStoreException in the middle of copying will leave earlier + * elements copied, but later elements unchanged. + * + * @param src the array to copy elements from + * @param srcStart the starting position in src + * @param dest the array to copy elements to + * @param destStart the starting position in dest + * @param len the number of elements to copy + * @throws NullPointerException if src or dest is null + * @throws ArrayStoreException if src or dest is not an array, if they are + * not compatible array types, or if an incompatible runtime type + * is stored in dest + * @throws IndexOutOfBoundsException if len is negative, or if the start or + * end copy position in either array is out of bounds + */ + public static void arraycopy(Object src, int srcStart, + Object dest, int destStart, int len) + { + VMSystem.arraycopy(src, srcStart, dest, destStart, len); + } + + /** + * Get a hash code computed by the VM for the Object. This hash code will + * be the same as Object's hashCode() method. It is usually some + * convolution of the pointer to the Object internal to the VM. It + * follows standard hash code rules, in that it will remain the same for a + * given Object for the lifetime of that Object. + * + * @param o the Object to get the hash code for + * @return the VM-dependent hash code for this Object + * @since 1.1 + */ + public static int identityHashCode(Object o) + { + return VMSystem.identityHashCode(o); + } + + /** + * Get all the system properties at once. A security check may be performed, + * checkPropertiesAccess. Note that a security manager may + * allow getting a single property, but not the entire group. + * + *

    The required properties include: + *

    + *
    java.version
    Java version number
    + *
    java.vendor
    Java vendor specific string
    + *
    java.vendor.url
    Java vendor URL
    + *
    java.home
    Java installation directory
    + *
    java.vm.specification.version
    VM Spec version
    + *
    java.vm.specification.vendor
    VM Spec vendor
    + *
    java.vm.specification.name
    VM Spec name
    + *
    java.vm.version
    VM implementation version
    + *
    java.vm.vendor
    VM implementation vendor
    + *
    java.vm.name
    VM implementation name
    + *
    java.specification.version
    Java Runtime Environment version
    + *
    java.specification.vendor
    Java Runtime Environment vendor
    + *
    java.specification.name
    Java Runtime Environment name
    + *
    java.class.version
    Java class version number
    + *
    java.class.path
    Java classpath
    + *
    java.library.path
    Path for finding Java libraries
    + *
    java.io.tmpdir
    Default temp file path
    + *
    java.compiler
    Name of JIT to use
    + *
    java.ext.dirs
    Java extension path
    + *
    os.name
    Operating System Name
    + *
    os.arch
    Operating System Architecture
    + *
    os.version
    Operating System Version
    + *
    file.separator
    File separator ("/" on Unix)
    + *
    path.separator
    Path separator (":" on Unix)
    + *
    line.separator
    Line separator ("\n" on Unix)
    + *
    user.name
    User account name
    + *
    user.home
    User home directory
    + *
    user.dir
    User's current working directory
    + *
    + * + * In addition, gnu defines several other properties, where ? stands for + * each character in '0' through '9': + *
    + *
    gnu.classpath.home
    Path to the classpath libraries.
    + *
    gnu.classpath.version
    Version of the classpath libraries.
    + *
    gnu.classpath.vm.shortname
    Succinct version of the VM name; + * used for finding property files in file system
    + *
    gnu.classpath.home.url
    Base URL; used for finding + * property files in file system
    + *
    gnu.cpu.endian
    big or little
    + *
    gnu.java.io.encoding_scheme_alias.iso-8859-?
    8859_?
    + *
    gnu.java.io.encoding_scheme_alias.iso8859_?
    8859_?
    + *
    gnu.java.io.encoding_scheme_alias.iso-latin-_?
    8859_?
    + *
    gnu.java.io.encoding_scheme_alias.latin?
    8859_?
    + *
    gnu.java.io.encoding_scheme_alias.utf-8
    UTF8
    + *
    gnu.java.util.zoneinfo.dir
    Root of zoneinfo tree
    + *
    gnu.javax.print.server
    Hostname of external CUPS server.
    + *
    + * + * @return the system properties, will never be null + * @throws SecurityException if permission is denied + */ + public static Properties getProperties() + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPropertiesAccess(); + return SystemProperties.getProperties(); + } + + /** + * Set all the system properties at once. A security check may be performed, + * checkPropertiesAccess. Note that a security manager may + * allow setting a single property, but not the entire group. An argument + * of null resets the properties to the startup default. + * + * @param properties the new set of system properties + * @throws SecurityException if permission is denied + */ + public static void setProperties(Properties properties) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPropertiesAccess(); + SystemProperties.setProperties(properties); + } + + /** + * Get a single system property by name. A security check may be performed, + * checkPropertyAccess(key). + * + * @param key the name of the system property to get + * @return the property, or null if not found + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + */ + public static String getProperty(String key) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPropertyAccess(key); + if (key.length() == 0) + throw new IllegalArgumentException("key can't be empty"); + return SystemProperties.getProperty(key); + } + + /** + * Get a single system property by name. A security check may be performed, + * checkPropertyAccess(key). + * + * @param key the name of the system property to get + * @param def the default + * @return the property, or def if not found + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + */ + public static String getProperty(String key, String def) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPropertyAccess(key); + // This handles both the null pointer exception and the illegal + // argument exception. + if (key.length() == 0) + throw new IllegalArgumentException("key can't be empty"); + return SystemProperties.getProperty(key, def); + } + + /** + * Set a single system property by name. A security check may be performed, + * checkPropertyAccess(key, "write"). + * + * @param key the name of the system property to set + * @param value the new value + * @return the previous value, or null + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + * @since 1.2 + */ + public static String setProperty(String key, String value) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new PropertyPermission(key, "write")); + // This handles both the null pointer exception and the illegal + // argument exception. + if (key.length() == 0) + throw new IllegalArgumentException("key can't be empty"); + return SystemProperties.setProperty(key, value); + } + + /** + * Remove a single system property by name. A security check may be + * performed, checkPropertyAccess(key, "write"). + * + * @param key the name of the system property to remove + * @return the previous value, or null + * @throws SecurityException if permission is denied + * @throws NullPointerException if key is null + * @throws IllegalArgumentException if key is "" + * @since 1.5 + */ + public static String clearProperty(String key) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new PropertyPermission(key, "write")); + // This handles both the null pointer exception and the illegal + // argument exception. + if (key.length() == 0) + throw new IllegalArgumentException("key can't be empty"); + return SystemProperties.remove(key); + } + + /** + * Gets the value of an environment variable. + * + * @param name the name of the environment variable + * @return the string value of the variable or null when the + * environment variable is not defined. + * @throws NullPointerException + * @throws SecurityException if permission is denied + * @since 1.5 + * @specnote This method was deprecated in some JDK releases, but + * was restored in 1.5. + */ + public static String getenv(String name) + { + if (name == null) + throw new NullPointerException(); + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("getenv." + name)); + return VMSystem.getenv(name); + } + + /** + *

    + * Returns an unmodifiable view of the system environment variables. + * If the underlying system does not support environment variables, + * an empty map is returned. + *

    + *

    + * The returned map is read-only and does not accept queries using + * null keys or values, or those of a type other than String. + * Attempts to modify the map will throw an + * UnsupportedOperationException, while attempts + * to pass in a null value will throw a + * NullPointerException. Types other than String + * throw a ClassCastException. + *

    + *

    + * As the returned map is generated using data from the underlying + * platform, it may not comply with the equals() + * and hashCode() contracts. It is also likely that + * the keys of this map will be case-sensitive. + *

    + *

    + * Use of this method may require a security check for the + * RuntimePermission "getenv.*". + *

    + * + * @return a map of the system environment variables. + * @throws SecurityException if the checkPermission method of + * an installed security manager prevents access to + * the system environment variables. + * @since 1.5 + */ + public static Map getenv() + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("getenv.*")); + + if (environmentMap == null) + { + Map variables = new EnvironmentMap(); + List environ = (List)VMSystem.environ(); + for (String envEntry : environ) + { + // avoid broken and null entries + if (envEntry != null && !envEntry.endsWith("=")) + { + // it's perfectly legal that some entries may be in the form + // key=value=value=value + int equalSignIndex = envEntry.indexOf('='); + String key = envEntry.substring(0, equalSignIndex); + String value = envEntry.substring(equalSignIndex + 1); + variables.put(key, value); + } + } + + environmentMap = Collections.unmodifiableMap(variables); + } + + return environmentMap; + } + + /** + * Terminate the Virtual Machine. This just calls + * Runtime.getRuntime().exit(status), and never returns. + * Obviously, a security check is in order, checkExit. + * + * @param status the exit status; by convention non-zero is abnormal + * @throws SecurityException if permission is denied + * @see Runtime#exit(int) + */ + public static void exit(int status) + { + Runtime.getRuntime().exit(status); + } + + /** + * Calls the garbage collector. This is only a hint, and it is up to the + * implementation what this hint suggests, but it usually causes a + * best-effort attempt to reclaim unused memory from discarded objects. + * This calls Runtime.getRuntime().gc(). + * + * @see Runtime#gc() + */ + public static void gc() + { + Runtime.getRuntime().gc(); + } + + /** + * Runs object finalization on pending objects. This is only a hint, and + * it is up to the implementation what this hint suggests, but it usually + * causes a best-effort attempt to run finalizers on all objects ready + * to be reclaimed. This calls + * Runtime.getRuntime().runFinalization(). + * + * @see Runtime#runFinalization() + */ + public static void runFinalization() + { + Runtime.getRuntime().runFinalization(); + } + + /** + * Tell the Runtime whether to run finalization before exiting the + * JVM. This is inherently unsafe in multi-threaded applications, + * since it can force initialization on objects which are still in use + * by live threads, leading to deadlock; therefore this is disabled by + * default. There may be a security check, checkExit(0). This + * calls Runtime.runFinalizersOnExit(). + * + * @param finalizeOnExit whether to run finalizers on exit + * @throws SecurityException if permission is denied + * @see Runtime#runFinalizersOnExit(boolean) + * @since 1.1 + * @deprecated never rely on finalizers to do a clean, thread-safe, + * mop-up from your code + */ + public static void runFinalizersOnExit(boolean finalizeOnExit) + { + Runtime.runFinalizersOnExit(finalizeOnExit); + } + + /** + * Load a code file using its explicit system-dependent filename. A security + * check may be performed, checkLink. This just calls + * Runtime.getRuntime().load(filename). + * + *

    + * The library is loaded using the class loader associated with the + * class associated with the invoking method. + * + * @param filename the code file to load + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the file cannot be loaded + * @see Runtime#load(String) + */ + public static void load(String filename) + { + Runtime.getRuntime().load(filename, VMStackWalker.getCallingClassLoader()); + } + + /** + * Load a library using its explicit system-dependent filename. A security + * check may be performed, checkLink. This just calls + * Runtime.getRuntime().load(filename). + * + *

    + * The library is loaded using the class loader associated with the + * class associated with the invoking method. + * + * @param libname the library file to load + * @throws SecurityException if permission is denied + * @throws UnsatisfiedLinkError if the file cannot be loaded + * @see Runtime#load(String) + */ + public static void loadLibrary(String libname) + { + Runtime.getRuntime().loadLibrary(libname, + VMStackWalker.getCallingClassLoader()); + } + + /** + * Convert a library name to its platform-specific variant. + * + * @param libname the library name, as used in loadLibrary + * @return the platform-specific mangling of the name + * @since 1.2 + */ + public static String mapLibraryName(String libname) + { + return VMRuntime.mapLibraryName(libname); + } + + /** + * Returns the inherited channel of the VM. + * + * This wraps the inheritedChannel() call of the system's default + * {@link SelectorProvider}. + * + * @return the inherited channel of the VM + * + * @throws IOException If an I/O error occurs + * @throws SecurityException If an installed security manager denies access + * to RuntimePermission("inheritedChannel") + * + * @since 1.5 + */ + public static Channel inheritedChannel() + throws IOException + { + return SelectorProvider.provider().inheritedChannel(); + } + + /** + * This is a specialised Collection, providing + * the necessary provisions for the collections used by the + * environment variable map. Namely, it prevents + * querying anything but Strings. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static class EnvironmentCollection + extends AbstractCollection + { + + /** + * The wrapped collection. + */ + protected Collection c; + + /** + * Constructs a new environment collection, which + * wraps the elements of the supplied collection. + * + * @param coll the collection to use as a base for + * this collection. + */ + public EnvironmentCollection(Collection coll) + { + c = coll; + } + + /** + * Blocks queries containing a null object or an object which + * isn't of type String. All other queries + * are forwarded to the underlying collection. + * + * @param obj the object to look for. + * @return true if the object exists in the collection. + * @throws NullPointerException if the specified object is null. + * @throws ClassCastException if the specified object is not a String. + */ + public boolean contains(Object obj) + { + if (obj == null) + throw new + NullPointerException("This collection does not support " + + "null values."); + if (!(obj instanceof String)) + throw new + ClassCastException("This collection only supports Strings."); + return c.contains(obj); + } + + /** + * Blocks queries where the collection contains a null object or + * an object which isn't of type String. All other + * queries are forwarded to the underlying collection. + * + * @param coll the collection of objects to look for. + * @return true if the collection contains all elements in the collection. + * @throws NullPointerException if the collection is null. + * @throws NullPointerException if any collection entry is null. + * @throws ClassCastException if any collection entry is not a String. + */ + public boolean containsAll(Collection coll) + { + for (Object o: coll) + { + if (o == null) + throw new + NullPointerException("This collection does not support " + + "null values."); + if (!(o instanceof String)) + throw new + ClassCastException("This collection only supports Strings."); + } + return c.containsAll(coll); + } + + /** + * This returns an iterator over the map elements, with the + * same provisions as for the collection and underlying map. + * + * @return an iterator over the map elements. + */ + public Iterator iterator() + { + return c.iterator(); + } + + /** + * Blocks the removal of elements from the collection. + * + * @return true if the removal was sucessful. + * @throws NullPointerException if the collection is null. + * @throws NullPointerException if any collection entry is null. + * @throws ClassCastException if any collection entry is not a String. + */ + public boolean remove(Object key) + { + if (key == null) + throw new + NullPointerException("This collection does not support " + + "null values."); + if (!(key instanceof String)) + throw new + ClassCastException("This collection only supports Strings."); + return c.contains(key); + } + + /** + * Blocks the removal of all elements in the specified + * collection from the collection. + * + * @param coll the collection of elements to remove. + * @return true if the elements were removed. + * @throws NullPointerException if the collection is null. + * @throws NullPointerException if any collection entry is null. + * @throws ClassCastException if any collection entry is not a String. + */ + public boolean removeAll(Collection coll) + { + for (Object o: coll) + { + if (o == null) + throw new + NullPointerException("This collection does not support " + + "null values."); + if (!(o instanceof String)) + throw new + ClassCastException("This collection only supports Strings."); + } + return c.removeAll(coll); + } + + /** + * Blocks the retention of all elements in the specified + * collection from the collection. + * + * @param coll the collection of elements to retain. + * @return true if the other elements were removed. + * @throws NullPointerException if the collection is null. + * @throws NullPointerException if any collection entry is null. + * @throws ClassCastException if any collection entry is not a String. + */ + public boolean retainAll(Collection coll) + { + for (Object o: coll) + { + if (o == null) + throw new + NullPointerException("This collection does not support " + + "null values."); + if (!(o instanceof String)) + throw new + ClassCastException("This collection only supports Strings."); + } + return c.containsAll(coll); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @return the size of the underlying collection. + */ + public int size() + { + return c.size(); + } + + } // class EnvironmentCollection + + /** + * This is a specialised HashMap, which + * prevents the addition or querying of anything other than + * String objects. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + static class EnvironmentMap + extends HashMap + { + + /** + * Cache the entry set. + */ + private transient Set> entries; + + /** + * Cache the key set. + */ + private transient Set keys; + + /** + * Cache the value collection. + */ + private transient Collection values; + + /** + * Constructs a new empty EnvironmentMap. + */ + EnvironmentMap() + { + super(); + } + + /** + * Constructs a new EnvironmentMap containing + * the contents of the specified map. + * + * @param m the map to be added to this. + * @throws NullPointerException if a key or value is null. + * @throws ClassCastException if a key or value is not a String. + */ + EnvironmentMap(Map m) + { + super(m); + } + + /** + * Blocks queries containing a null key or one which is not + * of type String. All other queries + * are forwarded to the superclass. + * + * @param key the key to look for in the map. + * @return true if the key exists in the map. + * @throws NullPointerException if the specified key is null. + */ + public boolean containsKey(Object key) + { + if (key == null) + throw new + NullPointerException("This map does not support null keys."); + if (!(key instanceof String)) + throw new + ClassCastException("This map only allows queries using Strings."); + return super.containsKey(key); + } + + /** + * Blocks queries using a null or non-String value. + * All other queries are forwarded to the superclass. + * + * @param value the value to look for in the map. + * @return true if the value exists in the map. + * @throws NullPointerException if the specified value is null. + */ + public boolean containsValue(Object value) + { + if (value == null) + throw new + NullPointerException("This map does not support null values."); + if (!(value instanceof String)) + throw new + ClassCastException("This map only allows queries using Strings."); + return super.containsValue(value); + } + + /** + * Returns a set view of the map entries, with the same + * provisions as for the underlying map. + * + * @return a set containing the map entries. + */ + public Set> entrySet() + { + if (entries == null) + entries = super.entrySet(); + return entries; + } + + /** + * Blocks queries containing a null or non-String key. + * All other queries are passed on to the superclass. + * + * @param key the key to retrieve the value for. + * @return the value associated with the given key. + * @throws NullPointerException if the specified key is null. + * @throws ClassCastException if the specified key is not a String. + */ + public String get(Object key) + { + if (key == null) + throw new + NullPointerException("This map does not support null keys."); + if (!(key instanceof String)) + throw new + ClassCastException("This map only allows queries using Strings."); + return super.get(key); + } + + /** + * Returns a set view of the keys, with the same + * provisions as for the underlying map. + * + * @return a set containing the keys. + */ + public Set keySet() + { + if (keys == null) + keys = new EnvironmentSet(super.keySet()); + return keys; + } + + /** + * Associates the given key to the given value. If the + * map already contains the key, its value is replaced. + * The map does not accept null keys or values, or keys + * and values not of type {@link String}. + * + * @param key the key to map. + * @param value the value to be mapped. + * @return the previous value of the key, or null if there was no mapping + * @throws NullPointerException if a key or value is null. + * @throws ClassCastException if a key or value is not a String. + */ + public String put(String key, String value) + { + if (key == null) + throw new NullPointerException("A new key is null."); + if (value == null) + throw new NullPointerException("A new value is null."); + if (!(key instanceof String)) + throw new ClassCastException("A new key is not a String."); + if (!(value instanceof String)) + throw new ClassCastException("A new value is not a String."); + return super.put(key, value); + } + + /** + * Removes a key-value pair from the map. The queried key may not + * be null or of a type other than a String. + * + * @param key the key of the entry to remove. + * @return the removed value. + * @throws NullPointerException if the specified key is null. + * @throws ClassCastException if the specified key is not a String. + */ + public String remove(Object key) + { + if (key == null) + throw new + NullPointerException("This map does not support null keys."); + if (!(key instanceof String)) + throw new + ClassCastException("This map only allows queries using Strings."); + return super.remove(key); + } + + /** + * Returns a collection view of the values, with the same + * provisions as for the underlying map. + * + * @return a collection containing the values. + */ + public Collection values() + { + if (values == null) + values = new EnvironmentCollection(super.values()); + return values; + } + + } + + /** + * This is a specialised Set, providing + * the necessary provisions for the collections used by the + * environment variable map. Namely, it prevents + * modifications and the use of queries with null + * or non-String values. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static class EnvironmentSet + extends EnvironmentCollection + implements Set + { + + /** + * Constructs a new environment set, which + * wraps the elements of the supplied set. + * + * @param set the set to use as a base for + * this set. + */ + public EnvironmentSet(Set set) + { + super(set); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @param obj the object to compare with. + * @return true if the two objects are equal. + */ + public boolean equals(Object obj) + { + return c.equals(obj); + } + + /** + * This simply calls the same method on the wrapped + * collection. + * + * @return the hashcode of the collection. + */ + public int hashCode() + { + return c.hashCode(); + } + + } // class EnvironmentSet + +} // class System diff --git a/libjava/classpath/java/lang/Thread.java b/libjava/classpath/java/lang/Thread.java new file mode 100644 index 000000000..bc2dbb5b7 --- /dev/null +++ b/libjava/classpath/java/lang/Thread.java @@ -0,0 +1,1379 @@ +/* Thread -- an independent thread of executable code + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.classpath.VMStackWalker; +import gnu.java.util.WeakIdentityHashMap; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + +import java.security.Permission; + +import java.util.HashMap; +import java.util.Map; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete to version 1.4, with caveats. We do not + * implement the deprecated (and dangerous) stop, suspend, and resume + * methods. Security implementation is not complete. + */ + +/** + * Thread represents a single thread of execution in the VM. When an + * application VM starts up, it creates a non-daemon Thread which calls the + * main() method of a particular class. There may be other Threads running, + * such as the garbage collection thread. + * + *

    Threads have names to identify them. These names are not necessarily + * unique. Every Thread has a priority, as well, which tells the VM which + * Threads should get more running time. New threads inherit the priority + * and daemon status of the parent thread, by default. + * + *

    There are two methods of creating a Thread: you may subclass Thread and + * implement the run() method, at which point you may start the + * Thread by calling its start() method, or you may implement + * Runnable in the class you want to use and then call new + * Thread(your_obj).start(). + * + *

    The virtual machine runs until all non-daemon threads have died (either + * by returning from the run() method as invoked by start(), or by throwing + * an uncaught exception); or until System.exit is called with + * adequate permissions. + * + *

    It is unclear at what point a Thread should be added to a ThreadGroup, + * and at what point it should be removed. Should it be inserted when it + * starts, or when it is created? Should it be removed when it is suspended + * or interrupted? The only thing that is clear is that the Thread should be + * removed when it is stopped. + * + * @author Tom Tromey + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Runnable + * @see Runtime#exit(int) + * @see #run() + * @see #start() + * @see ThreadLocal + * @since 1.0 + * @status updated to 1.4 + */ +public class Thread implements Runnable +{ + /** The minimum priority for a Thread. */ + public static final int MIN_PRIORITY = 1; + + /** The priority a Thread gets by default. */ + public static final int NORM_PRIORITY = 5; + + /** The maximum priority for a Thread. */ + public static final int MAX_PRIORITY = 10; + + /** The underlying VM thread, only set when the thread is actually running. + */ + volatile VMThread vmThread; + + /** + * The group this thread belongs to. This is set to null by + * ThreadGroup.removeThread when the thread dies. + */ + volatile ThreadGroup group; + + /** The object to run(), null if this is the target. */ + final Runnable runnable; + + /** The thread name, non-null. */ + volatile String name; + + /** Whether the thread is a daemon. */ + volatile boolean daemon; + + /** The thread priority, 1 to 10. */ + volatile int priority; + + /** Native thread stack size. 0 = use default */ + private long stacksize; + + /** Was the thread stopped before it was started? */ + Throwable stillborn; + + /** The context classloader for this Thread. */ + private ClassLoader contextClassLoader; + private boolean contextClassLoaderIsSystemClassLoader; + + /** This thread's ID. */ + private final long threadId; + + /** The park blocker. See LockSupport. */ + Object parkBlocker; + + /** The next thread number to use. */ + private static int numAnonymousThreadsCreated; + + /** Used to generate the next thread ID to use. */ + private static long totalThreadsCreated; + + /** The default exception handler. */ + private static UncaughtExceptionHandler defaultHandler; + + /** Thread local storage. Package accessible for use by + * InheritableThreadLocal. + */ + final ThreadLocalMap locals; + + /** The uncaught exception handler. */ + UncaughtExceptionHandler exceptionHandler; + + /** + * Allocates a new Thread object. This constructor has + * the same effect as Thread(null, null, + * gname), where gname is + * a newly generated name. Automatically generated names are of the + * form "Thread-"+n, where n is an integer. + *

    + * Threads created this way must have overridden their + * run() method to actually do anything. An example + * illustrating this method being used follows: + *

    +   *     import java.lang.*;
    +   *
    +   *     class plain01 implements Runnable {
    +   *         String name;
    +   *         plain01() {
    +   *             name = null;
    +   *         }
    +   *         plain01(String s) {
    +   *             name = s;
    +   *         }
    +   *         public void run() {
    +   *             if (name == null)
    +   *                 System.out.println("A new thread created");
    +   *             else
    +   *                 System.out.println("A new thread with name " + name +
    +   *                                    " created");
    +   *         }
    +   *     }
    +   *     class threadtest01 {
    +   *         public static void main(String args[] ) {
    +   *             int failed = 0 ;
    +   *
    +   *             Thread t1 = new Thread();
    +   *             if (t1 != null)
    +   *                 System.out.println("new Thread() succeed");
    +   *             else {
    +   *                 System.out.println("new Thread() failed");
    +   *                 failed++;
    +   *             }
    +   *         }
    +   *     }
    +   * 
    + * + * @see java.lang.Thread#Thread(java.lang.ThreadGroup, + * java.lang.Runnable, java.lang.String) + */ + public Thread() + { + this(null, (Runnable) null); + } + + /** + * Allocates a new Thread object. This constructor has + * the same effect as Thread(null, target, + * gname), where gname is + * a newly generated name. Automatically generated names are of the + * form "Thread-"+n, where n is an integer. + * + * @param target the object whose run method is called. + * @see java.lang.Thread#Thread(java.lang.ThreadGroup, + * java.lang.Runnable, java.lang.String) + */ + public Thread(Runnable target) + { + this(null, target); + } + + /** + * Allocates a new Thread object. This constructor has + * the same effect as Thread(null, null, name). + * + * @param name the name of the new thread. + * @see java.lang.Thread#Thread(java.lang.ThreadGroup, + * java.lang.Runnable, java.lang.String) + */ + public Thread(String name) + { + this(null, null, name, 0); + } + + /** + * Allocates a new Thread object. This constructor has + * the same effect as Thread(group, target, + * gname), where gname is + * a newly generated name. Automatically generated names are of the + * form "Thread-"+n, where n is an integer. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @throws SecurityException if this thread cannot access group + * @throws IllegalThreadStateException if group is destroyed + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(ThreadGroup group, Runnable target) + { + this(group, target, createAnonymousThreadName(), 0); + } + + /** + * Allocates a new Thread object. This constructor has + * the same effect as Thread(group, null, name) + * + * @param group the group to put the Thread into + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access group + * @throws IllegalThreadStateException if group is destroyed + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(ThreadGroup group, String name) + { + this(group, null, name, 0); + } + + /** + * Allocates a new Thread object. This constructor has + * the same effect as Thread(null, target, name). + * + * @param target the Runnable object to execute + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @see #Thread(ThreadGroup, Runnable, String) + */ + public Thread(Runnable target, String name) + { + this(null, target, name, 0); + } + + /** + * Allocate a new Thread object, with the specified ThreadGroup and name, and + * using the specified Runnable object's run() method to + * execute. If the Runnable object is null, this (which is + * a Runnable) is used instead. + * + *

    If the ThreadGroup is null, the security manager is checked. If a + * manager exists and returns a non-null object for + * getThreadGroup, that group is used; otherwise the group + * of the creating thread is used. Note that the security manager calls + * checkAccess if the ThreadGroup is not null. + * + *

    The new Thread will inherit its creator's priority and daemon status. + * These can be changed with setPriority and + * setDaemon. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @param name the name for the Thread + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access group + * @throws IllegalThreadStateException if group is destroyed + * @see Runnable#run() + * @see #run() + * @see #setDaemon(boolean) + * @see #setPriority(int) + * @see SecurityManager#checkAccess(ThreadGroup) + * @see ThreadGroup#checkAccess() + */ + public Thread(ThreadGroup group, Runnable target, String name) + { + this(group, target, name, 0); + } + + /** + * Allocate a new Thread object, as if by + * Thread(group, null, name), and give it the specified stack + * size, in bytes. The stack size is highly platform independent, + * and the virtual machine is free to round up or down, or ignore it + * completely. A higher value might let you go longer before a + * StackOverflowError, while a lower value might let you go + * longer before an OutOfMemoryError. Or, it may do absolutely + * nothing! So be careful, and expect to need to tune this value if your + * virtual machine even supports it. + * + * @param group the group to put the Thread into + * @param target the Runnable object to execute + * @param name the name for the Thread + * @param size the stack size, in bytes; 0 to be ignored + * @throws NullPointerException if name is null + * @throws SecurityException if this thread cannot access group + * @throws IllegalThreadStateException if group is destroyed + * @since 1.4 + */ + public Thread(ThreadGroup group, Runnable target, String name, long size) + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + Thread current = currentThread(); + if (group == null) + { + if (sm != null) + group = sm.getThreadGroup(); + if (group == null) + group = current.group; + } + if (sm != null) + sm.checkAccess(group); + + this.group = group; + // Use toString hack to detect null. + this.name = name.toString(); + this.runnable = target; + this.stacksize = size; + this.locals = new ThreadLocalMap(); + + synchronized (Thread.class) + { + this.threadId = ++totalThreadsCreated; + } + + priority = current.priority; + daemon = current.daemon; + contextClassLoader = current.contextClassLoader; + contextClassLoaderIsSystemClassLoader = + current.contextClassLoaderIsSystemClassLoader; + + group.addThread(this); + InheritableThreadLocal.newChildThread(this); + } + + /** + * Used by the VM to create thread objects for threads started outside + * of Java. Note: caller is responsible for adding the thread to + * a group and InheritableThreadLocal. + * Note: This constructor should not call any methods that could result + * in a call to Thread.currentThread(), because that makes life harder + * for the VM. + * + * @param vmThread the native thread + * @param name the thread name or null to use the default naming scheme + * @param priority current priority + * @param daemon is the thread a background thread? + */ + Thread(VMThread vmThread, String name, int priority, boolean daemon) + { + this.locals = new ThreadLocalMap(); + this.vmThread = vmThread; + this.runnable = null; + if (name == null) + name = createAnonymousThreadName(); + this.name = name; + this.priority = priority; + this.daemon = daemon; + // By default the context class loader is the system class loader, + // we set a flag to signal this because we don't want to call + // ClassLoader.getSystemClassLoader() at this point, because on + // VMs that lazily create the system class loader that might result + // in running user code (when a custom system class loader is specified) + // and that user code could call Thread.currentThread(). + // ClassLoader.getSystemClassLoader() can also return null, if the system + // is currently in the process of constructing the system class loader + // (and, as above, the constructiong sequence calls Thread.currenThread()). + contextClassLoaderIsSystemClassLoader = true; + synchronized (Thread.class) + { + this.threadId = ++totalThreadsCreated; + } + } + + /** + * Generate a name for an anonymous thread. + */ + private static synchronized String createAnonymousThreadName() + { + return "Thread-" + ++numAnonymousThreadsCreated; + } + + /** + * Get the number of active threads in the current Thread's ThreadGroup. + * This implementation calls + * currentThread().getThreadGroup().activeCount(). + * + * @return the number of active threads in the current ThreadGroup + * @see ThreadGroup#activeCount() + */ + public static int activeCount() + { + return currentThread().group.activeCount(); + } + + /** + * Check whether the current Thread is allowed to modify this Thread. This + * passes the check on to SecurityManager.checkAccess(this). + * + * @throws SecurityException if the current Thread cannot modify this Thread + * @see SecurityManager#checkAccess(Thread) + */ + public final void checkAccess() + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkAccess(this); + } + + /** + * Count the number of stack frames in this Thread. The Thread in question + * must be suspended when this occurs. + * + * @return the number of stack frames in this Thread + * @throws IllegalThreadStateException if this Thread is not suspended + * @deprecated pointless, since suspend is deprecated + */ + public int countStackFrames() + { + VMThread t = vmThread; + if (t == null || group == null) + throw new IllegalThreadStateException(); + + return t.countStackFrames(); + } + + /** + * Get the currently executing Thread. In the situation that the + * currently running thread was created by native code and doesn't + * have an associated Thread object yet, a new Thread object is + * constructed and associated with the native thread. + * + * @return the currently executing Thread + */ + public static Thread currentThread() + { + return VMThread.currentThread(); + } + + /** + * Originally intended to destroy this thread, this method was never + * implemented by Sun, and is hence a no-op. + * + * @deprecated This method was originally intended to simply destroy + * the thread without performing any form of cleanup operation. + * However, it was never implemented. It is now deprecated + * for the same reason as suspend(), + * stop() and resume(); namely, + * it is prone to deadlocks. If a thread is destroyed while + * it still maintains a lock on a resource, then this resource + * will remain locked and any attempts by other threads to + * access the resource will result in a deadlock. Thus, even + * an implemented version of this method would be still be + * deprecated, due to its unsafe nature. + * @throws NoSuchMethodError as this method was never implemented. + */ + public void destroy() + { + throw new NoSuchMethodError(); + } + + /** + * Print a stack trace of the current thread to stderr using the same + * format as Throwable's printStackTrace() method. + * + * @see Throwable#printStackTrace() + */ + public static void dumpStack() + { + new Throwable().printStackTrace(); + } + + /** + * Copy every active thread in the current Thread's ThreadGroup into the + * array. Extra threads are silently ignored. This implementation calls + * getThreadGroup().enumerate(array), which may have a + * security check, checkAccess(group). + * + * @param array the array to place the Threads into + * @return the number of Threads placed into the array + * @throws NullPointerException if array is null + * @throws SecurityException if you cannot access the ThreadGroup + * @see ThreadGroup#enumerate(Thread[]) + * @see #activeCount() + * @see SecurityManager#checkAccess(ThreadGroup) + */ + public static int enumerate(Thread[] array) + { + return currentThread().group.enumerate(array); + } + + /** + * Get this Thread's name. + * + * @return this Thread's name + */ + public final String getName() + { + VMThread t = vmThread; + return t == null ? name : t.getName(); + } + + /** + * Get this Thread's priority. + * + * @return the Thread's priority + */ + public final synchronized int getPriority() + { + VMThread t = vmThread; + return t == null ? priority : t.getPriority(); + } + + /** + * Get the ThreadGroup this Thread belongs to. If the thread has died, this + * returns null. + * + * @return this Thread's ThreadGroup + */ + public final ThreadGroup getThreadGroup() + { + return group; + } + + /** + * Checks whether the current thread holds the monitor on a given object. + * This allows you to do assert Thread.holdsLock(obj). + * + * @param obj the object to test lock ownership on. + * @return true if the current thread is currently synchronized on obj + * @throws NullPointerException if obj is null + * @since 1.4 + */ + public static boolean holdsLock(Object obj) + { + return VMThread.holdsLock(obj); + } + + /** + * Interrupt this Thread. First, there is a security check, + * checkAccess. Then, depending on the current state of the + * thread, various actions take place: + * + *

    If the thread is waiting because of {@link #wait()}, + * {@link #sleep(long)}, or {@link #join()}, its interrupt status + * will be cleared, and an InterruptedException will be thrown. Notice that + * this case is only possible if an external thread called interrupt(). + * + *

    If the thread is blocked in an interruptible I/O operation, in + * {@link java.nio.channels.InterruptibleChannel}, the interrupt + * status will be set, and ClosedByInterruptException will be thrown. + * + *

    If the thread is blocked on a {@link java.nio.channels.Selector}, the + * interrupt status will be set, and the selection will return, with + * a possible non-zero value, as though by the wakeup() method. + * + *

    Otherwise, the interrupt status will be set. + * + * @throws SecurityException if you cannot modify this Thread + */ + public synchronized void interrupt() + { + checkAccess(); + VMThread t = vmThread; + if (t != null) + t.interrupt(); + } + + /** + * Determine whether the current Thread has been interrupted, and clear + * the interrupted status in the process. + * + * @return whether the current Thread has been interrupted + * @see #isInterrupted() + */ + public static boolean interrupted() + { + return VMThread.interrupted(); + } + + /** + * Determine whether the given Thread has been interrupted, but leave + * the interrupted status alone in the process. + * + * @return whether the Thread has been interrupted + * @see #interrupted() + */ + public boolean isInterrupted() + { + VMThread t = vmThread; + return t != null && t.isInterrupted(); + } + + /** + * Determine whether this Thread is alive. A thread which is alive has + * started and not yet died. + * + * @return whether this Thread is alive + */ + public final boolean isAlive() + { + return vmThread != null && group != null; + } + + /** + * Tell whether this is a daemon Thread or not. + * + * @return whether this is a daemon Thread or not + * @see #setDaemon(boolean) + */ + public final boolean isDaemon() + { + VMThread t = vmThread; + return t == null ? daemon : t.isDaemon(); + } + + /** + * Wait forever for the Thread in question to die. + * + * @throws InterruptedException if the Thread is interrupted; it's + * interrupted status will be cleared + */ + public final void join() throws InterruptedException + { + join(0, 0); + } + + /** + * Wait the specified amount of time for the Thread in question to die. + * + * @param ms the number of milliseconds to wait, or 0 for forever + * @throws InterruptedException if the Thread is interrupted; it's + * interrupted status will be cleared + */ + public final void join(long ms) throws InterruptedException + { + join(ms, 0); + } + + /** + * Wait the specified amount of time for the Thread in question to die. + * + *

    Note that 1,000,000 nanoseconds == 1 millisecond, but most VMs do + * not offer that fine a grain of timing resolution. Besides, there is + * no guarantee that this thread can start up immediately when time expires, + * because some other thread may be active. So don't expect real-time + * performance. + * + * @param ms the number of milliseconds to wait, or 0 for forever + * @param ns the number of extra nanoseconds to sleep (0-999999) + * @throws InterruptedException if the Thread is interrupted; it's + * interrupted status will be cleared + * @throws IllegalArgumentException if ns is invalid + */ + public final void join(long ms, int ns) throws InterruptedException + { + if (ms < 0 || ns < 0 || ns > 999999) + throw new IllegalArgumentException(); + + VMThread t = vmThread; + if (t != null) + t.join(ms, ns); + } + + /** + * Resume this Thread. If the thread is not suspended, this method does + * nothing. To mirror suspend(), there may be a security check: + * checkAccess. + * + * @throws SecurityException if you cannot resume the Thread + * @see #checkAccess() + * @see #suspend() + * @deprecated pointless, since suspend is deprecated + */ + public final synchronized void resume() + { + checkAccess(); + VMThread t = vmThread; + if (t != null) + t.resume(); + } + + /** + * The method of Thread that will be run if there is no Runnable object + * associated with the Thread. Thread's implementation does nothing at all. + * + * @see #start() + * @see #Thread(ThreadGroup, Runnable, String) + */ + public void run() + { + if (runnable != null) + runnable.run(); + } + + /** + * Set the daemon status of this Thread. If this is a daemon Thread, then + * the VM may exit even if it is still running. This may only be called + * before the Thread starts running. There may be a security check, + * checkAccess. + * + * @param daemon whether this should be a daemon thread or not + * @throws SecurityException if you cannot modify this Thread + * @throws IllegalThreadStateException if the Thread is active + * @see #isDaemon() + * @see #checkAccess() + */ + public final synchronized void setDaemon(boolean daemon) + { + if (vmThread != null) + throw new IllegalThreadStateException(); + checkAccess(); + this.daemon = daemon; + } + + /** + * Returns the context classloader of this Thread. The context + * classloader can be used by code that want to load classes depending + * on the current thread. Normally classes are loaded depending on + * the classloader of the current class. There may be a security check + * for RuntimePermission("getClassLoader") if the caller's + * class loader is not null or an ancestor of this thread's context class + * loader. + * + * @return the context class loader + * @throws SecurityException when permission is denied + * @see #setContextClassLoader(ClassLoader) + * @since 1.2 + */ + public synchronized ClassLoader getContextClassLoader() + { + ClassLoader loader = contextClassLoaderIsSystemClassLoader ? + ClassLoader.getSystemClassLoader() : contextClassLoader; + // Check if we may get the classloader + SecurityManager sm = SecurityManager.current; + if (loader != null && sm != null) + { + // Get the calling classloader + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl != null && !cl.isAncestorOf(loader)) + sm.checkPermission(new RuntimePermission("getClassLoader")); + } + return loader; + } + + /** + * Sets the context classloader for this Thread. When not explicitly set, + * the context classloader for a thread is the same as the context + * classloader of the thread that created this thread. The first thread has + * as context classloader the system classloader. There may be a security + * check for RuntimePermission("setContextClassLoader"). + * + * @param classloader the new context class loader + * @throws SecurityException when permission is denied + * @see #getContextClassLoader() + * @since 1.2 + */ + public synchronized void setContextClassLoader(ClassLoader classloader) + { + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + this.contextClassLoader = classloader; + contextClassLoaderIsSystemClassLoader = false; + } + + /** + * Set this Thread's name. There may be a security check, + * checkAccess. + * + * @param name the new name for this Thread + * @throws NullPointerException if name is null + * @throws SecurityException if you cannot modify this Thread + */ + public final synchronized void setName(String name) + { + checkAccess(); + // The Class Libraries book says ``threadName cannot be null''. I + // take this to mean NullPointerException. + if (name == null) + throw new NullPointerException(); + VMThread t = vmThread; + if (t != null) + t.setName(name); + else + this.name = name; + } + + /** + * Yield to another thread. The Thread will not lose any locks it holds + * during this time. There are no guarantees which thread will be + * next to run, and it could even be this one, but most VMs will choose + * the highest priority thread that has been waiting longest. + */ + public static void yield() + { + VMThread.yield(); + } + + /** + * Suspend the current Thread's execution for the specified amount of + * time. The Thread will not lose any locks it has during this time. There + * are no guarantees which thread will be next to run, but most VMs will + * choose the highest priority thread that has been waiting longest. + * + * @param ms the number of milliseconds to sleep, or 0 for forever + * @throws InterruptedException if the Thread is (or was) interrupted; + * it's interrupted status will be cleared + * @throws IllegalArgumentException if ms is negative + * @see #interrupt() + * @see #notify() + * @see #wait(long) + */ + public static void sleep(long ms) throws InterruptedException + { + sleep(ms, 0); + } + + /** + * Suspend the current Thread's execution for the specified amount of + * time. The Thread will not lose any locks it has during this time. There + * are no guarantees which thread will be next to run, but most VMs will + * choose the highest priority thread that has been waiting longest. + *

    + * Note that 1,000,000 nanoseconds == 1 millisecond, but most VMs + * do not offer that fine a grain of timing resolution. When ms is + * zero and ns is non-zero the Thread will sleep for at least one + * milli second. There is no guarantee that this thread can start up + * immediately when time expires, because some other thread may be + * active. So don't expect real-time performance. + * + * @param ms the number of milliseconds to sleep, or 0 for forever + * @param ns the number of extra nanoseconds to sleep (0-999999) + * @throws InterruptedException if the Thread is (or was) interrupted; + * it's interrupted status will be cleared + * @throws IllegalArgumentException if ms or ns is negative + * or ns is larger than 999999. + * @see #interrupt() + * @see #notify() + * @see #wait(long, int) + */ + public static void sleep(long ms, int ns) throws InterruptedException + { + // Check parameters + if (ms < 0 ) + throw new IllegalArgumentException("Negative milliseconds: " + ms); + + if (ns < 0 || ns > 999999) + throw new IllegalArgumentException("Nanoseconds ouf of range: " + ns); + + // Really sleep + VMThread.sleep(ms, ns); + } + + /** + * Start this Thread, calling the run() method of the Runnable this Thread + * was created with, or else the run() method of the Thread itself. This + * is the only way to start a new thread; calling run by yourself will just + * stay in the same thread. The virtual machine will remove the thread from + * its thread group when the run() method completes. + * + * @throws IllegalThreadStateException if the thread has already started + * @see #run() + */ + public synchronized void start() + { + if (vmThread != null || group == null) + throw new IllegalThreadStateException(); + + VMThread.create(this, stacksize); + } + + /** + * Cause this Thread to stop abnormally because of the throw of a ThreadDeath + * error. If you stop a Thread that has not yet started, it will stop + * immediately when it is actually started. + * + *

    This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * checkAccess(this), plus another one if the current thread + * is not this: RuntimePermission("stopThread"). If you must + * catch a ThreadDeath, be sure to rethrow it after you have cleaned up. + * ThreadDeath is the only exception which does not print a stack trace when + * the thread dies. + * + * @throws SecurityException if you cannot stop the Thread + * @see #interrupt() + * @see #checkAccess() + * @see #start() + * @see ThreadDeath + * @see ThreadGroup#uncaughtException(Thread, Throwable) + * @see SecurityManager#checkAccess(Thread) + * @see SecurityManager#checkPermission(Permission) + * @deprecated unsafe operation, try not to use + */ + public final void stop() + { + stop(new ThreadDeath()); + } + + /** + * Cause this Thread to stop abnormally and throw the specified exception. + * If you stop a Thread that has not yet started, the stop is ignored + * (contrary to what the JDK documentation says). + * WARNINGThis bypasses Java security, and can throw a checked + * exception which the call stack is unprepared to handle. Do not abuse + * this power. + * + *

    This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * checkAccess(this), plus another one if the current thread + * is not this: RuntimePermission("stopThread"). If you must + * catch a ThreadDeath, be sure to rethrow it after you have cleaned up. + * ThreadDeath is the only exception which does not print a stack trace when + * the thread dies. + * + * @param t the Throwable to throw when the Thread dies + * @throws SecurityException if you cannot stop the Thread + * @throws NullPointerException in the calling thread, if t is null + * @see #interrupt() + * @see #checkAccess() + * @see #start() + * @see ThreadDeath + * @see ThreadGroup#uncaughtException(Thread, Throwable) + * @see SecurityManager#checkAccess(Thread) + * @see SecurityManager#checkPermission(Permission) + * @deprecated unsafe operation, try not to use + */ + public final synchronized void stop(Throwable t) + { + if (t == null) + throw new NullPointerException(); + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + if (sm != null) + { + sm.checkAccess(this); + if (this != currentThread() || !(t instanceof ThreadDeath)) + sm.checkPermission(new RuntimePermission("stopThread")); + } + VMThread vt = vmThread; + if (vt != null) + vt.stop(t); + else + stillborn = t; + } + + /** + * Suspend this Thread. It will not come back, ever, unless it is resumed. + * + *

    This is inherently unsafe, as the suspended thread still holds locks, + * and can potentially deadlock your program. Hence, there is a security + * check: checkAccess. + * + * @throws SecurityException if you cannot suspend the Thread + * @see #checkAccess() + * @see #resume() + * @deprecated unsafe operation, try not to use + */ + public final synchronized void suspend() + { + checkAccess(); + VMThread t = vmThread; + if (t != null) + t.suspend(); + } + + /** + * Set this Thread's priority. There may be a security check, + * checkAccess, then the priority is set to the smaller of + * priority and the ThreadGroup maximum priority. + * + * @param priority the new priority for this Thread + * @throws IllegalArgumentException if priority exceeds MIN_PRIORITY or + * MAX_PRIORITY + * @throws SecurityException if you cannot modify this Thread + * @see #getPriority() + * @see #checkAccess() + * @see ThreadGroup#getMaxPriority() + * @see #MIN_PRIORITY + * @see #MAX_PRIORITY + */ + public final synchronized void setPriority(int priority) + { + checkAccess(); + if (priority < MIN_PRIORITY || priority > MAX_PRIORITY) + throw new IllegalArgumentException("Invalid thread priority value " + + priority + "."); + priority = Math.min(priority, group.getMaxPriority()); + VMThread t = vmThread; + if (t != null) + t.setPriority(priority); + else + this.priority = priority; + } + + /** + * Returns a string representation of this thread, including the + * thread's name, priority, and thread group. + * + * @return a human-readable String representing this Thread + */ + public String toString() + { + return ("Thread[" + name + "," + priority + "," + + (group == null ? "" : group.getName()) + "]"); + } + + /** + * Clean up code, called by VMThread when thread dies. + */ + synchronized void die() + { + group.removeThread(this); + vmThread = null; + locals.clear(); + } + + /** + * Returns the map used by ThreadLocal to store the thread local values. + */ + static ThreadLocalMap getThreadLocals() + { + return currentThread().locals; + } + + /** + * Assigns the given UncaughtExceptionHandler to this + * thread. This will then be called if the thread terminates due + * to an uncaught exception, pre-empting that of the + * ThreadGroup. + * + * @param h the handler to use for this thread. + * @throws SecurityException if the current thread can't modify this thread. + * @since 1.5 + */ + public void setUncaughtExceptionHandler(UncaughtExceptionHandler h) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkAccess(this); + exceptionHandler = h; + } + + /** + *

    + * Returns the handler used when this thread terminates due to an + * uncaught exception. The handler used is determined by the following: + *

    + *
      + *
    • If this thread has its own handler, this is returned.
    • + *
    • If not, then the handler of the thread's ThreadGroup + * object is returned.
    • + *
    • If both are unavailable, then null is returned + * (which can only happen when the thread was terminated since + * then it won't have an associated thread group anymore).
    • + *
    + * + * @return the appropriate UncaughtExceptionHandler or + * null if one can't be obtained. + * @since 1.5 + */ + public UncaughtExceptionHandler getUncaughtExceptionHandler() + { + return exceptionHandler != null ? exceptionHandler : group; + } + + /** + *

    + * Sets the default uncaught exception handler used when one isn't + * provided by the thread or its associated ThreadGroup. + * This exception handler is used when the thread itself does not + * have an exception handler, and the thread's ThreadGroup + * does not override this default mechanism with its own. As the group + * calls this handler by default, this exception handler should not defer + * to that of the group, as it may lead to infinite recursion. + *

    + *

    + * Uncaught exception handlers are used when a thread terminates due to + * an uncaught exception. Replacing this handler allows default code to + * be put in place for all threads in order to handle this eventuality. + *

    + * + * @param h the new default uncaught exception handler to use. + * @throws SecurityException if a security manager is present and + * disallows the runtime permission + * "setDefaultUncaughtExceptionHandler". + * @since 1.5 + */ + public static void + setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler h) + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("setDefaultUncaughtExceptionHandler")); + defaultHandler = h; + } + + /** + * Returns the handler used by default when a thread terminates + * unexpectedly due to an exception, or null if one doesn't + * exist. + * + * @return the default uncaught exception handler. + * @since 1.5 + */ + public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() + { + return defaultHandler; + } + + /** + * Returns the unique identifier for this thread. This ID is generated + * on thread creation, and may be re-used on its death. + * + * @return a positive long number representing the thread's ID. + * @since 1.5 + */ + public long getId() + { + return threadId; + } + + /** + *

    + * This interface is used to handle uncaught exceptions + * which cause a Thread to terminate. When + * a thread, t, is about to terminate due to an uncaught + * exception, the virtual machine looks for a class which + * implements this interface, in order to supply it with + * the dying thread and its uncaught exception. + *

    + *

    + * The virtual machine makes two attempts to find an + * appropriate handler for the uncaught exception, in + * the following order: + *

    + *
      + *
    1. + * t.getUncaughtExceptionHandler() -- + * the dying thread is queried first for a handler + * specific to that thread. + *
    2. + *
    3. + * t.getThreadGroup() -- + * the thread group of the dying thread is used to + * handle the exception. If the thread group has + * no special requirements for handling the exception, + * it may simply forward it on to + * Thread.getDefaultUncaughtExceptionHandler(), + * the default handler, which is used as a last resort. + *
    4. + *
    + *

    + * The first handler found is the one used to handle + * the uncaught exception. + *

    + * + * @author Tom Tromey + * @author Andrew John Hughes + * @since 1.5 + * @see Thread#getUncaughtExceptionHandler() + * @see Thread#setUncaughtExceptionHandler(UncaughtExceptionHandler) + * @see Thread#getDefaultUncaughtExceptionHandler() + * @see + * Thread#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler) + */ + public interface UncaughtExceptionHandler + { + /** + * Invoked by the virtual machine with the dying thread + * and the uncaught exception. Any exceptions thrown + * by this method are simply ignored by the virtual + * machine. + * + * @param thr the dying thread. + * @param exc the uncaught exception. + */ + void uncaughtException(Thread thr, Throwable exc); + } + + /** + *

    + * Represents the current state of a thread, according to the VM rather + * than the operating system. It can be one of the following: + *

    + *
      + *
    • NEW -- The thread has just been created but is not yet running.
    • + *
    • RUNNABLE -- The thread is currently running or can be scheduled + * to run.
    • + *
    • BLOCKED -- The thread is blocked waiting on an I/O operation + * or to obtain a lock.
    • + *
    • WAITING -- The thread is waiting indefinitely for another thread + * to do something.
    • + *
    • TIMED_WAITING -- The thread is waiting for a specific amount of time + * for another thread to do something.
    • + *
    • TERMINATED -- The thread has exited.
    • + *
    + * + * @since 1.5 + */ + public enum State + { + BLOCKED, NEW, RUNNABLE, TERMINATED, TIMED_WAITING, WAITING; + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 605505746047245783L; + } + + + /** + * Returns the current state of the thread. This + * is designed for monitoring thread behaviour, rather + * than for synchronization control. + * + * @return the current thread state. + */ + public State getState() + { + VMThread t = vmThread; + if (t != null) + return State.valueOf(t.getState()); + if (group == null) + return State.TERMINATED; + return State.NEW; + } + + /** + *

    + * Returns a map of threads to stack traces for each + * live thread. The keys of the map are {@link Thread} + * objects, which map to arrays of {@link StackTraceElement}s. + * The results obtained from Calling this method are + * equivalent to calling {@link getStackTrace()} on each + * thread in succession. Threads may be executing while + * this takes place, and the results represent a snapshot + * of the thread at the time its {@link getStackTrace()} + * method is called. + *

    + *

    + * The stack trace information contains the methods called + * by the thread, with the most recent method forming the + * first element in the array. The array will be empty + * if the virtual machine can not obtain information on the + * thread. + *

    + *

    + * To execute this method, the current security manager + * (if one exists) must allow both the + * "getStackTrace" and + * "modifyThreadGroup" {@link RuntimePermission}s. + *

    + * + * @return a map of threads to arrays of {@link StackTraceElement}s. + * @throws SecurityException if a security manager exists, and + * prevents either or both the runtime + * permissions specified above. + * @since 1.5 + * @see #getStackTrace() + */ + public static Map getAllStackTraces() + { + ThreadGroup group = currentThread().group; + while (group.getParent() != null) + group = group.getParent(); + int arraySize = group.activeCount(); + Thread[] threadList = new Thread[arraySize]; + int filled = group.enumerate(threadList); + while (filled == arraySize) + { + arraySize *= 2; + threadList = new Thread[arraySize]; + filled = group.enumerate(threadList); + } + Map traces = new HashMap(); + for (int a = 0; a < filled; ++a) + traces.put(threadList[a], + threadList[a].getStackTrace()); + return traces; + } + + /** + *

    + * Returns an array of {@link StackTraceElement}s + * representing the current stack trace of this thread. + * The first element of the array is the most recent + * method called, and represents the top of the stack. + * The elements continue in this order, with the last + * element representing the bottom of the stack. + *

    + *

    + * A zero element array is returned for threads which + * have not yet started (and thus have not yet executed + * any methods) or for those which have terminated. + * Where the virtual machine can not obtain a trace for + * the thread, an empty array is also returned. The + * virtual machine may also omit some methods from the + * trace in non-zero arrays. + *

    + *

    + * To execute this method, the current security manager + * (if one exists) must allow both the + * "getStackTrace" and + * "modifyThreadGroup" {@link RuntimePermission}s. + *

    + * + * @return a stack trace for this thread. + * @throws SecurityException if a security manager exists, and + * prevents the use of the + * "getStackTrace" + * permission. + * @since 1.5 + * @see #getAllStackTraces() + */ + public StackTraceElement[] getStackTrace() + { + SecurityManager sm = SecurityManager.current; // Be thread-safe. + if (sm != null) + sm.checkPermission(new RuntimePermission("getStackTrace")); + ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + ThreadInfo info = bean.getThreadInfo(threadId, Integer.MAX_VALUE); + return info.getStackTrace(); + } + +} diff --git a/libjava/classpath/java/lang/ThreadDeath.java b/libjava/classpath/java/lang/ThreadDeath.java new file mode 100644 index 000000000..c7d88fb2a --- /dev/null +++ b/libjava/classpath/java/lang/ThreadDeath.java @@ -0,0 +1,68 @@ +/* ThreadDeath.java - special exception registering Thread death + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * ThreadDeath is thrown in a thread when someone calls stop() + * on that thread. Important: Make sure you rethrow this exception + * if you catch it. If you don't, the thread will not die. + * + *

    This is an Error rather than an exception, so that normal code will + * not catch it. It is intended for asynchronous cleanup when using the + * deprecated Thread.stop() method. + * + * @author John Keiser + * @author Tom Tromey (tromey@cygnus.com) + * @see Thread#stop() + * @status updated to 1.4 + */ +public class ThreadDeath extends Error +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4417128565033088268L; + + /** + * Create an error without a message. + */ + public ThreadDeath() + { + } +} diff --git a/libjava/classpath/java/lang/ThreadGroup.java b/libjava/classpath/java/lang/ThreadGroup.java new file mode 100644 index 000000000..00f2f8ec4 --- /dev/null +++ b/libjava/classpath/java/lang/ThreadGroup.java @@ -0,0 +1,791 @@ +/* ThreadGroup -- a group of Threads + Copyright (C) 1998, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Vector; + +/** + * ThreadGroup allows you to group Threads together. There is a hierarchy + * of ThreadGroups, and only the initial ThreadGroup has no parent. A Thread + * may access information about its own ThreadGroup, but not its parents or + * others outside the tree. + * + * @author John Keiser + * @author Tom Tromey + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Thread + * @since 1.0 + * @status updated to 1.4 + */ +public class ThreadGroup implements UncaughtExceptionHandler +{ + /** The Initial, top-level ThreadGroup. */ + static ThreadGroup root = new ThreadGroup(); + + /** + * This flag is set if an uncaught exception occurs. The runtime should + * check this and exit with an error status if it is set. + */ + static boolean had_uncaught_exception; + + /** The parent thread group. */ + final ThreadGroup parent; + + /** The group name, non-null. */ + final String name; + + /** The threads in the group. */ + private final Vector threads = new Vector(); + + /** Child thread groups, or null when this group is destroyed. */ + private Vector groups = new Vector(); + + /** If all threads in the group are daemons. */ + private boolean daemon_flag = false; + + /** The maximum group priority. */ + private int maxpri; + + /** + * Hidden constructor to build the root node. + */ + private ThreadGroup() + { + name = "main"; + parent = null; + maxpri = Thread.MAX_PRIORITY; + } + + /** + * Create a new ThreadGroup using the given name and the current thread's + * ThreadGroup as a parent. There may be a security check, + * checkAccess. + * + * @param name the name to use for the ThreadGroup + * @throws SecurityException if the current thread cannot create a group + * @see #checkAccess() + */ + public ThreadGroup(String name) + { + this(Thread.currentThread().group, name); + } + + /** + * Create a new ThreadGroup using the given name and parent group. The new + * group inherits the maximum priority and daemon status of its parent + * group. There may be a security check, checkAccess. + * + * @param name the name to use for the ThreadGroup + * @param parent the ThreadGroup to use as a parent + * @throws NullPointerException if parent is null + * @throws SecurityException if the current thread cannot create a group + * @throws IllegalThreadStateException if the parent is destroyed + * @see #checkAccess() + */ + public ThreadGroup(ThreadGroup parent, String name) + { + parent.checkAccess(); + this.parent = parent; + this.name = name; + maxpri = parent.maxpri; + daemon_flag = parent.daemon_flag; + synchronized (parent) + { + if (parent.groups == null) + throw new IllegalThreadStateException(); + parent.groups.add(this); + } + } + + /** + * Get the name of this ThreadGroup. + * + * @return the name of this ThreadGroup + */ + public final String getName() + { + return name; + } + + /** + * Get the parent of this ThreadGroup. If the parent is not null, there + * may be a security check, checkAccess. + * + * @return the parent of this ThreadGroup + * @throws SecurityException if permission is denied + */ + public final ThreadGroup getParent() + { + if (parent != null) + parent.checkAccess(); + return parent; + } + + /** + * Get the maximum priority of Threads in this ThreadGroup. Threads created + * after this call in this group may not exceed this priority. + * + * @return the maximum priority of Threads in this ThreadGroup + */ + public final int getMaxPriority() + { + return maxpri; + } + + /** + * Tell whether this ThreadGroup is a daemon group. A daemon group will + * be automatically destroyed when its last thread is stopped and + * its last thread group is destroyed. + * + * @return whether this ThreadGroup is a daemon group + */ + public final boolean isDaemon() + { + return daemon_flag; + } + + /** + * Tell whether this ThreadGroup has been destroyed or not. + * + * @return whether this ThreadGroup has been destroyed or not + * @since 1.1 + */ + public synchronized boolean isDestroyed() + { + return groups == null; + } + + /** + * Set whether this ThreadGroup is a daemon group. A daemon group will be + * destroyed when its last thread is stopped and its last thread group is + * destroyed. There may be a security check, checkAccess. + * + * @param daemon whether this ThreadGroup should be a daemon group + * @throws SecurityException if you cannot modify this ThreadGroup + * @see #checkAccess() + */ + public final void setDaemon(boolean daemon) + { + checkAccess(); + daemon_flag = daemon; + } + + /** + * Set the maximum priority for Threads in this ThreadGroup. setMaxPriority + * can only be used to reduce the current maximum. If maxpri is greater + * than the current Maximum of the parent group, the current value is not + * changed. Otherwise, all groups which belong to this have their priority + * adjusted as well. Calling this does not affect threads already in this + * ThreadGroup. There may be a security check, checkAccess. + * + * @param maxpri the new maximum priority for this ThreadGroup + * @throws SecurityException if you cannot modify this ThreadGroup + * @see #getMaxPriority() + * @see #checkAccess() + */ + public final synchronized void setMaxPriority(int maxpri) + { + checkAccess(); + if (maxpri < Thread.MIN_PRIORITY || maxpri > Thread.MAX_PRIORITY) + return; + if (parent != null && maxpri > parent.maxpri) + maxpri = parent.maxpri; + this.maxpri = maxpri; + if (groups == null) + return; + int i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).setMaxPriority(maxpri); + } + + /** + * Check whether this ThreadGroup is an ancestor of the specified + * ThreadGroup, or if they are the same. + * + * @param group the group to test on + * @return whether this ThreadGroup is a parent of the specified group + */ + public final boolean parentOf(ThreadGroup group) + { + while (group != null) + { + if (group == this) + return true; + group = group.parent; + } + return false; + } + + /** + * Find out if the current Thread can modify this ThreadGroup. This passes + * the check on to SecurityManager.checkAccess(this). + * + * @throws SecurityException if the current Thread cannot modify this + * ThreadGroup + * @see SecurityManager#checkAccess(ThreadGroup) + */ + public final void checkAccess() + { + // Bypass System.getSecurityManager, for bootstrap efficiency. + SecurityManager sm = SecurityManager.current; + if (sm != null) + sm.checkAccess(this); + } + + /** + * Return an estimate of the total number of active threads in this + * ThreadGroup and all its descendants. This cannot return an exact number, + * since the status of threads may change after they were counted; but it + * should be pretty close. Based on a JDC bug, + * + * 4089701, we take active to mean isAlive(). + * + * @return count of active threads in this ThreadGroup and its descendants + */ + public int activeCount() + { + int total = 0; + if (groups == null) + return total; + int i = threads.size(); + while (--i >= 0) + if (((Thread) threads.get(i)).isAlive()) + total++; + i = groups.size(); + while (--i >= 0) + total += ((ThreadGroup) groups.get(i)).activeCount(); + return total; + } + + /** + * Copy all of the active Threads from this ThreadGroup and its descendants + * into the specified array. If the array is not big enough to hold all + * the Threads, extra Threads will simply not be copied. There may be a + * security check, checkAccess. + * + * @param array the array to put the threads into + * @return the number of threads put into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if array is null + * @throws ArrayStoreException if a thread does not fit in the array + * @see #activeCount() + * @see #checkAccess() + * @see #enumerate(Thread[], boolean) + */ + public int enumerate(Thread[] array) + { + return enumerate(array, 0, true); + } + + /** + * Copy all of the active Threads from this ThreadGroup and, if desired, + * from its descendants, into the specified array. If the array is not big + * enough to hold all the Threads, extra Threads will simply not be copied. + * There may be a security check, checkAccess. + * + * @param array the array to put the threads into + * @param recurse whether to recurse into descendent ThreadGroups + * @return the number of threads put into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if array is null + * @throws ArrayStoreException if a thread does not fit in the array + * @see #activeCount() + * @see #checkAccess() + */ + public int enumerate(Thread[] array, boolean recurse) + { + return enumerate(array, 0, recurse); + } + + /** + * Get the number of active groups in this ThreadGroup. This group itself + * is not included in the count. A sub-group is active if it has not been + * destroyed. This cannot return an exact number, since the status of + * threads may change after they were counted; but it should be pretty close. + * + * @return the number of active groups in this ThreadGroup + */ + public int activeGroupCount() + { + if (groups == null) + return 0; + int total = groups.size(); + int i = total; + while (--i >= 0) + total += ((ThreadGroup) groups.get(i)).activeGroupCount(); + return total; + } + + /** + * Copy all active ThreadGroups that are descendants of this ThreadGroup + * into the specified array. If the array is not large enough to hold all + * active ThreadGroups, extra ThreadGroups simply will not be copied. There + * may be a security check, checkAccess. + * + * @param array the array to put the ThreadGroups into + * @return the number of ThreadGroups copied into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if array is null + * @throws ArrayStoreException if a group does not fit in the array + * @see #activeCount() + * @see #checkAccess() + * @see #enumerate(ThreadGroup[], boolean) + */ + public int enumerate(ThreadGroup[] array) + { + return enumerate(array, 0, true); + } + + /** + * Copy all active ThreadGroups that are children of this ThreadGroup into + * the specified array, and if desired, also all descendents. If the array + * is not large enough to hold all active ThreadGroups, extra ThreadGroups + * simply will not be copied. There may be a security check, + * checkAccess. + * + * @param array the array to put the ThreadGroups into + * @param recurse whether to recurse into descendent ThreadGroups + * @return the number of ThreadGroups copied into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if array is null + * @throws ArrayStoreException if a group does not fit in the array + * @see #activeCount() + * @see #checkAccess() + */ + public int enumerate(ThreadGroup[] array, boolean recurse) + { + return enumerate(array, 0, recurse); + } + + /** + * Stop all Threads in this ThreadGroup and its descendants. + * + *

    This is inherently unsafe, as it can interrupt synchronized blocks and + * leave data in bad states. Hence, there is a security check: + * checkAccess(), followed by further checks on each thread + * being stopped. + * + * @throws SecurityException if permission is denied + * @see #checkAccess() + * @see Thread#stop(Throwable) + * @deprecated unsafe operation, try not to use + */ + public final synchronized void stop() + { + checkAccess(); + if (groups == null) + return; + int i = threads.size(); + while (--i >= 0) + ((Thread) threads.get(i)).stop(); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).stop(); + } + + /** + * Interrupt all Threads in this ThreadGroup and its sub-groups. There may + * be a security check, checkAccess. + * + * @throws SecurityException if permission is denied + * @see #checkAccess() + * @see Thread#interrupt() + * @since 1.2 + */ + public final synchronized void interrupt() + { + checkAccess(); + if (groups == null) + return; + int i = threads.size(); + while (--i >= 0) + ((Thread) threads.get(i)).interrupt(); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).interrupt(); + } + + /** + * Suspend all Threads in this ThreadGroup and its descendants. + * + *

    This is inherently unsafe, as suspended threads still hold locks, + * which can lead to deadlock. Hence, there is a security check: + * checkAccess(), followed by further checks on each thread + * being suspended. + * + * @throws SecurityException if permission is denied + * @see #checkAccess() + * @see Thread#suspend() + * @deprecated unsafe operation, try not to use + */ + public final synchronized void suspend() + { + checkAccess(); + if (groups == null) + return; + int i = threads.size(); + while (--i >= 0) + ((Thread) threads.get(i)).suspend(); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).suspend(); + } + + /** + * Resume all suspended Threads in this ThreadGroup and its descendants. + * To mirror suspend(), there is a security check: + * checkAccess(), followed by further checks on each thread + * being resumed. + * + * @throws SecurityException if permission is denied + * @see #checkAccess() + * @see Thread#suspend() + * @deprecated pointless, since suspend is deprecated + */ + public final synchronized void resume() + { + checkAccess(); + if (groups == null) + return; + int i = threads.size(); + while (--i >= 0) + ((Thread) threads.get(i)).resume(); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).resume(); + } + + /** + * Destroy this ThreadGroup. The group must be empty, meaning that all + * threads and sub-groups have completed execution. Daemon groups are + * destroyed automatically. There may be a security check, + * checkAccess. + * + * @throws IllegalThreadStateException if the ThreadGroup is not empty, or + * was previously destroyed + * @throws SecurityException if permission is denied + * @see #checkAccess() + */ + public final synchronized void destroy() + { + checkAccess(); + if (! threads.isEmpty() || groups == null) + throw new IllegalThreadStateException(); + int i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).destroy(); + groups = null; + if (parent != null) + parent.removeGroup(this); + } + + /** + * Print out information about this ThreadGroup to System.out. This is + * meant for debugging purposes. WARNING: This method is not secure, + * and can print the name of threads to standard out even when you cannot + * otherwise get at such threads. + */ + public void list() + { + list(""); + } + + /** + * When a Thread in this ThreadGroup does not catch an exception, the + * virtual machine calls this method. The default implementation simply + * passes the call to the parent; then in top ThreadGroup, it will + * ignore ThreadDeath and print the stack trace of any other throwable. + * Override this method if you want to handle the exception in a different + * manner. + * + * @param thread the thread that exited + * @param t the uncaught throwable + * @throws NullPointerException if t is null + * @see ThreadDeath + * @see System#err + * @see Throwable#printStackTrace() + */ + public void uncaughtException(Thread thread, Throwable t) + { + if (parent != null) + parent.uncaughtException(thread, t); + else if (Thread.getDefaultUncaughtExceptionHandler() != null) + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(thread, t); + else if (! (t instanceof ThreadDeath)) + { + if (t == null) + throw new NullPointerException(); + had_uncaught_exception = true; + try + { + if (thread != null) + System.err.print("Exception in thread \"" + thread.name + "\" "); + t.printStackTrace(System.err); + } + catch (Throwable x) + { + // This means that something is badly screwed up with the runtime, + // or perhaps someone overloaded the Throwable.printStackTrace to + // die. In any case, try to deal with it gracefully. + try + { + System.err.println(t); + System.err.println("*** Got " + x + + " while trying to print stack trace."); + } + catch (Throwable x2) + { + // Here, someone may have overloaded t.toString() or + // x.toString() to die. Give up all hope; we can't even chain + // the exception, because the chain would likewise die. + System.err.println("*** Catastrophic failure while handling " + + "uncaught exception."); + throw new InternalError(); + } + } + } + } + + /** + * Originally intended to tell the VM whether it may suspend Threads in + * low memory situations, this method was never implemented by Sun, and + * is hence a no-op. + * + * @param allow whether to allow low-memory thread suspension; ignored + * @return false + * @since 1.1 + * @deprecated pointless, since suspend is deprecated + */ + public boolean allowThreadSuspension(boolean allow) + { + return false; + } + + /** + * Return a human-readable String representing this ThreadGroup. The format + * of the string is:
    + * getClass().getName() + "[name=" + getName() + ",maxpri=" + * + getMaxPriority() + ']'. + * + * @return a human-readable String representing this ThreadGroup + */ + public String toString() + { + return getClass().getName() + "[name=" + name + ",maxpri=" + maxpri + ']'; + } + + /** + * Implements enumerate. + * + * @param list the array to put the threads into + * @param next the next open slot in the array + * @param recurse whether to recurse into descendent ThreadGroups + * @return the number of threads put into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if list is null + * @throws ArrayStoreException if a thread does not fit in the array + * @see #enumerate(Thread[]) + * @see #enumerate(Thread[], boolean) + */ + private int enumerate(Thread[] list, int next, boolean recurse) + { + checkAccess(); + if (groups == null) + return next; + int i = threads.size(); + while (--i >= 0 && next < list.length) + { + Thread t = (Thread) threads.get(i); + if (t.isAlive()) + list[next++] = t; + } + if (recurse) + { + i = groups.size(); + while (--i >= 0 && next < list.length) + { + ThreadGroup g = (ThreadGroup) groups.get(i); + next = g.enumerate(list, next, true); + } + } + return next; + } + + /** + * Implements enumerate. + * + * @param list the array to put the groups into + * @param next the next open slot in the array + * @param recurse whether to recurse into descendent ThreadGroups + * @return the number of groups put into the array + * @throws SecurityException if permission was denied + * @throws NullPointerException if list is null + * @throws ArrayStoreException if a group does not fit in the array + * @see #enumerate(ThreadGroup[]) + * @see #enumerate(ThreadGroup[], boolean) + */ + private int enumerate(ThreadGroup[] list, int next, boolean recurse) + { + checkAccess(); + if (groups == null) + return next; + int i = groups.size(); + while (--i >= 0 && next < list.length) + { + ThreadGroup g = (ThreadGroup) groups.get(i); + list[next++] = g; + if (recurse && next != list.length) + next = g.enumerate(list, next, true); + } + return next; + } + + /** + * Implements list. + * + * @param indentation the current level of indentation + * @see #list() + */ + private void list(String indentation) + { + if (groups == null) + return; + System.out.println(indentation + this); + indentation += " "; + int i = threads.size(); + while (--i >= 0) + System.out.println(indentation + threads.get(i)); + i = groups.size(); + while (--i >= 0) + ((ThreadGroup) groups.get(i)).list(indentation); + } + + /** + * Add a thread to the group. Called by Thread constructors. + * + * @param t the thread to add, non-null + * @throws IllegalThreadStateException if the group is destroyed + */ + final synchronized void addThread(Thread t) + { + if (groups == null) + throw new IllegalThreadStateException("ThreadGroup is destroyed"); + threads.add(t); + } + + /** + * Called by the VM to remove a thread that has died. + * + * @param t the thread to remove, non-null + * @XXX A ThreadListener to call this might be nice. + */ + final synchronized void removeThread(Thread t) + { + if (groups == null) + return; + threads.remove(t); + t.group = null; + // Daemon groups are automatically destroyed when all their threads die. + if (daemon_flag && groups.size() == 0 && threads.size() == 0) + { + // We inline destroy to avoid the access check. + groups = null; + if (parent != null) + parent.removeGroup(this); + } + } + + /** + * Called when a group is destroyed, to remove it from its parent. + * + * @param g the destroyed group, non-null + */ + final synchronized void removeGroup(ThreadGroup g) + { + groups.remove(g); + // Daemon groups are automatically destroyed when all their threads die. + if (daemon_flag && groups.size() == 0 && threads.size() == 0) + { + // We inline destroy to avoid the access check. + groups = null; + if (parent != null) + parent.removeGroup(this); + } + } + + /* + * Helper method for the VM. Find a Thread by its Id. + * + * @param id The Thread Id. + * @return Thread object or null if thread doesn't exist. + */ + static Thread getThreadFromId(long id) + { + return root.getThreadFromIdImpl(id); + } + + private Thread getThreadFromIdImpl(long id) + { + synchronized (threads) + { + for (int i = 0; i < threads.size(); i++) + { + Thread t = (Thread) threads.get(i); + if (t.getId() == id) + return t; + } + } + Vector groups = this.groups; + if (groups != null) + { + synchronized (groups) + { + for (int i = 0; i < groups.size(); i++) + { + ThreadGroup g = (ThreadGroup) groups.get(i); + Thread t = g.getThreadFromIdImpl(id); + if (t != null) + return t; + } + } + } + return null; + } +} // class ThreadGroup diff --git a/libjava/classpath/java/lang/ThreadLocal.java b/libjava/classpath/java/lang/ThreadLocal.java new file mode 100644 index 000000000..71b722e55 --- /dev/null +++ b/libjava/classpath/java/lang/ThreadLocal.java @@ -0,0 +1,182 @@ +/* ThreadLocal -- a variable with a unique value per thread + Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +/** + * ThreadLocal objects have a different state associated with every + * Thread that accesses them. Every access to the ThreadLocal object + * (through the get() and set() methods) + * only affects the state of the object as seen by the currently + * executing Thread. + * + *

    The first time a ThreadLocal object is accessed on a particular + * Thread, the state for that Thread's copy of the local variable is set by + * executing the method initialValue(). + *

    + * + *

    An example how you can use this: + *

    + * + *
    + * class Connection
    + * {
    + *   private static ThreadLocal owner = new ThreadLocal()
    + *     {
    + *       public Object initialValue()
    + *       {
    + *         return("nobody");
    + *       }
    + *     };
    + * ...
    + * }
    + * 
    + * + *

    Now all instances of connection can see who the owner of the currently + * executing Thread is by calling owner.get(). By default any + * Thread would be associated with 'nobody'. But the Connection object could + * offer a method that changes the owner associated with the Thread on + * which the method was called by calling owner.put("somebody"). + * (Such an owner changing method should then be guarded by security checks.) + *

    + * + *

    When a Thread is garbage collected all references to values of + * the ThreadLocal objects associated with that Thread are removed. + *

    + * + * @author Mark Wielaard (mark@klomp.org) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.5 + */ +public class ThreadLocal +{ + /** + * Placeholder to distinguish between uninitialized and null set by the + * user. Do not expose this to the public. Package visible for use by + * InheritableThreadLocal + */ + static final Object sentinel = new Object(); + + /** + * The base for the computation of the next hash for a thread local. + */ + private static int nextHashBase = 1; + + /** + * Allocate a new hash. + */ + private synchronized int computeNextHash() + { + return nextHashBase++ * 6709; + } + + /** + * Hash code computed for ThreadLocalMap + */ + final int fastHash; + + /** + * Creates a ThreadLocal object without associating any value to it yet. + */ + public ThreadLocal() + { + fastHash = computeNextHash(); + } + + /** + * Called once per thread on the first invocation of get(), if set() was + * not already called. The default implementation returns null. + * Often, this method is overridden to create the appropriate initial object + * for the current thread's view of the ThreadLocal. + * + * @return the initial value of the variable in this thread + */ + protected T initialValue() + { + return null; + } + + /** + * Gets the value associated with the ThreadLocal object for the currently + * executing Thread. If this is the first time the current thread has called + * get(), and it has not already called set(), the value is obtained by + * initialValue(). + * + * @return the value of the variable in this thread + */ + public T get() + { + ThreadLocalMap map = Thread.getThreadLocals(); + // Note that we don't have to synchronize, as only this thread will + // ever modify the map. + T value = (T) map.get(this); + if (value == sentinel) + { + value = initialValue(); + map.set(this, value); + } + return value; + } + + /** + * Sets the value associated with the ThreadLocal object for the currently + * executing Thread. This overrides any existing value associated with the + * current Thread and prevents initialValue() from being + * called if this is the first access to this ThreadLocal in this Thread. + * + * @param value the value to set this thread's view of the variable to + */ + public void set(T value) + { + ThreadLocalMap map = Thread.getThreadLocals(); + // Note that we don't have to synchronize, as only this thread will + // ever modify the map. + map.set(this, value); + } + + /** + * Removes the value associated with the ThreadLocal object for the + * currently executing Thread. + * @since 1.5 + */ + public void remove() + { + ThreadLocalMap map = Thread.getThreadLocals(); + map.remove(this); + } +} diff --git a/libjava/classpath/java/lang/ThreadLocalMap.java b/libjava/classpath/java/lang/ThreadLocalMap.java new file mode 100644 index 000000000..7f67b1311 --- /dev/null +++ b/libjava/classpath/java/lang/ThreadLocalMap.java @@ -0,0 +1,325 @@ +/* ThreadLocal -- a variable with a unique value per thread + Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import java.lang.ref.WeakReference; + +/** + * ThreadLocalMap is the basic storage for the map of ThreadLocal instance + * to a thread's current value. + * + * Some applications really work out ThreadLocals, leading to this + * optimized implementation. + */ +final class ThreadLocalMap +{ + /** + * The log (base 2) of the initial size of the map + */ + private static final int LOG_INITIAL_SIZE = 3; + + /** + * The maximum occupancy rate (after which we grow) + */ + private static final float MAX_OCCUPANCY = 0.7f; + + /** + * The target occupancy rate. + */ + private static final float TARGET_OCCUPANCY = 0.5f; + + /** + * The deleted entry sentinel value. + */ + private static final Entry deletedEntry = new Entry(null); + + /** + * Constructor + */ + ThreadLocalMap() + { + /* Dummy value to ensure fast path can be optimized */ + entries = new Entry[1]; + hashMask = 0; + count = 0; + } + + /** + * The map entries + */ + private Entry[] entries; + + /** + * Used for start index computation + */ + private int hashMask; + + /** + * The number of entries currently in the map + */ + private int count; + + /** + * Create or grow the table to the specified size. The size must be a + * power of two for the efficient mask/hash computation. + * + * @param newSize The new table size. + */ + private void newEntryArray(int newSize) + { + int mask = newSize - 1; + Entry[] oldEntries = this.entries; + this.entries = new Entry[newSize]; + this.hashMask = mask; + + /* Copy old entries to new table */ + count = 0; + if (oldEntries != null) + { + for(Entry e: oldEntries) + { + if (e != null) + { + ThreadLocal key = e.get(); + if (e != deletedEntry && key != null) + { + for(int i = key.fastHash & mask;; i = (i + 1) & mask) + { + if (entries[i] == null) + { + entries[i] = e; + count++; + break; + } + } + } + } + } + } + } + + /** + * We have run out of space in our locals. We use this as the + * trigger to attempt to find unused slots as ThreadLocals have + * died. If we recover any slots this way then we do not grow. + */ + private void overflow() + { + /* First 'actual' use */ + if (entries.length == 1) + { + newEntryArray(1 << LOG_INITIAL_SIZE); + return; + } + + /* Attempt to recover unused slots */ + int deleted = 0; + for(int i=0; i < entries.length; i++) + { + Entry e = entries[i]; + if (e != null) + { + if (e == deletedEntry) + { + deleted++; + } + else if (e.get() == null) + { + entries[i] = deletedEntry; + deleted++; + } + } + } + + if ((count-deleted) <= (TARGET_OCCUPANCY * entries.length)) + { + /* We currently rehash by simple reallocating into a same-sized table. + * An alternative would be to implement a clever hashing algorithm but + * as this happens infrequently this seems preferred */ + newEntryArray(entries.length); + return; + } + + /* Double the size */ + newEntryArray(entries.length << 1); + } + + /** + * This is the class that is used to refer to a thread local weakly. + * + * As we want to minimize indirections we extend WeakReference. + */ + static final class Entry extends WeakReference> { + /** + * The value stored in this slot + */ + Object value; + + /** + * Constructor + */ + Entry(ThreadLocal threadLocal) { + super(threadLocal); + } + } + + /** + * Gets the value associated with the ThreadLocal object for the currently + * executing Thread. If this is the first time the current thread has called + * get(), and it has not already called set(), the sentinel value is returned. + * + * @return the value of the variable in this thread, or sentinel if not present. + */ + public Object get(ThreadLocal key) + { + int mask = this.hashMask; + for(int i = key.fastHash & mask;; i = (i + 1) & mask) { + Entry e = entries[i]; + if (e != null) { + if (e.get() == key) { + return e.value; + } + } else { + return ThreadLocal.sentinel; + } + } + } + + /** + * Sets the value associated with the ThreadLocal object for the currently + * executing Thread. This overrides any existing value associated with the + * current Thread and prevents initialValue() from being + * called if this is the first access to this ThreadLocal in this Thread. + * + * @param value the value to set this thread's view of the variable to + */ + public void set(ThreadLocal key, Object value) + { + /* Overflow ? */ + if ((count+1) >= (MAX_OCCUPANCY * entries.length)) + { + overflow(); + } + + /* Set the entry */ + int mask = this.hashMask; + for(int i = key.fastHash & mask;; i = (i + 1) & mask) + { + Entry e = entries[i]; + if (e == null || e == deletedEntry) + { + /* Create entry */ + if (e == null) count++; + entries[i] = e = new Entry(key); + e.value = value; + return; + } + else + { + ThreadLocal entryKey = e.get(); + if (entryKey == null) + { + entries[i] = deletedEntry; + } + else if (entryKey == key) + { + /* Update entry */ + e.value = value; + return; + } + } + } + } + + /** + * Removes the value associated with the ThreadLocal object for the + * currently executing Thread. + * @since 1.5 + */ + public void remove(ThreadLocal key) + { + int mask = this.hashMask; + for(int i = key.fastHash & mask;; i = (i + 1) & mask) + { + Entry e = entries[i]; + if (e != null) + { + ThreadLocal entryKey = e.get(); + if (entryKey != key) + { + if (entryKey == null) { + entries[i] = deletedEntry; + } + continue; + } + else + { + /* Remove from the table */ + entries[i] = deletedEntry; + } + } + return; + } + } + + /** + * Clear out the map. Done once during thread death. + */ + void clear() { + entries = null; + } + + /** + * Inherit all the InheritableThreadLocal instances from the given parent. + * + * @param parentMap The map to inherit from. + */ + public void inherit(ThreadLocalMap parentMap) { + for(Entry e: parentMap.entries) + { + if (e != null && e != deletedEntry) + { + ThreadLocal key = e.get(); + if (key instanceof InheritableThreadLocal) + { + set(key, ((InheritableThreadLocal)key).childValue(e.value)); + } + } + } + } +} diff --git a/libjava/classpath/java/lang/Throwable.java b/libjava/classpath/java/lang/Throwable.java new file mode 100644 index 000000000..13bc5b903 --- /dev/null +++ b/libjava/classpath/java/lang/Throwable.java @@ -0,0 +1,565 @@ +/* java.lang.Throwable -- Root class for all Exceptions and Errors + Copyright (C) 1998, 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.CPStringBuilder; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; + +/** + * Throwable is the superclass of all exceptions that can be raised. + * + *

    There are two special cases: {@link Error} and {@link RuntimeException}: + * these two classes (and their subclasses) are considered unchecked + * exceptions, and are either frequent enough or catastrophic enough that you + * do not need to declare them in throws clauses. Everything + * else is a checked exception, and is ususally a subclass of + * {@link Exception}; these exceptions have to be handled or declared. + * + *

    Instances of this class are usually created with knowledge of the + * execution context, so that you can get a stack trace of the problem spot + * in the code. Also, since JDK 1.4, Throwables participate in "exception + * chaining." This means that one exception can be caused by another, and + * preserve the information of the original. + * + *

    One reason this is useful is to wrap exceptions to conform to an + * interface. For example, it would be bad design to require all levels + * of a program interface to be aware of the low-level exceptions thrown + * at one level of abstraction. Another example is wrapping a checked + * exception in an unchecked one, to communicate that failure occured + * while still obeying the method throws clause of a superclass. + * + *

    A cause is assigned in one of two ways; but can only be assigned once + * in the lifetime of the Throwable. There are new constructors added to + * several classes in the exception hierarchy that directly initialize the + * cause, or you can use the initCause method. This second + * method is especially useful if the superclass has not been retrofitted + * with new constructors:
    + *

    + * try
    + *   {
    + *     lowLevelOp();
    + *   }
    + * catch (LowLevelException lle)
    + *   {
    + *     throw (HighLevelException) new HighLevelException().initCause(lle);
    + *   }
    + * 
    + * Notice the cast in the above example; without it, your method would need + * a throws clase that declared Throwable, defeating the purpose of chainig + * your exceptions. + * + *

    By convention, exception classes have two constructors: one with no + * arguments, and one that takes a String for a detail message. Further, + * classes which are likely to be used in an exception chain also provide + * a constructor that takes a Throwable, with or without a detail message + * string. + * + *

    Another 1.4 feature is the StackTrace, a means of reflection that + * allows the program to inspect the context of the exception, and which is + * serialized, so that remote procedure calls can correctly pass exceptions. + * + * @author Brian Jones + * @author John Keiser + * @author Mark Wielaard + * @author Tom Tromey + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.0 + * @status updated to 1.4 + */ +public class Throwable implements Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -3042686055658047285L; + + /** + * The detail message. + * + * @serial specific details about the exception, may be null + */ + private final String detailMessage; + + /** + * The cause of the throwable, including null for an unknown or non-chained + * cause. This may only be set once; so the field is set to + * this until initialized. + * + * @serial the cause, or null if unknown, or this if not yet set + * @since 1.4 + */ + private Throwable cause = this; + + /** + * The stack trace, in a serialized form. + * + * @serial the elements of the stack trace; this is non-null, and has + * no null entries + * @since 1.4 + */ + private StackTraceElement[] stackTrace; + + /** + * Instantiate this Throwable with an empty message. The cause remains + * uninitialized. {@link #fillInStackTrace()} will be called to set + * up the stack trace. + */ + public Throwable() + { + this((String) null); + } + + /** + * Instantiate this Throwable with the given message. The cause remains + * uninitialized. {@link #fillInStackTrace()} will be called to set + * up the stack trace. + * + * @param message the message to associate with the Throwable + */ + public Throwable(String message) + { + fillInStackTrace(); + detailMessage = message; + } + + /** + * Instantiate this Throwable with the given message and cause. Note that + * the message is unrelated to the message of the cause. + * {@link #fillInStackTrace()} will be called to set up the stack trace. + * + * @param message the message to associate with the Throwable + * @param cause the cause, may be null + * @since 1.4 + */ + public Throwable(String message, Throwable cause) + { + this(message); + this.cause = cause; + } + + /** + * Instantiate this Throwable with the given cause. The message is then + * built as cause == null ? null : cause.toString(). + * {@link #fillInStackTrace()} will be called to set up the stack trace. + * + * @param cause the cause, may be null + * @since 1.4 + */ + public Throwable(Throwable cause) + { + this(cause == null ? null : cause.toString(), cause); + } + + /** + * Get the message associated with this Throwable. + * + * @return the error message associated with this Throwable, may be null + */ + public String getMessage() + { + return detailMessage; + } + + /** + * Get a localized version of this Throwable's error message. + * This method must be overridden in a subclass of Throwable + * to actually produce locale-specific methods. The Throwable + * implementation just returns getMessage(). + * + * @return a localized version of this error message + * @see #getMessage() + * @since 1.1 + */ + public String getLocalizedMessage() + { + return getMessage(); + } + + /** + * Returns the cause of this exception, or null if the cause is not known + * or non-existant. This cause is initialized by the new constructors, + * or by calling initCause. + * + * @return the cause of this Throwable + * @since 1.4 + */ + public Throwable getCause() + { + return cause == this ? null : cause; + } + + /** + * Initialize the cause of this Throwable. This may only be called once + * during the object lifetime, including implicitly by chaining + * constructors. + * + * @param cause the cause of this Throwable, may be null + * @return this + * @throws IllegalArgumentException if cause is this (a Throwable can't be + * its own cause!) + * @throws IllegalStateException if the cause has already been set + * @since 1.4 + */ + public Throwable initCause(Throwable cause) + { + if (cause == this) + throw new IllegalArgumentException(); + if (this.cause != this) + throw new IllegalStateException(); + this.cause = cause; + return this; + } + + /** + * Get a human-readable representation of this Throwable. The detail message + * is retrieved by getLocalizedMessage(). Then, with a null detail + * message, this string is simply the object's class name; otherwise + * the string is getClass().getName() + ": " + message. + * + * @return a human-readable String represting this Throwable + */ + public String toString() + { + String msg = getLocalizedMessage(); + return getClass().getName() + (msg == null ? "" : ": " + msg); + } + + /** + * Print a stack trace to the standard error stream. This stream is the + * current contents of System.err. The first line of output + * is the result of {@link #toString()}, and the remaining lines represent + * the data created by {@link #fillInStackTrace()}. While the format is + * unspecified, this implementation uses the suggested format, demonstrated + * by this example:
    + *

    +   * public class Junk
    +   * {
    +   *   public static void main(String args[])
    +   *   {
    +   *     try
    +   *       {
    +   *         a();
    +   *       }
    +   *     catch(HighLevelException e)
    +   *       {
    +   *         e.printStackTrace();
    +   *       }
    +   *   }
    +   *   static void a() throws HighLevelException
    +   *   {
    +   *     try
    +   *       {
    +   *         b();
    +   *       }
    +   *     catch(MidLevelException e)
    +   *       {
    +   *         throw new HighLevelException(e);
    +   *       }
    +   *   }
    +   *   static void b() throws MidLevelException
    +   *   {
    +   *     c();
    +   *   }
    +   *   static void c() throws MidLevelException
    +   *   {
    +   *     try
    +   *       {
    +   *         d();
    +   *       }
    +   *     catch(LowLevelException e)
    +   *       {
    +   *         throw new MidLevelException(e);
    +   *       }
    +   *   }
    +   *   static void d() throws LowLevelException
    +   *   {
    +   *     e();
    +   *   }
    +   *   static void e() throws LowLevelException
    +   *   {
    +   *     throw new LowLevelException();
    +   *   }
    +   * }
    +   * class HighLevelException extends Exception
    +   * {
    +   *   HighLevelException(Throwable cause) { super(cause); }
    +   * }
    +   * class MidLevelException extends Exception
    +   * {
    +   *   MidLevelException(Throwable cause)  { super(cause); }
    +   * }
    +   * class LowLevelException extends Exception
    +   * {
    +   * }
    +   * 
    + *

    + *

    +   *  HighLevelException: MidLevelException: LowLevelException
    +   *          at Junk.a(Junk.java:13)
    +   *          at Junk.main(Junk.java:4)
    +   *  Caused by: MidLevelException: LowLevelException
    +   *          at Junk.c(Junk.java:23)
    +   *          at Junk.b(Junk.java:17)
    +   *          at Junk.a(Junk.java:11)
    +   *          ... 1 more
    +   *  Caused by: LowLevelException
    +   *          at Junk.e(Junk.java:30)
    +   *          at Junk.d(Junk.java:27)
    +   *          at Junk.c(Junk.java:21)
    +   *          ... 3 more
    +   * 
    + */ + public void printStackTrace() + { + printStackTrace(System.err); + } + + /** + * Print a stack trace to the specified PrintStream. See + * {@link #printStackTrace()} for the sample format. + * + * @param s the PrintStream to write the trace to + */ + public void printStackTrace(PrintStream s) + { + s.print(stackTraceString()); + } + + /** + * Prints the exception, the detailed message and the stack trace + * associated with this Throwable to the given PrintWriter. + * The actual output written is implemention specific. Use the result of + * getStackTrace() when more precise information is needed. + * + *

    This implementation first prints a line with the result of this + * object's toString() method. + *
    + * Then for all elements given by getStackTrace it prints + * a line containing three spaces, the string "at " and the result of calling + * the toString() method on the StackTraceElement + * object. If getStackTrace() returns an empty array it prints + * a line containing three spaces and the string + * "<<No stacktrace available>>". + *
    + * Then if getCause() doesn't return null it adds a line + * starting with "Caused by: " and the result of calling + * toString() on the cause. + *
    + * Then for every cause (of a cause, etc) the stacktrace is printed the + * same as for the top level Throwable except that as soon + * as all the remaining stack frames of the cause are the same as the + * the last stack frames of the throwable that the cause is wrapped in + * then a line starting with three spaces and the string "... X more" is + * printed, where X is the number of remaining stackframes. + * + * @param pw the PrintWriter to write the trace to + * @since 1.1 + */ + public void printStackTrace (PrintWriter pw) + { + pw.print(stackTraceString()); + } + + /* + * We use inner class to avoid a static initializer in this basic class. + */ + private static class StaticData + { + static final String nl = SystemProperties.getProperty("line.separator"); + } + + // Create whole stack trace in a stringbuffer so we don't have to print + // it line by line. This prevents printing multiple stack traces from + // different threads to get mixed up when written to the same PrintWriter. + private String stackTraceString() + { + CPStringBuilder sb = new CPStringBuilder(); + + // Main stacktrace + StackTraceElement[] stack = getStackTrace(); + stackTraceStringBuffer(sb, this.toString(), stack, 0); + + // The cause(s) + Throwable cause = getCause(); + while (cause != null) + { + // Cause start first line + sb.append("Caused by: "); + + // Cause stacktrace + StackTraceElement[] parentStack = stack; + stack = cause.getStackTrace(); + if (parentStack == null || parentStack.length == 0) + stackTraceStringBuffer(sb, cause.toString(), stack, 0); + else + { + int equal = 0; // Count how many of the last stack frames are equal + int frame = stack.length-1; + int parentFrame = parentStack.length-1; + while (frame > 0 && parentFrame > 0) + { + if (stack[frame].equals(parentStack[parentFrame])) + { + equal++; + frame--; + parentFrame--; + } + else + break; + } + stackTraceStringBuffer(sb, cause.toString(), stack, equal); + } + cause = cause.getCause(); + } + + return sb.toString(); + } + + // Adds to the given StringBuffer a line containing the name and + // all stacktrace elements minus the last equal ones. + private static void stackTraceStringBuffer(CPStringBuilder sb, String name, + StackTraceElement[] stack, int equal) + { + String nl = StaticData.nl; + // (finish) first line + sb.append(name); + sb.append(nl); + + // The stacktrace + if (stack == null || stack.length == 0) + { + sb.append(" <>"); + sb.append(nl); + } + else + { + for (int i = 0; i < stack.length-equal; i++) + { + sb.append(" at "); + sb.append(stack[i] == null ? "<>" : stack[i].toString()); + sb.append(nl); + } + if (equal > 0) + { + sb.append(" ..."); + sb.append(equal); + sb.append(" more"); + sb.append(nl); + } + } + } + + /** + * Fill in the stack trace with the current execution stack. + * + * @return this same throwable + * @see #printStackTrace() + */ + public Throwable fillInStackTrace() + { + vmState = VMThrowable.fillInStackTrace(this); + stackTrace = null; // Should be regenerated when used. + + return this; + } + + /** + * Provides access to the information printed in {@link #printStackTrace()}. + * The array is non-null, with no null entries, although the virtual + * machine is allowed to skip stack frames. If the array is not 0-length, + * then slot 0 holds the information on the stack frame where the Throwable + * was created (or at least where fillInStackTrace() was + * called). + * + * @return an array of stack trace information, as available from the VM + * @since 1.4 + */ + public StackTraceElement[] getStackTrace() + { + if (stackTrace == null) + if (vmState == null) + stackTrace = new StackTraceElement[0]; + else + { + stackTrace = vmState.getStackTrace(this); + vmState = null; // No longer needed + } + + return stackTrace; + } + + /** + * Change the stack trace manually. This method is designed for remote + * procedure calls, which intend to alter the stack trace before or after + * serialization according to the context of the remote call. + *

    + * The contents of the given stacktrace is copied so changes to the + * original array do not change the stack trace elements of this + * throwable. + * + * @param stackTrace the new trace to use + * @throws NullPointerException if stackTrace is null or has null elements + * @since 1.4 + */ + public void setStackTrace(StackTraceElement[] stackTrace) + { + int i = stackTrace.length; + StackTraceElement[] st = new StackTraceElement[i]; + + while (--i >= 0) + { + st[i] = stackTrace[i]; + if (st[i] == null) + throw new NullPointerException("Element " + i + " null"); + } + + this.stackTrace = st; + } + + /** + * VM state when fillInStackTrace was called. + * Used by getStackTrace() to get an array of StackTraceElements. + * Cleared when no longer needed. + */ + private transient VMThrowable vmState; +} diff --git a/libjava/classpath/java/lang/TypeNotPresentException.java b/libjava/classpath/java/lang/TypeNotPresentException.java new file mode 100644 index 000000000..4d6bf2399 --- /dev/null +++ b/libjava/classpath/java/lang/TypeNotPresentException.java @@ -0,0 +1,98 @@ +/* TypeNotPresentException.java -- Thrown when a string-based type is missing + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + *

    + * Thrown when a type is accessed using a String-based + * representation, but no definition of the supplied type is found. + * This is effectively an unchecked equivalent of the existing + * ClassNotFound exception. + *

    + *

    + * It may occur due to an attempt to load a missing class, interface or + * annotation, or when an undefined type variable is accessed. + *

    + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see ClassNotFoundException + * @since 1.5 + */ +public class TypeNotPresentException + extends RuntimeException +{ + private static final long serialVersionUID = -5101214195716534496L; + + /** + * Constructs a TypeNotPresentException for + * the supplied type. The specified cause Throwable + * may be used to provide additional history, with regards to the + * root of the problem. It is perfectly valid for this to be null, + * if the cause of the problem is unknown. + * + * @param typeName the name of the missing type. + * @param cause the cause of this exception, or null if the cause + * is unknown. + */ + public TypeNotPresentException(String typeName, Throwable cause) + { + super("type \"" + typeName + "\" not found", cause); + this.typeName = typeName; + } + + /** + * Returns the name of the missing type. + * + * @return the missing type's name. + */ + public String typeName() + { + return typeName; + } + + /** + * The name of the missing type. + * + * @serial the missing type's name. + */ + // Name fixed by serialization. + private String typeName; + +} diff --git a/libjava/classpath/java/lang/UnknownError.java b/libjava/classpath/java/lang/UnknownError.java new file mode 100644 index 000000000..7b317bd2a --- /dev/null +++ b/libjava/classpath/java/lang/UnknownError.java @@ -0,0 +1,72 @@ +/* UnknownError.java -- thrown when the VM cannot provide more information + about a catastrophic error + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An UnknownError is thrown when a serious but unknown + * problem has occurred in the Java Virtual Machine. + * + * @author Brian Jones + * @status updated to 1.4 + */ +public class UnknownError extends VirtualMachineError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 2524784860676771849L; + + /** + * Create an error without a message. + */ + public UnknownError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public UnknownError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/UnsatisfiedLinkError.java b/libjava/classpath/java/lang/UnsatisfiedLinkError.java new file mode 100644 index 000000000..0d513d8e0 --- /dev/null +++ b/libjava/classpath/java/lang/UnsatisfiedLinkError.java @@ -0,0 +1,74 @@ +/* UnsatisfiedLinkError.java -- thrown when a native method cannot be loaded + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A UnsatisfiedLinkError is thrown if an appropriate + * native language definition of a method declared native + * cannot be found by the Java Virtual Machine. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @see Runtime + * @status updated to 1.4 + */ +public class UnsatisfiedLinkError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4019343241616879428L; + + /** + * Create an error without a message. + */ + public UnsatisfiedLinkError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public UnsatisfiedLinkError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/UnsupportedClassVersionError.java b/libjava/classpath/java/lang/UnsupportedClassVersionError.java new file mode 100644 index 000000000..d6974b769 --- /dev/null +++ b/libjava/classpath/java/lang/UnsupportedClassVersionError.java @@ -0,0 +1,74 @@ +/* UnsupportedClassVersionError.java -- thrown when a class file version + exceeds the capability of the virtual machine + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * An UnsupportedClassVersionError is thrown when the + * Java Virtual Machine determines it does not support the major and minor + * version numbers in the class file it is attempting to read. + * + * @author Brian Jones + * @since 1.2 + * @status updated to 1.4 + */ +public class UnsupportedClassVersionError extends ClassFormatError +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -7123279212883497373L; + + /** + * Create an error without a message. + */ + public UnsupportedClassVersionError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public UnsupportedClassVersionError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/UnsupportedOperationException.java b/libjava/classpath/java/lang/UnsupportedOperationException.java new file mode 100644 index 000000000..4994f6714 --- /dev/null +++ b/libjava/classpath/java/lang/UnsupportedOperationException.java @@ -0,0 +1,127 @@ +/* UnsupportedOperationException.java -- thrown when an operation is not + supported + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * This exception is thrown by an object when an operation is + * requested of it that it does not support. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.2 + * @status updated to 1.5 + */ +public class UnsupportedOperationException extends RuntimeException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -1242599979055084673L; + + /** + * Create an exception without a message. + */ + public UnsupportedOperationException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnsupportedOperationException(String s) + { + super(s); + } + + /** + *

    + * Constructs a UnsupportedOperationException using + * the specified error message, which should give further details + * as to the reason for this exception. The specified cause + * Throwable may be used to provide additional history, + * with regards to the root of the problem. It is perfectly valid + * for this to be null, if the cause of the problem is unknown. + *

    + *

    + * Note: the detail message from the cause is not + * automatically incorporated into the resulting detail message of + * this exception. + *

    + * + * @param message the detail message, which should give the reason for + * this exception being thrown. + * @param cause the cause of this exception, or null if the cause + * is unknown. + * @since 1.5 + */ + public UnsupportedOperationException(String message, Throwable cause) + { + super(message, cause); + } + + /** + *

    + * Constructs a UnsupportedOperationException using + * the specified cause Throwable, which may be used + * to provide additional history, with regards to the root of the + * problem. It is perfectly valid for this to be null, if the + * cause of the problem is unknown. + *

    + *

    + * The detail message is automatically constructed from the detail + * message of the supplied causal exception. If the cause is null, + * then the detail message will also be null. Otherwise, the detail + * message of this exception will be that of the causal exception. + * This makes this constructor very useful for simply wrapping another + * exception. + *

    + * + * @param cause the cause of this exception, or null if the cause + * is unknown. + * @since 1.5 + */ + public UnsupportedOperationException(Throwable cause) + { + super(cause); + } + +} diff --git a/libjava/classpath/java/lang/VerifyError.java b/libjava/classpath/java/lang/VerifyError.java new file mode 100644 index 000000000..350ceaa5e --- /dev/null +++ b/libjava/classpath/java/lang/VerifyError.java @@ -0,0 +1,72 @@ +/* VerifyError.java -- thrown when a class fails verification + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A VerifyError is thrown if there is a security problem or + * internal inconsistency in a class file as detected by the "verifier." + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public class VerifyError extends LinkageError +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 7001962396098498785L; + + /** + * Create an error without a message. + */ + public VerifyError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public VerifyError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/VirtualMachineError.java b/libjava/classpath/java/lang/VirtualMachineError.java new file mode 100644 index 000000000..3062c4fe8 --- /dev/null +++ b/libjava/classpath/java/lang/VirtualMachineError.java @@ -0,0 +1,73 @@ +/* VirtualMachineError.java -- thrown when the Virtual Machine has a problem + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang; + +/** + * A VirtualMachineError or its subclasses are thrown to + * indicate there is something wrong with the Java Virtual Machine or that + * it does not have the resources needed for it to continue execution. + * + * @author Brian Jones + * @author Tom Tromey (tromey@cygnus.com) + * @status updated to 1.4 + */ +public abstract class VirtualMachineError extends Error +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4161983926571568670L; + + /** + * Create an error without a message. + */ + public VirtualMachineError() + { + } + + /** + * Create an error with a message. + * + * @param s the message + */ + public VirtualMachineError(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/Void.java b/libjava/classpath/java/lang/Void.java new file mode 100644 index 000000000..05ed98503 --- /dev/null +++ b/libjava/classpath/java/lang/Void.java @@ -0,0 +1,68 @@ +/* Void.class - defines void.class + Copyright (C) 1998, 1999, 2001, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang; + + +/** + * Void is a placeholder class so that the variable Void.TYPE + * (also available as void.class) can be supported for + * reflection return types. + * + *

    This class could be Serializable, but that is up to Sun.

    + * + * @author Paul Fisher + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.5 + */ +public final class Void +{ + /** + * The return type void is represented by this + * Class object. + */ + public static final Class TYPE = (Class) VMClassLoader.getPrimitiveClass('V'); + + /** + * Void is non-instantiable. + */ + private Void() + { + } +} diff --git a/libjava/classpath/java/lang/annotation/Annotation.java b/libjava/classpath/java/lang/annotation/Annotation.java new file mode 100644 index 000000000..aac8bb9f1 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/Annotation.java @@ -0,0 +1,135 @@ +/* Annotation.java - Base interface for all annotations + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +/** + * This is the common interface for all annotations. Note that classes + * that implement this class manually are not classed as annotations, and + * that this interface does not define an annotation type in itself. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Annotation +{ + + /** + * Returns the type of this annotation. + * + * @return the class of which this annotation is an instance. + */ + Class annotationType(); + + /** + *

    + * Returns true if the supplied object is equivalent to this annotation. + * For this property to hold, the following must be true of o: + *

    + *
      + *
    • The object is also an instance of the same annotation type.
    • + *
    • The members of the supplied annotation are equal to those of this + * annotation, according to the following: + *
        + *
      • If the members are floats, then, for floats + * x and y, + * Float.valueOf(x).equals(Float.valueOf(y) must return + * true. This differs from the usual (==) comparison + * in that NaN is considered equal to itself and positive + * and negative zero are seen as different.
      • + *
      • Likewise, if the members are doubles, then, for doubles + * x and y, + * Double.valueOf(x).equals(Double.valueOf(y) must return + * true. This differs from the usual (==) comparison + * in that NaN is considered equal to itself and positive + * and negative zero are seen as different.
      • + *
      • Strings, classes, enumerations and annotations are considered + * equal according to the equals() implementation for these + * types.
      • + *
      • Arrays are considered equal according to Arrays.equals() + *
      • + *
      • Any remaining types are considered equal using ==.
      • + * + *
      + * + * @param o the object to compare with this annotation. + * @return true if the supplied object is an annotation with equivalent + * members. + */ + boolean equals(Object o); + + /** + *

      + * Returns the hash code of the annotation. This is computed as the + * sum of the hash codes of the annotation's members. + *

      + *

      + * The hash code of a member of the annotation is the result of XORing + * the hash code of its value with the result of multiplying the hash code + * of its name by 127. Formally, if the value is v and the + * name is n, the hash code of the member is + * v.hashCode() XOR (127 * String.hashCode(n)). v.hashCode() + * is defined as follows: + *

      + *
        + *
      • The hash code of a primitive value (i.e. byte, + * char, double, float, + * int, long, short and + * boolean) is the hash code obtained from its corresponding + * wrapper class using valueOf(v).hashCode(), where + * v is the primitive value.
      • + *
      • The hash code of an enumeration, string, class or other annotation + * is obtained using v.hashCode().
      • + *
      • The hash code of an array is computed using + * Arrays.hashCode(v).
      • + *
      + * + * @return the hash code of the annotation, computed as the sum of its + * member hashcodes. + */ + int hashCode(); + + /** + * Returns a textual representation of the annotation. This is + * implementation-dependent, but is expected to include the classname + * and the names and values of each member. + * + * @return a textual representation of the annotation. + */ + String toString(); +} diff --git a/libjava/classpath/java/lang/annotation/AnnotationFormatError.java b/libjava/classpath/java/lang/annotation/AnnotationFormatError.java new file mode 100644 index 000000000..36f600632 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/AnnotationFormatError.java @@ -0,0 +1,105 @@ +/* AnnotationFormatError.java - Thrown when an binary annotation is malformed + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +/** + * Thrown when an annotation found in a class file is + * malformed. When the virtual machine finds a class file + * containing annotations, it attempts to parse them. + * This error is thrown if this operation fails. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class AnnotationFormatError extends Error +{ + private static final long serialVersionUID = -4256701562333669892L; + + /** + * Constructs a new AnnotationFormatError + * using the specified message to give details of the error. + * + * @param message the message to use in the error output. + */ + public AnnotationFormatError(String message) + { + super(message); + } + + /** + *

      + * Constructs a new AnnotationFormatError + * using the specified message to give details of the error. + * The supplied cause Throwable is used to + * provide additional history, with regards to the root + * of the problem. It is perfectly valid for this to be null, if + * the cause is unknown. + *

      + *

      + * Note: if a cause is supplied, the error + * message from this cause is not automatically included in the + * error message given by this error. + *

      + * + * @param message the message to use in the error output + * @param cause the cause of this error, or null if the cause + * is unknown. + */ + public AnnotationFormatError(String message, Throwable cause) + { + super(message, cause); + } + + /** + * Constructs a new AnnotationFormatError using + * the supplied cause Throwable to + * provide additional history, with regards to the root + * of the problem. It is perfectly valid for this to be null, if + * the cause is unknown. If the cause is not null, the error + * message from this cause will also be used as the message + * for this error. + * + * @param cause the cause of the error. + */ + public AnnotationFormatError(Throwable cause) + { + super(cause); + } + +} diff --git a/libjava/classpath/java/lang/annotation/AnnotationTypeMismatchException.java b/libjava/classpath/java/lang/annotation/AnnotationTypeMismatchException.java new file mode 100644 index 000000000..bab32e0ea --- /dev/null +++ b/libjava/classpath/java/lang/annotation/AnnotationTypeMismatchException.java @@ -0,0 +1,116 @@ +/* AnnotationTypeMismatchException.java - Thrown when annotation has changed + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +import java.lang.reflect.Method; + +/** + * Thrown when accessing an element within an annotation for + * which the type has changed, since compilation or serialization + * took place. The mismatch between the compiled or serialized + * type and the current type causes this exception to be thrown. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class AnnotationTypeMismatchException extends RuntimeException +{ + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 8125925355765570191L; + + /** + * Constructs an AnnotationTypeMismatchException + * which is due to a mismatched type in the annotation + * element, m. The erroneous type used for the + * data in m is represented by the string, + * type. This string is of an undefined format, + * and may contain the value as well as the type. + * + * @param m the element from the annotation. + * @param type the name of the erroneous type found in m. + */ + public AnnotationTypeMismatchException(Method m, String type) + { + this.element = m; + this.foundType = type; + } + + /** + * Returns the element from the annotation, for which a + * mismatch occurred. + * + * @return the element with the mismatched type. + */ + public Method element() + { + return element; + } + + /** + * Returns the erroneous type used by the element, + * represented as a String. The format + * of this String is not explicitly specified, + * and may contain the value as well as the type. + * + * @return the type found in the element. + */ + public String foundType() + { + return foundType; + } + + // Names are chosen from serialization spec. + /** + * The element from the annotation. + * + * @serial the element with the mismatched type. + */ + private Method element; + + /** + * The erroneous type used by the element. + * + * @serial the type found in the element. + */ + private String foundType; + +} diff --git a/libjava/classpath/java/lang/annotation/Documented.java b/libjava/classpath/java/lang/annotation/Documented.java new file mode 100644 index 000000000..9a51bc2f0 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/Documented.java @@ -0,0 +1,50 @@ +/* Documented.java - Indicates documented source element + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +@Documented @Retention(RUNTIME) +public @interface Documented +{ +} diff --git a/libjava/classpath/java/lang/annotation/ElementType.java b/libjava/classpath/java/lang/annotation/ElementType.java new file mode 100644 index 000000000..3ab89c946 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/ElementType.java @@ -0,0 +1,59 @@ +/* ElementType.java - Enum listing Java source elements + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +/** + * @since 1.5 + */ +public enum ElementType +{ + ANNOTATION_TYPE, + CONSTRUCTOR, + FIELD, + LOCAL_VARIABLE, + METHOD, + PACKAGE, + PARAMETER, + TYPE; + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 2798216111136361587L; + +} diff --git a/libjava/classpath/java/lang/annotation/IncompleteAnnotationException.java b/libjava/classpath/java/lang/annotation/IncompleteAnnotationException.java new file mode 100644 index 000000000..b511b36c2 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/IncompleteAnnotationException.java @@ -0,0 +1,107 @@ +/* IncompleteAnnotationException.java - Thrown when annotation has changed + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +/** + * Thrown when accessing an element within an annotation which + * was added since compilation or serialization took place, and + * does not have a default value. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IncompleteAnnotationException extends RuntimeException +{ + + /** + * Constructs a new IncompleteAnnotationException + * which indicates that the element, name, was missing + * from the annotation, type at compile time and does + * not have a default value. + * + * @param type the type of annotation from which an element is missing. + * @param name the name of the missing element. + */ + public IncompleteAnnotationException(Class type, + String name) + { + this.annotationType = type; + this.elementName = name; + } + + /** + * Returns the class representing the type of annotation + * from which an element was missing. + * + * @return the type of annotation. + */ + public Class annotationType() + { + return annotationType; + } + + /** + * Returns the name of the missing annotation element. + * + * @return the element name. + */ + public String elementName() + { + return elementName; + } + + // Names are chosen from serialization spec. + + /** + * The class representing the type of annotation from + * which an element was found to be missing. + * + * @serial the type of the annotation from which an + * element was missing. + */ + private Class annotationType; + + /** + * The name of the missing element. + * + * @serial the name of the missing element. + */ + private String elementName; + +} diff --git a/libjava/classpath/java/lang/annotation/Inherited.java b/libjava/classpath/java/lang/annotation/Inherited.java new file mode 100644 index 000000000..34acbf47c --- /dev/null +++ b/libjava/classpath/java/lang/annotation/Inherited.java @@ -0,0 +1,51 @@ +/* Inherited.java - Indicates inherited annotation + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; + +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +@Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) +public @interface Inherited +{ +} diff --git a/libjava/classpath/java/lang/annotation/Retention.java b/libjava/classpath/java/lang/annotation/Retention.java new file mode 100644 index 000000000..8d8a79dbc --- /dev/null +++ b/libjava/classpath/java/lang/annotation/Retention.java @@ -0,0 +1,59 @@ +/* Retention.java - Retention policy for an annotation + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; + +/** + * This annotation is used to specify the desired lifetime of another + * annotation. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see RetentionPolicy + * @since 1.5 + */ +@Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) +public @interface Retention +{ + /** + * The value holds the lifetime of the annotation. + */ + RetentionPolicy value(); +} diff --git a/libjava/classpath/java/lang/annotation/RetentionPolicy.java b/libjava/classpath/java/lang/annotation/RetentionPolicy.java new file mode 100644 index 000000000..56d2af1b7 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/RetentionPolicy.java @@ -0,0 +1,66 @@ +/* RetentionPolicy.java - Enum listing lifetimes for an annotation + Copyright (C) 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +/** + * This enum is used to control the lifetime of an annotation. + * + * @see Retention + * + * @since 1.5 + */ +public enum RetentionPolicy +{ + /** Indicates that the annotation should be stored in class files. */ + CLASS, + + /** Indicates that the annotation should be available at runtime. */ + RUNTIME, + + /** + * Indicates that the annotation should only be available when + * parsing the source code. + */ + SOURCE; + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = -1700821648800605045L; + +} diff --git a/libjava/classpath/java/lang/annotation/Target.java b/libjava/classpath/java/lang/annotation/Target.java new file mode 100644 index 000000000..c9d968632 --- /dev/null +++ b/libjava/classpath/java/lang/annotation/Target.java @@ -0,0 +1,52 @@ +/* Target.java - Indicate where an annotation may be applied + Copyright (C) 2004, 2005 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.annotation; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; + +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +@Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) +public @interface Target +{ + ElementType[] value(); +} diff --git a/libjava/classpath/java/lang/annotation/package.html b/libjava/classpath/java/lang/annotation/package.html new file mode 100644 index 000000000..ee70daf9e --- /dev/null +++ b/libjava/classpath/java/lang/annotation/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.lang.annotation + + +

      Classes to handle annotations.

      + + + diff --git a/libjava/classpath/java/lang/instrument/ClassDefinition.java b/libjava/classpath/java/lang/instrument/ClassDefinition.java new file mode 100644 index 000000000..f2fbf9296 --- /dev/null +++ b/libjava/classpath/java/lang/instrument/ClassDefinition.java @@ -0,0 +1,86 @@ +/* ClassDefinition.java -- Class that binds a class with a new class file + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.instrument; + +/** + * This class binds a class that will be redefined with a new + * class file. + * + * @author Nicolas Geoffray (nicolas.geoffray@menlina.com) + * @see Instrumentation#redefineClasses(java.lang.instrument.ClassDefinition[]) + * @since 1.5 + */ +public final class ClassDefinition +{ + + /* The class it's related */ + private Class theClass; + + /* The new bytecode of theClass */ + private byte[] theClassFile; + + /** + * @param theClass the Class that will be redefined + * @param theClassFile the new class file + * @throws NullPointerException if one of the argument is null + */ + public ClassDefinition(Class theClass, byte[] theClassFile) + { + if (theClass == null || theClassFile == null) + throw new NullPointerException(); + this.theClass = theClass; + this.theClassFile = theClassFile; + } + + /** + * @return the Class + */ + public Class getDefinitionClass() + { + return theClass; + } + + /** + * @return the bytes + */ + public byte[] getDefinitionClassFile() + { + return theClassFile; + } +} diff --git a/libjava/classpath/java/lang/instrument/ClassFileTransformer.java b/libjava/classpath/java/lang/instrument/ClassFileTransformer.java new file mode 100644 index 000000000..189a46a3c --- /dev/null +++ b/libjava/classpath/java/lang/instrument/ClassFileTransformer.java @@ -0,0 +1,84 @@ +/* ClassFileTransformer.java -- Implementation of this interface is used by an agent to + transform class files. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.instrument; + +import java.security.ProtectionDomain; + +/** + * This interface should be implemented by classes wishing to transform + * classes bytecode when defining or redefining classes. + * + * @author Nicolas Geoffray (nicolas.geoffray@menlina.com) + * @see Instrumentation + * @see Instrumentation#addTransformer(java.lang.instrument.ClassFileTransformer) + * @see Instrumentation#removeTransformer(java.lang.instrument.ClassFileTransformer) + * @since 1.5 + */ +public interface ClassFileTransformer +{ + + /** + * Implementation of this method transforms a class by redefining its + * bytecodes. Once a ClassFileTransformer object registers itself to the + * Instrumentation object, this method will be called each time a class is + * defined (ClassLoader.defineClass) or redefined + * (Instrumentation.redefineClasses) + * @param loader the loader of the class + * @param className the name of the class with packages separated with "/" + * @param classBeingRedefined the class being redefined if it's the case, + * null otherwise + * @param protectionDomain the protection domain of the class being defined or + * redefined + * @param classfileBuffer the input byte buffer in class file format + * + * @return a class file buffer or null when no transformation has been performed + * + * @throws IllegalClassFormatException if the byte buffer does not represent + * a well-formed class file + * @see Instrumentation#redefineClasses(java.lang.instrument.ClassDefinition[]) + * + */ + byte[] transform(ClassLoader loader, + String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) + throws IllegalClassFormatException; +} diff --git a/libjava/classpath/java/lang/instrument/IllegalClassFormatException.java b/libjava/classpath/java/lang/instrument/IllegalClassFormatException.java new file mode 100644 index 000000000..c75bde003 --- /dev/null +++ b/libjava/classpath/java/lang/instrument/IllegalClassFormatException.java @@ -0,0 +1,70 @@ +/* IllegalClassFormatException.java -- thrown when an array of byte does + not represent a valid class file + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.instrument; + +/** + * @author Nicolas Geoffray (nicolas.geoffray@menlina.com) + * @since 1.5 + */ +public class IllegalClassFormatException extends Exception +{ + + /** + * Compatible with JDK 1.5+. + */ + private static final long serialVersionUID = -3841736710924794009L; + + /** + * Create an exception without a message. + */ + public IllegalClassFormatException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public IllegalClassFormatException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/instrument/Instrumentation.java b/libjava/classpath/java/lang/instrument/Instrumentation.java new file mode 100644 index 000000000..42e3ab5be --- /dev/null +++ b/libjava/classpath/java/lang/instrument/Instrumentation.java @@ -0,0 +1,139 @@ +/* Instrumentation.java -- Implementation of this interface is used to + instrument Java bytecode. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.instrument; + +/** + * An Instrumentation object has transformers that will + * be called each time a class is defined or redefined. + * The object is given to a premain function + * that is called before the main function. + * + * @author Nicolas Geoffray (nicolas.geoffray@menlina.com) + * @since 1.5 + */ +public interface Instrumentation +{ + + /** + * Adds a ClassFileTransformer object + * to the instrumentation. Each time a class is defined + * or redefined, the transform method of the + * transformer object is called. + * + * @param transformer the transformer to add + * @throws NullPointerException if transformer is null + */ + void addTransformer(ClassFileTransformer transformer); + + /** + * Removes the given transformer from the set of transformers + * this Instrumentation object has. + * + * @param transformer the transformer to remove + * @return true if the transformer was found and removed, false if + * the transformer was not found + * @throws NullPointerException if transformer is null + */ + boolean removeTransformer(ClassFileTransformer transformer); + + /** + * Returns if the current JVM supports class redefinition + * + * @return true if the current JVM supports class redefinition + */ + boolean isRedefineClassesSupported(); + + /** + * Redefine classes present in the definitions array, with + * the corresponding class files. + * + * @param definitions an array of classes to redefine + * + * @throws ClassNotFoundException if a class cannot be found + * @throws UnmodifiableClassException if a class cannot be modified + * @throws UnsupportedOperationException if the JVM does not support + * redefinition or the redefinition made unsupported changes + * @throws ClassFormatError if a class file is not valid + * @throws NoClassDefFoundError if a class name is not equal to the name + * in the class file specified + * @throws UnsupportedClassVersionError if the class file version numbers + * are unsupported + * @throws ClassCircularityError if circularity occured with the new + * classes + * @throws LinkageError if a linkage error occurs + * @throws NullPointerException if the definitions array is null, or any + * of its element + * + * @see #isRedefineClassesSupported() + * @see #addTransformer(java.lang.instrument.ClassFileTransformer) + * @see ClassFileTransformer + */ + void redefineClasses(ClassDefinition[] definitions) + throws ClassNotFoundException, + UnmodifiableClassException; + + + /** + * Get all the classes loaded by the JVM. + * + * @return an array containing all the classes loaded by the JVM. The array + * is empty if no class is loaded. + */ + Class[] getAllLoadedClasses(); + + /** + * Get all the classes loaded by a given class loader + * + * @param loader the loader + * + * @return an array containing all the classes loaded by the given loader. + * The array is empty if no class was loaded by the loader. + */ + Class[] getInitiatedClasses(ClassLoader loader); + + /** + * Get the size of an object. It contains the size of all fields. + * + * @param objectToSize the object + * @return the size of the object + * @throws NullPointerException if objectToSize is null. + */ + long getObjectSize(Object objectToSize); +} diff --git a/libjava/classpath/java/lang/instrument/UnmodifiableClassException.java b/libjava/classpath/java/lang/instrument/UnmodifiableClassException.java new file mode 100644 index 000000000..a01bd701d --- /dev/null +++ b/libjava/classpath/java/lang/instrument/UnmodifiableClassException.java @@ -0,0 +1,69 @@ +/* UnmodifiableClassException.java -- thrown when attempting to redefine + an unmodifiable class + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.instrument; + +/** + * @author Nicolas Geoffray (nicolas.geoffray@menlina.com) + * @since 1.5 + */ +public class UnmodifiableClassException extends Exception +{ + /** + * Compatible with JDK 1.5+. + */ + private static final long serialVersionUID = 1716652643585309178L; + + /** + * Create an exception without a message. + */ + public UnmodifiableClassException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnmodifiableClassException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/lang/management/ClassLoadingMXBean.java b/libjava/classpath/java/lang/management/ClassLoadingMXBean.java new file mode 100644 index 000000000..491af247f --- /dev/null +++ b/libjava/classpath/java/lang/management/ClassLoadingMXBean.java @@ -0,0 +1,102 @@ +/* ClassLoadingMXBean.java - Interface for a class loading bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the class loading + * behaviour of the current invocation of the virtual + * machine. An instance of this bean is obtained by calling + * {@link ManagementFactory#getClassLoadingMXBean()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface ClassLoadingMXBean +{ + + /** + * Returns the number of classes currently loaded by + * the virtual machine. + * + * @return the number of loaded classes. + */ + int getLoadedClassCount(); + + /** + * Returns the total number of classes loaded by the + * virtual machine since it was started. This is the + * sum of the currently loaded classes and those that + * have been unloaded. + * + * @return the total number of classes that have been + * loaded by the virtual machine since it started. + */ + long getTotalLoadedClassCount(); + + /** + * Returns the number of classes that have been unloaded + * by the virtual machine since it was started. + * + * @return the number of unloaded classes. + */ + long getUnloadedClassCount(); + + /** + * Returns true if the virtual machine will emit additional + * information when classes are loaded and unloaded. The + * format of the output is left up to the virtual machine. + * + * @return true if verbose class loading output is on. + */ + boolean isVerbose(); + + /** + * Turns on or off the emission of additional information + * when classes are loaded and unloaded. The format of the + * output is left up to the virtual machine. This method + * may be called by multiple threads concurrently, but there + * is only one global setting of verbosity that is affected. + * + * @param verbose the new setting for verbose class loading + * output. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + */ + void setVerbose(boolean verbose); + +} diff --git a/libjava/classpath/java/lang/management/CompilationMXBean.java b/libjava/classpath/java/lang/management/CompilationMXBean.java new file mode 100644 index 000000000..36f69c44b --- /dev/null +++ b/libjava/classpath/java/lang/management/CompilationMXBean.java @@ -0,0 +1,85 @@ +/* CompilationMXBean.java - Interface for a compilation bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the Just-In-Time + * (JIT) compiler provided by the virtual machine, if one + * exists. An instance of this bean is obtainable by + * calling {@link ManagementFactory#getCompilationMXBean()} + * if a JIT is available. Otherwise, the method returns + * null. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface CompilationMXBean +{ + + /** + * Returns the name of the Just-In-Time (JIT) compiler. + * + * @return the name of the JIT compiler. + */ + String getName(); + + /** + * Returns true if the virtual machine's JIT compiler + * supports monitoring of the time spent compiling. + * + * @return true if the JIT compiler can be monitored + * for time spent compiling. + */ + boolean isCompilationTimeMonitoringSupported(); + + /** + * Returns the accumulated time, in milliseconds, that + * the JIT compiler has spent compiling Java bytecodes + * to native machine code. This value represents a single + * time measurement for the whole virtual machine, including + * all multiple threads of operation. The value is not + * intended as a performance measurement. + * + * @return the accumulated number of milliseconds the JIT + * compiler has spent compiling. + * @throws UnsupportedOperationException if time monitoring + * is not supported. + */ + long getTotalCompilationTime(); + +} diff --git a/libjava/classpath/java/lang/management/GarbageCollectorMXBean.java b/libjava/classpath/java/lang/management/GarbageCollectorMXBean.java new file mode 100644 index 000000000..f3da0042d --- /dev/null +++ b/libjava/classpath/java/lang/management/GarbageCollectorMXBean.java @@ -0,0 +1,79 @@ +/* GarbageCollectorMXBean.java - Interface for a garbage collector bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the garbage collectors + * of the virtual machine. Garbage collectors are responsible + * for removing unreferenced objects from memory. A garbage + * collector is a type of memory manager, so this interface + * is combined with that of generic memory managers. An instance + * of this bean for each garbage collector is obtained by calling + * {@link ManagementFactory#getGarbageCollectorMXBeans()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface GarbageCollectorMXBean + extends MemoryManagerMXBean +{ + + /** + * Returns the number of collections the garbage collector + * represented by this bean has made. -1 is returned if the + * collection count is undefined. + * + * @return the number of collections made, or -1 if this is + * undefined. + */ + long getCollectionCount(); + + /** + * Returns the accumulated number of milliseconds this garbage + * collector has spent freeing the memory used by unreferenced + * objects. -1 is returned if the collection time is undefined. + * Note that the accumulated time may not change, even when the + * collection count increases, if the time taken is sufficiently + * short; this depends on the resolution of the timer used. + * + * @return the accumulated number of milliseconds spent collecting, + * or -1 if this is undefined. + */ + long getCollectionTime(); + +} diff --git a/libjava/classpath/java/lang/management/LockInfo.java b/libjava/classpath/java/lang/management/LockInfo.java new file mode 100644 index 000000000..ae5166834 --- /dev/null +++ b/libjava/classpath/java/lang/management/LockInfo.java @@ -0,0 +1,114 @@ +/* LockInfo.java - Information on a lock. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import java.beans.ConstructorProperties; + +/** + * Provides information on a lock held by a thread. + * A lock can be either a built-in monitor, an + * ownable synchronizer (i.e. a subclass + * of {@link java.util.concurrent.locks.AbstractOwnableSynchronizer}), + * or a {@link java.util.concurrent.locks.Condition} + * object. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public class LockInfo +{ + + /** + * The class name of the lock object. + */ + private String className; + + /** + * The identity hash code of the lock object. + */ + private int identityHashCode; + + /** + * Constructs a new {@link LockInfo} object with the + * specified class name and identity hash code. + * + * @param className the name of the class of the lock object. + * @param identityHashCode the identity hash code of the + * lock object. + */ + @ConstructorProperties({"className","identityHashCode"}) + public LockInfo(String className, int identityHashCode) + { + this.className = className; + this.identityHashCode = identityHashCode; + } + + /** + * Returns the class name of the lock object. + * + * @return the class name of the lock object. + */ + public String getClassName() + { + return className; + } + + /** + * Returns the identity hash code of the lock object. + * + * @return the identity hash code of the lock object. + */ + public int getIdentityHashCode() + { + return identityHashCode; + } + + /** + * Returns a textual representation of the lock, + * constructed by concatenating the class name, + * '@' and the identity hash code + * in unsigned hexadecimal form. + * + * @return a textual representation of the lock. + */ + public String toString() + { + return className + '@' + Integer.toHexString(identityHashCode); + } + +} diff --git a/libjava/classpath/java/lang/management/ManagementFactory.java b/libjava/classpath/java/lang/management/ManagementFactory.java new file mode 100644 index 000000000..c054f5dd9 --- /dev/null +++ b/libjava/classpath/java/lang/management/ManagementFactory.java @@ -0,0 +1,817 @@ +/* ManagementFactory.java - Factory for obtaining system beans. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.management.ClassLoadingMXBeanImpl; +import gnu.java.lang.management.CompilationMXBeanImpl; +import gnu.java.lang.management.GarbageCollectorMXBeanImpl; +import gnu.java.lang.management.OperatingSystemMXBeanImpl; +import gnu.java.lang.management.MemoryMXBeanImpl; +import gnu.java.lang.management.MemoryManagerMXBeanImpl; +import gnu.java.lang.management.MemoryPoolMXBeanImpl; +import gnu.java.lang.management.RuntimeMXBeanImpl; +import gnu.java.lang.management.ThreadMXBeanImpl; + +import java.io.IOException; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import java.util.logging.LogManager; + +import javax.management.Attribute; +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +/** + *

      + * Provides access to the system's management beans via a series + * of static methods. + *

      + *

      + * An instance of a system management bean can be obtained by + * using one of the following methods: + *

      + *
        + *
      1. Calling the appropriate static method of this factory. + *
      2. + *
      3. Using the platform {@link javax.management.MBeanServer} + * to access the beans locally, or an + * {@link javax.management.MBeanServerConnection} for remote + * access. The attributes and operations use the limited + * range of data types specified below.
      4. + *
      + *

      Open Data Types

      + *

      + * The data types used by the management beans are restricted + * to open 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}. + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      Data Type UsedData Type Returned
      Primitive types (int, char, etc.)Same
      Wrapper classes ({@link{java.lang.Integer}, + * @link{java.lang.Character}, etc.)Same
      An {@link java.lang.Enum}The name of the enumeration constant
      An array of type EAn array of the same dimensions with this mapping applied + * to E.
      A class with `getter' methods and a + * from({@link javax.management.openmbean.CompositeData}) + * method.The equivalent {@link javax.management.openmbean.CompositeData} + * instance, specified by the from method.
      A map with keys of type K and values of + * type V.A {@link javax.management.openmbean.TabularData} instance, + * with the row type containing two items, "key" and + * "value" with the types K and V + * respectively (with translation applied).
      A list of type E.An array with this mapping applied to E.
      + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class ManagementFactory +{ + + /** + * The object name for the class loading bean. + */ + public static final String CLASS_LOADING_MXBEAN_NAME = + "java.lang:type=ClassLoading"; + + /** + * The object name for the compilation bean. + */ + public static final String COMPILATION_MXBEAN_NAME = + "java.lang:type=Compilation"; + + /** + * The domain for the garbage collecting beans. + */ + public static final String GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE = + "java.lang:type=GarbageCollector"; + + /** + * The domain for the memory manager beans. + */ + public static final String MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE = + "java.lang:type=MemoryManager"; + + /** + * The object name for the memory bean. + */ + public static final String MEMORY_MXBEAN_NAME = + "java.lang:type=Memory"; + + /** + * The domain for the memory pool beans. + */ + public static final String MEMORY_POOL_MXBEAN_DOMAIN_TYPE = + "java.lang:type=MemoryPool"; + + /** + * The object name for the operating system bean. + */ + public static final String OPERATING_SYSTEM_MXBEAN_NAME = + "java.lang:type=OperatingSystem"; + + /** + * The object name for the runtime bean. + */ + public static final String RUNTIME_MXBEAN_NAME = + "java.lang:type=Runtime"; + + /** + * The object name for the threading bean. + */ + public static final String THREAD_MXBEAN_NAME = + "java.lang:type=Threading"; + + /** + * The operating system management bean. + */ + private static OperatingSystemMXBean osBean; + + /** + * The runtime management bean. + */ + private static RuntimeMXBean runtimeBean; + + /** + * The class loading management bean. + */ + private static ClassLoadingMXBean classLoadingBean; + + /** + * The thread bean. + */ + private static ThreadMXBean threadBean; + + /** + * The memory bean. + */ + private static MemoryMXBean memoryBean; + + /** + * The compilation bean (may remain null). + */ + private static CompilationMXBean compilationBean; + + /** + * The platform server. + */ + private static MBeanServer platformServer; + + /** + * Private constructor to prevent instance creation. + */ + private ManagementFactory() {} + + /** + * Returns the operating system management bean for the + * operating system on which the virtual machine is running. + * + * @return an instance of {@link OperatingSystemMXBean} for + * the underlying operating system. + */ + public static OperatingSystemMXBean getOperatingSystemMXBean() + { + if (osBean == null) + try + { + osBean = new OperatingSystemMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "operating system bean is not a " + + "compliant management bean."); + } + return osBean; + } + + /** + * Returns the runtime management bean for the + * running virtual machine. + * + * @return an instance of {@link RuntimeMXBean} for + * this virtual machine. + */ + public static RuntimeMXBean getRuntimeMXBean() + { + if (runtimeBean == null) + try + { + runtimeBean = new RuntimeMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "runtime bean is not a compliant " + + "management bean."); + } + return runtimeBean; + } + + /** + * Returns the class loading management bean for the + * running virtual machine. + * + * @return an instance of {@link ClassLoadingMXBean} for + * this virtual machine. + */ + public static ClassLoadingMXBean getClassLoadingMXBean() + { + if (classLoadingBean == null) + try + { + classLoadingBean = new ClassLoadingMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "class loading bean is not a " + + "compliant management bean."); + } + return classLoadingBean; + } + + /** + * Returns the thread management bean for the running + * virtual machine. + * + * @return an instance of {@link ThreadMXBean} for + * this virtual machine. + */ + public static ThreadMXBean getThreadMXBean() + { + if (threadBean == null) + try + { + threadBean = new ThreadMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "thread bean is not a compliant " + + "management bean."); + } + return threadBean; + } + + /** + * Returns the memory management bean for the running + * virtual machine. + * + * @return an instance of {@link MemoryMXBean} for + * this virtual machine. + */ + public static MemoryMXBean getMemoryMXBean() + { + if (memoryBean == null) + try + { + memoryBean = new MemoryMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "memory bean is not a compliant " + + "management bean."); + } + return memoryBean; + } + + /** + * Returns the compilation bean for the running + * virtual machine, if supported. Otherwise, + * it returns null. + * + * @return an instance of {@link CompilationMXBean} for + * this virtual machine, or null + * if the virtual machine doesn't include + * a Just-In-Time (JIT) compiler. + */ + public static CompilationMXBean getCompilationMXBean() + { + if (compilationBean == null && + SystemProperties.getProperty("gnu.java.compiler.name") != null) + try + { + compilationBean = new CompilationMXBeanImpl(); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "compilation bean is not a compliant " + + "management bean."); + } + return compilationBean; + } + + /** + * Returns the memory pool beans for the running + * virtual machine. These may change during the course + * of execution. + * + * @return a list of memory pool beans, one for each pool. + */ + public static List getMemoryPoolMXBeans() + { + List poolBeans = + new ArrayList(); + String[] names = VMManagementFactory.getMemoryPoolNames(); + for (int a = 0; a < names.length; ++a) + try + { + poolBeans.add(new MemoryPoolMXBeanImpl(names[a])); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "memory pool bean, " + a + ", is " + + "not a compliant management bean."); + } + return poolBeans; + } + + /** + * Returns the memory manager beans for the running + * virtual machine. These may change during the course + * of execution. + * + * @return a list of memory manager beans, one for each manager. + */ + public static List getMemoryManagerMXBeans() + { + List managerBeans = + new ArrayList(); + String[] names = VMManagementFactory.getMemoryManagerNames(); + for (int a = 0; a < names.length; ++a) + try + { + managerBeans.add(new MemoryManagerMXBeanImpl(names[a])); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "memory manager bean, " + a + ", is " + + "not a compliant management bean."); + } + managerBeans.addAll(getGarbageCollectorMXBeans()); + return managerBeans; + } + + /** + * Returns the garbage collector beans for the running + * virtual machine. These may change during the course + * of execution. + * + * @return a list of garbage collector beans, one for each pool. + */ + public static List getGarbageCollectorMXBeans() + { + List gcBeans = + new ArrayList(); + String[] names = VMManagementFactory.getGarbageCollectorNames(); + for (int a = 0; a < names.length; ++a) + try + { + gcBeans.add(new GarbageCollectorMXBeanImpl(names[a])); + } + catch (NotCompliantMBeanException e) + { + throw new InternalError("The GNU implementation of the " + + "garbage collector bean, " + a + + ", is not a compliant management " + + "bean."); + } + return gcBeans; + } + + /** + *

      + * Returns the platform {@link javax.management.MBeanServer}. On the + * first call to this method, a server instance is retrieved from + * the {@link javax.management.MBeanServerFactory} and each of the + * beans are registered with it. Subsequent calls return the existing + * instance. If the property javax.management.builder.initial + * is set, its value will be used as the name of the class which is used + * to provide the server instance. + *

      + *

      + * It is recommended that the platform server is used for other beans as + * well, in order to simplify their discovery and publication. Name conflicts + * should be avoided. + *

      + * + * @return the platform {@link javax.management.MBeanServer} + * @throws SecurityException if a security manager exists and the + * caller's permissions don't imply {@link + * MBeanServerPermission(String)}("createMBeanServer") + * @see javax.management.MBeanServerFactory + * @see javax.management.MBeanServerFactory#createMBeanServer() + */ + public static MBeanServer getPlatformMBeanServer() + { + if (platformServer == null) + { + platformServer = MBeanServerFactory.createMBeanServer(); + try + { + platformServer.registerMBean(getOperatingSystemMXBean(), + new ObjectName(OPERATING_SYSTEM_MXBEAN_NAME)); + platformServer.registerMBean(getRuntimeMXBean(), + new ObjectName(RUNTIME_MXBEAN_NAME)); + platformServer.registerMBean(getClassLoadingMXBean(), + new ObjectName(CLASS_LOADING_MXBEAN_NAME)); + platformServer.registerMBean(getThreadMXBean(), + new ObjectName(THREAD_MXBEAN_NAME)); + platformServer.registerMBean(getMemoryMXBean(), + new ObjectName(MEMORY_MXBEAN_NAME)); + CompilationMXBean compBean = getCompilationMXBean(); + if (compBean != null) + platformServer.registerMBean(compBean, + new ObjectName(COMPILATION_MXBEAN_NAME)); + Iterator beans = getMemoryPoolMXBeans().iterator(); + while (beans.hasNext()) + { + MemoryPoolMXBean bean = (MemoryPoolMXBean) beans.next(); + platformServer.registerMBean(bean, + new ObjectName(MEMORY_POOL_MXBEAN_DOMAIN_TYPE + + ",name=" + + bean.getName())); + } + beans = getMemoryManagerMXBeans().iterator(); + while (beans.hasNext()) + { + MemoryManagerMXBean bean = (MemoryManagerMXBean) beans.next(); + platformServer.registerMBean(bean, + new ObjectName(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE + + ",name=" + + bean.getName())); + } + beans = getGarbageCollectorMXBeans().iterator(); + while (beans.hasNext()) + { + GarbageCollectorMXBean bean = (GarbageCollectorMXBean) beans.next(); + platformServer.registerMBean(bean, + new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + + ",name=" + + bean.getName())); + } + platformServer.registerMBean(LogManager.getLoggingMXBean(), + new ObjectName(LogManager.LOGGING_MXBEAN_NAME)); + } + catch (InstanceAlreadyExistsException e) + { + throw (Error) + (new InternalError("One of the management beans is " + + "already registered.").initCause(e)); + } + catch (MBeanRegistrationException e) + { + throw (Error) + (new InternalError("One of the management beans' preRegister " + + "methods threw an exception.").initCause(e)); + } + catch (NotCompliantMBeanException e) + { + throw (Error) + (new InternalError("One of the management beans is " + + "not compliant.").initCause(e)); + } + catch (MalformedObjectNameException e) + { + throw (Error) + (new InternalError("The object name of a management bean is " + + "not compliant.").initCause(e)); + } + } + return platformServer; + } + + /** + *

      + * Returns a proxy for the specified platform bean. A proxy object is created + * using Proxy.newProxyInstance(mxbeanInterface.getClassLoader(), + * new Class[] { mxbeanInterface }, handler). The + * {@link javax.management.NotificationEmitter} class is also added to the + * array if the bean provides notifications. handler refers + * to the invocation handler which forwards calls to the connection, and + * also provides translation between the Java data types used in the + * bean interfaces and the open data types, as specified in the description + * of this class. It is this translation that makes the + * usual {@link javax.management.MBeanServerInvocationHandler} inappropriate + * for providing such a proxy. + *

      + *

      + * Note: use of the proxy may result in + * {@link java.io.IOException}s from the underlying {@link MBeanServerConnection} + * and a {@link java.io.InvalidObjectException} if enum constants + * used on the client and the server don't match. + *

      + * + * @param connection the server connection to use to access the bean. + * @param mxbeanName the {@link javax.management.ObjectName} of the + * bean to provide a proxy for. + * @param mxbeanInterface the interface for the bean being proxied. + * @return a proxy for the specified bean. + * @throws IllegalArgumentException if mxbeanName is not a valid + * {@link javax.management.ObjectName}, + * the interface and name do not match the + * same bean, the name does not refer to a + * platform bean or the bean is not registered + * with the server accessed by connection. + * @throws IOException if the connection throws one. + */ + public static T newPlatformMXBeanProxy(MBeanServerConnection connection, + String mxbeanName, + Class mxbeanInterface) + throws IOException + { + if (!(mxbeanName.equals(CLASS_LOADING_MXBEAN_NAME) || + mxbeanName.equals(COMPILATION_MXBEAN_NAME) || + mxbeanName.startsWith(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE) || + mxbeanName.startsWith(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE) || + mxbeanName.equals(MEMORY_MXBEAN_NAME) || + mxbeanName.startsWith(MEMORY_POOL_MXBEAN_DOMAIN_TYPE) || + mxbeanName.equals(OPERATING_SYSTEM_MXBEAN_NAME) || + mxbeanName.equals(RUNTIME_MXBEAN_NAME) || + mxbeanName.equals(THREAD_MXBEAN_NAME))) + { + throw new IllegalArgumentException("The named bean, " + mxbeanName + + ", is not a platform name."); + } + if ((mxbeanName.equals(CLASS_LOADING_MXBEAN_NAME) && + mxbeanInterface != ClassLoadingMXBean.class) || + (mxbeanName.equals(COMPILATION_MXBEAN_NAME) && + mxbeanInterface != CompilationMXBean.class) || + (mxbeanName.startsWith(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE) && + mxbeanInterface != GarbageCollectorMXBean.class) || + (mxbeanName.startsWith(MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE) && + mxbeanInterface != MemoryManagerMXBean.class) || + (mxbeanName.equals(MEMORY_MXBEAN_NAME) && + mxbeanInterface != MemoryMXBean.class) || + (mxbeanName.startsWith(MEMORY_POOL_MXBEAN_DOMAIN_TYPE) && + mxbeanInterface != MemoryPoolMXBean.class) || + (mxbeanName.equals(OPERATING_SYSTEM_MXBEAN_NAME) && + mxbeanInterface != OperatingSystemMXBean.class) || + (mxbeanName.equals(RUNTIME_MXBEAN_NAME) && + mxbeanInterface != RuntimeMXBean.class) || + (mxbeanName.equals(THREAD_MXBEAN_NAME) && + mxbeanInterface != ThreadMXBean.class)) + throw new IllegalArgumentException("The interface, " + mxbeanInterface + + ", does not match the bean, " + mxbeanName); + ObjectName bean; + try + { + bean = new ObjectName(mxbeanName); + } + catch (MalformedObjectNameException e) + { + throw new IllegalArgumentException("The named bean is invalid."); + } + if (!(connection.isRegistered(bean))) + throw new IllegalArgumentException("The bean is not registered on this connection."); + Class[] interfaces; + if (mxbeanName.equals(MEMORY_MXBEAN_NAME)) + interfaces = new Class[] { mxbeanInterface, NotificationEmitter.class }; + else + interfaces = new Class[] { mxbeanInterface }; + return (T) Proxy.newProxyInstance(mxbeanInterface.getClassLoader(), + interfaces, + new ManagementInvocationHandler(connection, bean)); + } + + /** + * This invocation handler provides method calls for a platform bean + * by forwarding them to a {@link MBeanServerConnection}. Translation from + * Java data types to open data types is performed as specified above. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static class ManagementInvocationHandler + implements InvocationHandler + { + + /** + * The encapsulated connection. + */ + private MBeanServerConnection conn; + + /** + * The bean being proxied. + */ + private ObjectName bean; + + /** + * Constructs a new {@link InvocationHandler} which proxies + * for the specified bean using the supplied connection. + * + * @param conn the connection on which to forward method calls. + * @param bean the bean to proxy. + */ + public ManagementInvocationHandler(MBeanServerConnection conn, + ObjectName bean) + throws IOException + { + this.conn = conn; + this.bean = bean; + } + + /** + * Called by the proxy class whenever a method is called. The method + * is emulated by retrieving an attribute from, setting an attribute on + * or invoking a method on the server connection as required. Translation + * between the Java data types supplied as arguments to the open types used + * by the bean is provided, as well as translation of the return value back + * in to the appropriate Java type. + * + * @param proxy the proxy on which the method was called. + * @param method the method which was called. + * @param args the arguments supplied to the method. + * @return the return value from the method. + * @throws Throwable if an exception is thrown in performing the + * method emulation. + */ + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable + { + String name = method.getName(); + if (name.equals("toString")) + return "Proxy for " + bean + " using " + conn; + if (name.equals("addNotificationListener")) + { + conn.addNotificationListener(bean, + (NotificationListener) args[0], + (NotificationFilter) args[1], + args[2]); + return null; + } + if (name.equals("getNotificationInfo")) + return conn.getMBeanInfo(bean).getNotifications(); + if (name.equals("removeNotificationListener")) + { + if (args.length == 1) + conn.removeNotificationListener(bean, + (NotificationListener) + args[0]); + else + conn.removeNotificationListener(bean, + (NotificationListener) + args[0], + (NotificationFilter) + args[1], args[2]); + return null; + } + String attrib = null; + if (name.startsWith("get")) + attrib = name.substring(3); + else if (name.startsWith("is")) + attrib = name.substring(2); + if (attrib != null) + return translate(conn.getAttribute(bean, attrib), method); + else if (name.startsWith("set")) + { + conn.setAttribute(bean, new Attribute(name.substring(3), + args[0])); + return null; + } + else + return translate(conn.invoke(bean, name, args, null), method); + } + + /** + * Translates the returned open data type to the value + * required by the interface. + * + * @param otype the open type returned by the method call. + * @param method the method that was called. + * @return the equivalent return type required by the interface. + * @throws Throwable if an exception is thrown in performing the + * conversion. + */ + private final Object translate(Object otype, Method method) + throws Throwable + { + Class returnType = method.getReturnType(); + if (returnType.isEnum()) + { + String ename = (String) otype; + Enum[] constants = (Enum[]) returnType.getEnumConstants(); + for (Enum c : constants) + if (c.name().equals(ename)) + return c; + } + if (List.class.isAssignableFrom(returnType)) + { + Object[] elems = (Object[]) otype; + List l = new ArrayList(elems.length); + for (Object elem : elems) + l.add(elem); + return l; + } + if (Map.class.isAssignableFrom(returnType)) + { + TabularData data = (TabularData) otype; + Map m = new HashMap(data.size()); + for (Object val : data.values()) + { + CompositeData vals = (CompositeData) val; + m.put(vals.get("key"), vals.get("value")); + } + return m; + } + try + { + Method m = returnType.getMethod("from", + new Class[] + { CompositeData.class }); + return m.invoke(null, (CompositeData) otype); + } + catch (NoSuchMethodException e) + { + /* Ignored; we expect this if this + isn't a from(CompositeData) class */ + } + return otype; + } + + } +} diff --git a/libjava/classpath/java/lang/management/ManagementPermission.java b/libjava/classpath/java/lang/management/ManagementPermission.java new file mode 100644 index 000000000..73794571c --- /dev/null +++ b/libjava/classpath/java/lang/management/ManagementPermission.java @@ -0,0 +1,131 @@ +/* ManagementPermission.java - Permissions for system management. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import java.security.BasicPermission; + +/** + *

      + * Represents the permission to view or modify the data + * which forms part of the system management interfaces. + * Calls to methods of the system management beans, + * provided by the {@link ManagementFactory}, may perform + * checks against the current {@link java.lang.SecurityManager} + * (if any) before allowing the operation to proceed. + * Instances of this object are supplied to the + * {@link java.lang.SecurityManager} in order to perform + * these checks. It is not normal for instances of this + * class to be created outside the use of the + * {@link java.lang.SecurityManager}. + *

      + *

      + * This object can represent two types of management + * permission: + *

      + *
        + *
      • monitor — this allows access + * to information such as the arguments supplied to the + * virtual machine, the currently loaded classes and the + * stack traces of running threads. Malicious code may + * use this to obtain information about the system and + * exploit any vulnerabilities found.
      • + *
      • control — this allows the + * information stored by the management beans to be altered. + * For example, additional debugging information (such + * as class loading traces) may be turned on or memory + * usage limits changed. Malicious code could use + * this to alter the behaviour of the system.
      • + *
      + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public final class ManagementPermission + extends BasicPermission +{ + + /** + * Compatible with JDK 1.5 + */ + private static final long serialVersionUID = 1897496590799378737L; + + /** + * Constructs a new ManagementPermission + * for one of the two permission targets, "monitor" + * and "control". + * + * @param name the name of the permission this instance + * should represent; either "monitor" or + * "control". + * @throws IllegalArgumentException if the name is not + * either "monitor" + * or "control". + */ + public ManagementPermission(String name) + { + super(name); + if (!(name.equals("monitor") || name.equals("control"))) + throw new IllegalArgumentException("Invalid permission."); + } + + /** + * Constructs a new ManagementPermission + * for one of the two permission targets, "monitor" + * and "control". Actions are not supported, so + * this value should be either null + * or the empty string. + * + * @param name the name of the permission this instance + * should represent; either "monitor" or + * "control". + * @param actions either null or the + * empty string. + * @throws IllegalArgumentException if the name is not + * either "monitor" + * or "control", or + * a value for actions + * is specified. + */ + public ManagementPermission(String name, String actions) + { + this(name); + if (!(actions == null || actions.equals(""))) + throw new IllegalArgumentException("Invalid actions."); + } + +} diff --git a/libjava/classpath/java/lang/management/MemoryMXBean.java b/libjava/classpath/java/lang/management/MemoryMXBean.java new file mode 100644 index 000000000..acf0e6795 --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryMXBean.java @@ -0,0 +1,172 @@ +/* MemoryMXBean.java - Interface for a memory bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + *

      + * Provides access to information about the memory used + * by the virtual machine. An instance of this bean is + * obtained by calling + * {@link ManagementFactory#getMemoryMXBean()}. + *

      + *

      + * The Java virtual machine uses two types of memory: + * heap memory and non-heap memory. The heap is the + * storage location for class and array instances, and is + * thus the main source of memory associated with running + * Java programs. The heap is created when the virtual + * machine is started, and is periodically scanned by the + * garbage collector(s), in order to reclaim memory which + * is no longer used (e.g. because an object reference has + * gone out of scope). + *

      + *

      + * Non-heap memory is used by the virtual machine in order to + * perform its duties. Thus, it mainly acts as the storage + * location for structures created as a result of parsing Java + * bytecode, such as the constant pool and constructor/method + * declarations. When a Just-In-Time (JIT) compiler is in + * operation, this will use non-heap memory to store compiled + * bytecode. + *

      + *

      + * Both types of memory may be non-contiguous. During the + * lifetime of the virtual machine, the size of both may + * either change (either expanding or contracting) or stay + * the same. + *

      + *

      Notifications

      + *

      + * Implementations of this interface also conform to the + * {@link javax.management.NotificationEmitter} interface, + * and supply two notifications reflecting memory usage. + * These notifications occur when a usage threshold is + * exceeded; for more details of these, see the documentation + * of {@link MemoryPoolMXBean}. If threshold monitoring + * is supported, then a notification will be emitted each time + * the threshold is crossed. Another notification will not + * be emitted unless the usage level has dropped below the + * threshold again in the meantime. + *

      + *

      + * The emitted notifications are instances of + * {@link javax.management.Notification}, with a type of + * either + * {@link java.lang.management.MemoryNotificationInfo#MEMORY_THRESHOLD_EXCEEDED} + * or + * {@link java.lang.management.MemoryNotificationInfo#MEMORY_COLLECTION_THRESHOLD_EXCEEDED} + * (depending on whether the notification refers to the general + * usage threshold or the garbage collection threshold) and an instance + * of {@link java.lang.management.MemoryNotificationInfo} contained + * in the user data section. This is wrapped inside an instance + * of {@link javax.management.openmbean.CompositeData}, as explained + * in the documentation for + * {@link java.lang.management.MemoryNotificationInfo}. + *

      + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface MemoryMXBean +{ + + /** + * Instigates a garbage collection cycle. The virtual + * machine's garbage collector should make the best + * attempt it can at reclaiming unused memory. This + * is equivalent to invoking {@link java.lang.System.gc()}. + * + * @see java.lang.System#gc() + */ + void gc(); + + /** + * Returns a {@link MemoryUsage} object representing the + * current state of the heap. This incorporates various + * statistics on both the initial and current memory + * allocations used by the heap. + * + * @return a {@link MemoryUsage} object for the heap. + */ + MemoryUsage getHeapMemoryUsage(); + + /** + * Returns a {@link MemoryUsage} object representing the + * current state of non-heap memory. This incorporates + * various statistics on both the initial and current + * memory allocations used by non-heap memory.. + * + * @return a {@link MemoryUsage} object for non-heap + * memory. + */ + MemoryUsage getNonHeapMemoryUsage(); + + /** + * Returns the number of objects which are waiting to + * be garbage collected (finalized). An object is + * finalized when the garbage collector determines that + * there are no more references to that object are in + * use. + * + * @return the number of objects awaiting finalization. + */ + int getObjectPendingFinalizationCount(); + + /** + * Returns true if the virtual machine will emit additional + * information when memory is allocated and deallocated. The + * format of the output is left up to the virtual machine. + * + * @return true if verbose memory output is on. + */ + boolean isVerbose(); + + /** + * Turns on or off the emission of additional information + * when memory is allocated and deallocated. The format of the + * output is left up to the virtual machine. This method + * may be called by multiple threads concurrently, but there + * is only one global setting of verbosity that is affected. + * + * @param verbose the new setting for verbose memory output. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + */ + void setVerbose(boolean verbose); + +} diff --git a/libjava/classpath/java/lang/management/MemoryManagerMXBean.java b/libjava/classpath/java/lang/management/MemoryManagerMXBean.java new file mode 100644 index 000000000..1b10e998f --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryManagerMXBean.java @@ -0,0 +1,77 @@ +/* MemoryManagerMXBean.java - Interface for a memory manager bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the memory managers + * of the virtual machine. An instance of this bean for each + * memory manager is obtained by calling + * {@link ManagementFactory#getMemoryManagerMXBeans()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface MemoryManagerMXBean +{ + + /** + * Returns an array containing the names of the memory pools + * this memory manager manages. + * + * @return an array containing the name of each memory pool + * this manager is responsible for. + */ + String[] getMemoryPoolNames(); + + /** + * Returns the name of the memory manager. + * + * @return the memory manager name. + */ + String getName(); + + /** + * Returns true if this memory manager is still valid. A memory + * manager becomes invalid when it is removed by the virtual machine + * and no longer used. + * + * @return true if this memory manager is valid. + */ + boolean isValid(); + +} diff --git a/libjava/classpath/java/lang/management/MemoryNotificationInfo.java b/libjava/classpath/java/lang/management/MemoryNotificationInfo.java new file mode 100644 index 000000000..85856e058 --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryNotificationInfo.java @@ -0,0 +1,214 @@ +/* MemoryNotificationInfo.java - Emitted memory notification info. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import gnu.java.lang.management.MemoryMXBeanImpl; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.SimpleType; + +/** + *

      + * Represents the content of a notification emitted by the + * {@link MemoryMXBean}. Such notifications are emitted when + * one of the memory pools exceeds its usage or collection + * usage threshold. This object contains the following information, + * representing the state of the pool at the time of the + * notification: + *

      + *
        + *
      • The name of the pool.
      • + *
      • The memory usage of the pool at the time of notification.
      • + *
      • The number of times the pool has exceeded this particular + * threshold in the past.
      • + *
      + *

      + * Two types of notification are emitted by the {@link MemoryMXBean}: + * one for exceeding the usage threshold and one for exceeding the + * collection usage threshold. The value returned by {@link #getCount()} + * is dependent on this type; if the threshold exceeded is the usage + * threshold, then the usage threshold count is returned. If, instead, + * the collection usage threshold is exceeded, then the collection usage + * threshold count is returned. + *

      + *

      + * This data is held in the user data part of the notification (returned + * by {@link javax.management.Notification#getUserData()}) encapsulated in + * a {@link javax.management.openmbean.CompositeData} object. The + * {@link #from(javax.management.openmbean.CompositeData)} method may be + * used to unwrap the value and obtain an instance of this class. + *

      + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MemoryNotificationInfo +{ + + /** + * The type of notification emitted when the usage threshold is exceeded. + * After a notification is emitted, the usage level must drop below the + * threshold again before another notification is emitted. The value is + * java.management.memory.threshold.exceeded. + */ + public static final String MEMORY_THRESHOLD_EXCEEDED = + "java.management.memory.threshold.exceeded"; + + /** + * The type of notification emitted when the collection usage threshold + * is exceeded, following a garbage collection cycle. The value is + * java.management.memory.collection.threshold.exceeded. + */ + public static final String MEMORY_COLLECTION_THRESHOLD_EXCEEDED = + "java.management.memory.collection.threshold.exceeded"; + + /** + * The name of the memory pool which exceeded the threshold. + */ + private String poolName; + + /** + * The usage level of the memory pool at the time of notification. + */ + private MemoryUsage usage; + + /** + * The number of times the threshold has been crossed. + */ + private long count; + + /** + * Constructs a new {@link MemoryNotificationInfo} object using the + * specified pool name, usage level and threshold crossing count. + * + * @param poolName the name of the pool which has exceeded a threshold. + * @param usage the usage level of the pool at the time of notification. + * @param count the number of times the threshold has been crossed. + */ + public MemoryNotificationInfo(String poolName, MemoryUsage usage, long count) + { + this.poolName = poolName; + this.usage = usage; + this.count = count; + } + + /** + *

      + * Returns a {@link MemoryNotificationInfo} instance using the values + * given in the supplied + * {@link javax.management.openmbean.CompositeData} object. + * The composite data instance should contain the following + * attributes with the specified types: + *

      + * + * + * + * + * + *
      NameType
      poolNamejava.lang.String
      usagejavax.management.openmbean.CompositeData + *
      countjava.lang.Long
      + *

      + * The usage level is further described as: + *

      + * + * + * + * + * + * + *
      NameType
      initjava.lang.Long
      usedjava.lang.Long
      committedjava.lang.Long
      maxjava.lang.Long
      + * + * @param data the composite data structure to take values from. + * @return a new instance containing the values from the + * composite data structure, or null + * if the data structure was also null. + * @throws IllegalArgumentException if the composite data structure + * does not match the structure + * outlined above. + */ + public static MemoryNotificationInfo from(CompositeData data) + { + if (data == null) + return null; + CompositeType type = data.getCompositeType(); + ThreadInfo.checkAttribute(type, "poolName", SimpleType.STRING); + ThreadInfo.checkAttribute(type, "usage", MemoryMXBeanImpl.usageType); + ThreadInfo.checkAttribute(type, "count", SimpleType.LONG); + MemoryUsage usage = MemoryUsage.from((CompositeData) data.get("usage")); + return new MemoryNotificationInfo(((String) data.get("poolName")), + usage, + ((Long) data.get("count")).longValue()); + } + + /** + * Returns the number of times the memory pool has crossed the usage + * threshold, as of the time of notification. If this is the notification + * represented by the type {@link #MEMORY_THRESHOLD_EXCEEDED}, then the + * count is the usage threshold count. If this is the notification + * represented by the type {@link #MEMORY_COLLECTION_THRESHOLD_EXCEEDED}, + * then the count is the collection usage threshold count. + * + * @return the number of times the appropriate threshold has been crossed. + */ + public long getCount() + { + return count; + } + + /** + * Returns the name of the pool which has crossed a threshold. + * + * @return the name of the pool. + */ + public String getPoolName() + { + return poolName; + } + + /** + * Returns the usage levels at the time of notification. + * + * @return the usage levels. + */ + public MemoryUsage getUsage() + { + return usage; + } + +} diff --git a/libjava/classpath/java/lang/management/MemoryPoolMXBean.java b/libjava/classpath/java/lang/management/MemoryPoolMXBean.java new file mode 100644 index 000000000..9729093b2 --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryPoolMXBean.java @@ -0,0 +1,318 @@ +/* MemoryPoolMXBean.java - Interface for a memory pool bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + *

      + * Provides access to information about one of the memory + * resources or pools used by the virtual machine. Instances + * of this bean are obtained by calling + * {@link ManagementFactory#getMemoryPoolMXBeans()}. One + * bean is returned for each memory pool provided. + *

      + *

      + * The memory pool bean allows the usage of the pool to be + * monitored. The bean can provide statistics on the current + * and peak usage of the pool, and on the threshold levels the + * pool uses. + *

      + *

      + * {@link getUsage()} returns an approximation of the current + * usage of the pool. Calls to this method are expected to be + * generally quick to perform; if the call is expensive, the + * documentation of the bean should specify so. For memory + * pool beans that represent the memory used by garbage + * collectors, the usage level includes both referenced and + * unreferenced objects. + *

      + *

      + * {@link getPeakUsage()} and {@link resetPeakUsage()} enable + * the retrieval of the peak usage level and setting it to the + * current usage level, respectively. Initially, the peak usage + * level is relative to the start of the virtual machine. + *

      + *

      + * Memory pools may also include optional support for usage thresholds. + * The usage threshold is a particular level of memory usage. When this + * value is crossed (the current memory usage becomes equal to or greater + * than this threshold level), the usage threshold count is increased. + * This feature is designed for monitoring the trend in memory usage. + * Support for a collection usage threshold is also provided, for + * particular garbage collectors. This is used to monitor the amount + * of memory left uncollected after a garbage collection cycle. There + * is no need to make special garbage collection runs to support this; + * the level following collection just needs to be monitored. + *

      + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface MemoryPoolMXBean +{ + + /** + * Returns memory usage statistics after a best-effort attempt + * has been made to remove unused objects from the pool. This + * method is designed for use by the pools of garbage collectors, + * in order to monitor the amount of memory used after collections. + * It will return null if such functionality is + * unsupported by the memory pool represented by this bean. + * + * @return the memory usage of the memory pool after the most + * recent garbage collection cycle, or null + * if this operation is not supported. + */ + MemoryUsage getCollectionUsage(); + + /** + * Returns the collection usage threshold level in bytes. This + * value is initially zero. + * + * @return the collection usage threshold in bytes. + * @throws UnsupportedOperationException if the collection usage + * threshold is not supported. + * @see #getCollectionUsageThresholdCount() + * @see #isCollectionUsageThresholdExceeded() + * @see #isCollectionUsageThresholdSupported() + * @see #setCollectionUsageThreshold(long) + */ + long getCollectionUsageThreshold(); + + /** + * Returns the number of times the usage level has matched or + * exceeded the collection usage threshold. + * + * @return the number of times the usage level has matched + * or exceeded the collection usage threshold. + * @throws UnsupportedOperationException if the collection usage + * threshold is not supported. + * @see #getCollectionUsageThreshold() + * @see #isCollectionUsageThresholdExceeded() + * @see #isCollectionUsageThresholdSupported() + * @see #setCollectionUsageThreshold(long) + */ + long getCollectionUsageThresholdCount(); + + /** + * Returns the names of the memory managers associated with this + * pool. Each pool has at least one memory manager. + * + * @return an array containing the name of each memory manager + * responsible for this pool. + */ + String[] getMemoryManagerNames(); + + /** + * Returns the name of the memory pool. + * + * @return the memory pool name. + */ + String getName(); + + /** + * Returns memory usage statistics for the peak memory usage + * of the pool. The peak is the maximum memory usage occurring + * since the virtual machine was started or since the peak + * was reset by {@link #resetPeakUsage()}. The return value + * may be null if this pool is no longer valid. + * + * @return the memory usage of the memory pool at its peak, + * or null if this pool is no longer valid. + */ + MemoryUsage getPeakUsage(); + + /** + * Returns the type of memory used by this pool. This can be + * either heap or non-heap memory. + * + * @return the type of this pool. + */ + MemoryType getType(); + + /** + * Returns memory usage statistics for the current memory usage + * of the pool. The return value may be null if + * this pool is no longer valid. Obtaining these values is + * expected to be a relatively quick operation; if this will + * instead be an expensive operation to perform, the documentation + * of the implementating bean should specify that this is the + * case. The values are intended to be an estimate for monitoring + * purposes. + * + * @return the memory usage of the memory pool at present, + * or null if this pool is no longer valid. + */ + MemoryUsage getUsage(); + + /** + * Returns the usage threshold level in bytes. This + * value is initially defined by the virtual machine. + * + * @return the usage threshold in bytes. + * @throws UnsupportedOperationException if the usage threshold + * is not supported. + * @see #getUsageThresholdCount() + * @see #isUsageThresholdExceeded() + * @see #isUsageThresholdSupported() + * @see #setUsageThreshold(long) + */ + long getUsageThreshold(); + + /** + * Returns the number of times the usage level has matched or + * exceeded the usage threshold. + * + * @return the number of times the usage level has matched + * or exceeded the usage threshold. + * @throws UnsupportedOperationException if the usage threshold + * is not supported. + * @see #getUsageThreshold() + * @see #isUsageThresholdExceeded() + * @see #isUsageThresholdSupported() + * @see #setUsageThreshold(long) + */ + long getUsageThresholdCount(); + + /** + * Returns true if the collection usage level is equal to + * or greater than the collection usage threshold. + * + * @return true if the collection usage threshold has been + * matched or exceeded. + * @throws UnsupportedOperationException if the collection usage + * threshold is not supported. + * @see #getCollectionUsageThreshold() + * @see #getCollectionUsageThresholdCount() + * @see #isCollectionUsageThresholdSupported() + * @see #setCollectionUsageThreshold(long) + */ + boolean isCollectionUsageThresholdExceeded(); + + /** + * Returns true if this memory pool supports a collection usage + * level threshold. + * + * @return true if a collection usage level threshold is supported. + * @see #getCollectionUsageThreshold() + * @see #getCollectionUsageThresholdCount() + * @see #isCollectionUsageThresholdExceeded() + * @see #setCollectionUsageThreshold(long) + */ + boolean isCollectionUsageThresholdSupported(); + + /** + * Returns true if the usage level is equal to + * or greater than the usage threshold. + * + * @return true if the usage threshold has been + * matched or exceeded. + * @throws UnsupportedOperationException if the usage threshold + * is not supported. + * @see #getUsageThreshold() + * @see #getUsageThresholdCount() + * @see #isUsageThresholdSupported() + * @see #setUsageThreshold(long) + */ + boolean isUsageThresholdExceeded(); + + /** + * Returns true if this memory pool supports a usage level threshold. + * + * @return true if a usage level threshold is supported. + * @see #getUsageThreshold() + * @see #getUsageThresholdCount() + * @see #isUsageThresholdExceeded() + * @see #setUsageThreshold(long) + */ + boolean isUsageThresholdSupported(); + + /** + * Returns true if this memory pool is still valid. A memory pool + * becomes invalid when it is removed by the virtual machine and + * no longer used. + * + * @return true if this memory pool is valid. + */ + boolean isValid(); + + /** + * Resets the peak memory usage level to the current memory usage + * level. + * + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + */ + void resetPeakUsage(); + + /** + * Sets the collection threshold usage level to the given value. + * A value of zero disables the collection threshold. + * + * @param threshold the new threshold level. + * @throws IllegalArgumentException if the threshold hold level + * is negative. + * @throws UnsupportedOperationException if the collection usage + * threshold is not supported. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + * @see #getCollectionUsageThreshold() + * @see #getCollectionUsageThresholdCount() + * @see #isCollectionUsageThresholdExceeded() + * @see #isCollectionUsageThresholdSupported() + */ + void setCollectionUsageThreshold(long threshold); + + /** + * Sets the threshold usage level to the given value. A value of + * zero disables the threshold. + * + * @param threshold the new threshold level. + * @throws IllegalArgumentException if the threshold hold level + * is negative. + * @throws UnsupportedOperationException if the usage threshold + * is not supported. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + * @see #getUsageThreshold() + * @see #getUsageThresholdCount() + * @see #isUsageThresholdExceeded() + * @see #isUsageThresholdSupported() + */ + void setUsageThreshold(long threshold); + +} diff --git a/libjava/classpath/java/lang/management/MemoryType.java b/libjava/classpath/java/lang/management/MemoryType.java new file mode 100644 index 000000000..c864d5407 --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryType.java @@ -0,0 +1,50 @@ +/* MemoryType.java - Enumeration of the types of memory pools. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Enumerates the possible types of memory pools. A value of this + * type is returned by {@link MemoryPoolMXBean#getMemoryType()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public enum MemoryType +{ + HEAP, NON_HEAP; +} diff --git a/libjava/classpath/java/lang/management/MemoryUsage.java b/libjava/classpath/java/lang/management/MemoryUsage.java new file mode 100644 index 000000000..42aef3cc5 --- /dev/null +++ b/libjava/classpath/java/lang/management/MemoryUsage.java @@ -0,0 +1,264 @@ +/* MemoryUsage.java - Information on the usage of a memory pool. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.SimpleType; +/** + *

      + * Retains information on the usage of a particular memory + * pool, or heap/non-heap memory as a whole. Memory usage + * is represented by four values (all in bytes): + *

      + *
        + *
      • Initial Level: This is the initial + * amount of memory allocated for the pool by the operating + * system. This value may be undefined.
      • + *
      • Used Level: This is the amount of + * memory currently in use.
      • + *
      • Committed Level: This is the current + * amount of memory allocated for the pool by the operating + * system. This value will always be equal to or greater than + * the current amount of memory in use. It may drop below + * the initial amount, if the virtual machine judges this to + * be practical.
      • + *
      • Maximum Level: This is the maximum + * amount of memory that may be allocated for the pool by + * the operating system. Like the initial amount, it may + * be undefined. If it is defined, it will be greater than + * or equal to the used and committed amounts and may change + * over time. It is not guaranteed that the maximum amount + * of memory may actually be allocated to the pool. For + * example, a request for an amount of memory greater than + * the current committed level, but less than the maximum, + * may still fail due to resources at the operating system + * level not being sufficient to fulfill the demand.
      • + *
      + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + * @see MemoryMXBean + * @see MemoryPoolMXBean + */ +public class MemoryUsage +{ + + /** + * The initial amount of memory allocated. + */ + private long init; + + /** + * The amount of memory used. + */ + private long used; + + /** + * The amount of memory committed for use. + */ + private long committed; + + /** + * The maximum amount of memory available. + */ + private long maximum; + + /** + * Constructs a new {@link MemoryUsage} object with + * the specified allocation levels. + * + * @param init the initial amount of memory allocated, + * or -1 if this value is undefined. Must + * be >= -1. + * @param used the amount of memory used. Must be >= 0, + * and <= committed. + * @param committed the amount of memory committed for use + * at present. Must be >= 0 and <= + * maximum (if defined). + * @param maximum the maximum amount of memory that may be + * used, or -1 if this value is undefined. + * Must be >= -1. + * @throws IllegalArgumentException if the values break any + * of the limits specified + * above. + */ + public MemoryUsage(long init, long used, long committed, + long maximum) + { + if (init < -1) + throw new IllegalArgumentException("Initial value of " + + init + " is too small."); + if (used < 0) + throw new IllegalArgumentException("Used value of " + + used + " is too small."); + if (committed < 0) + throw new IllegalArgumentException("Committed value of " + + committed + " is too small."); + if (committed < used) + throw new IllegalArgumentException("Committed value of " + + committed + " is below " + + used + ", the amount used."); + if (maximum < -1) + throw new IllegalArgumentException("Maximum value of " + + maximum + " is too small."); + if (maximum != -1 && maximum < committed) + throw new IllegalArgumentException("Maximum value of " + + maximum + " is below " + + committed + ", the amount " + + "committed."); + this.init = init; + this.used = used; + this.committed = committed; + this.maximum = maximum; + } + + /** + *

      + * Returns a {@link MemoryUsage} instance using the values + * given in the supplied + * {@link javax.management.openmbean.CompositeData} object. + * The composite data instance should contain the following + * attributes: + *

      + *
        + *
      • init
      • + *
      • used
      • + *
      • committed
      • + *
      • max
      • + *
      + *

      + * All should have the type, java.lang.Long. + *

      + * + * @param data the composite data structure to take values from. + * @return a new instance containing the values from the + * composite data structure, or null + * if the data structure was also null. + * @throws IllegalArgumentException if the composite data structure + * does not match the structure + * outlined above, or the values + * are invalid. + */ + public static MemoryUsage from(CompositeData data) + { + if (data == null) + return null; + CompositeType type = data.getCompositeType(); + ThreadInfo.checkAttribute(type, "Init", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "Used", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "Committed", SimpleType.LONG); + ThreadInfo.checkAttribute(type, "Max", SimpleType.LONG); + return new MemoryUsage(((Long) data.get("Init")).longValue(), + ((Long) data.get("Used")).longValue(), + ((Long) data.get("Committed")).longValue(), + ((Long) data.get("Max")).longValue()); + } + + /** + * Returns the amount of memory committed for use by this + * memory pool (in bytes). This amount is guaranteed to + * be available, unlike the maximum. + * + * @return the committed amount of memory. + */ + public long getCommitted() + { + return committed; + } + + /** + * Returns the initial amount of memory allocated to the + * pool (in bytes). This method may return -1, if the + * value is undefined. + * + * @return the initial amount of memory allocated, or -1 + * if this value is undefined. + */ + public long getInit() + { + return init; + } + + /** + * Returns the maximum amount of memory available for this + * pool (in bytes). This amount is not guaranteed to + * actually be usable. This method may return -1, if the + * value is undefined. + * + * @return the maximum amount of memory available, or -1 + * if this value is undefined. + */ + public long getMax() + { + return maximum; + } + + /** + * Returns the amount of memory used (in bytes). + * + * @return the amount of used memory. + */ + public long getUsed() + { + return used; + } + + /** + * Returns a {@link java.lang.String} representation of + * this {@link MemoryUsage} object. This takes the form + * java.lang.management.MemoryUsage[init=i, used=u, + * committed=c, maximum=m], where i + * is the initial level, u is the used level, + * c is the committed level and m + * is the maximum level. + * + * @return the string specified above. + */ + public String toString() + { + int megabyte = 1024 * 1024; + return getClass().getName() + + "[init=" + init + " bytes (~" + (init / megabyte) + + "MB), used=" + used + " bytes (~" + (used / megabyte) + + "MB), committed=" + committed + " bytes (~" + (committed / megabyte) + + "MB), maximum=" + maximum + " bytes (~" + (maximum / megabyte) + + "MB)]"; + } + +} diff --git a/libjava/classpath/java/lang/management/MonitorInfo.java b/libjava/classpath/java/lang/management/MonitorInfo.java new file mode 100644 index 000000000..fd7233716 --- /dev/null +++ b/libjava/classpath/java/lang/management/MonitorInfo.java @@ -0,0 +1,179 @@ +/* MonitorInfo.java - Information on a monitor lock. + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.SimpleType; + +/** + * Provides information on a monitor lock held by a thread. + * A monitor lock is obtained when a thread enters a synchronized + * block or method. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public class MonitorInfo + extends LockInfo +{ + + /** + * The stack depth at which the lock was obtained. + */ + private int stackDepth; + + /** + * The stack frame at which the lock was obtained. + */ + private StackTraceElement stackFrame; + + /** + * Constructs a new {@link MonitorInfo} using the specified + * lock class name and identity hash code, and the given + * stack depth and frame. + * + * @param className the class name of the lock object. + * @param identityHashCode the identity hash code of the lock object. + * @param stackDepth the depth of the stack at which the lock + * was obtained. + * @param stackFrame the frame of the stack at which the lock was + * obtained. + * @throws IllegalArgumentException if the stack depth and frame are + * inconsistent i.e. the frame is + * null but the depth is + * ≥ 0, or the frame is not + * null but the depth is + * < 0. + */ + public MonitorInfo(String className, int identityHashCode, int stackDepth, + StackTraceElement stackFrame) + { + super(className, identityHashCode); + if (stackFrame == null && stackDepth >= 0) + throw new IllegalArgumentException("The stack frame is null, but the " + + "stack depth is greater than or equal " + + "to zero."); + if (stackFrame != null && stackDepth < 0) + throw new IllegalArgumentException("The stack frame is not null, but the " + + "stack depth is less than zero."); + this.stackDepth = stackDepth; + this.stackFrame = stackFrame; + } + + /** + *

      + * Returns a {@link MonitorInfo} instance using the values + * given in the supplied + * {@link javax.management.openmbean.CompositeData} object. + * The composite data instance should contain the following + * attributes with the specified types: + *

      + * + * + * + * + * + * + *
      NameType
      classNamejava.lang.String
      identityHashCodejava.lang.Integer
      lockedStackDepthjava.lang.Integer
      lockedStackFramejavax.management.openmbean.CompositeData + *
      + *

      + * The stack trace is further described as: + *

      + * + * + * + * + * + * + * + *
      NameType
      classNamejava.lang.String
      methodNamejava.lang.String
      fileNamejava.lang.String
      lineNumberjava.lang.Integer
      nativeMethodjava.lang.Boolean
      + * + * @param data the composite data structure to take values from. + * @return a new instance containing the values from the + * composite data structure, or null + * if the data structure was also null. + * @throws IllegalArgumentException if the composite data structure + * does not match the structure + * outlined above. + */ + public static MonitorInfo from(CompositeData data) + { + if (data == null) + return null; + CompositeType type = data.getCompositeType(); + ThreadInfo.checkAttribute(type, "ClassName", SimpleType.STRING); + ThreadInfo.checkAttribute(type, "IdentityHashCode", SimpleType.INTEGER); + ThreadInfo.checkAttribute(type, "LockedStackDepth", SimpleType.INTEGER); + ThreadInfo.checkAttribute(type, "LockedStackFrame", + ThreadInfo.getStackTraceType()); + CompositeData frame = (CompositeData) data.get("LockedStackFrame"); + return new MonitorInfo((String) data.get("ClassName"), + (Integer) data.get("IdentityHashCode"), + (Integer) data.get("LockedStackDepth"), + new StackTraceElement((String) frame.get("ClassName"), + (String) frame.get("MethodName"), + (String) frame.get("FileName"), + (Integer) frame.get("LineNumber"))); + } + + /** + * Returns the depth of the stack at which the lock was obtained. + * This works as an index into the array returned by + * {@link ThreadInfo#getStackTrace()}. + * + * @return the depth of the stack at which the lock was obtained, + * or a negative number if this information is unavailable. + */ + public int getLockedStackDepth() + { + return stackDepth; + } + + /** + * Returns the stack frame at which the lock was obtained. + * + * @return the stack frame at which the lock was obtained, + * or null if this informati0on is unavailable. + */ + public StackTraceElement getLockedStackFrame() + { + return stackFrame; + } + +} diff --git a/libjava/classpath/java/lang/management/OperatingSystemMXBean.java b/libjava/classpath/java/lang/management/OperatingSystemMXBean.java new file mode 100644 index 000000000..dcc963ef1 --- /dev/null +++ b/libjava/classpath/java/lang/management/OperatingSystemMXBean.java @@ -0,0 +1,119 @@ +/* OperatingSystemMXBean.java - Interface for an operating system bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + * Provides access to information about the underlying operating + * system. An instance of this bean is obtained by calling + * {@link ManagementFactory#getOperatingSystemMXBean()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface OperatingSystemMXBean +{ + + /** + * Returns the name of the underlying system architecture. This + * is equivalent to obtaining the os.arch property + * via {@link System#getProperty(String)}. + * + * @return the name of the underlying system architecture on which + * the VM is running. + * @throws SecurityException if a security manager exists which + * prevents access to the name property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getArch(); + + /** + * Returns the number of processors currently available to the + * virtual machine. This number is subject to change during + * execution of the virtual machine, and will always be >= 1. + * The call is equivalent to {@link Runtime#availableProcessors()}. + * + * @return the number of processors available to the VM. + */ + int getAvailableProcessors(); + + /** + * Returns the name of the underlying operating system. This + * is equivalent to obtaining the os.name property + * via {@link System#getProperty(String)}. + * + * @return the name of the operating system on which the VM + * is running. + * @throws SecurityException if a security manager exists which + * prevents access to the name property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getName(); + + /** + * Returns the system load average for the last minute, or -1 + * if this is unavailable. The availability and calculation + * of the load average is system-dependent, but is usually + * a damped time-dependent average obtained by monitoring the + * number of queued and running processes. It is expected + * that this method will be called frequently to monitor the + * average over time, so it may not be implemented on systems + * where such a call is expensive. + * + * @return the system load average for the last minute, or -1 + * if this is unavailable. + * @since 1.6 + */ + double getSystemLoadAverage(); + + /** + * Returns the version of the underlying operating system. This + * is equivalent to obtaining the os.version property + * via {@link System#getProperty(String)}. + * + * @return the version of the operating system on which the VM + * is running. + * @throws SecurityException if a security manager exists which + * prevents access to the name property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getVersion(); + +} diff --git a/libjava/classpath/java/lang/management/RuntimeMXBean.java b/libjava/classpath/java/lang/management/RuntimeMXBean.java new file mode 100644 index 000000000..1e2aa017f --- /dev/null +++ b/libjava/classpath/java/lang/management/RuntimeMXBean.java @@ -0,0 +1,278 @@ +/* RuntimeMXBean.java - Interface for a runtime bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import java.util.List; +import java.util.Map; + +/** + * Provides access to information about the underlying virtual + * machine. An instance of this bean is obtained by calling + * {@link ManagementFactory#getRuntimeMXBean()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface RuntimeMXBean +{ + + /** + *

      + * Returns the boot classpath used by the virtual machine. This + * value follows the standard path syntax used by the underlying + * operating system (e.g. directories separated by ':' on UNIX + * or ';' on Windows). + *

      + *

      + * Supplying this value is optional. Users should check the + * return value of {@link isBootClassPathSupported()} prior to + * calling this method. + *

      + * + * @return the boot classpath of the virtual machine, if supported. + * @throws UnsupportedOperationException in cases where this + * functionality is not + * supported by the VM. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @see #isBootClassPathSupported() + * @see java.lang.management.ManagementPermission + */ + String getBootClassPath(); + + /** + * Returns the classpath used by the system classloader. This + * is equivalent to obtaining the java.class.path + * property via {@link System#getProperty(String)}. This value + * follows the standard path syntax used by the underlying operating + * system (e.g. directories separated by ':' on UNIX or ';' on + * Windows). + * + * @return the classpath used by the system class loader. + * @throws SecurityException if a security manager exists which + * prevents access to the classpath + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getClassPath(); + + /** + * Returns a list of the arguments given to the virtual machine, + * excluding those that apply to the main() method + * of the class file being executed. These may not just be those + * specified at the command line, but may also include arguments + * from environment variables, configuration files, etc. All + * command line arguments may not reach the virtual machine, so + * these are not included in this list. + * + * @return a list of arguments passed to the virtual machine. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @see java.lang.management.ManagementPermission + */ + List getInputArguments(); + + /** + * Returns the library path. This is equivalent to obtaining the + * java.library.path property via + * {@link System#getProperty(String)}. This value follows the + * standard path syntax used by the underlying operating + * system (e.g. directories separated by ':' on UNIX or ';' on + * Windows). + * + * @return the library path. + * @throws SecurityException if a security manager exists which + * prevents access to the library path + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getLibraryPath(); + + /** + * Returns the version of the management specification + * implemented by the virtual machine. + * + * @return the version of the management specification + * implemented. + */ + String getManagementSpecVersion(); + + /** + * Returns the name of this virtual machine. The content + * of this property is left up to the developer of the + * virtual machine. It may include a number of system + * attributes and may differ between instances of the + * same virtual machine (for example, it might include + * the process identifier or the host name of the machine + * on which it is running). The intention is that this + * name refers to the precise entity that the other data + * supplied by this bean refers to, rather than the VM + * in general. + * + * @return the name of this virtual machine. + */ + String getName(); + + /** + * Returns the specification name of the virtual machine. + * This is equivalent to obtaining the + * java.vm.specification.name property via + * {@link System#getProperty(String)}. + * + * @return the specification name of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM + * specification name property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getSpecName(); + + /** + * Returns the specification vendor of the virtual machine. + * This is equivalent to obtaining the + * java.vm.specification.vendor property via + * {@link System#getProperty(String)}. + * + * @return the specification vendor of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM + * specification vendor property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getSpecVendor(); + + /** + * Returns the specification version of the virtual machine. + * This is equivalent to obtaining the + * java.vm.specification.version property via + * {@link System#getProperty(String)}. + * + * @return the specification version of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM + * specification version property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getSpecVersion(); + + /** + * Returns the approximate start time of the virtual machine + * in milliseconds. + * + * @return the start time of the virtual machine. + */ + long getStartTime(); + + /** + * Returns a map containing the keys and values of the system + * properties. This gives largely the same result as calling + * {@link System#getProperties()}, but the resulting map + * is filtered so as to only provide keys and values that + * are Strings. + * + * @return the map of system properties. + */ + Map getSystemProperties(); + + /** + * Returns the uptime of the virtual machine in milliseconds. + * + * @return the uptime of the virtual machine. + */ + long getUptime(); + + /** + * Returns the implementation name of the virtual machine. + * This is equivalent to obtaining the + * java.vm.name property via + * {@link System#getProperty(String)}. + * + * @return the implementation name of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM name + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getVmName(); + + /** + * Returns the implementation vendor of the virtual machine. + * This is equivalent to obtaining the + * java.vm.vendor property via + * {@link System#getProperty(String)}. + * + * @return the implementation vendor of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM vendor + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getVmVendor(); + + /** + * Returns the implementation version of the virtual machine. + * This is equivalent to obtaining the + * java.vm.version property via + * {@link System#getProperty(String)}. + * + * @return the implementation version of the VM. + * @throws SecurityException if a security manager exists which + * prevents access to the VM version + * property. + * @see java.lang.System#getProperty(String) + * @see java.lang.SecurityManager#checkPropertyAccess(String) + */ + String getVmVersion(); + + /** + * Returns true if the virtual machine supports the boot classpath + * mechanism. + * + * @return true if the boot classpath property is supported by the + * virtual machine. + */ + boolean isBootClassPathSupported(); + +} diff --git a/libjava/classpath/java/lang/management/ThreadInfo.java b/libjava/classpath/java/lang/management/ThreadInfo.java new file mode 100644 index 000000000..a4ecc76f5 --- /dev/null +++ b/libjava/classpath/java/lang/management/ThreadInfo.java @@ -0,0 +1,895 @@ +/* ThreadInfo.java - Information on a thread + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +import java.util.Arrays; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +/** + *

      + * A class which maintains information about a particular + * thread. This information includes: + *

      + *
        + *
      • General Thread Information: + *
          + *
        • The identifier of the thread.
        • + *
        • The name of the thread.
        • + *
        + *
      • + *
      • Execution Information: + *
          + *
        • The current state of the thread (e.g. blocked, runnable)
        • + *
        • The object upon which the thread is blocked, either because + * the thread is waiting to obtain the monitor of that object to enter + * one of its synchronized monitor, or because + * {@link java.lang.Object#wait()} has been called while the thread + * was within a method of that object.
        • + *
        • The thread identifier of the current thread holding an object's + * monitor, upon which the thread described here is blocked.
        • + *
        • The stack trace of the thread (if requested on creation + * of this object
        • + *
        • The current locks held on object monitors by the thread.
        • + *
        • The current locks held on ownable synchronizers by the thread.
        • + *
        + *
      • Synchronization Statistics + *
          + *
        • The number of times the thread has been blocked waiting for + * an object's monitor or in a {@link java.lang.Object#wait()} call.
        • + *
        • The accumulated time the thread has been blocked waiting for + * an object's monitor on in a {@link java.lang.Object#wait()} call. + * The availability of these statistics depends on the virtual machine's + * support for thread contention monitoring (see + * {@link ThreadMXBean#isThreadContentionMonitoringSupported()}.
        • + *
        + *
      • + *
      + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + * @see ThreadMXBean#isThreadContentionMonitoringSupported() + */ +public class ThreadInfo +{ + + /** + * The id of the thread which this instance concerns. + */ + private long threadId; + + /** + * The name of the thread which this instance concerns. + */ + private String threadName; + + /** + * The state of the thread which this instance concerns. + */ + private Thread.State threadState; + + /** + * The number of times the thread has been blocked. + */ + private long blockedCount; + + /** + * The accumulated number of milliseconds the thread has + * been blocked (used only with thread contention monitoring + * support). + */ + private long blockedTime; + + /** + * The name of the monitor lock on which this thread + * is blocked (if any). + */ + private String lockName; + + /** + * The id of the thread which owns the monitor lock on + * which this thread is blocked, or -1 + * if there is no owner. + */ + private long lockOwnerId; + + /** + * The name of the thread which owns the monitor lock on + * which this thread is blocked, or null + * if there is no owner. + */ + private String lockOwnerName; + + /** + * The number of times the thread has been in a waiting + * state. + */ + private long waitedCount; + + /** + * The accumulated number of milliseconds the thread has + * been waiting (used only with thread contention monitoring + * support). + */ + private long waitedTime; + + /** + * True if the thread is in a native method. + */ + private boolean isInNative; + + /** + * True if the thread is suspended. + */ + private boolean isSuspended; + + /** + * The stack trace of the thread. + */ + private StackTraceElement[] trace; + + /** + * The array of information on monitors locked by the thread. + */ + private MonitorInfo[] lockedMonitors; + + /** + * The array of information on ownable synchronizers locked + * by the thread. + */ + private LockInfo[] lockedSynchronizers; + + /** + * Cache a local reference to the thread management bean. + */ + private static ThreadMXBean bean = null; + + /** + * Cache the {@link javax.management.openmbean.CompositeType} + * for the {@link StackTraceElement}. + */ + private static CompositeType seType; + + /** + * Constructs a new {@link ThreadInfo} corresponding + * to the thread details specified. + * + * @param threadId the id of the thread on which this + * new instance will be based. + * @param threadName the name of the thread on which + * this new instance will be based. + * @param threadState the state of the thread on which + * this new instance will be based. + * @param blockedCount the number of times the thread + * has been blocked. + * @param blockedTime the accumulated number of milliseconds + * the specified thread has been blocked + * (only used with contention monitoring enabled) + * @param lockName the name of the monitor lock the thread is waiting for + * (only used if blocked) + * @param lockOwnerId the id of the thread which owns the monitor + * lock, or -1 if it doesn't have an owner + * (only used if blocked) + * @param lockOwnerName the name of the thread which owns the monitor + * lock, or null if it doesn't have an + * owner (only used if blocked) + * @param waitedCount the number of times the thread has been in a + * waiting state. + * @param waitedTime the accumulated number of milliseconds the + * specified thread has been waiting + * (only used with contention monitoring enabled) + * @param isInNative true if the thread is in a native method. + * @param isSuspended true if the thread is suspended. + * @param trace the stack trace of the thread to a pre-determined + * depth (see VMThreadMXBeanImpl) + * @param lockedMonitors an array of {@link MonitorInfo} objects + * representing locks held on object monitors + * by the thread. + * @param lockedSynchronizers an array of {@link LockInfo} objects + * representing locks held on ownable + * synchronizers by the thread. + * + * @since 1.6 + */ + private ThreadInfo(long threadId, String threadName, Thread.State threadState, + long blockedCount, long blockedTime, String lockName, + long lockOwnerId, String lockOwnerName, long waitedCount, + long waitedTime, boolean isInNative, boolean isSuspended, + StackTraceElement[] trace, MonitorInfo[] lockedMonitors, + LockInfo[] lockedSynchronizers) + { + this.threadId = threadId; + this.threadName = threadName; + this.threadState = threadState; + this.blockedCount = blockedCount; + this.blockedTime = blockedTime; + this.lockName = lockName; + this.lockOwnerId = lockOwnerId; + this.lockOwnerName = lockOwnerName; + this.waitedCount = waitedCount; + this.waitedTime = waitedTime; + this.isInNative = isInNative; + this.isSuspended = isSuspended; + this.trace = trace; + this.lockedMonitors = lockedMonitors; + this.lockedSynchronizers = lockedSynchronizers; + } + + /** + * Checks for an attribute in a {@link CompositeData} structure + * with the correct type. + * + * @param ctype the composite data type to check. + * @param name the name of the attribute. + * @param type the type to check for. + * @throws IllegalArgumentException if the attribute is absent + * or of the wrong type. + */ + static void checkAttribute(CompositeType ctype, String name, + OpenType type) + throws IllegalArgumentException + { + OpenType foundType = ctype.getType(name); + if (foundType == null) + throw new IllegalArgumentException("Could not find a field named " + + name); + if (!(foundType.equals(type))) + throw new IllegalArgumentException("Field " + name + " is not of " + + "type " + type.getClassName()); + } + + /** + * Returns the {@link javax.management.openmbean.CompositeType} for + * a {@link StackTraceElement}. + * + * @return the type for the stack trace element. + */ + static CompositeType getStackTraceType() + { + if (seType == null) + try + { + seType = new CompositeType(StackTraceElement.class.getName(), + "An element of a stack trace", + new String[] { "className", "methodName", + "fileName", "lineNumber", + "nativeMethod" + }, + new String[] { "Name of the class", + "Name of the method", + "Name of the source code file", + "Line number", + "True if this is a native method" + }, + new OpenType[] { + SimpleType.STRING, SimpleType.STRING, + SimpleType.STRING, SimpleType.INTEGER, + SimpleType.BOOLEAN + }); + } + catch (OpenDataException e) + { + throw new IllegalStateException("Something went wrong in creating " + + "the composite data type for the " + + "stack trace element.", e); + } + return seType; + } + + /** + *

      + * Returns a {@link ThreadInfo} instance using the values + * given in the supplied + * {@link javax.management.openmbean.CompositeData} object. + * The composite data instance should contain the following + * attributes with the specified types: + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      NameType
      threadIdjava.lang.Long
      threadNamejava.lang.String
      threadStatejava.lang.String
      suspendedjava.lang.Boolean
      inNativejava.lang.Boolean
      blockedCountjava.lang.Long
      blockedTimejava.lang.Long
      waitedCountjava.lang.Long
      waitedTimejava.lang.Long
      lockNamejava.lang.String
      lockOwnerIdjava.lang.Long
      lockOwnerNamejava.lang.String
      stackTracejavax.management.openmbean.CompositeData[] + *
      + *

      + * The stack trace is further described as: + *

      + * + * + * + * + * + * + * + *
      NameType
      classNamejava.lang.String
      methodNamejava.lang.String
      fileNamejava.lang.String
      lineNumberjava.lang.Integer
      nativeMethodjava.lang.Boolean
      + * + * @param data the composite data structure to take values from. + * @return a new instance containing the values from the + * composite data structure, or null + * if the data structure was also null. + * @throws IllegalArgumentException if the composite data structure + * does not match the structure + * outlined above. + */ + public static ThreadInfo from(CompositeData data) + { + if (data == null) + return null; + CompositeType type = data.getCompositeType(); + checkAttribute(type, "ThreadId", SimpleType.LONG); + checkAttribute(type, "ThreadName", SimpleType.STRING); + checkAttribute(type, "ThreadState", SimpleType.STRING); + checkAttribute(type, "Suspended", SimpleType.BOOLEAN); + checkAttribute(type, "InNative", SimpleType.BOOLEAN); + checkAttribute(type, "BlockedCount", SimpleType.LONG); + checkAttribute(type, "BlockedTime", SimpleType.LONG); + checkAttribute(type, "WaitedCount", SimpleType.LONG); + checkAttribute(type, "WaitedTime", SimpleType.LONG); + checkAttribute(type, "LockName", SimpleType.STRING); + checkAttribute(type, "LockOwnerId", SimpleType.LONG); + checkAttribute(type, "LockOwnerName", SimpleType.STRING); + try + { + checkAttribute(type, "StackTrace", + new ArrayType(1, getStackTraceType())); + } + catch (OpenDataException e) + { + throw new IllegalStateException("Something went wrong in creating " + + "the array for the stack trace element.", + e); + } + OpenType foundType = type.getType("LockedMonitors"); + if (foundType != null) + try + { + CompositeType mType = new CompositeType(MonitorInfo.class.getName(), + "Information on a object monitor lock", + new String[] { "ClassName", + "IdentityHashCode", + "LockedStackDepth", + "LockedStackFrame" + }, + new String[] { "Name of the class", + "Identity hash code " + + "of the class", + "Stack depth at time " + + "of lock", + "Stack frame at time " + + "of lock", + }, + new OpenType[] { + SimpleType.STRING, SimpleType.INTEGER, + SimpleType.INTEGER, getStackTraceType() + }); + if (!(foundType.equals(new ArrayType(1, mType)))) + throw new IllegalArgumentException("Field LockedMonitors is not of " + + "type " + mType.getClassName()); + } + catch (OpenDataException e) + { + throw new IllegalStateException("Something went wrong in creating " + + "the composite data type for the " + + "object monitor information array.", e); + } + foundType = type.getType("LockedSynchronizers"); + if (foundType != null) + try + { + CompositeType lType = new CompositeType(LockInfo.class.getName(), + "Information on a lock", + new String[] { "ClassName", + "IdentityHashCode" + }, + new String[] { "Name of the class", + "Identity hash code " + + "of the class" + }, + new OpenType[] { + SimpleType.STRING, SimpleType.INTEGER + }); + if (!(foundType.equals(new ArrayType(1, lType)))) + throw new IllegalArgumentException("Field LockedSynchronizers is not of " + + "type " + lType.getClassName()); + } + catch (OpenDataException e) + { + throw new IllegalStateException("Something went wrong in creating " + + "the composite data type for the " + + "ownable synchronizerinformation array.", e); + } + CompositeData[] dTraces = (CompositeData[]) data.get("StackTrace"); + StackTraceElement[] traces = new StackTraceElement[dTraces.length]; + for (int a = 0; a < dTraces.length; ++a) + /* FIXME: We can't use the boolean as there is no available + constructor. */ + traces[a] = + new StackTraceElement((String) dTraces[a].get("ClassName"), + (String) dTraces[a].get("MethodName"), + (String) dTraces[a].get("FileName"), + ((Integer) + dTraces[a].get("LineNumber")).intValue()); + MonitorInfo[] mInfo; + if (data.containsKey("LockedMonitors")) + { + CompositeData[] dmInfos = (CompositeData[]) data.get("LockedMonitors"); + mInfo = new MonitorInfo[dmInfos.length]; + for (int a = 0; a < dmInfos.length; ++a) + mInfo[a] = MonitorInfo.from(dmInfos[a]); + } + else + mInfo = new MonitorInfo[]{}; + LockInfo[] lInfo; + if (data.containsKey("LockedSynchronizers")) + { + CompositeData[] dlInfos = (CompositeData[]) data.get("LockedSynchronizers"); + lInfo = new LockInfo[dlInfos.length]; + for (int a = 0; a < dlInfos.length; ++a) + lInfo[a] = new LockInfo((String) dlInfos[a].get("ClassName"), + (Integer) dlInfos[a].get("IdentityHashCode")); + } + else + lInfo = new LockInfo[]{}; + return new ThreadInfo(((Long) data.get("ThreadId")).longValue(), + (String) data.get("ThreadName"), + Thread.State.valueOf((String) data.get("ThreadState")), + ((Long) data.get("BlockedCount")).longValue(), + ((Long) data.get("BlockedTime")).longValue(), + (String) data.get("LockName"), + ((Long) data.get("LockOwnerId")).longValue(), + (String) data.get("LockOwnerName"), + ((Long) data.get("WaitedCount")).longValue(), + ((Long) data.get("WaitedTime")).longValue(), + ((Boolean) data.get("InNative")).booleanValue(), + ((Boolean) data.get("Suspended")).booleanValue(), + traces, mInfo, lInfo); + } + + /** + * Returns the number of times this thread has been + * in the {@link java.lang.Thread.State#BLOCKED} state. + * A thread enters this state when it is waiting to + * obtain an object's monitor. This may occur either + * on entering a synchronized method for the first time, + * or on re-entering it following a call to + * {@link java.lang.Object#wait()}. + * + * @return the number of times this thread has been blocked. + */ + public long getBlockedCount() + { + return blockedCount; + } + + /** + *

      + * Returns the accumulated number of milliseconds this + * thread has been in the + * {@link java.lang.Thread.State#BLOCKED} state + * since thread contention monitoring was last enabled. + * A thread enters this state when it is waiting to + * obtain an object's monitor. This may occur either + * on entering a synchronized method for the first time, + * or on re-entering it following a call to + * {@link java.lang.Object#wait()}. + *

      + *

      + * Use of this method requires virtual machine support + * for thread contention monitoring and for this support + * to be enabled. + *

      + * + * @return the accumulated time (in milliseconds) that this + * thread has spent in the blocked state, since + * thread contention monitoring was enabled, or -1 + * if thread contention monitoring is disabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support contention + * monitoring. + * @see ThreadMXBean#isThreadContentionMonitoringEnabled() + * @see ThreadMXBean#isThreadContentionMonitoringSupported() + */ + public long getBlockedTime() + { + if (bean == null) + bean = ManagementFactory.getThreadMXBean(); + // Will throw UnsupportedOperationException for us + if (bean.isThreadContentionMonitoringEnabled()) + return blockedTime; + else + return -1; + } + + /** + * Returns an array of {@link MonitorInfo} objects representing + * information on the locks on object monitors held by the thread. + * If no locks are held, or such information was not requested + * on creating this {@link ThreadInfo} object, a zero-length + * array will be returned. + * + * @return information on object monitors locked by this thread. + */ + public MonitorInfo[] getLockedMonitors() + { + return lockedMonitors; + } + + /** + * Returns an array of {@link LockInfo} objects representing + * information on the locks on ownable synchronizers held by the thread. + * If no locks are held, or such information was not requested + * on creating this {@link ThreadInfo} object, a zero-length + * array will be returned. + * + * @return information on ownable synchronizers locked by this thread. + */ + public LockInfo[] getLockedSynchronizers() + { + return lockedSynchronizers; + } + + /** + *

      + * Returns a {@link LockInfo} object representing the + * lock on which this thread is blocked. If the thread + * is not blocked, this method returns null. + *

      + *

      + * The thread may be blocked due to one of three reasons: + *

      + *
        + *
      1. The thread is in the BLOCKED state + * waiting to acquire an object monitor in order to enter + * a synchronized method or block.
      2. + *
      3. The thread is in the WAITING or + * TIMED_WAITING state due to a call to + * {@link java.lang.Object#wait()}.
      4. + *
      5. The thread is in the WAITING or + * TIMED_WAITING state due to a call + * to {@link java.util.concurrent.locks.LockSupport#park()}. + * The lock is the return value of + * {@link java.util.concurrent.locks.LockSupport#getBlocker()}.
      6. + *
      + * + * @return a {@link LockInfo} object representing the lock on + * which the thread is blocked, or null if + * the thread isn't blocked. + * @since 1.6 + * @see #getLockName() + */ + public LockInfo getLockInfo() + { + String lockName = getLockName(); + int at = lockName.indexOf('@'); + return new LockInfo(lockName.substring(0, at), + Integer.decode(lockName.substring(at + 1))); + } + + /** + *

      + * Returns a {@link java.lang.String} representation of + * the lock on which this thread is blocked. If + * the thread is not blocked, this method returns + * null. + *

      + *

      + * The returned {@link java.lang.String} is constructed + * using the class name and identity hashcode (usually + * the memory address of the object) of the lock. The + * two are separated by the '@' character, and the identity + * hashcode is represented in hexadecimal. Thus, for a + * lock, l, the returned value is + * the result of concatenating + * l.getClass().getName(), "@" + * and + * Integer.toHexString(System.identityHashCode(l)). + * The value is only unique to the extent that the identity + * hash code is also unique. The value is the same as would + * be returned by getLockInfo().toString() + *

      + * + * @return a string representing the lock on which this + * thread is blocked, or null if + * the thread is not blocked. + */ + public String getLockName() + { + if (!isThreadBlocked()) + return null; + return lockName; + } + + /** + * Returns the identifier of the thread which owns the + * monitor lock this thread is waiting for. -1 is returned + * if either this thread is not blocked, or the lock is + * not held by any other thread. + * + * @return the thread identifier of thread holding the lock + * this thread is waiting for, or -1 if the thread + * is not blocked or the lock is not held by another + * thread. + */ + public long getLockOwnerId() + { + if (!isThreadBlocked()) + return -1; + return lockOwnerId; + } + + /** + * Returns the name of the thread which owns the + * monitor lock this thread is waiting for. null + * is returned if either this thread is not blocked, + * or the lock is not held by any other thread. + * + * @return the thread identifier of thread holding the lock + * this thread is waiting for, or null + * if the thread is not blocked or the lock is not + * held by another thread. + */ + public String getLockOwnerName() + { + if (!isThreadBlocked()) + return null; + return lockOwnerName; + } + + /** + *

      + * Returns the stack trace of this thread to the depth + * specified on creation of this {@link ThreadInfo} + * object. If the depth is zero, an empty array will + * be returned. For non-zero arrays, the elements + * start with the most recent trace at position zero. + * The bottom of the stack represents the oldest method + * invocation which meets the depth requirements. + *

      + *

      + * Some virtual machines may not be able to return + * stack trace information for a thread. In these + * cases, an empty array will also be returned. + *

      + * + * @return an array of {@link java.lang.StackTraceElement}s + * representing the trace of this thread. + */ + public StackTraceElement[] getStackTrace() + { + return trace; + } + + /** + * Returns the identifier of the thread associated with + * this instance of {@link ThreadInfo}. + * + * @return the thread's identifier. + */ + public long getThreadId() + { + return threadId; + } + + /** + * Returns the name of the thread associated with + * this instance of {@link ThreadInfo}. + * + * @return the thread's name. + */ + public String getThreadName() + { + return threadName; + } + + /** + * Returns the state of the thread associated with + * this instance of {@link ThreadInfo}. + * + * @return the thread's state. + */ + public Thread.State getThreadState() + { + return threadState; + } + + /** + * Returns the number of times this thread has been + * in the {@link java.lang.Thread.State#WAITING} + * or {@link java.lang.Thread.State#TIMED_WAITING} state. + * A thread enters one of these states when it is waiting + * due to a call to {@link java.lang.Object.wait()}, + * {@link java.lang.Object.join()} or + * {@link java.lang.concurrent.locks.LockSupport.park()}, + * either with an infinite or timed delay, respectively. + * + * @return the number of times this thread has been waiting. + */ + public long getWaitedCount() + { + return waitedCount; + } + + /** + *

      + * Returns the accumulated number of milliseconds this + * thread has been in the + * {@link java.lang.Thread.State#WAITING} or + * {@link java.lang.Thread.State#TIMED_WAITING} state, + * since thread contention monitoring was last enabled. + * A thread enters one of these states when it is waiting + * due to a call to {@link java.lang.Object.wait()}, + * {@link java.lang.Object.join()} or + * {@link java.lang.concurrent.locks.LockSupport.park()}, + * either with an infinite or timed delay, respectively. + *

      + *

      + * Use of this method requires virtual machine support + * for thread contention monitoring and for this support + * to be enabled. + *

      + * + * @return the accumulated time (in milliseconds) that this + * thread has spent in one of the waiting states, since + * thread contention monitoring was enabled, or -1 + * if thread contention monitoring is disabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support contention + * monitoring. + * @see ThreadMXBean#isThreadContentionMonitoringEnabled() + * @see ThreadMXBean#isThreadContentionMonitoringSupported() + */ + public long getWaitedTime() + { + if (bean == null) + bean = ManagementFactory.getThreadMXBean(); + // Will throw UnsupportedOperationException for us + if (bean.isThreadContentionMonitoringEnabled()) + return waitedTime; + else + return -1; + } + + /** + * Returns true if the thread is in a native method. This + * excludes native code which forms part of the virtual + * machine itself, or which results from Just-In-Time + * compilation. + * + * @return true if the thread is in a native method, false + * otherwise. + */ + public boolean isInNative() + { + return isInNative; + } + + /** + * Returns true if the thread has been suspended using + * {@link java.lang.Thread#suspend()}. + * + * @return true if the thread is suspended, false otherwise. + */ + public boolean isSuspended() + { + return isSuspended; + } + + /** + * Returns a {@link java.lang.String} representation of + * this {@link ThreadInfo} object. This takes the form + * java.lang.management.ThreadInfo[id=tid, name=n, + * state=s, blockedCount=bc, waitedCount=wc, isInNative=iin, + * isSuspended=is], where tid is + * the thread identifier, n is the + * thread name, s is the thread state, + * bc is the blocked state count, + * wc is the waiting state count and + * iin and is are boolean + * flags to indicate the thread is in native code or + * suspended respectively. If the thread is blocked, + * lock=l, lockOwner=lo is also included, + * where l is the lock waited for, and + * lo is the thread which owns the lock + * (or null if there is no owner). + * + * @return the string specified above. + */ + public String toString() + { + return getClass().getName() + + "[id=" + threadId + + ", name=" + threadName + + ", state=" + threadState + + ", blockedCount=" + blockedCount + + ", waitedCount=" + waitedCount + + ", isInNative=" + isInNative + + ", isSuspended=" + isSuspended + + (isThreadBlocked() ? + ", lockOwnerId=" + lockOwnerId + + ", lockOwnerName=" + lockOwnerName : "") + + ", lockedMonitors=" + Arrays.toString(lockedMonitors) + + ", lockedSynchronizers=" + Arrays.toString(lockedSynchronizers) + + "]"; + } + + /** + *

      + * Returns true if the thread is in a blocked state. + * The thread is regarded as blocked if: + *

      + *
        + *
      1. The thread is in the BLOCKED state + * waiting to acquire an object monitor in order to enter + * a synchronized method or block.
      2. + *
      3. The thread is in the WAITING or + * TIMED_WAITING state due to a call to + * {@link java.lang.Object#wait()}.
      4. + *
      5. The thread is in the WAITING or + * TIMED_WAITING state due to a call + * to {@link java.util.concurrent.locks.LockSupport#park()}. + * The lock is the return value of + * {@link java.util.concurrent.locks.LockSupport#getBlocker()}.
      6. + *
      + * + * @return true if the thread is blocked. + */ + private boolean isThreadBlocked() + { + return (threadState == Thread.State.BLOCKED || + threadState == Thread.State.WAITING || + threadState == Thread.State.TIMED_WAITING); + } + +} diff --git a/libjava/classpath/java/lang/management/ThreadMXBean.java b/libjava/classpath/java/lang/management/ThreadMXBean.java new file mode 100644 index 000000000..551c53282 --- /dev/null +++ b/libjava/classpath/java/lang/management/ThreadMXBean.java @@ -0,0 +1,642 @@ +/* ThreadMXBean.java - Interface for a thread bean + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.management; + +/** + *

      + * Provides access to information about the threads + * of the virtual machine. An instance of this bean is + * obtained by calling + * {@link ManagementFactory#getThreadMXBean()}. + *

      + *

      + * Each thread within the virtual machine is given an + * identifier, which is guaranteed to be unique to a + * particular thread over its lifetime (after which it + * may be reused). The identifier for a thread may be + * obtained by calling {@link java.lang.Thread#getId()}. + * This identifier is used within implementations of this + * interface to obtain information about a particular thread + * (or series of threads, in the case of an array of identifiers). + *

      + *

      + * This bean supports some optional behaviour, which all + * virtual machines may not choose to implement. Specifically, + * this includes the monitoring of: + *

      + *
        + *
      • the CPU time used by a thread
      • + *
      • thread contention
      • + *
      • object monitor usage
      • + *
      • ownable synchronizer usage
      • + *
      + *

      + * The monitoring of CPU time is further subdivided into + * the monitoring of either just the current thread or all + * threads. The methods + * {@link #isThreadCpuTimeSupported()}, + * {@link #isCurrentThreadCpuTimeSupported()} + * {@link #isThreadContentionMonitoringSupported()}, + * {@link #isObjectMonitorUsageSupported()} and + * {@link #isSynchronizerUsageSupported()} may be + * used to determine whether or not this functionality is + * supported. + *

      + *

      + * Furthermore, both time and contention monitoring may be + * disabled. In fact, thread contention monitoring is disabled + * by default, and must be explictly turned on by calling + * the {@link #setThreadContentionMonitoringEnabled(boolean)} + * method. + *

      + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface ThreadMXBean +{ + + /** + * This method returns information on all live threads at the + * time of execution (some threads may have terminated by the + * time the method completes). This method is simply a shorthand + * for calling {@link #getThreadInfo(long[], boolean, + * boolean)} with the return value of {@link #getAllThreadIds()}. + * + * @param lockedMonitors true if the returned {@link ThreadInfo} + * objects should contain information on + * locked monitors. + * @param lockedSynchronizers true if the returned {@link ThreadInfo} + * objects should contain information + * on locked ownable synchronizers. + * @return an array of {@link ThreadInfo} objects for all live threads. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @throws UnsupportedOperationException if lockedMonitors + * is true, but object monitor + * usage monitoring is not supported + * by the VM, or + * lockedSynchronizers + * is true, but ownable synchronizer + * usage monitoring is not supported + * by the VM. + * @since 1.6 + * @see #getThreadInfo(long[], boolean, boolean) + * @see #getAllThreadIds() + * @see #isObjectMonitorUsageSupported() + * @see #isSynchronizerUsageSupported() + */ + ThreadInfo[] dumpAllThreads(boolean lockedMonitors, + boolean lockedSynchronizers); + + /** + *

      + * This method obtains a list of threads which are deadlocked + * waiting to obtain monitor or ownable synchronizer ownership. + * This is similar to the behaviour described for + * {@link #getMonitorDeadlockedThreads()}, except this method also + * takes in to account deadlocks involving ownable synchronizers. + *

      + *

      + * Note that this method is not designed for controlling + * synchronization, but for troubleshooting problems which cause such + * deadlocks; it may be prohibitively expensive to use in normal + * operation. If only deadlocks involving monitors are of interest, + * then {@link #findMonitorDeadlockedThreads()} should be used in + * preference to this method. + *

      + * + * @return an array of thread identifiers, corresponding to threads + * which are currently in a deadlocked situation, or + * null if there are no deadlocks. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @throws UnsupportedOperationException if the VM does not support + * the monitoring of ownable + * synchronizer usage. + * @since 1.6 + * @see #findMonitorDeadlockedThreads() + * @see #isSynchronizerUsageSupported() + */ + long[] findDeadlockedThreads(); + + /** + *

      + * This method obtains a list of threads which are deadlocked + * waiting to obtain monitor ownership. On entering a synchronized + * method of an object, or re-entering it after returning from an + * {@link java.lang.Object#wait()} call, a thread obtains ownership + * of the object's monitor. + *

      + *

      + * Deadlocks can occur in this situation if one or more threads end up + * waiting for a monitor, P, while also retaining ownership of a monitor, + * Q, required by the thread that currently owns P. To give a simple + * example, imagine thread A calls a synchronized method, R, obtaining the + * monitor, P. It then sleeps within that method, allowing thread B + * to run, but still retaining ownership of P. B calls another + * synchronized method, S, which causes it to obtain the monitor, Q, + * of a different object. While in that method, it then wants to + * call the original synchronized method, R, called by A. Doing so + * requires ownership of P, which is still held by A. Hence, it + * becomes blocked. + *

      + *

      + * A then finishes its sleep, becomes runnable, and is then allowed + * to run, being the only eligible thread in this scenario. A tries + * to call the synchronized method, S. It also gets blocked, because + * B still holds the monitor, Q. Hence, the two threads, A and B, + * are deadlocked, as neither can give up its monitor without first + * obtaining the monitor held by the other thread. + *

      + *

      + * Calling this method in this scenario would return the thread IDs + * of A and B. Note that this method is not designed for controlling + * synchronization, but for troubleshooting problems which cause such + * deadlocks; it may be prohibitively expensive to use in normal + * operation. This method only returns deadlocks involving monitors; + * to include deadlocks involving ownable synchronizers, + * {@link #findDeadlockedThreads()} should be used instead. + *

      + * + * @return an array of thread identifiers, corresponding to threads + * which are currently in a deadlocked situation, or + * null if there are no deadlocks. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @see #findDeadlockedThreads() + */ + long[] findMonitorDeadlockedThreads(); + + /** + * Returns all live thread identifiers at the time of initial + * execution. Some thread identifiers in the returned array + * may refer to terminated threads, if this occurs during the + * lifetime of this method. + * + * @return an array of thread identifiers, corresponding to + * current live threads. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + long[] getAllThreadIds(); + + /** + *

      + * Returns the total number of nanoseconds of CPU time + * the current thread has used. This is equivalent to calling + * {@link #getThreadCpuTime()}(Thread.currentThread.getId()). + *

      + *

      + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. The use of this method depends on virtual machine + * support for measurement of the CPU time of the current thread, + * and on this functionality being enabled. + *

      + * + * @return the total number of nanoseconds of CPU time the current + * thread has used, or -1 if CPU time monitoring is disabled. + * @throws UnsupportedOperationException if CPU time monitoring is not + * supported. + * @see #getCurrentThreadUserTime() + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + long getCurrentThreadCpuTime(); + + /** + *

      + * Returns the total number of nanoseconds of CPU time + * the current thread has executed in user mode. This is + * equivalent to calling + * {@link #getThreadUserTime()}(Thread.currentThread.getId()). + *

      + *

      + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. The use of this method depends on virtual machine + * support for measurement of the CPU time of the current thread, + * and on this functionality being enabled. + *

      + * + * @return the total number of nanoseconds of CPU time the current + * thread has executed in user mode, or -1 if CPU time + * monitoring is disabled. + * @throws UnsupportedOperationException if CPU time monitoring is not + * supported. + * @see #getCurrentThreadCpuTime() + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + long getCurrentThreadUserTime(); + + /** + * Returns the number of live daemon threads. + * + * @return the number of live daemon threads. + */ + int getDaemonThreadCount(); + + /** + * Returns the peak number of live threads since + * the virtual machine was started or the count + * reset using {@link #resetPeakThreadCount()}. + * + * @return the peak live thread count. + * @see #resetPeakThreadCount() + */ + int getPeakThreadCount(); + + /** + * Returns the number of live threads, including + * both daemon threads and non-daemon threads. + * + * @return the current number of live threads. + */ + int getThreadCount(); + + /** + *

      + * Returns the total number of nanoseconds of CPU time + * the specified thread has used. + *

      + *

      + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. The use of this method depends on virtual machine + * support for measurement of the CPU time of the current thread, + * and on this functionality being enabled. + *

      + * + * @param id the thread identifier of the thread whose CPU time is being + * monitored. + * @return the total number of nanoseconds of CPU time the specified + * thread has used, or -1 if CPU time monitoring is disabled. + * @throws IllegalArgumentException if id <= 0. + * @throws UnsupportedOperationException if CPU time monitoring is not + * supported. + * @see #getThreadUserTime(long) + * @see #isThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + long getThreadCpuTime(long id); + + /** + * Returns information on the specified thread without any + * stack trace information. This is equivalent to + * {@link #getThreadInfo}(id, 0). If the + * identifier specifies a thread which is either non-existant + * or not alive, then the method returns null. + * + * @param id the identifier of the thread to return information + * on. + * @return a {@link ThreadInfo} object pertaining to the specified + * thread, or null if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if id <= 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + ThreadInfo getThreadInfo(long id); + + /** + * Returns information on the specified threads without any + * stack trace information. This is equivalent to + * {@link #getThreadInfo}(ids, 0). If an + * identifier specifies a thread which is either non-existant + * or not alive, then the corresponding element in the returned + * array is null. + * + * @param ids an array of thread identifiers to return information + * on. + * @return an array of {@link ThreadInfo} objects matching the + * specified threads. The corresponding element is + * null if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if an identifier in the array is + * <= 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + ThreadInfo[] getThreadInfo(long[] ids); + + /** + * Returns information on the specified threads with full + * stack trace information and optional synchronization + * information. If lockedMonitors is false, + * or there are no locked monitors for a particular thread, + * then the corresponding {@link ThreadInfo} object will have + * an empty {@link MonitorInfo} array. Likewise, if + * lockedSynchronizers is false, or there are + * no locked ownable synchronizers for a particular thread, + * then the corresponding {@link ThreadInfo} object will have + * an empty {@link LockInfo} array. If both + * lockedMonitors and lockedSynchronizers + * are false, the return value is equivalent to that from + * {@link #getThreadInfo}(ids, Integer.MAX_VALUE). + * If an identifier specifies a thread which is either non-existant + * or not alive, then the corresponding element in the returned + * array is null. + * + * @param ids an array of thread identifiers to return information + * on. + * @param lockedMonitors true if information on locked monitors + * should be included. + * @param lockedSynchronizers true if information on locked + * ownable synchronizers should be included. + * @return an array of {@link ThreadInfo} objects matching the + * specified threads. The corresponding element is + * null if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if an identifier in the array is + * <= 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + * @throws UnsupportedOperationException if lockedMonitors + * is true, but object monitor + * usage monitoring is not supported + * by the VM, or + * lockedSynchronizers + * is true, but ownable synchronizer + * usage monitoring is not supported + * by the VM. + * @since 1.6 + * @see #isObjectMonitorUsageSupported() + * @see #isSynchronizerUsageSupported() + */ + ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, + boolean lockedSynchronizers); + + /** + * Returns information on the specified thread with + * stack trace information to the supplied depth. If the + * identifier specifies a thread which is either non-existant + * or not alive, then the method returns null. + * A maximum depth of 0 corresponds to an empty stack trace + * (an empty array is returned by the appropriate + * {@link ThreadInfo} method). A maximum depth of + * Integer.MAX_VALUE returns the full stack trace. + * + * @param id the identifier of the thread to return information + * on. + * @param maxDepth the maximum depth of the stack trace. + * Values of 0 or Integer.MAX_VALUE + * correspond to an empty and full stack trace + * respectively. + * @return a {@link ThreadInfo} object pertaining to the specified + * thread, or null if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if id <= 0. + * @throws IllegalArgumentException if maxDepth < 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + ThreadInfo getThreadInfo(long id, int maxDepth); + + /** + * Returns information on the specified threads with + * stack trace information to the supplied depth. If an + * identifier specifies a thread which is either non-existant + * or not alive, then the corresponding element in the returned + * array is null. A maximum depth of 0 corresponds + * to an empty stack trace (an empty array is returned by the + * appropriate {@link ThreadInfo} method). A maximum depth of + * Integer.MAX_VALUE returns the full stack trace. + * + * @param ids an array of thread identifiers to return information + * on. + * @param maxDepth the maximum depth of the stack trace. + * Values of 0 or Integer.MAX_VALUE + * correspond to an empty and full stack trace + * respectively. + * @return an array of {@link ThreadInfo} objects matching the + * specified threads. The corresponding element is + * null if the identifier specifies + * a thread that doesn't exist or is not alive. + * @throws IllegalArgumentException if an identifier in the array is + * <= 0. + * @throws IllegalArgumentException if maxDepth < 0. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("monitor"). + */ + ThreadInfo[] getThreadInfo(long[] ids, int maxDepth); + + /** + *

      + * Returns the total number of nanoseconds of CPU time + * the specified thread has executed in user mode. + *

      + *

      + * Note that the value is only nanosecond-precise, and not accurate; there + * is no guarantee that the difference between two values is really a + * nanosecond. Also, the value is prone to overflow if the offset + * exceeds 2^63. The use of this method depends on virtual machine + * support for measurement of the CPU time of the current thread, + * and on this functionality being enabled. + *

      + * + * @param id the thread identifier of the thread whose CPU time is being + * monitored. + * @return the total number of nanoseconds of CPU time the specified + * thread has executed in user mode, or -1 if CPU time monitoring + * is disabled. + * @throws IllegalArgumentException if id <= 0. + * @throws UnsupportedOperationException if CPU time monitoring is not + * supported. + * @see #getThreadCpuTime(long) + * @see #isThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + long getThreadUserTime(long id); + + /** + * Returns the total number of threads that have been + * created and started during the lifetime of the virtual + * machine. + * + * @return the total number of started threads. + */ + long getTotalStartedThreadCount(); + + /** + * Returns true if the virtual machine supports the monitoring + * of the CPU time used by the current thread. This is implied + * by {@link isThreadCpuTimeSupported()} returning true. + * + * @return true if monitoring of the CPU time used by the current + * thread is supported by the virtual machine. + * @see #isThreadCpuTimeEnabled() + * @see #isThreadCpuTimeSupported() + * @see #setThreadCpuTimeEnabled(boolean) + */ + boolean isCurrentThreadCpuTimeSupported(); + + /** + * Returns true if the virtual machine supports the monitoring + * of object monitor usage. + * + * @return true if the monitoring of object monitor usage + * is supported by the virtual machine. + * @since 1.6 + */ + boolean isObjectMonitorUsageSupported(); + + /** + * Returns true if the virtual machine supports the monitoring + * of ownable synchronizer usage. + * + * @return true if the monitoring of ownable synchronizer usage + * is supported by the virtual machine. + * @since 1.6 + */ + boolean isSynchronizerUsageSupported(); + + /** + * Returns true if thread contention monitoring is currently + * enabled. + * + * @return true if thread contention monitoring is enabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support contention + * monitoring. + * @see #isThreadContentionMonitoringSupported() + * @see #setThreadContentionMonitoringEnabled(boolean) + */ + boolean isThreadContentionMonitoringEnabled(); + + /** + * Returns true if thread contention monitoring is supported + * by the virtual machine. + * + * @return true if thread contention monitoring is supported + * by the virtual machine. + * @see #isThreadContentionMonitoringEnabled() + * @see #setThreadContentionMonitoringEnabled(boolean) + */ + boolean isThreadContentionMonitoringSupported(); + + /** + * Returns true if monitoring of the CPU time used by a thread + * is currently enabled. + * + * @return true if thread CPU time monitoring is enabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support CPU time + * monitoring. + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeSupported() + * @see #setThreadCpuTimeEnabled(boolean) + */ + boolean isThreadCpuTimeEnabled(); + + /** + * Returns true if the virtual machine supports the monitoring + * of the CPU time used by all threads. This implies + * that {@link isCurrentThreadCpuTimeSupported()} returns true. + * + * @return true if monitoring of the CPU time used by the current + * thread is supported by the virtual machine. + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #setThreadCpuTimeEnabled(boolean) + */ + boolean isThreadCpuTimeSupported(); + + /** + * Resets the peak live thread count to the + * current number of live threads, as returned + * by {@link #getThreadCount()}. + * + * @see #getPeakThreadCount() + * @see #getThreadCount() + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + */ + void resetPeakThreadCount(); + + /** + * Toggles the monitoring of thread contention. Thread + * contention monitoring is disabled by default. Each + * time contention monitoring is re-enabled, the times + * it maintains are reset. + * + * @param enable true if monitoring should be enabled, + * false if it should be disabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support contention + * monitoring. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + * @see #isThreadContentionMonitoringEnabled() + * @see #isThreadContentionMonitoringSupported() + */ + void setThreadContentionMonitoringEnabled(boolean enable); + + /** + * Toggles the monitoring of CPU time used by threads. The + * initial setting is dependent on the underlying virtual + * machine. On enabling CPU time monitoring, the virtual + * machine may take any value up to and including the current + * time as the start time for monitoring. + * + * @param enable true if monitoring should be enabled, + * false if it should be disabled. + * @throws UnsupportedOperationException if the virtual + * machine does not + * support CPU time + * monitoring. + * @throws SecurityException if a security manager exists and + * denies ManagementPermission("control"). + * @see #isCurrentThreadCpuTimeSupported() + * @see #isThreadCpuTimeEnabled() + * @see #isThreadCpuTimeSupported() + */ + void setThreadCpuTimeEnabled(boolean enable); + +} diff --git a/libjava/classpath/java/lang/management/package.html b/libjava/classpath/java/lang/management/package.html new file mode 100644 index 000000000..1b37cc1a5 --- /dev/null +++ b/libjava/classpath/java/lang/management/package.html @@ -0,0 +1,64 @@ + + + + +GNU Classpath - java.lang.management + + + +

      +A series of management beans which provide access to information about the +virtual machine and its underlying operating system. +

      +

      The following beans are provided:

      +
        +
      • +{@link java.lang.management.OperatingSystemMXBean} +— Information about the underlying operating system. +
      • +
      +

      Accessing the Beans

      +

      +An instance of a bean can be obtained by using one of the following methods: +

      +
        +
      1. Calling the appropriate static method of the {@link java.lang.management.ManagementFactory} +
      2. +
      + + diff --git a/libjava/classpath/java/lang/package.html b/libjava/classpath/java/lang/package.html new file mode 100644 index 000000000..715418dbd --- /dev/null +++ b/libjava/classpath/java/lang/package.html @@ -0,0 +1,48 @@ + + + + +GNU Classpath - java.lang + + +

      Core classes including wrappers for primitive types, classes, packages +and class loaders, representations of the system, processes, threads and +the core exception hierarchy.

      + + + diff --git a/libjava/classpath/java/lang/ref/PhantomReference.java b/libjava/classpath/java/lang/ref/PhantomReference.java new file mode 100644 index 000000000..29215ead7 --- /dev/null +++ b/libjava/classpath/java/lang/ref/PhantomReference.java @@ -0,0 +1,73 @@ +/* java.lang.ref.PhantomReference + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * A phantom reference is useful, to get notified, when an object got + * finalized. You can't access that object though, since it is + * finalized. This is the reason, why get() always + * returns null. + * + * @author Jochen Hoenicke + */ +public class PhantomReference + extends Reference +{ + /** + * Creates a new phantom reference. + * @param referent the object that should be watched. + * @param q the queue that should be notified, if the referent was + * finalized. This mustn't be null. + * @exception NullPointerException if q is null. + */ + public PhantomReference(T referent, ReferenceQueue q) + { + super(referent, q); + } + + /** + * Returns the object, this reference refers to. + * @return null, since the refered object may be + * finalized and thus not accessible. + */ + public T get() + { + return null; + } +} diff --git a/libjava/classpath/java/lang/ref/Reference.java b/libjava/classpath/java/lang/ref/Reference.java new file mode 100644 index 000000000..37cda6f02 --- /dev/null +++ b/libjava/classpath/java/lang/ref/Reference.java @@ -0,0 +1,176 @@ +/* java.lang.ref.Reference + Copyright (C) 1999, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * This is the base class of all references. A reference allows + * refering to an object without preventing the garbage collector to + * collect it. The only way to get the referred object is via the + * get()-method. This method will return + * null if the object was collected.
      + * + * A reference may be registered with a queue. When a referred + * element gets collected the reference will be put on the queue, so + * that you will be notified.
      + * + * There are currently three types of references: soft reference, + * weak reference and phantom reference.
      + * + * Soft references will be cleared if the garbage collector is told + * to free some memory and there are no unreferenced or weakly referenced + * objects. It is useful for caches.
      + * + * Weak references will be cleared as soon as the garbage collector + * determines that the refered object is only weakly reachable. They + * are useful as keys in hashtables (see WeakHashtable) as + * you get notified when nobody has the key anymore. + * + * Phantom references don't prevent finalization. If an object is only + * phantom reachable, it will be finalized, and the reference will be + * enqueued, but not cleared. Since you mustn't access an finalized + * object, the get method of a phantom reference will never + * work. It is useful to keep track, when an object is finalized. + * + * @author Jochen Hoenicke + * @see java.util.WeakHashMap + */ +public abstract class Reference +{ + /** + * The underlying object. This field is handled in a special way by + * the garbage collector. + */ + T referent; + + /** + * The queue this reference is registered on. This is null, if this + * wasn't registered to any queue or reference was already enqueued. + */ + volatile ReferenceQueue queue; + + /** + * Link to the next entry on the queue. If this is null, this + * reference is not enqueued. Otherwise it points to the next + * reference. The last reference on a queue will point to itself + * (not to null, that value is used to mark a not enqueued + * reference). + */ + volatile Reference nextOnQueue; + + /** + * This lock should be taken by the garbage collector, before + * determining reachability. It will prevent the get()-method to + * return the reference so that reachability doesn't change. + */ + static Object lock = new Object(); + + /** + * Creates a new reference that is not registered to any queue. + * Since it is package private, it is not possible to overload this + * class in a different package. + * @param ref the object we refer to. + */ + Reference(T ref) + { + referent = ref; + } + + /** + * Creates a reference that is registered to a queue. Since this is + * package private, it is not possible to overload this class in a + * different package. + * @param ref the object we refer to. + * @param q the reference queue to register on. + * @exception NullPointerException if q is null. + */ + Reference(T ref, ReferenceQueue q) + { + if (q == null) + throw new NullPointerException(); + referent = ref; + queue = q; + } + + /** + * Returns the object, this reference refers to. + * @return the object, this reference refers to, or null if the + * reference was cleared. + */ + public T get() + { + synchronized (lock) + { + return referent; + } + } + + /** + * Clears the reference, so that it doesn't refer to its object + * anymore. For soft and weak references this is called by the + * garbage collector. For phantom references you should call + * this when enqueuing the reference. + */ + public void clear() + { + referent = null; + } + + /** + * Tells if the object is enqueued on a reference queue. + * @return true if it is enqueued, false otherwise. + */ + public boolean isEnqueued() + { + return nextOnQueue != null; + } + + /** + * Enqueue an object on a reference queue. This is normally executed + * by the garbage collector. + */ + public boolean enqueue() + { + ReferenceQueue q = queue; + if (q != null) + { + return q.enqueue(this); + } + return false; + } +} diff --git a/libjava/classpath/java/lang/ref/ReferenceQueue.java b/libjava/classpath/java/lang/ref/ReferenceQueue.java new file mode 100644 index 000000000..73f62d64a --- /dev/null +++ b/libjava/classpath/java/lang/ref/ReferenceQueue.java @@ -0,0 +1,164 @@ +/* java.lang.ref.ReferenceQueue + Copyright (C) 1999, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * This is the queue, where references can enqueue themselve on. Each + * reference may be registered to a queue at initialization time and + * will be appended to the queue, when the enqueue method is called. + * + * The enqueue method may be automatically called by the garbage + * collector if it detects, that the object is only reachable through + * the Reference objects. + * + * @author Jochen Hoenicke + * @see Reference#enqueue() + */ +public class ReferenceQueue +{ + /** + * This is a linked list of references. If this is null, the list is + * empty. Otherwise this points to the first reference on the queue. + * The first reference will point to the next reference via the + * nextOnQueue field. The last reference will point to + * itself (not to null, since nextOnQueue is used to + * determine if a reference is enqueued). + */ + private Reference 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() + { + } + + /** + * Checks if there is a reference on the queue, returning it + * immediately. The reference will be dequeued. + * + * @return a reference on the queue, if there is one, + * null otherwise. + */ + public Reference poll() + { + return dequeue(); + } + + /** + * 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. + */ + final boolean enqueue(Reference 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; + } + } + + /** + * Remove a reference from the queue, if there is one. + * @return the first element of the queue, or null if there isn't any. + */ + private Reference dequeue() + { + synchronized (lock) + { + if (first == null) + return null; + + Reference result = first; + first = (first == first.nextOnQueue) ? null : first.nextOnQueue; + result.nextOnQueue = null; + return result; + } + } + + /** + * Removes a reference from the queue, blocking for timeout + * until a reference is enqueued. + * @param timeout the timeout period in milliseconds, 0 means + * wait forever. + * @return the reference removed from the queue, or + * null if timeout period expired. + * @exception InterruptedException if the wait was interrupted. + */ + public Reference remove(long timeout) + throws InterruptedException + { + synchronized (lock) + { + if (first == null) + lock.wait(timeout); + } + + return dequeue(); + } + + + /** + * Removes a reference from the queue, blocking until a reference is + * enqueued. + * + * @return the reference removed from the queue. + * @exception InterruptedException if the wait was interrupted. + */ + public Reference remove() + throws InterruptedException + { + return remove(0L); + } +} diff --git a/libjava/classpath/java/lang/ref/SoftReference.java b/libjava/classpath/java/lang/ref/SoftReference.java new file mode 100644 index 000000000..269ffd0ec --- /dev/null +++ b/libjava/classpath/java/lang/ref/SoftReference.java @@ -0,0 +1,84 @@ +/* java.lang.ref.SoftReference + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * A soft reference will be cleared, if the object is only softly + * reachable and the garbage collection needs more memory. The garbage + * collection will use an intelligent strategy to determine which soft + * references it should clear. This makes a soft reference ideal for + * caches.
      + * + * @author Jochen Hoenicke + */ +public class SoftReference + extends Reference +{ + /** + * Create a new soft reference, that is not registered to any queue. + * @param referent the object we refer to. + */ + public SoftReference(T referent) + { + super(referent); + } + + /** + * Create a new soft reference. + * @param referent the object we refer to. + * @param q the reference queue to register on. + * @exception NullPointerException if q is null. + */ + public SoftReference(T referent, ReferenceQueue q) + { + super(referent, q); + } + + /** + * Returns the object, this reference refers to. + * @return the object, this reference refers to, or null if the + * reference was cleared. + */ + public T get() + { + /* Why is this overloaded??? + * Maybe for a kind of LRU strategy. */ + return super.get(); + } +} diff --git a/libjava/classpath/java/lang/ref/WeakReference.java b/libjava/classpath/java/lang/ref/WeakReference.java new file mode 100644 index 000000000..973564336 --- /dev/null +++ b/libjava/classpath/java/lang/ref/WeakReference.java @@ -0,0 +1,79 @@ +/* java.lang.ref.WeakReference + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.ref; + +/** + * A weak reference will be cleared, if the object is only weakly + * reachable. It is useful for lookup tables, where you aren't + * interested in an entry, if the key isn't reachable anymore. + * WeakHashtable is a complete implementation of such a + * table.
      + * + * It is also useful to make objects unique: You create a set of weak + * references to those objects, and when you create a new object you + * look in this set, if the object already exists and return it. If + * an object is not referenced anymore, the reference will + * automatically cleared, and you may remove it from the set.
      + * + * @author Jochen Hoenicke + * @see java.util.WeakHashMap + */ +public class WeakReference + extends Reference +{ + /** + * Create a new weak reference, that is not registered to any queue. + * @param referent the object we refer to. + */ + public WeakReference(T referent) + { + super(referent); + } + + /** + * Create a new weak reference. + * @param referent the object we refer to. + * @param q the reference queue to register on. + * @exception NullPointerException if q is null. + */ + public WeakReference(T referent, ReferenceQueue q) + { + super(referent, q); + } +} diff --git a/libjava/classpath/java/lang/ref/package.html b/libjava/classpath/java/lang/ref/package.html new file mode 100644 index 000000000..d3d176280 --- /dev/null +++ b/libjava/classpath/java/lang/ref/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.lang.ref + + +

      Low level manipulation and monitoring of object references.

      + + + diff --git a/libjava/classpath/java/lang/reflect/AccessibleObject.java b/libjava/classpath/java/lang/reflect/AccessibleObject.java new file mode 100644 index 000000000..75676913d --- /dev/null +++ b/libjava/classpath/java/lang/reflect/AccessibleObject.java @@ -0,0 +1,233 @@ +/* java.lang.reflect.AccessibleObject + Copyright (C) 2001, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import java.lang.annotation.Annotation; + +/** + * This class is the superclass of various reflection classes, and + * allows sufficiently trusted code to bypass normal restrictions to + * do necessary things like invoke private methods outside of the + * class during Serialization. If you don't have a good reason + * to mess with this, don't try. Fortunately, there are adequate + * security checks before you can set a reflection object as accessible. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Field + * @see Constructor + * @see Method + * @see ReflectPermission + * @since 1.2 + * @status updated to 1.5 + */ +public class AccessibleObject + implements AnnotatedElement +{ + /** + * True if this object is marked accessible, which means the reflected + * object bypasses normal security checks. + */ + // default visibility for use by inherited classes + boolean flag = false; + + /** + * Only the three reflection classes that extend this can create an + * accessible object. This is not serializable for security reasons. + */ + protected AccessibleObject() + { + } + + /** + * Return the accessibility status of this object. + * + * @return true if this object bypasses security checks + */ + public boolean isAccessible() + { + return flag; + } + + /** + * Convenience method to set the flag on a number of objects with a single + * security check. If a security manager exists, it is checked for + * ReflectPermission("suppressAccessChecks").

      + * + * It is forbidden to set the accessibility flag to true on any constructor + * for java.lang.Class. This will result in a SecurityException. If the + * SecurityException is thrown for any of the passed AccessibleObjects, + * the accessibility flag will be set on AccessibleObjects in the array prior + * to the one which resulted in the exception. + * + * @param array the array of accessible objects + * @param flag the desired state of accessibility, true to bypass security + * @throws NullPointerException if array is null + * @throws SecurityException if the request is denied + * @see SecurityManager#checkPermission(java.security.Permission) + * @see RuntimePermission + */ + public static void setAccessible(AccessibleObject[] array, boolean flag) + { + checkPermission(); + for (int i = 0; i < array.length; i++) + array[i].secureSetAccessible(flag); + } + + /** + * Sets the accessibility flag for this reflection object. If a security + * manager exists, it is checked for + * ReflectPermission("suppressAccessChecks").

      + * + * It is forbidden to set the accessibility flag to true on any constructor for + * java.lang.Class. This will result in a SecurityException. + * + * @param flag the desired state of accessibility, true to bypass security + * @throws NullPointerException if array is null + * @throws SecurityException if the request is denied + * @see SecurityManager#checkPermission(java.security.Permission) + * @see RuntimePermission + */ + public void setAccessible(boolean flag) + { + checkPermission(); + secureSetAccessible(flag); + } + + /** + * Performs the specified security check, for + * ReflectPermission("suppressAccessChecks"). + * + * @throws SecurityException if permission is denied + */ + private static void checkPermission() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new ReflectPermission("suppressAccessChecks")); + } + + /** + * Performs the actual accessibility change, this must always be invoked + * after calling checkPermission. + * + * @param flag the desired status + * @throws SecurityException if flag is true and this is a constructor + * for java.lang.Class. + */ + private void secureSetAccessible(boolean flag) + { + if (flag && + (this instanceof Constructor + && ((Constructor) this).getDeclaringClass() == Class.class)) + throw new SecurityException("Cannot make object accessible: " + this); + this.flag = flag; + } + + /** + *

      + * Returns the element's annotation for the specified annotation type, + * or null if no such annotation exists. + *

      + *

      + * This method must be overridden by subclasses to provide + * appropriate behaviour. + *

      + * + * @param annotationClass the type of annotation to look for. + * @return this element's annotation for the specified type, or + * null if no such annotation exists. + * @throws NullPointerException if the annotation class is null. + */ + public T getAnnotation(Class annotationClass) + { + throw new AssertionError("Subclass must override this method"); + } + + /** + * Returns all annotations associated with the element. If there are + * no annotations associated with the element, then a zero-length array + * will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of the + * element, and hence no effect on the return value of this method for + * future callers. + * + * @return this element's annotations. + */ + public Annotation[] getAnnotations() + { + return getDeclaredAnnotations(); + } + + /** + *

      + * Returns all annotations directly defined by the element. If there are + * no annotations directly associated with the element, then a zero-length + * array will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * class, and hence no effect on the return value of this method for + * future callers. + *

      + *

      + * This method must be overridden by subclasses to provide + * appropriate behaviour. + *

      + * + * @return the annotations directly defined by the element. + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() + { + throw new AssertionError("Subclass must override this method"); + } + + /** + * Returns true if an annotation for the specified type is associated + * with the element. This is primarily a short-hand for using marker + * annotations. + * + * @param annotationClass the type of annotation to look for. + * @return true if an annotation exists for the specified type. + * @since 1.5 + */ + public boolean isAnnotationPresent(Class annotationClass) + { + return getAnnotation(annotationClass) != null; + } +} diff --git a/libjava/classpath/java/lang/reflect/AnnotatedElement.java b/libjava/classpath/java/lang/reflect/AnnotatedElement.java new file mode 100644 index 000000000..0179a0f4c --- /dev/null +++ b/libjava/classpath/java/lang/reflect/AnnotatedElement.java @@ -0,0 +1,115 @@ +/* AnnotatedElement.java + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import java.lang.annotation.Annotation; + +/** + *

      + * Represents an element that can be annotated. The methods of this interface + * provide reflection-based access to the annotations associated with a + * particular element, such as a class, field, method or package. Each + * annotation returned by these methods is both immutable and serializable. + * The returned arrays may be freely modified, without any effect on the + * arrays returned to future callers. + *

      + *

      + * If an annotation refers to a type or enumeration constant that is + * inaccessible, then a TypeNotPresentException or + * EnumConstantNotPresentException will be thrown. Likewise, + * invalid annotations will produce either a + * AnnotationTypeMismatchException or + * IncompleteAnnotationException. + *

      + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface AnnotatedElement +{ + + /** + * Returns the element's annotation for the specified annotation type, + * or null if no such annotation exists. + * + * @param annotationClass the type of annotation to look for. + * @return this element's annotation for the specified type, or + * null if no such annotation exists. + * @throws NullPointerException if the annotation class is null. + */ + T getAnnotation(Class annotationClass); + + /** + * Returns all annotations associated with the element. If there are + * no annotations associated with the element, then a zero-length array + * will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of the + * element, and hence no effect on the return value of this method for + * future callers. + * + * @return this element's annotations. + */ + Annotation[] getAnnotations(); + + /** + * Returns all annotations directly defined by the element. If there are + * no annotations directly associated with the element, then a zero-length + * array will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * class, and hence no effect on the return value of this method for + * future callers. + * + * @return the annotations directly defined by the element. + * @since 1.5 + */ + Annotation[] getDeclaredAnnotations(); + + /** + * Returns true if an annotation for the specified type is associated + * with the element. This is primarily a short-hand for using marker + * annotations. + * + * @param annotationClass the type of annotation to look for. + * @return true if an annotation exists for the specified type. + * @since 1.5 + */ + boolean isAnnotationPresent(Class annotationClass); + +} diff --git a/libjava/classpath/java/lang/reflect/Array.java b/libjava/classpath/java/lang/reflect/Array.java new file mode 100644 index 000000000..a31e48ea6 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Array.java @@ -0,0 +1,655 @@ +/* java.lang.reflect.Array - manipulate arrays by reflection + Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Array holds static helper functions that allow you to create and + * manipulate arrays by reflection. Operations know how to perform widening + * conversions, but throw {@link IllegalArgumentException} if you attempt + * a narrowing conversion. Also, when accessing primitive arrays, this + * class performs object wrapping and unwrapping as necessary.

      + * + * Note: This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,, also available as boolean.class, + * byte.class, etc. These are not to be confused with the + * classes java.lang.Boolean, java.lang.Byte, etc., which are + * real classes. Note also that the shorthand Object[].class + * is a convenient way to get array Classes.

      + * + * Performance note: This class performs best when it does not have + * to convert primitive types. The further along the chain it has to convert, + * the worse performance will be. You're best off using the array as whatever + * type it already is, and then converting the result. You will do even + * worse if you do this and use the generic set() function. + * + * @author John Keiser + * @author Eric Blake (ebb9@email.byu.edu) + * @author Per Bothner (bothner@cygnus.com) + * @see java.lang.Boolean#TYPE + * @see java.lang.Byte#TYPE + * @see java.lang.Short#TYPE + * @see java.lang.Character#TYPE + * @see java.lang.Integer#TYPE + * @see java.lang.Long#TYPE + * @see java.lang.Float#TYPE + * @see java.lang.Double#TYPE + * @since 1.1 + * @status updated to 1.4 + */ +public final class Array +{ + + /** + * This class is uninstantiable. + */ + private Array() + { + } + + /** + * Creates a new single-dimensioned array. + * @param componentType the type of the array to create + * @param length the length of the array to create + * @return the created array, cast to an Object + * @throws NullPointerException if componentType is null + * @throws IllegalArgumentException if componentType is + * Void.TYPE + * @throws NegativeArraySizeException when length is less than 0 + * @throws OutOfMemoryError if memory allocation fails + */ + public static Object newInstance(Class componentType, int length) + { + if (! componentType.isPrimitive()) + return VMArray.createObjectArray(componentType, length); + if (componentType == boolean.class) + return new boolean[length]; + if (componentType == byte.class) + return new byte[length]; + if (componentType == char.class) + return new char[length]; + if (componentType == short.class) + return new short[length]; + if (componentType == int.class) + return new int[length]; + if (componentType == long.class) + return new long[length]; + if (componentType == float.class) + return new float[length]; + if (componentType == double.class) + return new double[length]; + // assert componentType == void.class + throw new IllegalArgumentException(); + } + + /** + * Creates a new multi-dimensioned array. The new array has the same + * component type as the argument class, and the number of dimensions + * in the new array is the sum of the dimensions of the argument class + * and the length of the argument dimensions. Virtual Machine limitations + * forbid too many dimensions (usually 255 is the maximum); but even + * 50 dimensions of 2 elements in each dimension would exceed your memory + * long beforehand! + * + * @param componentType the type of the array to create. + * @param dimensions the dimensions of the array to create. Each element + * in dimensions makes another dimension of the new + * array. Thus, Array.newInstance(java.lang.Boolean, + * new int[]{1,2,3}) is the same as + * new java.lang.Boolean[1][2][3] + * @return the created array, cast to an Object + * @throws NullPointerException if componentType or dimension is null + * @throws IllegalArgumentException if the the size of + * dimensions is 0 or exceeds the maximum number of + * array dimensions in the VM; or if componentType is Void.TYPE + * @throws NegativeArraySizeException when any of the dimensions is less + * than 0 + * @throws OutOfMemoryError if memory allocation fails + */ + public static Object newInstance(Class componentType, int[] dimensions) + { + if (dimensions.length <= 0) + throw new IllegalArgumentException ("Empty dimensions array."); + return createMultiArray(componentType, dimensions, 0); + } + + /** + * Gets the array length. + * @param array the array + * @return the length of the array + * @throws IllegalArgumentException if array is not an array + * @throws NullPointerException if array is null + */ + public static int getLength(Object array) + { + if (array instanceof Object[]) + return ((Object[]) array).length; + if (array instanceof boolean[]) + return ((boolean[]) array).length; + if (array instanceof byte[]) + return ((byte[]) array). length; + if (array instanceof char[]) + return ((char[]) array).length; + if (array instanceof short[]) + return ((short[]) array).length; + if (array instanceof int[]) + return ((int[]) array).length; + if (array instanceof long[]) + return ((long[]) array).length; + if (array instanceof float[]) + return ((float[]) array).length; + if (array instanceof double[]) + return ((double[]) array).length; + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of an array. Primitive elements will be wrapped in + * the corresponding class type. + * + * @param array the array to access + * @param index the array index to access + * @return the element at array[index] + * @throws IllegalArgumentException if array is not an array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #getBoolean(Object, int) + * @see #getByte(Object, int) + * @see #getChar(Object, int) + * @see #getShort(Object, int) + * @see #getInt(Object, int) + * @see #getLong(Object, int) + * @see #getFloat(Object, int) + * @see #getDouble(Object, int) + */ + public static Object get(Object array, int index) + { + if (array instanceof Object[]) + return ((Object[]) array)[index]; + if (array instanceof boolean[]) + return ((boolean[]) array)[index] ? Boolean.TRUE : Boolean.FALSE; + if (array instanceof byte[]) + return Byte.valueOf(((byte[]) array)[index]); + if (array instanceof char[]) + return Character.valueOf(((char[]) array)[index]); + if (array instanceof short[]) + return Short.valueOf(((short[]) array)[index]); + if (array instanceof int[]) + return Integer.valueOf(((int[]) array)[index]); + if (array instanceof long[]) + return Long.valueOf(((long[]) array)[index]); + if (array instanceof float[]) + return Float.valueOf(((float[]) array)[index]); + if (array instanceof double[]) + return Double.valueOf(((double[]) array)[index]); + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of a boolean array. + * + * @param array the array to access + * @param index the array index to access + * @return the boolean element at array[index] + * @throws IllegalArgumentException if array is not a boolean + * array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #get(Object, int) + */ + public static boolean getBoolean(Object array, int index) + { + if (array instanceof boolean[]) + return ((boolean[]) array)[index]; + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of a byte array. + * + * @param array the array to access + * @param index the array index to access + * @return the byte element at array[index] + * @throws IllegalArgumentException if array is not a byte + * array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #get(Object, int) + */ + public static byte getByte(Object array, int index) + { + if (array instanceof byte[]) + return ((byte[]) array)[index]; + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of a char array. + * + * @param array the array to access + * @param index the array index to access + * @return the char element at array[index] + * @throws IllegalArgumentException if array is not a char + * array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #get(Object, int) + */ + public static char getChar(Object array, int index) + { + if (array instanceof char[]) + return ((char[]) array)[index]; + if (array == null) + throw new NullPointerException(); + throw new IllegalArgumentException(); + } + + /** + * Gets an element of a short array. + * + * @param array the array to access + * @param index the array index to access + * @return the short element at array[index] + * @throws IllegalArgumentException if array is not a byte + * or char array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #get(Object, int) + */ + public static short getShort(Object array, int index) + { + if (array instanceof short[]) + return ((short[]) array)[index]; + return getByte(array, index); + } + + /** + * Gets an element of an int array. + * + * @param array the array to access + * @param index the array index to access + * @return the int element at array[index] + * @throws IllegalArgumentException if array is not a byte, + * char, short, or int array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #get(Object, int) + */ + public static int getInt(Object array, int index) + { + if (array instanceof int[]) + return ((int[]) array)[index]; + if (array instanceof char[]) + return ((char[]) array)[index]; + return getShort(array, index); + } + + /** + * Gets an element of a long array. + * + * @param array the array to access + * @param index the array index to access + * @return the long element at array[index] + * @throws IllegalArgumentException if array is not a byte, + * char, short, int, or long array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #get(Object, int) + */ + public static long getLong(Object array, int index) + { + if (array instanceof long[]) + return ((long[]) array)[index]; + return getInt(array, index); + } + + /** + * Gets an element of a float array. + * + * @param array the array to access + * @param index the array index to access + * @return the float element at array[index] + * @throws IllegalArgumentException if array is not a byte, + * char, short, int, long, or float array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #get(Object, int) + */ + public static float getFloat(Object array, int index) + { + if (array instanceof float[]) + return ((float[]) array)[index]; + return getLong(array, index); + } + + /** + * Gets an element of a double array. + * + * @param array the array to access + * @param index the array index to access + * @return the double element at array[index] + * @throws IllegalArgumentException if array is not a byte, + * char, short, int, long, float, or double array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #get(Object, int) + */ + public static double getDouble(Object array, int index) + { + if (array instanceof double[]) + return ((double[]) array)[index]; + return getFloat(array, index); + } + + /** + * Sets an element of an array. If the array is primitive, then the new + * value is unwrapped and widened. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not an array, + * or the array is primitive and unwrapping value fails, or the + * value is not assignable to the array component type + * @throws NullPointerException if array is null, or if array is primitive + * and value is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #setBoolean(Object, int, boolean) + * @see #setByte(Object, int, byte) + * @see #setChar(Object, int, char) + * @see #setShort(Object, int, short) + * @see #setInt(Object, int, int) + * @see #setLong(Object, int, long) + * @see #setFloat(Object, int, float) + * @see #setDouble(Object, int, double) + */ + public static void set(Object array, int index, Object value) + { + if (array instanceof Object[]) + { + // Too bad the API won't let us throw the easier ArrayStoreException! + if (value != null + && ! array.getClass().getComponentType().isInstance(value)) + throw new IllegalArgumentException(); + ((Object[]) array)[index] = value; + } + else if (value instanceof Byte) + setByte(array, index, ((Byte) value).byteValue()); + else if (value instanceof Short) + setShort(array, index, ((Short) value).shortValue()); + else if (value instanceof Integer) + setInt(array, index, ((Integer) value).intValue()); + else if (value instanceof Long) + setLong(array, index, ((Long) value).longValue()); + else if (value instanceof Float) + setFloat(array, index, ((Float) value).floatValue()); + else if (value instanceof Double) + setDouble(array, index, ((Double) value).doubleValue()); + else if (value instanceof Character) + setChar(array, index, ((Character) value).charValue()); + else if (value instanceof Boolean) + setBoolean(array, index, ((Boolean) value).booleanValue()); + else if (array == null) + throw new NullPointerException(); + else + throw new IllegalArgumentException(); + } + + /** + * Sets an element of a boolean array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not a boolean + * array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setBoolean(Object array, int index, boolean value) + { + if (array instanceof boolean[]) + ((boolean[]) array)[index] = value; + else if (array == null) + throw new NullPointerException(); + else + throw new IllegalArgumentException(); + } + + /** + * Sets an element of a byte array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not a byte, + * short, int, long, float, or double array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setByte(Object array, int index, byte value) + { + if (array instanceof byte[]) + ((byte[]) array)[index] = value; + else + setShort(array, index, value); + } + + /** + * Sets an element of a char array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not a char, + * int, long, float, or double array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setChar(Object array, int index, char value) + { + if (array instanceof char[]) + ((char[]) array)[index] = value; + else + setInt(array, index, value); + } + + /** + * Sets an element of a short array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not a short, + * int, long, float, or double array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setShort(Object array, int index, short value) + { + if (array instanceof short[]) + ((short[]) array)[index] = value; + else + setInt(array, index, value); + } + + /** + * Sets an element of an int array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not an int, + * long, float, or double array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setInt(Object array, int index, int value) + { + if (array instanceof int[]) + ((int[]) array)[index] = value; + else + setLong(array, index, value); + } + + /** + * Sets an element of a long array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not a long, + * float, or double array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setLong(Object array, int index, long value) + { + if (array instanceof long[]) + ((long[]) array)[index] = value; + else + setFloat(array, index, value); + } + + /** + * Sets an element of a float array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not a float + * or double array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setFloat(Object array, int index, float value) + { + if (array instanceof float[]) + ((float[]) array)[index] = value; + else + setDouble(array, index, value); + } + + /** + * Sets an element of a double array. + * + * @param array the array to set a value of + * @param index the array index to set the value to + * @param value the value to set + * @throws IllegalArgumentException if array is not a double + * array + * @throws NullPointerException if array is null + * @throws ArrayIndexOutOfBoundsException if index is out of + * bounds + * @see #set(Object, int, Object) + */ + public static void setDouble(Object array, int index, double value) + { + if (array instanceof double[]) + ((double[]) array)[index] = value; + else if (array == null) + throw new NullPointerException(); + else + throw new IllegalArgumentException(); + } + + /** + * Dynamically and recursively create a multi-dimensioned array of objects. + * + * @param type guaranteed to be a valid object type + * @param dimensions the dimensions of the array + * @param index index of the current dimension to build + * @return the new multi-dimensioned array + * @throws NegativeArraySizeException if any entry of dimensions is negative + * @throws OutOfMemoryError if memory allocation fails + */ + // This would be faster if implemented natively, using the multianewarray + // bytecode instead of this recursive call + private static Object createMultiArray(Class type, int[] dimensions, + int index) + { + if (index == dimensions.length - 1) + return newInstance(type, dimensions[index]); + + Object toAdd = createMultiArray(type, dimensions, index + 1); + Class thisType = toAdd.getClass(); + Object[] retval + = (Object[]) VMArray.createObjectArray(thisType, dimensions[index]); + if (dimensions[index] > 0) + retval[0] = toAdd; + int i = dimensions[index]; + while (--i > 0) + retval[i] = createMultiArray(type, dimensions, index + 1); + return retval; + } + +} diff --git a/libjava/classpath/java/lang/reflect/Constructor.java b/libjava/classpath/java/lang/reflect/Constructor.java new file mode 100644 index 000000000..d6c8d7934 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Constructor.java @@ -0,0 +1,453 @@ +/* java.lang.reflect.Constructor - reflection of Java constructors + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import gnu.java.lang.ClassHelper; +import gnu.java.lang.CPStringBuilder; + +import gnu.java.lang.reflect.MethodSignatureParser; + +import java.lang.annotation.Annotation; + +/** + * The Constructor class represents a constructor of a class. It also allows + * dynamic creation of an object, via reflection. Invocation on Constructor + * objects knows how to do widening conversions, but throws + * {@link IllegalArgumentException} if a narrowing conversion would be + * necessary. You can query for information on this Constructor regardless + * of location, but construction access may be limited by Java language + * access controls. If you can't do it in the compiler, you can't normally + * do it here either.

      + * + * Note: This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,, also available as boolean.class, + * byte.class, etc. These are not to be confused with the + * classes java.lang.Boolean, java.lang.Byte, etc., which are + * real classes.

      + * + * Also note that this is not a serializable class. It is entirely feasible + * to make it serializable using the Externalizable interface, but this is + * on Sun, not me. + * + * @author John Keiser + * @author Eric Blake + * @see Member + * @see Class + * @see java.lang.Class#getConstructor(Class[]) + * @see java.lang.Class#getDeclaredConstructor(Class[]) + * @see java.lang.Class#getConstructors() + * @see java.lang.Class#getDeclaredConstructors() + * @since 1.1 + * @status updated to 1.4 + */ +public final class Constructor + extends AccessibleObject + implements GenericDeclaration, Member +{ + private static final int CONSTRUCTOR_MODIFIERS + = Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC; + + private MethodSignatureParser p; + + VMConstructor cons; + + /** + * This class is uninstantiable outside this package. + */ + Constructor(VMConstructor cons) + { + this.cons = cons; + cons.cons = this; + } + + private Constructor() + { + } + + /** + * Gets the class that declared this constructor. + * @return the class that declared this member + */ + public Class getDeclaringClass() + { + // Inescapable as the VM layer is 1.4 based. + @SuppressWarnings("unchecked") + Class declClass = (Class) cons.getDeclaringClass(); + return declClass; + } + + /** + * Gets the name of this constructor (the non-qualified name of the class + * it was declared in). + * @return the name of this constructor + */ + public String getName() + { + return cons.getDeclaringClass().getName(); + } + + /** + * Gets the modifiers this constructor uses. Use the Modifier + * class to interpret the values. A constructor can only have a subset of the + * following modifiers: public, private, protected. + * + * @return an integer representing the modifiers to this Member + * @see Modifier + */ + public int getModifiers() + { + return cons.getModifiersInternal() & CONSTRUCTOR_MODIFIERS; + } + + /** + * Return true if this constructor is synthetic, false otherwise. + * A synthetic member is one which is created by the compiler, + * and which does not appear in the user's source code. + * @since 1.5 + */ + public boolean isSynthetic() + { + return (cons.getModifiersInternal() & Modifier.SYNTHETIC) != 0; + } + + /** + * Return true if this is a varargs constructor, that is if + * the constructor takes a variable number of arguments. + * @since 1.5 + */ + public boolean isVarArgs() + { + return (cons.getModifiersInternal() & Modifier.VARARGS) != 0; + } + + /** + * Get the parameter list for this constructor, in declaration order. If the + * constructor takes no parameters, returns a 0-length array (not null). + * + * @return a list of the types of the constructor's parameters + */ + public Class[] getParameterTypes() + { + return (Class[]) cons.getParameterTypes(); + } + + /** + * Get the exception types this constructor says it throws, in no particular + * order. If the constructor has no throws clause, returns a 0-length array + * (not null). + * + * @return a list of the types in the constructor's throws clause + */ + public Class[] getExceptionTypes() + { + return (Class[]) cons.getExceptionTypes(); + } + + /** + * Compare two objects to see if they are semantically equivalent. + * Two Constructors are semantically equivalent if they have the same + * declaring class and the same parameter list. This ignores different + * exception clauses, but since you can't create a Method except through the + * VM, this is just the == relation. + * + * @param o the object to compare to + * @return true if they are equal; false if not. + */ + public boolean equals(Object o) + { + return cons.equals(o); + } + + /** + * Get the hash code for the Constructor. The Constructor hash code is the + * hash code of the declaring class's name. + * + * @return the hash code for the object + */ + public int hashCode() + { + return getName().hashCode(); + } + + /** + * Get a String representation of the Constructor. A Constructor's String + * representation is "<modifier> <classname>(<paramtypes>) + * throws <exceptions>", where everything after ')' is omitted if + * there are no exceptions.
      Example: + * public java.io.FileInputStream(java.lang.Runnable) + * throws java.io.FileNotFoundException + * + * @return the String representation of the Constructor + */ + public String toString() + { + // 128 is a reasonable buffer initial size for constructor + CPStringBuilder sb = new CPStringBuilder(128); + Modifier.toString(getModifiers(), sb).append(' '); + sb.append(getDeclaringClass().getName()).append('('); + Class[] c = getParameterTypes(); + if (c.length > 0) + { + sb.append(ClassHelper.getUserName(c[0])); + for (int i = 1; i < c.length; i++) + sb.append(',').append(ClassHelper.getUserName(c[i])); + } + sb.append(')'); + c = getExceptionTypes(); + if (c.length > 0) + { + sb.append(" throws ").append(c[0].getName()); + for (int i = 1; i < c.length; i++) + sb.append(',').append(c[i].getName()); + } + return sb.toString(); + } + + static + void addTypeParameters(CPStringBuilder sb, TypeVariable[] typeArgs) + { + if (typeArgs.length == 0) + return; + sb.append('<'); + for (int i = 0; i < typeArgs.length; ++i) + { + if (i > 0) + sb.append(','); + sb.append(typeArgs[i]); + } + sb.append("> "); + } + + public String toGenericString() + { + CPStringBuilder sb = new CPStringBuilder(128); + Modifier.toString(getModifiers(), sb).append(' '); + addTypeParameters(sb, getTypeParameters()); + sb.append(getDeclaringClass().getName()).append('('); + Type[] types = getGenericParameterTypes(); + if (types.length > 0) + { + sb.append(types[0]); + for (int i = 1; i < types.length; ++i) + sb.append(',').append(types[i]); + } + sb.append(')'); + types = getGenericExceptionTypes(); + if (types.length > 0) + { + sb.append(" throws ").append(types[0]); + for (int i = 1; i < types.length; i++) + sb.append(',').append(types[i]); + } + return sb.toString(); + } + + /** + * Create a new instance by invoking the constructor. Arguments are + * automatically unwrapped and widened, if needed.

      + * + * If this class is abstract, you will get an + * InstantiationException. If the constructor takes 0 + * arguments, you may use null or a 0-length array for args.

      + * + * If this Constructor enforces access control, your runtime context is + * evaluated, and you may have an IllegalAccessException if + * you could not create this object in similar compiled code. If the class + * is uninitialized, you trigger class initialization, which may end in a + * ExceptionInInitializerError.

      + * + * Then, the constructor is invoked. If it completes normally, the return + * value will be the new object. If it completes abruptly, the exception is + * wrapped in an InvocationTargetException. + * + * @param args the arguments to the constructor + * @return the newly created object + * @throws IllegalAccessException if the constructor could not normally be + * called by the Java code (i.e. it is not public) + * @throws IllegalArgumentException if the number of arguments is incorrect; + * or if the arguments types are wrong even with a widening + * conversion + * @throws InstantiationException if the class is abstract + * @throws InvocationTargetException if the constructor throws an exception + * @throws ExceptionInInitializerError if construction triggered class + * initialization, which then failed + */ + public T newInstance(Object... args) + throws InstantiationException, IllegalAccessException, + InvocationTargetException + { + // Inescapable as the VM layer is 1.4 based. + @SuppressWarnings("unchecked") + T ins = (T) cons.construct(args); + return ins; + } + + /** + * Returns an array of TypeVariable objects that represents + * the type variables declared by this constructor, in declaration order. + * An array of size zero is returned if this constructor has no type + * variables. + * + * @return the type variables associated with this constructor. + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public TypeVariable>[] getTypeParameters() + { + if (p == null) + { + String sig = cons.getSignature(); + if (sig == null) + return new TypeVariable[0]; + p = new MethodSignatureParser(this, sig); + } + return p.getTypeParameters(); + } + + /** + * Returns an array of Type objects that represents + * the exception types declared by this constructor, in declaration order. + * An array of size zero is returned if this constructor declares no + * exceptions. + * + * @return the exception types declared by this constructor. + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public Type[] getGenericExceptionTypes() + { + if (p == null) + { + String sig = cons.getSignature(); + if (sig == null) + return getExceptionTypes(); + p = new MethodSignatureParser(this, sig); + } + return p.getGenericExceptionTypes(); + } + + /** + * Returns an array of Type objects that represents + * the parameter list for this constructor, in declaration order. + * An array of size zero is returned if this constructor takes no + * parameters. + * + * @return a list of the types of the constructor's parameters + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public Type[] getGenericParameterTypes() + { + if (p == null) + { + String sig = cons.getSignature(); + if (sig == null) + return getParameterTypes(); + p = new MethodSignatureParser(this, sig); + } + return p.getGenericParameterTypes(); + } + + /** + *

      + * Return an array of arrays representing the annotations on each + * of the constructor's parameters. The outer array is aligned against + * the parameters of the constructors and is thus equal in length to + * the number of parameters (thus having a length zero if there are none). + * Each array element in the outer array contains an inner array which + * holds the annotations. This array has a length of zero if the parameter + * has no annotations. + *

      + *

      + * The returned annotations are serialized. Changing the annotations has + * no affect on the return value of future calls to this method. + *

      + * + * @return an array of arrays which represents the annotations used on the + * parameters of this constructor. The order of the array elements + * matches the declaration order of the parameters. + * @since 1.5 + */ + public Annotation[][] getParameterAnnotations() + { + return cons.getParameterAnnotations(); + } + + /** + * Returns the element's annotation for the specified annotation type, + * or null if no such annotation exists. + * + * @param annotationClass the type of annotation to look for. + * @return this element's annotation for the specified type, or + * null if no such annotation exists. + * @throws NullPointerException if the annotation class is null. + */ + public T getAnnotation(Class annotationClass) + { + // Inescapable as the VM layer is 1.4 based. + @SuppressWarnings("unchecked") + T ann = (T) cons.getAnnotation(annotationClass); + return ann; + } + + /** + * Returns all annotations directly defined by the element. If there are + * no annotations directly associated with the element, then a zero-length + * array will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * class, and hence no effect on the return value of this method for + * future callers. + * + * @return the annotations directly defined by the element. + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() + { + return cons.getDeclaredAnnotations(); + } + +} diff --git a/libjava/classpath/java/lang/reflect/Field.java b/libjava/classpath/java/lang/reflect/Field.java new file mode 100644 index 000000000..29ca795b8 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Field.java @@ -0,0 +1,735 @@ +/* java.lang.reflect.Field - reflection of Java fields + Copyright (C) 1998, 2001, 2005, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import gnu.java.lang.ClassHelper; +import gnu.java.lang.CPStringBuilder; + +import gnu.java.lang.reflect.FieldSignatureParser; + +import java.lang.annotation.Annotation; + +/** + * The Field class represents a member variable of a class. It also allows + * dynamic access to a member, via reflection. This works for both + * static and instance fields. Operations on Field objects know how to + * do widening conversions, but throw {@link IllegalArgumentException} if + * a narrowing conversion would be necessary. You can query for information + * on this Field regardless of location, but get and set access may be limited + * by Java language access controls. If you can't do it in the compiler, you + * can't normally do it here either.

      + * + * Note: This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,, also available as boolean.class, + * byte.class, etc. These are not to be confused with the + * classes java.lang.Boolean, java.lang.Byte, etc., which are + * real classes.

      + * + * Also note that this is not a serializable class. It is entirely feasible + * to make it serializable using the Externalizable interface, but this is + * on Sun, not me. + * + * @author John Keiser + * @author Eric Blake + * @see Member + * @see Class + * @see Class#getField(String) + * @see Class#getDeclaredField(String) + * @see Class#getFields() + * @see Class#getDeclaredFields() + * @since 1.1 + * @status updated to 1.4 + */ +public final class Field +extends AccessibleObject implements Member +{ + static final int FIELD_MODIFIERS + = Modifier.FINAL | Modifier.PRIVATE | Modifier.PROTECTED + | Modifier.PUBLIC | Modifier.STATIC | Modifier.TRANSIENT + | Modifier.VOLATILE; + + private FieldSignatureParser p; + + VMField f; + + /** + * This class is uninstantiable outside the package. + */ + Field(VMField f) + { + this.f = f; + f.f = this; + } + + /** + * Gets the class that declared this field, or the class where this field + * is a non-inherited member. + * @return the class that declared this member + */ + public Class getDeclaringClass() + { + return (Class) f.getDeclaringClass(); + } + + /** + * Gets the name of this field. + * @return the name of this field + */ + public String getName() + { + return f.getName(); + } + + /** + * Gets the modifiers this field uses. Use the Modifier + * class to interpret the values. A field can only have a subset of the + * following modifiers: public, private, protected, static, final, + * transient, and volatile. + * + * @return an integer representing the modifiers to this Member + * @see Modifier + */ + public int getModifiers() + { + return f.getModifiersInternal() & FIELD_MODIFIERS; + } + + /** + * Return true if this field is synthetic, false otherwise. + * @since 1.5 + */ + public boolean isSynthetic() + { + return (f.getModifiersInternal() & Modifier.SYNTHETIC) != 0; + } + + /** + * Return true if this field represents an enum constant, + * false otherwise. + * @since 1.5 + */ + public boolean isEnumConstant() + { + return (f.getModifiersInternal() & Modifier.ENUM) != 0; + } + + /** + * Gets the type of this field. + * @return the type of this field + */ + public Class getType() + { + return f.getType(); + } + + /** + * Compare two objects to see if they are semantically equivalent. + * Two Fields are semantically equivalent if they have the same declaring + * class, name, and type. Since you can't creat a Field except through + * the VM, this is just the == relation. + * + * @param o the object to compare to + * @return true if they are equal; false if not + */ + public boolean equals(Object o) + { + return f.equals(o); + } + + /** + * Get the hash code for the Field. The Field hash code is the hash code + * of its name XOR'd with the hash code of its class name. + * + * @return the hash code for the object. + */ + public int hashCode() + { + return f.getDeclaringClass().getName().hashCode() ^ f.getName().hashCode(); + } + + /** + * Get a String representation of the Field. A Field's String + * representation is "<modifiers> <type> + * <class>.<fieldname>".
      Example: + * public transient boolean gnu.parse.Parser.parseComplete + * + * @return the String representation of the Field + */ + public String toString() + { + // 64 is a reasonable buffer initial size for field + CPStringBuilder sb = new CPStringBuilder(64); + Modifier.toString(getModifiers(), sb).append(' '); + sb.append(ClassHelper.getUserName(getType())).append(' '); + sb.append(getDeclaringClass().getName()).append('.'); + sb.append(getName()); + return sb.toString(); + } + + public String toGenericString() + { + CPStringBuilder sb = new CPStringBuilder(64); + Modifier.toString(getModifiers(), sb).append(' '); + sb.append(getGenericType()).append(' '); + sb.append(getDeclaringClass().getName()).append('.'); + sb.append(getName()); + return sb.toString(); + } + + /** + * Get the value of this Field. If it is primitive, it will be wrapped + * in the appropriate wrapper type (boolean = java.lang.Boolean).

      + * + * If the field is static, o will be ignored. Otherwise, if + * o is null, you get a NullPointerException, + * and if it is incompatible with the declaring class of the field, you + * get an IllegalArgumentException.

      + * + * Next, if this Field enforces access control, your runtime context is + * evaluated, and you may have an IllegalAccessException if + * you could not access this field in similar compiled code. If the field + * is static, and its class is uninitialized, you trigger class + * initialization, which may end in a + * ExceptionInInitializerError.

      + * + * Finally, the field is accessed, and primitives are wrapped (but not + * necessarily in new objects). This method accesses the field of the + * declaring class, even if the instance passed in belongs to a subclass + * which declares another field to hide this one. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if o is not an instance of + * the class or interface declaring this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #getBoolean(Object) + * @see #getByte(Object) + * @see #getChar(Object) + * @see #getShort(Object) + * @see #getInt(Object) + * @see #getLong(Object) + * @see #getFloat(Object) + * @see #getDouble(Object) + */ + public Object get(Object o) + throws IllegalAccessException + { + return f.get(o); + } + + /** + * Get the value of this boolean Field. If the field is static, + * o will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a boolean field of + * o, or if o is not an instance of the + * declaring class of this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public boolean getBoolean(Object o) + throws IllegalAccessException + { + return f.getBoolean(o); + } + + /** + * Get the value of this byte Field. If the field is static, + * o will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte field of + * o, or if o is not an instance of the + * declaring class of this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public byte getByte(Object o) + throws IllegalAccessException + { + return f.getByte(o); + } + + /** + * Get the value of this Field as a char. If the field is static, + * o will be ignored. + * + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a char field of + * o, or if o is not an instance + * of the declaring class of this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public char getChar(Object o) + throws IllegalAccessException + { + return f.getChar(o); + } + + /** + * Get the value of this Field as a short. If the field is static, + * o will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte or short + * field of o, or if o is not an instance + * of the declaring class of this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public short getShort(Object o) + throws IllegalAccessException + { + return f.getShort(o); + } + + /** + * Get the value of this Field as an int. If the field is static, + * o will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, char, or + * int field of o, or if o is not an + * instance of the declaring class of this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public int getInt(Object o) + throws IllegalAccessException + { + return f.getInt(o); + } + + /** + * Get the value of this Field as a long. If the field is static, + * o will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, char, int, + * or long field of o, or if o is not an + * instance of the declaring class of this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public long getLong(Object o) + throws IllegalAccessException + { + return f.getLong(o); + } + + /** + * Get the value of this Field as a float. If the field is static, + * o will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, char, int, + * long, or float field of o, or if o is + * not an instance of the declaring class of this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public float getFloat(Object o) + throws IllegalAccessException + { + return f.getFloat(o); + } + + /** + * Get the value of this Field as a double. If the field is static, + * o will be ignored. + * + * @param o the object to get the value of this Field from + * @return the value of the Field + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, char, int, + * long, float, or double field of o, or if + * o is not an instance of the declaring class of this + * field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #get(Object) + */ + public double getDouble(Object o) + throws IllegalAccessException + { + return f.getDouble(o); + } + + /** + * Set the value of this Field. If it is a primitive field, the value + * will be unwrapped from the passed object (boolean = java.lang.Boolean).

      + * + * If the field is static, o will be ignored. Otherwise, if + * o is null, you get a NullPointerException, + * and if it is incompatible with the declaring class of the field, you + * get an IllegalArgumentException.

      + * + * Next, if this Field enforces access control, your runtime context is + * evaluated, and you may have an IllegalAccessException if + * you could not access this field in similar compiled code. This also + * occurs whether or not there is access control if the field is final. + * If the field is primitive, and unwrapping your argument fails, you will + * get an IllegalArgumentException; likewise, this error + * happens if value cannot be cast to the correct object type. + * If the field is static, and its class is uninitialized, you trigger class + * initialization, which may end in a + * ExceptionInInitializerError.

      + * + * Finally, the field is set with the widened value. This method accesses + * the field of the declaring class, even if the instance passed in belongs + * to a subclass which declares another field to hide this one. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if value cannot be + * converted by a widening conversion to the underlying type of + * the Field, or if o is not an instance of the class + * declaring this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #setBoolean(Object, boolean) + * @see #setByte(Object, byte) + * @see #setChar(Object, char) + * @see #setShort(Object, short) + * @see #setInt(Object, int) + * @see #setLong(Object, long) + * @see #setFloat(Object, float) + * @see #setDouble(Object, double) + */ + public void set(Object o, Object value) + throws IllegalAccessException + { + f.set(o, value); + } + + /** + * Set this boolean Field. If the field is static, o will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a boolean field, or if + * o is not an instance of the class declaring this + * field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setBoolean(Object o, boolean value) + throws IllegalAccessException + { + f.setBoolean(o, value); + } + + /** + * Set this byte Field. If the field is static, o will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a byte, short, int, long, + * float, or double field, or if o is not an instance + * of the class declaring this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setByte(Object o, byte value) + throws IllegalAccessException + { + f.setByte(o, value); + } + + /** + * Set this char Field. If the field is static, o will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a char, int, long, + * float, or double field, or if o is not an instance + * of the class declaring this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setChar(Object o, char value) + throws IllegalAccessException + { + f.setChar(o, value); + } + + /** + * Set this short Field. If the field is static, o will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a short, int, long, + * float, or double field, or if o is not an instance + * of the class declaring this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setShort(Object o, short value) + throws IllegalAccessException + { + f.setShort(o, value); + } + + /** + * Set this int Field. If the field is static, o will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not an int, long, float, or + * double field, or if o is not an instance of the + * class declaring this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setInt(Object o, int value) + throws IllegalAccessException + { + f.setInt(o, value); + } + + /** + * Set this long Field. If the field is static, o will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a long, float, or double + * field, or if o is not an instance of the class + * declaring this field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setLong(Object o, long value) + throws IllegalAccessException + { + f.setLong(o, value); + } + + /** + * Set this float Field. If the field is static, o will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a float or long field, or + * if o is not an instance of the class declaring this + * field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setFloat(Object o, float value) + throws IllegalAccessException + { + f.setFloat(o, value); + } + + /** + * Set this double Field. If the field is static, o will be + * ignored. + * + * @param o the object to set this Field on + * @param value the value to set this Field to + * @throws IllegalAccessException if you could not normally access this field + * (i.e. it is not public) + * @throws IllegalArgumentException if this is not a double field, or if + * o is not an instance of the class declaring this + * field + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static field triggered + * class initialization, which then failed + * @see #set(Object, Object) + */ + public void setDouble(Object o, double value) + throws IllegalAccessException + { + f.setDouble(o, value); + } + + /** + * Return the generic type of the field. If the field type is not a generic + * type, the method returns the same as getType(). + * + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public Type getGenericType() + { + if (p == null) + { + String signature = f.getSignature(); + if (signature == null) + return getType(); + p = new FieldSignatureParser(getDeclaringClass(), + signature); + } + return p.getFieldType(); + } + + /** + * Returns the element's annotation for the specified annotation type, + * or null if no such annotation exists. + * + * @param annotationClass the type of annotation to look for. + * @return this element's annotation for the specified type, or + * null if no such annotation exists. + * @throws NullPointerException if the annotation class is null. + */ + public T getAnnotation(Class annotationClass) + { + // Inescapable as the VM layer is 1.4 based. T will erase to Annotation anyway. + @SuppressWarnings("unchecked") T ann = (T) f.getAnnotation(annotationClass); + return ann; + } + + /** + * Returns all annotations directly defined by the element. If there are + * no annotations directly associated with the element, then a zero-length + * array will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * class, and hence no effect on the return value of this method for + * future callers. + * + * @return the annotations directly defined by the element. + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() + { + return f.getDeclaredAnnotations(); + } + +} diff --git a/libjava/classpath/java/lang/reflect/GenericArrayType.java b/libjava/classpath/java/lang/reflect/GenericArrayType.java new file mode 100644 index 000000000..2e080247c --- /dev/null +++ b/libjava/classpath/java/lang/reflect/GenericArrayType.java @@ -0,0 +1,61 @@ +/* GenericArrayType.java - Represent an array type with a generic component + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Represents the type of an array's components, which may be + * either a parameterized type or a type variable. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface GenericArrayType + extends Type +{ + + /** + * Returns the Type of the components within the array. + * + * @return a Type instance representing the type of + * the array's components. + */ + Type getGenericComponentType(); + +} diff --git a/libjava/classpath/java/lang/reflect/GenericDeclaration.java b/libjava/classpath/java/lang/reflect/GenericDeclaration.java new file mode 100644 index 000000000..d78aba913 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/GenericDeclaration.java @@ -0,0 +1,62 @@ +/* GenericDeclaration.java + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Represents an entity that declares one or more type parameters. + * This includes classes and methods (including constructors). + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface GenericDeclaration +{ + /** + * Returns a TypeVariable object for each type variable + * declared by this entity, in order of declaration. An empty array + * is returned if no type variables are declared. + * + * @return an array of TypeVariable objects. + * @throws GenericSignatureFormatError if the signature format within the + * class file does not conform to that specified in the 3rd edition + * of the Java Virtual Machine Specification. + */ + TypeVariable[] getTypeParameters(); +} diff --git a/libjava/classpath/java/lang/reflect/GenericSignatureFormatError.java b/libjava/classpath/java/lang/reflect/GenericSignatureFormatError.java new file mode 100644 index 000000000..0f09522bc --- /dev/null +++ b/libjava/classpath/java/lang/reflect/GenericSignatureFormatError.java @@ -0,0 +1,63 @@ +/* GenericSignatureFormatError.java - Thrown when a signature is malformed. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Thrown on encountering a syntactically malformed signature in + * a reflective method. During reflection, the generic type signature + * of a type, method or constructor may be interpreted by the virtual + * machine. This error is thrown if this operation fails. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class GenericSignatureFormatError + extends ClassFormatError +{ + private static final long serialVersionUID = 6709919147137911034L; + + /** + * Constructs a new GenericSignatureFormatError. + */ + public GenericSignatureFormatError() + { + } + +} diff --git a/libjava/classpath/java/lang/reflect/InvocationHandler.java b/libjava/classpath/java/lang/reflect/InvocationHandler.java new file mode 100644 index 000000000..3d30afd93 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/InvocationHandler.java @@ -0,0 +1,137 @@ +/* java.lang.reflect.InvocationHandler - dynamically executes methods in + proxy instances + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * This interface defines an invocation handler. Suppose you are using + * reflection, and found a method that requires that its parameter + * be an object of a given interface. You want to call this method, + * but have no idea what classes implement that interface. So, you can + * create a {@link Proxy} instance, a convenient way to dynamically + * generate a class that meets all the necessary properties of that + * interface. But in order for the proxy instance to do any good, it + * needs to know what to do when interface methods are invoked! So, + * this interface is basically a cool wrapper that provides runtime + * code generation needed by proxy instances. + * + *

      While this interface was designed for use by Proxy, it will also + * work on any object in general.

      + * + *

      Hints for implementing this class:

      + * + *
        + *
      • Don't forget that Object.equals, Object.hashCode, and + * Object.toString will call this handler. In particular, + * a naive call to proxy.equals, proxy.hashCode, or proxy.toString + * will put you in an infinite loop. And remember that string + * concatenation also invokes toString.
      • + *
      • Obey the contract of the Method object you are handling, or + * the proxy instance will be forced to throw a + * {@link NullPointerException}, {@link ClassCastException}, + * or {@link UndeclaredThrowableException}.
      • + *
      • Be prepared to wrap/unwrap primitives as necessary.
      • + *
      • The Method object may be owned by a different interface than + * what was actually used as the qualifying type of the method + * invocation in the Java source code. This means that it might + * not always be safe to throw an exception listed as belonging + * to the method's throws clause.
      • + *
      + * + *

      For a fun time, create an InvocationHandler that handles the + * methods of a proxy instance of the InvocationHandler interface!

      + * + * @see Proxy + * @see UndeclaredThrowableException + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.4 + */ +public interface InvocationHandler +{ + /** + * When a method is invoked on a proxy instance, it is wrapped and + * this method is called instead, so that you may decide at runtime + * how the original method should behave. + * + * @param proxy the instance that the wrapped method should be + * invoked on. When this method is called by a Proxy object, + * `proxy' will be an instance of {@link Proxy}, and oddly enough, + * Proxy.getInvocationHandler(proxy) will return + * this! + * @param method the reflected method to invoke on the proxy. + * When this method is called by a Proxy object, 'method' + * will be the reflection object owned by the declaring + * class or interface, which may be a supertype of the + * interfaces the proxy directly implements. + * @param args the arguments passed to the original method, or + * null if the method takes no arguments. + * (But also be prepared to handle a 0-length array). + * Arguments of primitive type, such as boolean + * or int, are wrapped in the appropriate + * class such as {@link Boolean} or {@link Integer}. + * @return whatever is necessary to return from the wrapped method. + * If the wrapped method is void, the proxy + * instance will ignore it. If the wrapped method returns + * a primitive, this must be the correct wrapper type whose value + * is exactly assignable to the appropriate type (no widening + * will be performed); a null object in this case causes a + * {@link NullPointerException}. In all remaining cases, if + * the returned object is not assignment compatible to the + * declared type of the original method, the proxy instance + * will generate a {@link ClassCastException}. + * @throws Throwable this interface is listed as throwing anything, + * but the implementation should only throw unchecked + * exceptions and exceptions listed in the throws clause of + * all methods being overridden by the proxy instance. If + * something is thrown that is not compatible with the throws + * clause of all overridden methods, the proxy instance will + * wrap the exception in an UndeclaredThrowableException. + * Note that an exception listed in the throws clause of the + * `method' parameter might not be declared in additional + * interfaces also implemented by the proxy object. + * + * @see Proxy + * @see UndeclaredThrowableException + */ + Object invoke(Object proxy, Method method, Object[] args) + throws Throwable; + +} diff --git a/libjava/classpath/java/lang/reflect/InvocationTargetException.java b/libjava/classpath/java/lang/reflect/InvocationTargetException.java new file mode 100644 index 000000000..af79d3a19 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/InvocationTargetException.java @@ -0,0 +1,123 @@ +/* InvocationTargetException.java -- Wrapper exception for reflection + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * InvocationTargetException is sort of a way to "wrap" whatever exception + * comes up when a method or constructor is called via Reflection. As of + * JDK 1.4, it was retrofitted to match the exception chaining of all other + * exceptions, but getTargetException() still works. + * + * @author John Keiser + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Method#invoke(Object,Object[]) + * @see Constructor#newInstance(Object[]) + * @since 1.1 + * @status updated to 1.4 + */ +public class InvocationTargetException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4085088731926701167L; + + /** + * The chained exception. This field is only around for serial compatibility. + * + * @serial the chained exception + */ + private final Throwable target; + + /** + * Construct an exception with null as the cause. The cause is initialized + * to null. + */ + protected InvocationTargetException() + { + this(null, null); + } + + /** + * Create an InvocationTargetException using another + * exception. + * + * @param targetException the exception to wrap + */ + public InvocationTargetException(Throwable targetException) + { + this(targetException, null); + } + + /** + * Create an InvocationTargetException using another + * exception and an error message. + * + * @param targetException the exception to wrap + * @param err an extra reason for the exception-throwing + */ + public InvocationTargetException(Throwable targetException, String err) + { + super(err, targetException); + target = targetException; + } + + /** + * Get the wrapped (targeted) exception. + * + * @return the targeted exception + * @see #getCause() + */ + public Throwable getTargetException() + { + return target; + } + + /** + * Returns the cause of this exception (which may be null). + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return target; + } +} diff --git a/libjava/classpath/java/lang/reflect/MalformedParameterizedTypeException.java b/libjava/classpath/java/lang/reflect/MalformedParameterizedTypeException.java new file mode 100644 index 000000000..bfbe3bc89 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/MalformedParameterizedTypeException.java @@ -0,0 +1,60 @@ +/* MalformedParameterizedTypeException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * This exception class is thrown when one of the reflection + * methods encountered an invalid parameterized type within + * the metadata of a class. One possible reason for this + * exception being thrown is the specification of too few or + * too many type variables. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MalformedParameterizedTypeException + extends RuntimeException +{ + private static final long serialVersionUID = -5696557788586220964L; + + public MalformedParameterizedTypeException() + { + } +} diff --git a/libjava/classpath/java/lang/reflect/Member.java b/libjava/classpath/java/lang/reflect/Member.java new file mode 100644 index 000000000..fed962cf9 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Member.java @@ -0,0 +1,109 @@ +/* java.lang.reflect.Member - common query methods in reflection + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Member is an interface that represents any member of a class (field or + * method) or a constructor. You can get information about the declaring + * class, name or modifiers of the member with this interface. + * + * @author John Keiser + * @author Per Bothner (bothner@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Class + * @see Field + * @see Method + * @see Constructor + * @since 1.1 + * @status updated to 1.4 + */ +public interface Member +{ + /** + * Represents all members, whether public, private, protected or + * package-protected, but only which are declared in this class. + * Used in SecurityManager.checkMemberAccess() to determine the + * type of members to access. + * @see SecurityManager#checkMemberAccess(Class, int) + */ + int DECLARED = 1; + + /** + * Represents public members only, but includes all inherited members. + * Used in SecurityManager.checkMemberAccess() to determine the type of + * members to access. + * @see SecurityManager#checkMemberAccess(Class, int) + */ + int PUBLIC = 0; + + /** + * Gets the class that declared this member. This is not the class where + * this method was called, or even the class where this Member object + * came to life, but the class that declares the member this represents. + * + * @return the class that declared this member + */ + Class getDeclaringClass(); + + /** + * Gets the simple name of this member. This will be a valid Java + * identifier, with no qualification. + * + * @return the name of this member + */ + String getName(); + + /** + * Gets the modifiers this member uses. Use the Modifier + * class to interpret the values. + * + * @return an integer representing the modifiers to this Member + * @see Modifier + */ + int getModifiers(); + + /** + * Return true if this member is synthetic, meaning that it was + * created by the compiler and does not appear in the user's + * source code. + * @return true if the member is synthetic + * @since 1.5 + */ + boolean isSynthetic(); +} diff --git a/libjava/classpath/java/lang/reflect/Method.java b/libjava/classpath/java/lang/reflect/Method.java new file mode 100644 index 000000000..fe4b2eb1f --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Method.java @@ -0,0 +1,497 @@ +/* java.lang.reflect.Method - reflection of Java methods + Copyright (C) 1998, 2001, 2002, 2005, 2007, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import gnu.java.lang.ClassHelper; +import gnu.java.lang.CPStringBuilder; + +import gnu.java.lang.reflect.MethodSignatureParser; + +import java.lang.annotation.Annotation; + +/** + * The Method class represents a member method of a class. It also allows + * dynamic invocation, via reflection. This works for both static and + * instance methods. Invocation on Method objects knows how to do + * widening conversions, but throws {@link IllegalArgumentException} if + * a narrowing conversion would be necessary. You can query for information + * on this Method regardless of location, but invocation access may be limited + * by Java language access controls. If you can't do it in the compiler, you + * can't normally do it here either.

      + * + * Note: This class returns and accepts types as Classes, even + * primitive types; there are Class types defined that represent each + * different primitive type. They are java.lang.Boolean.TYPE, + * java.lang.Byte.TYPE,, also available as boolean.class, + * byte.class, etc. These are not to be confused with the + * classes java.lang.Boolean, java.lang.Byte, etc., which are + * real classes.

      + * + * Also note that this is not a serializable class. It is entirely feasible + * to make it serializable using the Externalizable interface, but this is + * on Sun, not me. + * + * @author John Keiser + * @author Eric Blake + * @see Member + * @see Class + * @see java.lang.Class#getMethod(String,Class[]) + * @see java.lang.Class#getDeclaredMethod(String,Class[]) + * @see java.lang.Class#getMethods() + * @see java.lang.Class#getDeclaredMethods() + * @since 1.1 + * @status updated to 1.4 + */ +public final class Method +extends AccessibleObject implements Member, GenericDeclaration +{ + private static final int METHOD_MODIFIERS + = Modifier.ABSTRACT | Modifier.FINAL | Modifier.NATIVE + | Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC + | Modifier.STATIC | Modifier.STRICT | Modifier.SYNCHRONIZED; + + private MethodSignatureParser p; + + VMMethod m; + + /** + * This class is uninstantiable outside this package. + */ + Method(VMMethod m) + { + this.m = m; + m.m = this; + } + + /** + * Gets the class that declared this method, or the class where this method + * is a non-inherited member. + * @return the class that declared this member + */ + public Class getDeclaringClass() + { + return (Class) m.getDeclaringClass(); + } + + /** + * Gets the name of this method. + * @return the name of this method + */ + public String getName() + { + return m.getName(); + } + + /** + * Gets the modifiers this method uses. Use the Modifier + * class to interpret the values. A method can only have a subset of the + * following modifiers: public, private, protected, abstract, static, + * final, synchronized, native, and strictfp. + * + * @return an integer representing the modifiers to this Member + * @see Modifier + */ + public int getModifiers() + { + return m.getModifiersInternal() & METHOD_MODIFIERS; + } + + /** + * Return true if this method is a bridge method. A bridge method + * is generated by the compiler in some situations involving + * generics and inheritance. + * @since 1.5 + */ + public boolean isBridge() + { + return (m.getModifiersInternal() & Modifier.BRIDGE) != 0; + } + + /** + * Return true if this method is synthetic, false otherwise. + * @since 1.5 + */ + public boolean isSynthetic() + { + return (m.getModifiersInternal() & Modifier.SYNTHETIC) != 0; + } + + /** + * Return true if this is a varargs method, that is if + * the method takes a variable number of arguments. + * @since 1.5 + */ + public boolean isVarArgs() + { + return (m.getModifiersInternal() & Modifier.VARARGS) != 0; + } + + /** + * Gets the return type of this method. + * @return the type of this method + */ + public Class getReturnType() + { + return (Class) m.getReturnType(); + } + + /** + * Get the parameter list for this method, in declaration order. If the + * method takes no parameters, returns a 0-length array (not null). + * + * @return a list of the types of the method's parameters + */ + public Class[] getParameterTypes() + { + return (Class[]) m.getParameterTypes(); + } + + /** + * Get the exception types this method says it throws, in no particular + * order. If the method has no throws clause, returns a 0-length array + * (not null). + * + * @return a list of the types in the method's throws clause + */ + public Class[] getExceptionTypes() + { + return (Class[]) m.getExceptionTypes(); + } + + /** + * Compare two objects to see if they are semantically equivalent. + * Two Methods are semantically equivalent if they have the same declaring + * class, name, parameter list, and return type. + * + * @param o the object to compare to + * @return true if they are equal; false if not + */ + public boolean equals(Object o) + { + return m.equals(o); + } + + /** + * Get the hash code for the Method. The Method hash code is the hash code + * of its name XOR'd with the hash code of its class name. + * + * @return the hash code for the object + */ + public int hashCode() + { + return m.getDeclaringClass().getName().hashCode() ^ m.getName().hashCode(); + } + + /** + * Get a String representation of the Method. A Method's String + * representation is "<modifiers> <returntype> + * <methodname>(<paramtypes>) throws <exceptions>", where + * everything after ')' is omitted if there are no exceptions.
      Example: + * public static int run(java.lang.Runnable,int) + * + * @return the String representation of the Method + */ + public String toString() + { + // 128 is a reasonable buffer initial size for constructor + CPStringBuilder sb = new CPStringBuilder(128); + Modifier.toString(getModifiers(), sb).append(' '); + sb.append(ClassHelper.getUserName(getReturnType())).append(' '); + sb.append(getDeclaringClass().getName()).append('.'); + sb.append(getName()).append('('); + Class[] c = getParameterTypes(); + if (c.length > 0) + { + sb.append(ClassHelper.getUserName(c[0])); + for (int i = 1; i < c.length; i++) + sb.append(',').append(ClassHelper.getUserName(c[i])); + } + sb.append(')'); + c = getExceptionTypes(); + if (c.length > 0) + { + sb.append(" throws ").append(c[0].getName()); + for (int i = 1; i < c.length; i++) + sb.append(',').append(c[i].getName()); + } + return sb.toString(); + } + + public String toGenericString() + { + // 128 is a reasonable buffer initial size for constructor + CPStringBuilder sb = new CPStringBuilder(128); + Modifier.toString(getModifiers(), sb).append(' '); + Constructor.addTypeParameters(sb, getTypeParameters()); + sb.append(getGenericReturnType()).append(' '); + sb.append(getDeclaringClass().getName()).append('.'); + sb.append(getName()).append('('); + Type[] types = getGenericParameterTypes(); + if (types.length > 0) + { + sb.append(types[0]); + for (int i = 1; i < types.length; i++) + sb.append(',').append(types[i]); + } + sb.append(')'); + types = getGenericExceptionTypes(); + if (types.length > 0) + { + sb.append(" throws ").append(types[0]); + for (int i = 1; i < types.length; i++) + sb.append(',').append(types[i]); + } + return sb.toString(); + } + + /** + * Invoke the method. Arguments are automatically unwrapped and widened, + * and the result is automatically wrapped, if needed.

      + * + * If the method is static, o will be ignored. Otherwise, + * the method uses dynamic lookup as described in JLS 15.12.4.4. You cannot + * mimic the behavior of nonvirtual lookup (as in super.foo()). This means + * you will get a NullPointerException if o is + * null, and an IllegalArgumentException if it is incompatible + * with the declaring class of the method. If the method takes 0 arguments, + * you may use null or a 0-length array for args.

      + * + * Next, if this Method enforces access control, your runtime context is + * evaluated, and you may have an IllegalAccessException if + * you could not acces this method in similar compiled code. If the method + * is static, and its class is uninitialized, you trigger class + * initialization, which may end in a + * ExceptionInInitializerError.

      + * + * Finally, the method is invoked. If it completes normally, the return value + * will be null for a void method, a wrapped object for a primitive return + * method, or the actual return of an Object method. If it completes + * abruptly, the exception is wrapped in an + * InvocationTargetException. + * + * @param o the object to invoke the method on + * @param args the arguments to the method + * @return the return value of the method, wrapped in the appropriate + * wrapper if it is primitive + * @throws IllegalAccessException if the method could not normally be called + * by the Java code (i.e. it is not public) + * @throws IllegalArgumentException if the number of arguments is incorrect; + * if the arguments types are wrong even with a widening conversion; + * or if o is not an instance of the class or interface + * declaring this method + * @throws InvocationTargetException if the method throws an exception + * @throws NullPointerException if o is null and this field + * requires an instance + * @throws ExceptionInInitializerError if accessing a static method triggered + * class initialization, which then failed + */ + public Object invoke(Object o, Object... args) + throws IllegalAccessException, InvocationTargetException + { + return m.invoke(o, args); + } + + /** + * Returns an array of TypeVariable objects that represents + * the type variables declared by this constructor, in declaration order. + * An array of size zero is returned if this class has no type + * variables. + * + * @return the type variables associated with this class. + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public TypeVariable[] getTypeParameters() + { + if (p == null) + { + String sig = m.getSignature(); + if (sig == null) + return (TypeVariable[]) new TypeVariable[0]; + p = new MethodSignatureParser(this, sig); + } + return p.getTypeParameters(); + } + + /** + * Returns an array of Type objects that represents + * the exception types declared by this method, in declaration order. + * An array of size zero is returned if this method declares no + * exceptions. + * + * @return the exception types declared by this method. + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public Type[] getGenericExceptionTypes() + { + if (p == null) + { + String sig = m.getSignature(); + if (sig == null) + return getExceptionTypes(); + p = new MethodSignatureParser(this, sig); + } + return p.getGenericExceptionTypes(); + } + + /** + * Returns an array of Type objects that represents + * the parameter list for this method, in declaration order. + * An array of size zero is returned if this method takes no + * parameters. + * + * @return a list of the types of the method's parameters + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public Type[] getGenericParameterTypes() + { + if (p == null) + { + String sig = m.getSignature(); + if (sig == null) + return getParameterTypes(); + p = new MethodSignatureParser(this, sig); + } + return p.getGenericParameterTypes(); + } + + /** + * Returns the return type of this method. + * + * @return the return type of this method + * @throws GenericSignatureFormatError if the generic signature does + * not conform to the format specified in the Virtual Machine + * specification, version 3. + * @since 1.5 + */ + public Type getGenericReturnType() + { + if (p == null) + { + String sig = m.getSignature(); + if (sig == null) + return getReturnType(); + p = new MethodSignatureParser(this, sig); + } + return p.getGenericReturnType(); + } + + /** + * If this method is an annotation method, returns the default + * value for the method. If there is no default value, or if the + * method is not a member of an annotation type, returns null. + * Primitive types are wrapped. + * + * @throws TypeNotPresentException if the method returns a Class, + * and the class cannot be found + * + * @since 1.5 + */ + public Object getDefaultValue() + { + return m.getDefaultValue(); + } + + /** + *

      + * Return an array of arrays representing the annotations on each + * of the method's parameters. The outer array is aligned against + * the parameters of the method and is thus equal in length to + * the number of parameters (thus having a length zero if there are none). + * Each array element in the outer array contains an inner array which + * holds the annotations. This array has a length of zero if the parameter + * has no annotations. + *

      + *

      + * The returned annotations are serialized. Changing the annotations has + * no affect on the return value of future calls to this method. + *

      + * + * @return an array of arrays which represents the annotations used on the + * parameters of this method. The order of the array elements + * matches the declaration order of the parameters. + * @since 1.5 + */ + public Annotation[][] getParameterAnnotations() + { + return m.getParameterAnnotations(); + } + + /** + * Returns the element's annotation for the specified annotation type, + * or null if no such annotation exists. + * + * @param annotationClass the type of annotation to look for. + * @return this element's annotation for the specified type, or + * null if no such annotation exists. + * @throws NullPointerException if the annotation class is null. + */ + public T getAnnotation(Class annotationClass) + { + // Inescapable as the VM layer is 1.4 based. T will erase to Annotation anyway. + @SuppressWarnings("unchecked") + T ann = (T) m.getAnnotation(annotationClass); + return ann; + } + + /** + * Returns all annotations directly defined by the element. If there are + * no annotations directly associated with the element, then a zero-length + * array will be returned. The returned array may be modified by the client + * code, but this will have no effect on the annotation content of this + * class, and hence no effect on the return value of this method for + * future callers. + * + * @return the annotations directly defined by the element. + * @since 1.5 + */ + public Annotation[] getDeclaredAnnotations() + { + return m.getDeclaredAnnotations(); + } + +} diff --git a/libjava/classpath/java/lang/reflect/Modifier.java b/libjava/classpath/java/lang/reflect/Modifier.java new file mode 100644 index 000000000..15bad05e7 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Modifier.java @@ -0,0 +1,354 @@ +/* java.lang.reflect.Modifier + Copyright (C) 1998, 1999, 2001, 2002, 2005, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import gnu.java.lang.CPStringBuilder; + +/** + * Modifier is a helper class with static methods to determine whether an + * int returned from getModifiers() represents static, public, protected, + * native, final, etc... and provides an additional method to print + * out all of the modifiers in an int in order. + *

      + * The methods in this class use the bitmask values in the VM spec to + * determine the modifiers of an int. This means that a VM must return a + * standard mask, conformant with the VM spec. I don't know if this is how + * Sun does it, but I'm willing to bet money that it is. + * + * @author John Keiser + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Member#getModifiers() + * @see Method#getModifiers() + * @see Field#getModifiers() + * @see Constructor#getModifiers() + * @see Class#getModifiers() + * @since 1.1 + */ +public class Modifier +{ + /** This constructor really shouldn't be here ... there are no + * instance methods or variables of this class, so instantiation is + * worthless. However, this function is in the 1.1 spec, so it is added + * for completeness. + */ + public Modifier() + { + } + + /** + * Public: accessible from any other class. + */ + public static final int PUBLIC = 0x0001; + + /** + * Private: accessible only from the same enclosing class. + */ + public static final int PRIVATE = 0x0002; + + /** + * Protected: accessible only to subclasses, or within the package. + */ + public static final int PROTECTED = 0x0004; + + /** + * Static:

        + *
      • Class: no enclosing instance for nested class.
      • + *
      • Field or Method: can be accessed or invoked without an + * instance of the declaring class.
      • + *
      + */ + public static final int STATIC = 0x0008; + + /** + * Final:
        + *
      • Class: no subclasses allowed.
      • + *
      • Field: cannot be changed.
      • + *
      • Method: cannot be overriden.
      • + *
      + */ + public static final int FINAL = 0x0010; + + /** + * Synchronized: Method: lock the class while calling this method. + */ + public static final int SYNCHRONIZED = 0x0020; + + /** + * Volatile: Field: cannot be cached. + */ + public static final int VOLATILE = 0x0040; + + /** + * Transient: Field: not serialized or deserialized. + */ + public static final int TRANSIENT = 0x0080; + + /** + * Native: Method: use JNI to call this method. + */ + public static final int NATIVE = 0x0100; + + /** + * Interface: Class: is an interface. + */ + public static final int INTERFACE = 0x0200; + + /** + * Abstract:
        + *
      • Class: may not be instantiated.
      • + *
      • Method: may not be called.
      • + *
      + */ + public static final int ABSTRACT = 0x0400; + + /** + * Strictfp: Method: expressions are FP-strict.

      + * Also used as a modifier for classes, to mean that all initializers + * and constructors are FP-strict, but does not show up in + * Class.getModifiers. + */ + public static final int STRICT = 0x0800; + + + /** + * Super - treat invokespecial as polymorphic so that super.foo() works + * according to the JLS. This is a reuse of the synchronized constant + * to patch a hole in JDK 1.0. *shudder*. + */ + static final int SUPER = 0x0020; + + /** + * All the flags, only used by code in this package. + */ + static final int ALL_FLAGS = 0xfff; + + /** + * Flag indicating a bridge method. + */ + static final int BRIDGE = 0x40; + + /** + * Flag indicating a varargs method. + */ + static final int VARARGS = 0x80; + + /** + * Flag indicating a synthetic member. + */ + static final int SYNTHETIC = 0x1000; + + /** + * Flag indicating an enum constant or an enum class. + */ + static final int ENUM = 0x4000; + + /** + * Check whether the given modifier is abstract. + * @param mod the modifier. + * @return true if abstract, false otherwise. + */ + public static boolean isAbstract(int mod) + { + return (mod & ABSTRACT) != 0; + } + + /** + * Check whether the given modifier is final. + * @param mod the modifier. + * @return true if final, false otherwise. + */ + public static boolean isFinal(int mod) + { + return (mod & FINAL) != 0; + } + + /** + * Check whether the given modifier is an interface. + * @param mod the modifier. + * @return true if an interface, false otherwise. + */ + public static boolean isInterface(int mod) + { + return (mod & INTERFACE) != 0; + } + + /** + * Check whether the given modifier is native. + * @param mod the modifier. + * @return true if native, false otherwise. + */ + public static boolean isNative(int mod) + { + return (mod & NATIVE) != 0; + } + + /** + * Check whether the given modifier is private. + * @param mod the modifier. + * @return true if private, false otherwise. + */ + public static boolean isPrivate(int mod) + { + return (mod & PRIVATE) != 0; + } + + /** + * Check whether the given modifier is protected. + * @param mod the modifier. + * @return true if protected, false otherwise. + */ + public static boolean isProtected(int mod) + { + return (mod & PROTECTED) != 0; + } + + /** + * Check whether the given modifier is public. + * @param mod the modifier. + * @return true if public, false otherwise. + */ + public static boolean isPublic(int mod) + { + return (mod & PUBLIC) != 0; + } + + /** + * Check whether the given modifier is static. + * @param mod the modifier. + * @return true if static, false otherwise. + */ + public static boolean isStatic(int mod) + { + return (mod & STATIC) != 0; + } + + /** + * Check whether the given modifier is strictfp. + * @param mod the modifier. + * @return true if strictfp, false otherwise. + */ + public static boolean isStrict(int mod) + { + return (mod & STRICT) != 0; + } + + /** + * Check whether the given modifier is synchronized. + * @param mod the modifier. + * @return true if synchronized, false otherwise. + */ + public static boolean isSynchronized(int mod) + { + return (mod & SYNCHRONIZED) != 0; + } + + /** + * Check whether the given modifier is transient. + * @param mod the modifier. + * @return true if transient, false otherwise. + */ + public static boolean isTransient(int mod) + { + return (mod & TRANSIENT) != 0; + } + + /** + * Check whether the given modifier is volatile. + * @param mod the modifier. + * @return true if volatile, false otherwise. + */ + public static boolean isVolatile(int mod) + { + return (mod & VOLATILE) != 0; + } + + /** + * Get a string representation of all the modifiers represented by the + * given int. The keywords are printed in this order: + * <public|protected|private> abstract static final transient + * volatile synchronized native strictfp interface. + * + * @param mod the modifier. + * @return the String representing the modifiers. + */ + public static String toString(int mod) + { + return toString(mod, new CPStringBuilder()).toString(); + } + + /** + * Package helper method that can take a CPStringBuilder. + * @param mod the modifier + * @param r the CPStringBuilder to which the String representation is appended + * @return r, with information appended + */ + static CPStringBuilder toString(int mod, CPStringBuilder r) + { + if (isPublic(mod)) + r.append("public "); + if (isProtected(mod)) + r.append("protected "); + if (isPrivate(mod)) + r.append("private "); + if (isAbstract(mod)) + r.append("abstract "); + if (isStatic(mod)) + r.append("static "); + if (isFinal(mod)) + r.append("final "); + if (isTransient(mod)) + r.append("transient "); + if (isVolatile(mod)) + r.append("volatile "); + if (isSynchronized(mod)) + r.append("synchronized "); + if (isNative(mod)) + r.append("native "); + if (isStrict(mod)) + r.append("strictfp "); + if (isInterface(mod)) + r.append("interface "); + + // Trim trailing space. + if ((mod & ALL_FLAGS) != 0) + r.setLength(r.length() - 1); + return r; + } +} diff --git a/libjava/classpath/java/lang/reflect/ParameterizedType.java b/libjava/classpath/java/lang/reflect/ParameterizedType.java new file mode 100644 index 000000000..7a8a7b4e7 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/ParameterizedType.java @@ -0,0 +1,122 @@ +/* ParameterizedType.java -- Represents parameterized types e.g. List + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + *

      + * Represents a type which is parameterized over one or more other + * types. For example, List<Integer> is a parameterized + * type, with List parameterized over the type + * Integer. + *

      + *

      + * Instances of this classes are created as needed, during reflection. + * On creating a parameterized type, p, the + * GenericTypeDeclaration corresponding to p + * is created and resolved. Each type argument of p + * is then created recursively; details of this process are availble + * in the documentation of TypeVariable. This creation + * process only happens once; repetition has no effect. + *

      + *

      + * Implementors of this interface must implement an appropriate + * equals() method. This method should equate any + * two instances of the implementing class that have the same + * GenericTypeDeclaration and Type + * parameters. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see GenericDeclaration + * @see TypeVariable + * @since 1.5 + */ +public interface ParameterizedType + extends Type +{ + + /** + *

      + * Returns an array of Type objects, which gives + * the parameters of this type. + *

      + *

      + * Note: the returned array may be empty. This + * occurs if the supposed ParameterizedType is simply + * a normal type wrapped inside a parameterized type. + *

      + * + * @return an array of Types, representing the arguments + * of this type. + * @throws TypeNotPresentException if any of the types referred to by + * the parameters of this type do not actually exist. + * @throws MalformedParameterizedTypeException if any of the types + * refer to a type which can not be instantiated. + */ + Type[] getActualTypeArguments(); + + /** + * Returns the type of which this type is a member. For example, + * in Top<String>.Bottom<Integer>, + * Bottom<Integer> is a member of + * Top<String>, and so the latter is returned + * by this method. Calling this method on top-level types (such as + * Top<String>) returns null. + * + * @return the type which owns this type. + * @throws TypeNotPresentException if the owner type referred to by + * this type do not actually exist. + * @throws MalformedParameterizedTypeException if the owner type + * referred to by this type can not be instantiated. + */ + Type getOwnerType(); + + /** + * Returns a version of this type without parameters, which corresponds + * to the class or interface which declared the type. For example, + * the raw type corresponding to List<Double> + * is List, which was declared by the List + * class. + * + * @return the raw variant of this type (i.e. the type without + * parameters). + */ + Type getRawType(); + +} diff --git a/libjava/classpath/java/lang/reflect/Proxy.java b/libjava/classpath/java/lang/reflect/Proxy.java new file mode 100644 index 000000000..0e76124df --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Proxy.java @@ -0,0 +1,1545 @@ +/* Proxy.java -- build a proxy class that implements reflected interfaces + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.lang.reflect.TypeSignature; + +import java.io.Serializable; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * This class allows you to dynamically create an instance of any (or + * even multiple) interfaces by reflection, and decide at runtime + * how that instance will behave by giving it an appropriate + * {@link InvocationHandler}. Proxy classes serialize specially, so + * that the proxy object can be reused between VMs, without requiring + * a persistent copy of the generated class code. + * + *

      Creation

      + * To create a proxy for some interface Foo: + * + *
      + *   InvocationHandler handler = new MyInvocationHandler(...);
      + *   Class proxyClass = Proxy.getProxyClass(
      + *       Foo.class.getClassLoader(), new Class[] { Foo.class });
      + *   Foo f = (Foo) proxyClass
      + *       .getConstructor(new Class[] { InvocationHandler.class })
      + *       .newInstance(new Object[] { handler });
      + * 
      + * or more simply: + *
      + *   Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
      + *                                        new Class[] { Foo.class },
      + *                                        handler);
      + * 
      + * + *

      Dynamic Proxy Classes

      + * A dynamic proxy class is created at runtime, and has the following + * properties: + *
        + *
      • The class is public and final, + * and is neither abstract nor an inner class.
      • + *
      • The class has no canonical name (there is no formula you can use + * to determine or generate its name), but begins with the + * sequence "$Proxy". Abuse this knowledge at your own peril. + * (For now, '$' in user identifiers is legal, but it may not + * be that way forever. You weren't using '$' in your + * identifiers, were you?)
      • + *
      • The class extends Proxy, and explicitly implements all the + * interfaces specified at creation, in order (this is important + * for determining how method invocation is resolved). Note that + * a proxy class implements {@link Serializable}, at least + * implicitly, since Proxy does, but true serial behavior + * depends on using a serializable invocation handler as well.
      • + *
      • If at least one interface is non-public, the proxy class + * will be in the same package. Otherwise, the package is + * unspecified. This will work even if the package is sealed + * from user-generated classes, because Proxy classes are + * generated by a trusted source. Meanwhile, the proxy class + * belongs to the classloader you designated.
      • + *
      • Reflection works as expected: {@link Class#getInterfaces()} and + * {@link Class#getMethods()} work as they do on normal classes.
      • + *
      • The method {@link #isProxyClass(Class)} will distinguish between + * true proxy classes and user extensions of this class. It only + * returns true for classes created by {@link #getProxyClass}.
      • + *
      • The {@link ProtectionDomain} of a proxy class is the same as for + * bootstrap classes, such as Object or Proxy, since it is created by + * a trusted source. This protection domain will typically be granted + * {@link java.security.AllPermission}. But this is not a security + * risk, since there are adequate permissions on reflection, which is + * the only way to create an instance of the proxy class.
      • + *
      • The proxy class contains a single constructor, which takes as + * its only argument an {@link InvocationHandler}. The method + * {@link #newProxyInstance(ClassLoader, Class[], InvocationHandler)} + * is shorthand to do the necessary reflection.
      • + *
      + * + *

      Proxy Instances

      + * A proxy instance is an instance of a proxy class. It has the + * following properties, many of which follow from the properties of a + * proxy class listed above: + *
        + *
      • For a proxy class with Foo listed as one of its interfaces, the + * expression proxy instanceof Foo will return true, + * and the expression (Foo) proxy will succeed without + * a {@link ClassCastException}.
      • + *
      • Each proxy instance has an invocation handler, which can be + * accessed by {@link #getInvocationHandler(Object)}. Any call + * to an interface method, including {@link Object#hashCode()}, + * {@link Object#equals(Object)}, or {@link Object#toString()}, + * but excluding the public final methods of Object, will be + * encoded and passed to the {@link InvocationHandler#invoke} + * method of this handler.
      • + *
      + * + *

      Inheritance Issues

      + * A proxy class may inherit a method from more than one interface. + * The order in which interfaces are listed matters, because it determines + * which reflected {@link Method} object will be passed to the invocation + * handler. This means that the dynamically generated class cannot + * determine through which interface a method is being invoked.

      + * + * In short, if a method is declared in Object (namely, hashCode, + * equals, or toString), then Object will be used; otherwise, the + * leftmost interface that inherits or declares a method will be used, + * even if it has a more permissive throws clause than what the proxy + * class is allowed. Thus, in the invocation handler, it is not always + * safe to assume that every class listed in the throws clause of the + * passed Method object can safely be thrown; fortunately, the Proxy + * instance is robust enough to wrap all illegal checked exceptions in + * {@link UndeclaredThrowableException}. + * + * @see InvocationHandler + * @see UndeclaredThrowableException + * @see Class + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.3 + * @status updated to 1.5, except for the use of ProtectionDomain + */ +public class Proxy implements Serializable +{ + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = -2222568056686623797L; + + /** + * Map of ProxyType to proxy class. + * + * @XXX This prevents proxy classes from being garbage collected. + * java.util.WeakHashSet is not appropriate, because that collects the + * keys, but we are interested in collecting the elements. + */ + private static final Map proxyClasses = new HashMap(); + + /** + * The invocation handler for this proxy instance. For Proxy, this + * field is unused, but it appears here in order to be serialized in all + * proxy classes. + * + * NOTE: This implementation is more secure for proxy classes + * than what Sun specifies. Sun does not require h to be immutable, but + * this means you could change h after the fact by reflection. However, + * by making h immutable, we may break non-proxy classes which extend + * Proxy. + * @serial invocation handler associated with this proxy instance + */ + protected InvocationHandler h; + + /** + * Constructs a new Proxy from a subclass (usually a proxy class), + * with the specified invocation handler. + * + * NOTE: This throws a NullPointerException if you attempt + * to create a proxy instance with a null handler using reflection. + * This behavior is not yet specified by Sun; see Sun Bug 4487672. + * + * @param handler the invocation handler, may be null if the subclass + * is not a proxy class + * @throws NullPointerException if handler is null and this is a proxy + * instance + */ + protected Proxy(InvocationHandler handler) + { + if (handler == null && isProxyClass(getClass())) + throw new NullPointerException("invalid handler"); + h = handler; + } + + /** + * Returns the proxy {@link Class} for the given ClassLoader and array + * of interfaces, dynamically generating it if necessary. + * + *

      There are several restrictions on this method, the violation of + * which will result in an IllegalArgumentException or + * NullPointerException:

      + * + *
        + *
      • All objects in `interfaces' must represent distinct interfaces. + * Classes, primitive types, null, and duplicates are forbidden.
      • + *
      • The interfaces must be visible in the specified ClassLoader. + * In other words, for each interface i: + * Class.forName(i.getName(), false, loader) == i + * must be true.
      • + *
      • All non-public interfaces (if any) must reside in the same + * package, or the proxy class would be non-instantiable. If + * there are no non-public interfaces, the package of the proxy + * class is unspecified.
      • + *
      • All interfaces must be compatible - if two declare a method + * with the same name and parameters, the return type must be + * the same and the throws clause of the proxy class will be + * the maximal subset of subclasses of the throws clauses for + * each method that is overridden.
      • + *
      • VM constraints limit the number of interfaces a proxy class + * may directly implement (however, the indirect inheritance + * of {@link Serializable} does not count against this limit). + * Even though most VMs can theoretically have 65535 + * superinterfaces for a class, the actual limit is smaller + * because a class's constant pool is limited to 65535 entries, + * and not all entries can be interfaces.
      • + *
      + * + *

      Note that different orders of interfaces produce distinct classes.

      + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the array of interfaces the proxy class implements, + * may be empty, but not null + * @return the Class object of the proxy class + * @throws IllegalArgumentException if the constraints above were + * violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry + */ + // synchronized so that we aren't trying to build the same class + // simultaneously in two threads + public static synchronized Class getProxyClass(ClassLoader loader, + Class... interfaces) + { + interfaces = (Class[]) interfaces.clone(); + ProxyType pt = new ProxyType(loader, interfaces); + Class clazz = (Class) proxyClasses.get(pt); + if (clazz == null) + { + if (VMProxy.HAVE_NATIVE_GET_PROXY_CLASS) + clazz = VMProxy.getProxyClass(loader, interfaces); + else + { + ProxyData data = (VMProxy.HAVE_NATIVE_GET_PROXY_DATA + ? VMProxy.getProxyData(loader, interfaces) + : ProxyData.getProxyData(pt)); + + clazz = (VMProxy.HAVE_NATIVE_GENERATE_PROXY_CLASS + ? VMProxy.generateProxyClass(loader, data) + : new ClassFactory(data).generate(loader)); + } + + Object check = proxyClasses.put(pt, clazz); + // assert check == null && clazz != null; + if (check != null || clazz == null) + throw new InternalError(/*"Fatal flaw in getProxyClass"*/); + } + return clazz; + } + + /** + * Combines several methods into one. This is equivalent to: + *
      +   *   Proxy.getProxyClass(loader, interfaces)
      +   *       .getConstructor(new Class[] {InvocationHandler.class})
      +   *       .newInstance(new Object[] {handler});
      +   * 
      + * except that it will not fail with the normal problems caused + * by reflection. It can still fail for the same reasons documented + * in getProxyClass, or if handler is null. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the array of interfaces the proxy class implements, + * may be empty, but not null + * @param handler the invocation handler, may not be null + * @return a proxy instance implementing the specified interfaces + * @throws IllegalArgumentException if the constraints for getProxyClass + * were violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry, or if handler is null + * @see #getProxyClass(ClassLoader, Class[]) + * @see Class#getConstructor(Class[]) + * @see Constructor#newInstance(Object[]) + */ + public static Object newProxyInstance(ClassLoader loader, + Class[] interfaces, + InvocationHandler handler) + { + try + { + // getProxyClass() and Proxy() throw the necessary exceptions + return getProxyClass(loader, interfaces) + .getConstructor(new Class[] {InvocationHandler.class}) + .newInstance(new Object[] {handler}); + } + catch (RuntimeException e) + { + // Let IllegalArgumentException, NullPointerException escape. + // assert e instanceof IllegalArgumentException + // || e instanceof NullPointerException; + throw e; + } + catch (InvocationTargetException e) + { + // Let wrapped NullPointerException escape. + // assert e.getTargetException() instanceof NullPointerException + throw (NullPointerException) e.getCause(); + } + catch (Exception e) + { + // Covers InstantiationException, IllegalAccessException, + // NoSuchMethodException, none of which should be generated + // if the proxy class was generated correctly. + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * Returns true if and only if the Class object is a dynamically created + * proxy class (created by getProxyClass or by the + * syntactic sugar of newProxyInstance). + * + *

      This check is secure (in other words, it is not simply + * clazz.getSuperclass() == Proxy.class), it will not + * be spoofed by non-proxy classes that extend Proxy. + * + * @param clazz the class to check, must not be null + * @return true if the class represents a proxy class + * @throws NullPointerException if clazz is null + */ + // This is synchronized on the off chance that another thread is + // trying to add a class to the map at the same time we read it. + public static synchronized boolean isProxyClass(Class clazz) + { + if (! Proxy.class.isAssignableFrom(clazz)) + return false; + // This is a linear search, even though we could do an O(1) search + // using new ProxyType(clazz.getClassLoader(), clazz.getInterfaces()). + return proxyClasses.containsValue(clazz); + } + + /** + * Returns the invocation handler for the given proxy instance.

      + * + * NOTE: We guarantee a non-null result if successful, + * but Sun allows the creation of a proxy instance with a null + * handler. See the comments for {@link #Proxy(InvocationHandler)}. + * + * @param proxy the proxy instance, must not be null + * @return the invocation handler, guaranteed non-null. + * @throws IllegalArgumentException if + * Proxy.isProxyClass(proxy.getClass()) returns false. + * @throws NullPointerException if proxy is null + */ + public static InvocationHandler getInvocationHandler(Object proxy) + { + if (! isProxyClass(proxy.getClass())) + throw new IllegalArgumentException("not a proxy instance"); + return ((Proxy) proxy).h; + } + + /** + * Helper class for mapping unique ClassLoader and interface combinations + * to proxy classes. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ProxyType + { + /** + * Store the class loader (may be null) + */ + final ClassLoader loader; + + /** + * Store the interfaces (never null, all elements are interfaces) + */ + final Class[] interfaces; + + /** + * Construct the helper object. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces an array of interfaces + */ + ProxyType(ClassLoader loader, Class[] interfaces) + { + this.loader = loader; + this.interfaces = interfaces; + } + + /** + * Calculates the hash code. + * + * @return a combination of the classloader and interfaces hashcodes. + */ + public int hashCode() + { + int hash = loader == null ? 0 : loader.hashCode(); + for (int i = 0; i < interfaces.length; i++) + hash = hash * 31 + interfaces[i].hashCode(); + return hash; + } + + /** + * Calculates equality. + * + * @param other object to compare to + * @return true if it is a ProxyType with same data + */ + public boolean equals(Object other) + { + ProxyType pt = (ProxyType) other; + if (loader != pt.loader || interfaces.length != pt.interfaces.length) + return false; + for (int i = 0; i < interfaces.length; i++) + if (interfaces[i] != pt.interfaces[i]) + return false; + return true; + } + } // class ProxyType + + /** + * Helper class which allows hashing of a method name and signature + * without worrying about return type, declaring class, or throws clause, + * and which reduces the maximally common throws clause between two methods + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ProxySignature + { + /** + * The core signatures which all Proxy instances handle. + */ + static final HashMap coreMethods = new HashMap(); + static + { + try + { + ProxySignature sig + = new ProxySignature(Object.class + .getMethod("equals", + new Class[] {Object.class})); + coreMethods.put(sig, sig); + sig = new ProxySignature(Object.class.getMethod("hashCode")); + coreMethods.put(sig, sig); + sig = new ProxySignature(Object.class.getMethod("toString")); + coreMethods.put(sig, sig); + } + catch (Exception e) + { + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * The underlying Method object, never null + */ + final Method method; + + /** + * The set of compatible thrown exceptions, may be empty + */ + final Set exceptions = new HashSet(); + + /** + * Construct a signature + * + * @param method the Method this signature is based on, never null + */ + ProxySignature(Method method) + { + this.method = method; + Class[] exc = method.getExceptionTypes(); + int i = exc.length; + while (--i >= 0) + { + // discard unchecked exceptions + if (Error.class.isAssignableFrom(exc[i]) + || RuntimeException.class.isAssignableFrom(exc[i])) + continue; + exceptions.add(exc[i]); + } + } + + /** + * Given a method, make sure it's return type is identical + * to this, and adjust this signature's throws clause appropriately + * + * @param other the signature to merge in + * @throws IllegalArgumentException if the return types conflict + */ + void checkCompatibility(ProxySignature other) + { + if (method.getReturnType() != other.method.getReturnType()) + throw new IllegalArgumentException("incompatible return types: " + + method + ", " + other.method); + + // if you can think of a more efficient way than this O(n^2) search, + // implement it! + int size1 = exceptions.size(); + int size2 = other.exceptions.size(); + boolean[] valid1 = new boolean[size1]; + boolean[] valid2 = new boolean[size2]; + Iterator itr = exceptions.iterator(); + int pos = size1; + while (--pos >= 0) + { + Class c1 = (Class) itr.next(); + Iterator itr2 = other.exceptions.iterator(); + int pos2 = size2; + while (--pos2 >= 0) + { + Class c2 = (Class) itr2.next(); + if (c2.isAssignableFrom(c1)) + valid1[pos] = true; + if (c1.isAssignableFrom(c2)) + valid2[pos2] = true; + } + } + pos = size1; + itr = exceptions.iterator(); + while (--pos >= 0) + { + itr.next(); + if (! valid1[pos]) + itr.remove(); + } + pos = size2; + itr = other.exceptions.iterator(); + while (--pos >= 0) + { + itr.next(); + if (! valid2[pos]) + itr.remove(); + } + exceptions.addAll(other.exceptions); + } + + /** + * Calculates the hash code. + * + * @return a combination of name and parameter types + */ + public int hashCode() + { + int hash = method.getName().hashCode(); + Class[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) + hash = hash * 31 + types[i].hashCode(); + return hash; + } + + /** + * Calculates equality. + * + * @param other object to compare to + * @return true if it is a ProxySignature with same data + */ + public boolean equals(Object other) + { + ProxySignature ps = (ProxySignature) other; + Class[] types1 = method.getParameterTypes(); + Class[] types2 = ps.method.getParameterTypes(); + if (! method.getName().equals(ps.method.getName()) + || types1.length != types2.length) + return false; + int i = types1.length; + while (--i >= 0) + if (types1[i] != types2[i]) + return false; + return true; + } + } // class ProxySignature + + /** + * A flat representation of all data needed to generate bytecode/instantiate + * a proxy class. This is basically a struct. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static final class ProxyData + { + /** + * The package this class is in including the trailing dot + * or an empty string for the unnamed (aka default) package. + */ + String pack = ""; + + /** + * The interfaces this class implements. Non-null, but possibly empty. + */ + Class[] interfaces; + + /** + * The Method objects this class must pass as the second argument to + * invoke (also useful for determining what methods this class has). + * Non-null, non-empty (includes at least Object.hashCode, Object.equals, + * and Object.toString). + */ + Method[] methods; + + /** + * The exceptions that do not need to be wrapped in + * UndeclaredThrowableException. exceptions[i] is the same as, or a + * subset of subclasses, of methods[i].getExceptionTypes(), depending on + * compatible throws clauses with multiple inheritance. It is unspecified + * if these lists include or exclude subclasses of Error and + * RuntimeException, but excluding them is harmless and generates a + * smaller class. + */ + Class[][] exceptions; + + /** + * For unique id's + */ + private static int count; + + /** + * The id of this proxy class + */ + final int id = count++; + + /** + * Construct a ProxyData with uninitialized data members. + */ + ProxyData() + { + } + + /** + * Return the name of a package (including the trailing dot) + * given the name of a class. + * Returns an empty string if no package. We use this in preference to + * using Class.getPackage() to avoid problems with ClassLoaders + * that don't set the package. + */ + private static String getPackage(Class k) + { + String name = k.getName(); + int idx = name.lastIndexOf('.'); + return name.substring(0, idx + 1); + } + + /** + * Verifies that the arguments are legal, and sets up remaining data + * This should only be called when a class must be generated, as + * it is expensive. + * + * @param pt the ProxyType to convert to ProxyData + * @return the flattened, verified ProxyData structure for use in + * class generation + * @throws IllegalArgumentException if `interfaces' contains + * non-interfaces or incompatible combinations, and verify is true + * @throws NullPointerException if interfaces is null or contains null + */ + static ProxyData getProxyData(ProxyType pt) + { + Map method_set = (Map) ProxySignature.coreMethods.clone(); + boolean in_package = false; // true if we encounter non-public interface + + ProxyData data = new ProxyData(); + data.interfaces = pt.interfaces; + + // if interfaces is too large, we croak later on when the constant + // pool overflows + int i = data.interfaces.length; + while (--i >= 0) + { + Class inter = data.interfaces[i]; + if (! inter.isInterface()) + throw new IllegalArgumentException("not an interface: " + inter); + try + { + if (Class.forName(inter.getName(), false, pt.loader) != inter) + throw new IllegalArgumentException("not accessible in " + + "classloader: " + inter); + } + catch (ClassNotFoundException e) + { + throw new IllegalArgumentException("not accessible in " + + "classloader: " + inter); + } + if (! Modifier.isPublic(inter.getModifiers())) + if (in_package) + { + String p = getPackage(inter); + if (! data.pack.equals(p)) + throw new IllegalArgumentException("non-public interfaces " + + "from different " + + "packages"); + } + else + { + in_package = true; + data.pack = getPackage(inter); + } + for (int j = i-1; j >= 0; j--) + if (data.interfaces[j] == inter) + throw new IllegalArgumentException("duplicate interface: " + + inter); + Method[] methods = inter.getMethods(); + int j = methods.length; + while (--j >= 0) + { + if (isCoreObjectMethod(methods[j])) + { + // In the case of an attempt to redefine a public non-final + // method of Object, we must skip it + continue; + } + ProxySignature sig = new ProxySignature(methods[j]); + ProxySignature old = (ProxySignature) method_set.put(sig, sig); + if (old != null) + sig.checkCompatibility(old); + } + } + + i = method_set.size(); + data.methods = new Method[i]; + data.exceptions = new Class[i][]; + Iterator itr = method_set.values().iterator(); + while (--i >= 0) + { + ProxySignature sig = (ProxySignature) itr.next(); + data.methods[i] = sig.method; + data.exceptions[i] = (Class[]) sig.exceptions + .toArray(new Class[sig.exceptions.size()]); + } + return data; + } + + /** + * Checks whether the method is similar to a public non-final method of + * Object or not (i.e. with the same name and parameter types). Note that we + * can't rely, directly or indirectly (via Collection.contains) on + * Method.equals as it would also check the declaring class, what we do not + * want. We only want to check that the given method have the same signature + * as a core method (same name and parameter types) + * + * @param method the method to check + * @return whether the method has the same name and parameter types as + * Object.equals, Object.hashCode or Object.toString + * @see java.lang.Object#equals(Object) + * @see java.lang.Object#hashCode() + * @see java.lang.Object#toString() + */ + private static boolean isCoreObjectMethod(Method method) + { + String methodName = method.getName(); + if (methodName.equals("equals")) + { + return Arrays.equals(method.getParameterTypes(), + new Class[] { Object.class }); + } + if (methodName.equals("hashCode")) + { + return method.getParameterTypes().length == 0; + } + if (methodName.equals("toString")) + { + return method.getParameterTypes().length == 0; + } + return false; + } + + } // class ProxyData + + /** + * Does all the work of building a class. By making this a nested class, + * this code is not loaded in memory if the VM has a native + * implementation instead. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ClassFactory + { + /** Constants for assisting the compilation */ + private static final byte FIELD = 1; + private static final byte METHOD = 2; + private static final byte INTERFACE = 3; + private static final String CTOR_SIG + = "(Ljava/lang/reflect/InvocationHandler;)V"; + private static final String INVOKE_SIG = "(Ljava/lang/Object;" + + "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"; + + /** Bytecodes for insertion in the class definition byte[] */ + private static final char ACONST_NULL = 1; + private static final char ICONST_0 = 3; + private static final char BIPUSH = 16; + private static final char SIPUSH = 17; + private static final char ILOAD = 21; + private static final char ILOAD_0 = 26; + private static final char ALOAD_0 = 42; + private static final char ALOAD_1 = 43; + private static final char AALOAD = 50; + private static final char AASTORE = 83; + private static final char DUP = 89; + private static final char DUP_X1 = 90; + private static final char SWAP = 95; + private static final char IRETURN = 172; + private static final char LRETURN = 173; + private static final char FRETURN = 174; + private static final char DRETURN = 175; + private static final char ARETURN = 176; + private static final char RETURN = 177; + private static final char GETSTATIC = 178; + private static final char GETFIELD = 180; + private static final char INVOKEVIRTUAL = 182; + private static final char INVOKESPECIAL = 183; + private static final char INVOKEINTERFACE = 185; + private static final char NEW = 187; + private static final char ANEWARRAY = 189; + private static final char ATHROW = 191; + private static final char CHECKCAST = 192; + + // Implementation note: we use StringBuffers to hold the byte data, since + // they automatically grow. However, we only use the low 8 bits of + // every char in the array, so we are using twice the necessary memory + // for the ease StringBuffer provides. + + /** The constant pool. */ + private final StringBuffer pool = new StringBuffer(); + /** The rest of the class data. */ + private final StringBuffer stream = new StringBuffer(); + + /** Map of strings to byte sequences, to minimize size of pool. */ + private final Map poolEntries = new HashMap(); + + /** The VM name of this proxy class. */ + private final String qualName; + + /** + * The Method objects the proxy class refers to when calling the + * invocation handler. + */ + private final Method[] methods; + + /** + * Initializes the buffers with the bytecode contents for a proxy class. + * + * @param data the remainder of the class data + * @throws IllegalArgumentException if anything else goes wrong this + * late in the game; as far as I can tell, this will only happen + * if the constant pool overflows, which is possible even when + * the user doesn't exceed the 65535 interface limit + */ + ClassFactory(ProxyData data) + { + methods = data.methods; + + // magic = 0xcafebabe + // minor_version = 0 + // major_version = 46 + // constant_pool_count: place-holder for now + pool.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0"); + // constant_pool[], filled in as we go + + // access_flags + putU2(Modifier.SUPER | Modifier.FINAL | Modifier.PUBLIC); + // this_class + qualName = (data.pack + "$Proxy" + data.id); + putU2(classInfo(TypeSignature.getEncodingOfClass(qualName, false))); + // super_class + putU2(classInfo("java/lang/reflect/Proxy")); + + // interfaces_count + putU2(data.interfaces.length); + // interfaces[] + for (int i = 0; i < data.interfaces.length; i++) + putU2(classInfo(data.interfaces[i])); + + // Recall that Proxy classes serialize specially, so we do not need + // to worry about a method for this field. Instead, we + // just assign it by reflection after the class is successfully loaded. + // fields_count - private static Method[] m; + putU2(1); + // fields[] + // m.access_flags + putU2(Modifier.PRIVATE | Modifier.STATIC); + // m.name_index + putU2(utf8Info("m")); + // m.descriptor_index + putU2(utf8Info("[Ljava/lang/reflect/Method;")); + // m.attributes_count + putU2(0); + // m.attributes[] + + // methods_count - # handler methods, plus + putU2(methods.length + 1); + // methods[] + // .access_flags + putU2(Modifier.PUBLIC); + // .name_index + putU2(utf8Info("")); + // .descriptor_index + putU2(utf8Info(CTOR_SIG)); + // .attributes_count - only Code is needed + putU2(1); + // .Code.attribute_name_index + putU2(utf8Info("Code")); + // .Code.attribute_length = 18 + // .Code.info: + // $Proxynn(InvocationHandler h) { super(h); } + // .Code.max_stack = 2 + // .Code.max_locals = 2 + // .Code.code_length = 6 + // .Code.code[] + stream.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0 + ALOAD_1 + + INVOKESPECIAL); + putU2(refInfo(METHOD, "java/lang/reflect/Proxy", "", CTOR_SIG)); + // .Code.exception_table_length = 0 + // .Code.exception_table[] + // .Code.attributes_count = 0 + // .Code.attributes[] + stream.append(RETURN + "\0\0\0\0"); + + for (int i = methods.length - 1; i >= 0; i--) + emitMethod(i, data.exceptions[i]); + + // attributes_count + putU2(0); + // attributes[] - empty; omit SourceFile attribute + // XXX should we mark this with a Synthetic attribute? + } + + /** + * Produce the bytecode for a single method. + * + * @param i the index of the method we are building + * @param e the exceptions possible for the method + */ + private void emitMethod(int i, Class[] e) + { + // First, we precalculate the method length and other information. + + Method m = methods[i]; + Class[] paramtypes = m.getParameterTypes(); + int wrap_overhead = 0; // max words taken by wrapped primitive + int param_count = 1; // 1 for this + int code_length = 16; // aload_0, getfield, aload_0, getstatic, const, + // aaload, const/aconst_null, invokeinterface + if (i > 5) + { + if (i > Byte.MAX_VALUE) + code_length += 2; // sipush + else + code_length++; // bipush + } + if (paramtypes.length > 0) + { + code_length += 3; // anewarray + if (paramtypes.length > Byte.MAX_VALUE) + code_length += 2; // sipush + else if (paramtypes.length > 5) + code_length++; // bipush + for (int j = 0; j < paramtypes.length; j++) + { + code_length += 4; // dup, const, load, store + Class type = paramtypes[j]; + if (j > 5) + { + if (j > Byte.MAX_VALUE) + code_length += 2; // sipush + else + code_length++; // bipush + } + if (param_count >= 4) + code_length++; // 2-byte load + param_count++; + if (type.isPrimitive()) + { + code_length += 7; // new, dup, invokespecial + if (type == long.class || type == double.class) + { + wrap_overhead = 3; + param_count++; + } + else if (wrap_overhead < 2) + wrap_overhead = 2; + } + } + } + int end_pc = code_length; + Class ret_type = m.getReturnType(); + if (ret_type == void.class) + code_length++; // return + else if (ret_type.isPrimitive()) + code_length += 7; // cast, invokevirtual, return + else + code_length += 4; // cast, return + int exception_count = 0; + boolean throws_throwable = false; + for (int j = 0; j < e.length; j++) + if (e[j] == Throwable.class) + { + throws_throwable = true; + break; + } + if (! throws_throwable) + { + exception_count = e.length + 3; // Throwable, Error, RuntimeException + code_length += 9; // new, dup_x1, swap, invokespecial, athrow + } + int handler_pc = code_length - 1; + CPStringBuilder signature = new CPStringBuilder("("); + for (int j = 0; j < paramtypes.length; j++) + signature.append(TypeSignature.getEncodingOfClass(paramtypes[j])); + signature.append(")").append(TypeSignature.getEncodingOfClass(ret_type)); + + // Now we have enough information to emit the method. + + // handler.access_flags + putU2(Modifier.PUBLIC | Modifier.FINAL); + // handler.name_index + putU2(utf8Info(m.getName())); + // handler.descriptor_index + putU2(utf8Info(signature.toString())); + // handler.attributes_count - Code is necessary, Exceptions possible + putU2(e.length > 0 ? 2 : 1); + + // handler.Code.info: + // type name(args) { + // try { + // return (type) h.invoke(this, methods[i], new Object[] {args}); + // } catch ( e) { + // throw e; + // } catch (Throwable t) { + // throw new UndeclaredThrowableException(t); + // } + // } + // Special cases: + // if arg_n is primitive, wrap it + // if method throws Throwable, try-catch is not needed + // if method returns void, return statement not needed + // if method returns primitive, unwrap it + // save space by sharing code for all the declared handlers + + // handler.Code.attribute_name_index + putU2(utf8Info("Code")); + // handler.Code.attribute_length + putU4(12 + code_length + 8 * exception_count); + // handler.Code.max_stack + putU2(param_count == 1 ? 4 : 7 + wrap_overhead); + // handler.Code.max_locals + putU2(param_count); + // handler.Code.code_length + putU4(code_length); + // handler.Code.code[] + putU1(ALOAD_0); + putU1(GETFIELD); + putU2(refInfo(FIELD, "java/lang/reflect/Proxy", "h", + "Ljava/lang/reflect/InvocationHandler;")); + putU1(ALOAD_0); + putU1(GETSTATIC); + putU2(refInfo(FIELD, TypeSignature.getEncodingOfClass(qualName, false), + "m", "[Ljava/lang/reflect/Method;")); + putConst(i); + putU1(AALOAD); + if (paramtypes.length > 0) + { + putConst(paramtypes.length); + putU1(ANEWARRAY); + putU2(classInfo("java/lang/Object")); + param_count = 1; + for (int j = 0; j < paramtypes.length; j++, param_count++) + { + putU1(DUP); + putConst(j); + if (paramtypes[j].isPrimitive()) + { + putU1(NEW); + putU2(classInfo(wrapper(paramtypes[j]))); + putU1(DUP); + } + putLoad(param_count, paramtypes[j]); + if (paramtypes[j].isPrimitive()) + { + putU1(INVOKESPECIAL); + putU2(refInfo(METHOD, wrapper(paramtypes[j]), "", + '(' + (TypeSignature + .getEncodingOfClass(paramtypes[j]) + + ")V"))); + if (paramtypes[j] == long.class + || paramtypes[j] == double.class) + param_count++; + } + putU1(AASTORE); + } + } + else + putU1(ACONST_NULL); + putU1(INVOKEINTERFACE); + putU2(refInfo(INTERFACE, "java/lang/reflect/InvocationHandler", + "invoke", INVOKE_SIG)); + putU1(4); // InvocationHandler, this, Method, Object[] + putU1(0); + if (ret_type == void.class) + putU1(RETURN); + else if (ret_type.isPrimitive()) + { + putU1(CHECKCAST); + putU2(classInfo(wrapper(ret_type))); + putU1(INVOKEVIRTUAL); + putU2(refInfo(METHOD, wrapper(ret_type), + ret_type.getName() + "Value", + "()" + TypeSignature.getEncodingOfClass(ret_type))); + if (ret_type == long.class) + putU1(LRETURN); + else if (ret_type == float.class) + putU1(FRETURN); + else if (ret_type == double.class) + putU1(DRETURN); + else + putU1(IRETURN); + } + else + { + putU1(CHECKCAST); + putU2(classInfo(ret_type)); + putU1(ARETURN); + } + if (! throws_throwable) + { + putU1(NEW); + putU2(classInfo("java/lang/reflect/UndeclaredThrowableException")); + putU1(DUP_X1); + putU1(SWAP); + putU1(INVOKESPECIAL); + putU2(refInfo(METHOD, + "java/lang/reflect/UndeclaredThrowableException", + "", "(Ljava/lang/Throwable;)V")); + putU1(ATHROW); + } + + // handler.Code.exception_table_length + putU2(exception_count); + // handler.Code.exception_table[] + if (! throws_throwable) + { + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo("java/lang/Error")); + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo("java/lang/RuntimeException")); + for (int j = 0; j < e.length; j++) + { + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo(e[j])); + } + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc - + // -8 for undeclared handler, which falls thru to normal one + putU2(handler_pc - 8); + // handler.Code.exception_table.catch_type + putU2(0); + } + // handler.Code.attributes_count + putU2(0); + // handler.Code.attributes[] + + if (e.length > 0) + { + // handler.Exceptions.attribute_name_index + putU2(utf8Info("Exceptions")); + // handler.Exceptions.attribute_length + putU4(2 * e.length + 2); + // handler.Exceptions.number_of_exceptions + putU2(e.length); + // handler.Exceptions.exception_index_table[] + for (int j = 0; j < e.length; j++) + putU2(classInfo(e[j])); + } + } + + /** + * Creates the Class object that corresponds to the bytecode buffers + * built when this object was constructed. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @return the proxy class Class object + */ + Class generate(ClassLoader loader) + { + byte[] bytecode = new byte[pool.length() + stream.length()]; + // More efficient to bypass calling charAt() repetitively. + char[] c = pool.toString().toCharArray(); + int i = c.length; + while (--i >= 0) + bytecode[i] = (byte) c[i]; + c = stream.toString().toCharArray(); + i = c.length; + int j = bytecode.length; + while (i > 0) + bytecode[--j] = (byte) c[--i]; + + // Patch the constant pool size, which we left at 0 earlier. + int count = poolEntries.size() + 1; + bytecode[8] = (byte) (count >> 8); + bytecode[9] = (byte) count; + + try + { + Class vmClassLoader = Class.forName("java.lang.VMClassLoader"); + Class[] types = {ClassLoader.class, String.class, + byte[].class, int.class, int.class, + ProtectionDomain.class }; + Method m = vmClassLoader.getDeclaredMethod("defineClass", types); + // We can bypass the security check of setAccessible(true), since + // we're in the same package. + m.flag = true; + + Object[] args = {loader, qualName, bytecode, Integer.valueOf(0), + Integer.valueOf(bytecode.length), + Object.class.getProtectionDomain() }; + Class clazz = (Class) m.invoke(null, args); + + // Finally, initialize the m field of the proxy class, before + // returning it. + Field f = clazz.getDeclaredField("m"); + f.flag = true; + // we can share the array, because it is not publicized + f.set(null, methods); + + return clazz; + } + catch (Exception e) + { + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * Put a single byte on the stream. + * + * @param i the information to add (only lowest 8 bits are used) + */ + private void putU1(int i) + { + stream.append((char) i); + } + + /** + * Put two bytes on the stream. + * + * @param i the information to add (only lowest 16 bits are used) + */ + private void putU2(int i) + { + stream.append((char) (i >> 8)).append((char) i); + } + + /** + * Put four bytes on the stream. + * + * @param i the information to add (treated as unsigned) + */ + private void putU4(int i) + { + stream.append((char) (i >> 24)).append((char) (i >> 16)); + stream.append((char) (i >> 8)).append((char) i); + } + + /** + * Put bytecode to load a constant integer on the stream. This only + * needs to work for values less than Short.MAX_VALUE. + * + * @param i the int to add + */ + private void putConst(int i) + { + if (i >= -1 && i <= 5) + putU1(ICONST_0 + i); + else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) + { + putU1(BIPUSH); + putU1(i); + } + else + { + putU1(SIPUSH); + putU2(i); + } + } + + /** + * Put bytecode to load a given local variable on the stream. + * + * @param i the slot to load + * @param type the base type of the load + */ + private void putLoad(int i, Class type) + { + int offset = 0; + if (type == long.class) + offset = 1; + else if (type == float.class) + offset = 2; + else if (type == double.class) + offset = 3; + else if (! type.isPrimitive()) + offset = 4; + if (i < 4) + putU1(ILOAD_0 + 4 * offset + i); + else + { + putU1(ILOAD + offset); + putU1(i); + } + } + + /** + * Given a primitive type, return its wrapper class name. + * + * @param clazz the primitive type (but not void.class) + * @return the internal form of the wrapper class name + */ + private String wrapper(Class clazz) + { + if (clazz == boolean.class) + return "java/lang/Boolean"; + if (clazz == byte.class) + return "java/lang/Byte"; + if (clazz == short.class) + return "java/lang/Short"; + if (clazz == char.class) + return "java/lang/Character"; + if (clazz == int.class) + return "java/lang/Integer"; + if (clazz == long.class) + return "java/lang/Long"; + if (clazz == float.class) + return "java/lang/Float"; + if (clazz == double.class) + return "java/lang/Double"; + // assert false; + return null; + } + + /** + * Returns the entry of this String in the Constant pool, adding it + * if necessary. + * + * @param str the String to resolve + * @return the index of the String in the constant pool + */ + private char utf8Info(String str) + { + String utf8 = toUtf8(str); + int len = utf8.length(); + return poolIndex("\1" + (char) (len >> 8) + (char) (len & 0xff) + utf8); + } + + /** + * Returns the entry of the appropriate class info structure in the + * Constant pool, adding it if necessary. + * + * @param name the class name, in internal form + * @return the index of the ClassInfo in the constant pool + */ + private char classInfo(String name) + { + char index = utf8Info(name); + char[] c = {7, (char) (index >> 8), (char) (index & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Returns the entry of the appropriate class info structure in the + * Constant pool, adding it if necessary. + * + * @param clazz the class type + * @return the index of the ClassInfo in the constant pool + */ + private char classInfo(Class clazz) + { + return classInfo(TypeSignature.getEncodingOfClass(clazz.getName(), + false)); + } + + /** + * Returns the entry of the appropriate fieldref, methodref, or + * interfacemethodref info structure in the Constant pool, adding it + * if necessary. + * + * @param structure FIELD, METHOD, or INTERFACE + * @param clazz the class name, in internal form + * @param name the simple reference name + * @param type the type of the reference + * @return the index of the appropriate Info structure in the constant pool + */ + private char refInfo(byte structure, String clazz, String name, + String type) + { + char cindex = classInfo(clazz); + char ntindex = nameAndTypeInfo(name, type); + // relies on FIELD == 1, METHOD == 2, INTERFACE == 3 + char[] c = {(char) (structure + 8), + (char) (cindex >> 8), (char) (cindex & 0xff), + (char) (ntindex >> 8), (char) (ntindex & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Returns the entry of the appropriate nameAndTyperef info structure + * in the Constant pool, adding it if necessary. + * + * @param name the simple name + * @param type the reference type + * @return the index of the NameAndTypeInfo structure in the constant pool + */ + private char nameAndTypeInfo(String name, String type) + { + char nindex = utf8Info(name); + char tindex = utf8Info(type); + char[] c = {12, (char) (nindex >> 8), (char) (nindex & 0xff), + (char) (tindex >> 8), (char) (tindex & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Converts a regular string to a UTF8 string, where the upper byte + * of every char is 0, and '\\u0000' is not in the string. This is + * basically to use a String as a fancy byte[], and while it is less + * efficient in memory use, it is easier for hashing. + * + * @param str the original, in straight unicode + * @return a modified string, in UTF8 format in the low bytes + */ + private String toUtf8(String str) + { + final char[] ca = str.toCharArray(); + final int len = ca.length; + + // Avoid object creation, if str is already fits UTF8. + int i; + for (i = 0; i < len; i++) + if (ca[i] == 0 || ca[i] > '\u007f') + break; + if (i == len) + return str; + + final CPStringBuilder sb = new CPStringBuilder(str); + sb.setLength(i); + for ( ; i < len; i++) + { + final char c = ca[i]; + if (c > 0 && c <= '\u007f') + sb.append(c); + else if (c <= '\u07ff') // includes '\0' + { + sb.append((char) (0xc0 | (c >> 6))); + sb.append((char) (0x80 | (c & 0x6f))); + } + else + { + sb.append((char) (0xe0 | (c >> 12))); + sb.append((char) (0x80 | ((c >> 6) & 0x6f))); + sb.append((char) (0x80 | (c & 0x6f))); + } + } + return sb.toString(); + } + + /** + * Returns the location of a byte sequence (conveniently wrapped in + * a String with all characters between \u0001 and \u00ff inclusive) + * in the constant pool, adding it if necessary. + * + * @param sequence the byte sequence to look for + * @return the index of the sequence + * @throws IllegalArgumentException if this would make the constant + * pool overflow + */ + private char poolIndex(String sequence) + { + Integer i = (Integer) poolEntries.get(sequence); + if (i == null) + { + // pool starts at index 1 + int size = poolEntries.size() + 1; + if (size >= 65535) + throw new IllegalArgumentException("exceeds VM limitations"); + i = Integer.valueOf(size); + poolEntries.put(sequence, i); + pool.append(sequence); + } + return (char) i.intValue(); + } + } // class ClassFactory +} diff --git a/libjava/classpath/java/lang/reflect/README b/libjava/classpath/java/lang/reflect/README new file mode 100644 index 000000000..99ea224d7 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/README @@ -0,0 +1,4 @@ +README for java.lang.reflect: + +java.lang.reflect is now mostly empty. We've carved out the classes that have +to do with the VM and put them into the VM interface. diff --git a/libjava/classpath/java/lang/reflect/ReflectPermission.java b/libjava/classpath/java/lang/reflect/ReflectPermission.java new file mode 100644 index 000000000..56eccf813 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/ReflectPermission.java @@ -0,0 +1,102 @@ +/* ReflectPermission.java - named permission for reflaction + Copyright (C) 2000, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.lang.reflect; + +import java.security.BasicPermission; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + */ + +/** + * This class implements permissions for reflection. This is a named + * permission, and the only defined name is suppressAccessChecks, which + * allows suppression of normal Java objects when using reflection. + * + * + * + * + * + * + * + * + * + * + * + * + *
      Permission Target NameWhat Permission AllowsRisk of Allowing Permission
      suppressAccessChecksAbility to access fields, invoke methods, and construct objects + * via reflection, including non-public members in contexts where + * such access is not legal at compile-time.This is dangerous. It exposes possibly confidential information, + * and malicious code could interfere with the internals of the Virtual + * Machine by corrupting private data.
      + * + * @author Tom Tromey (tromey@redhat.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + * @status updated to 1.4 + */ +public final class ReflectPermission + extends BasicPermission +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 7412737110241507485L; + + /** + * Construct a ReflectPermission with the given name. + * + * @param name The permission name + */ + public ReflectPermission(String name) + { + super(name); + } + + /** + * Construct a ReflectPermission with the given name. + * + * @param name The permission name + * @param actions The actions; this is ignored and should be null + */ + public ReflectPermission(String name, String actions) + { + super(name, actions); + } +} diff --git a/libjava/classpath/java/lang/reflect/TODO b/libjava/classpath/java/lang/reflect/TODO new file mode 100755 index 000000000..6514c7603 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/TODO @@ -0,0 +1,4 @@ +TODO for java.lang.reflect Java side + +- more tests! +- Java 2 support (waiting on java.lang Java 2 support) diff --git a/libjava/classpath/java/lang/reflect/Type.java b/libjava/classpath/java/lang/reflect/Type.java new file mode 100644 index 000000000..41b0a9ca9 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/Type.java @@ -0,0 +1,55 @@ +/* Type.java - Superinterface for all types. + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Represents any Type within the Java programming + * language. This may be a primitive type (e.g. int, + * an array type (e.g. double[]>/code>), a raw type + * (e.g. Calendar), a parameterized type + * (e.g. List<Boolean>, or a type + * variable (e.g. T extends String). + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Type +{ +} diff --git a/libjava/classpath/java/lang/reflect/TypeVariable.java b/libjava/classpath/java/lang/reflect/TypeVariable.java new file mode 100644 index 000000000..671c290ba --- /dev/null +++ b/libjava/classpath/java/lang/reflect/TypeVariable.java @@ -0,0 +1,96 @@ +/* TypeVariable.java + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + *

      + * This is a common interface for all type variables provided by + * the Java language. Instances are created the first time a type + * variable is needed by one of the reflective methods declared in + * this package. + *

      + *

      + * Creating a type variable requires resolving the appropriate type. + * This may involve resolving other classes as a side effect (e.g. + * if the type is nested inside other classes). Creation should not + * involve resolving the bounds. Repeated creation has no effect; an + * equivalent instance is returned. Caching is not required, but all + * instances must be equal() to each other. + *

      + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface TypeVariable extends Type +{ + + /** + * Returns an array of Type objects which represent the upper + * bounds of this type variable. There is always a default bound of + * Object. Any ParameterizedTypes will be + * created as necessary, and other types resolved. + * + * @return an array of Type objects representing the upper + * bounds. + * @throws TypeNotPresentException if any of the bounds refer to a + * non-existant type. + * @throws MalformedParameterizedTypeException if the creation of a + * ParameterizedType fails. + */ + Type[] getBounds(); + + + /** + * Returns a representation of the declaration used to declare this + * type variable. + * + * @return the GenericDeclaration object for this type + * variable. + */ + T getGenericDeclaration(); + + /** + * Returns the name of the type variable, as written in the source + * code. + * + * @return the name of the type variable. + */ + String getName(); +} diff --git a/libjava/classpath/java/lang/reflect/UndeclaredThrowableException.java b/libjava/classpath/java/lang/reflect/UndeclaredThrowableException.java new file mode 100644 index 000000000..ea574ad7c --- /dev/null +++ b/libjava/classpath/java/lang/reflect/UndeclaredThrowableException.java @@ -0,0 +1,128 @@ +/* UndeclaredThrowableException.java -- wraps an undeclared checked exception + thrown by a Proxy invocation handler + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * This exception class is thrown by a {@link Proxy} instance if + * the {@link InvocationHandler#invoke(Object, Method, Object[]) invoke} + * method of that instance's InvocationHandler attempts to throw an + * exception that not declared by the throws clauses of all of the + * interface methods that the proxy instance is implementing. + * + *

      When thrown by Proxy, this class will always wrap a checked + * exception, never {@link Error} or {@link RuntimeException}, + * which are unchecked. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Proxy + * @see InvocationHandler + * @since 1.3 + * @status updated to 1.4 + */ +public class UndeclaredThrowableException extends RuntimeException +{ + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = 330127114055056639L; + + /** + * The immutable exception that this wraps. This field is redundant + * with {@link Throwable#getCause()}, but is necessary for serial compatibility. + * + * @serial the chained exception + */ + private final Throwable undeclaredThrowable; + + /** + * Wraps the given checked exception into a RuntimeException, with no + * detail message. {@link Throwable#initCause(Throwable)} will fail + * on this instance. + * + * @param cause the undeclared throwable that caused this exception, + * may be null + */ + public UndeclaredThrowableException(Throwable cause) + { + this(cause, null); + } + + /** + * Wraps the given checked exception into a RuntimeException, with the + * specified detail message. {@link Throwable#initCause(Throwable)} will + * fail on this instance. + * + * @param cause the undeclared throwable that caused this exception, + * may be null + * @param message the message, may be null + */ + public UndeclaredThrowableException(Throwable cause, String message) + { + super(message, cause); + undeclaredThrowable = cause; + } + + /** + * Returns the cause of this exception. If this exception was created + * by a {@link Proxy} instance, it will be a non-null checked + * exception. This method pre-dates exception chaining, and is now + * simply a longer way to call getCause(). + * + * @return the cause of this exception, may be null + * @see #getCause() + */ + public Throwable getUndeclaredThrowable() + { + return undeclaredThrowable; + } + + /** + * Returns the cause of this exception. If this exception was created + * by a {@link Proxy} instance, it will be a non-null checked + * exception. + * + * @return the cause of this exception, may be null + * @since 1.4 + */ + public Throwable getCause() + { + return undeclaredThrowable; + } +} diff --git a/libjava/classpath/java/lang/reflect/WildcardType.java b/libjava/classpath/java/lang/reflect/WildcardType.java new file mode 100644 index 000000000..43b5d0a40 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/WildcardType.java @@ -0,0 +1,115 @@ +/* WildcardType.java -- A wildcard type expression e.g. ? extends String + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +/** + * Represents a wildcard type expression, where the type variable + * is unnamed. The simplest example of this is ?, + * which represents any unbounded type. Another example is + * ? extends Number, which specifies any type + * which is a subclass of Number (Number + * is the upper bound). + *

      + *

      + * ? super String gives the type a less common lower bound, + * which means that the type must be either a String or one + * of its superclasses. This can be useful in working with collections. + * You may want a method to add instances of a class to a collection + * with a more generic type (e.g. adding Strings to + * a list of Objects), but don't want to allow users + * to pass in a collection with a more specific type. + *

      + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface WildcardType extends Type +{ + + /** + *

      + * Returns an array of Types, which specify the + * lower bounds of this type. The default lower bound is + * null, which causes this method to return an + * empty array. + *

      + *

      + * In generating the array of Types, each + * ParameterizedType or TypeVariable is + * created, (see the documentation for these classes for details of this + * process), if necessary, while all other types are simply + * resolved. + *

      + * + * @return an array of Type objects, representing + * the wildcard type's lower bounds. + * @throws TypeNotPresentException if any of the types referred to by + * the lower bounds of this type do not actually exist. + * @throws MalformedParameterizedTypeException if any of the types + * refer to a type which can not be instantiated. + */ + Type[] getLowerBounds(); + + /** + *

      + * Returns an array of Types, which specify the + * upper bounds of this type. The default upper bound is + * Object, which causes this method to return an + * array, containing just the Type instance for + * Object. + *

      + *

      + * In generating the array of Types, each + * ParameterizedType or TypeVariable is + * created, (see the documentation for these classes for details of this + * process), if necessary, while all other types are simply + * resolved. + *

      + * + * @return an array of Type objects, representing + * the wildcard type's upper bounds. + * @throws TypeNotPresentException if any of the types referred to by + * the upper bounds of this type do not actually exist. + * @throws MalformedParameterizedTypeException if any of the types + * refer to a type which can not be instantiated. + */ + Type[] getUpperBounds(); + +} diff --git a/libjava/classpath/java/lang/reflect/package.html b/libjava/classpath/java/lang/reflect/package.html new file mode 100644 index 000000000..9f7ed6328 --- /dev/null +++ b/libjava/classpath/java/lang/reflect/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.lang.reflect + + +

      Runtime inspection and manipulation of object classes, methods, arguments +and fields.

      + + + diff --git a/libjava/classpath/java/math/BigDecimal.java b/libjava/classpath/java/math/BigDecimal.java new file mode 100644 index 000000000..7f4546cda --- /dev/null +++ b/libjava/classpath/java/math/BigDecimal.java @@ -0,0 +1,1559 @@ +/* java.math.BigDecimal -- Arbitrary precision decimals. + Copyright (C) 1999, 2000, 2001, 2003, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.math; + +import gnu.java.lang.CPStringBuilder; + +public class BigDecimal extends Number implements Comparable +{ + private BigInteger intVal; + private int scale; + private int precision = 0; + private static final long serialVersionUID = 6108874887143696463L; + + /** + * The constant zero as a BigDecimal with scale zero. + * @since 1.5 + */ + public static final BigDecimal ZERO = + new BigDecimal (BigInteger.ZERO, 0); + + /** + * The constant one as a BigDecimal with scale zero. + * @since 1.5 + */ + public static final BigDecimal ONE = + new BigDecimal (BigInteger.ONE, 0); + + /** + * The constant ten as a BigDecimal with scale zero. + * @since 1.5 + */ + public static final BigDecimal TEN = + new BigDecimal (BigInteger.TEN, 0); + + public static final int ROUND_UP = 0; + public static final int ROUND_DOWN = 1; + public static final int ROUND_CEILING = 2; + public static final int ROUND_FLOOR = 3; + public static final int ROUND_HALF_UP = 4; + public static final int ROUND_HALF_DOWN = 5; + public static final int ROUND_HALF_EVEN = 6; + public static final int ROUND_UNNECESSARY = 7; + + /** + * Constructs a new BigDecimal whose unscaled value is val and whose + * scale is zero. + * @param val the value of the new BigDecimal + * @since 1.5 + */ + public BigDecimal (int val) + { + this.intVal = BigInteger.valueOf(val); + this.scale = 0; + } + + /** + * Constructs a BigDecimal using the BigDecimal(int) constructor and then + * rounds according to the MathContext. + * @param val the value for the initial (unrounded) BigDecimal + * @param mc the MathContext specifying the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (int val, MathContext mc) + { + this (val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a new BigDecimal whose unscaled value is val and whose + * scale is zero. + * @param val the value of the new BigDecimal + */ + public BigDecimal (long val) + { + this.intVal = BigInteger.valueOf(val); + this.scale = 0; + } + + /** + * Constructs a BigDecimal from the long in the same way as BigDecimal(long) + * and then rounds according to the MathContext. + * @param val the long from which we create the initial BigDecimal + * @param mc the MathContext that specifies the rounding behaviour + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (long val, MathContext mc) + { + this(val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal whose value is given by num rounded according to + * mc. Since num is already a BigInteger, the rounding refers only to the + * precision setting in mc, if mc.getPrecision() returns an int lower than + * the number of digits in num, then rounding is necessary. + * @param num the unscaledValue, before rounding + * @param mc the MathContext that specifies the precision + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * * @since 1.5 + */ + public BigDecimal (BigInteger num, MathContext mc) + { + this (num, 0); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal from the String val according to the same + * rules as the BigDecimal(String) constructor and then rounds + * according to the MathContext mc. + * @param val the String from which we construct the initial BigDecimal + * @param mc the MathContext that specifies the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (String val, MathContext mc) + { + this (val); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal whose unscaled value is num and whose + * scale is zero. + * @param num the value of the new BigDecimal + */ + public BigDecimal (BigInteger num) + { + this (num, 0); + } + + /** + * Constructs a BigDecimal whose unscaled value is num and whose + * scale is scale. + * @param num + * @param scale + */ + public BigDecimal (BigInteger num, int scale) + { + this.intVal = num; + this.scale = scale; + } + + /** + * Constructs a BigDecimal using the BigDecimal(BigInteger, int) + * constructor and then rounds according to the MathContext. + * @param num the unscaled value of the unrounded BigDecimal + * @param scale the scale of the unrounded BigDecimal + * @param mc the MathContext specifying the rounding + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (BigInteger num, int scale, MathContext mc) + { + this (num, scale); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + /** + * Constructs a BigDecimal in the same way as BigDecimal(double) and then + * rounds according to the MathContext. + * @param num the double from which the initial BigDecimal is created + * @param mc the MathContext that specifies the rounding behaviour + * @throws ArithmeticException if the result is inexact but the rounding type + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal (double num, MathContext mc) + { + this (num); + if (mc.getPrecision() != 0) + { + BigDecimal result = this.round(mc); + this.intVal = result.intVal; + this.scale = result.scale; + this.precision = result.precision; + } + } + + public BigDecimal (double num) throws NumberFormatException + { + if (Double.isInfinite (num) || Double.isNaN (num)) + throw new NumberFormatException ("invalid argument: " + num); + // Note we can't convert NUM to a String and then use the + // String-based constructor. The BigDecimal documentation makes + // it clear that the two constructors work differently. + + final int mantissaBits = 52; + final int exponentBits = 11; + final long mantMask = (1L << mantissaBits) - 1; + final long expMask = (1L << exponentBits) - 1; + + long bits = Double.doubleToLongBits (num); + long mantissa = bits & mantMask; + long exponent = (bits >>> mantissaBits) & expMask; + boolean denormal = exponent == 0; + + // Correct the exponent for the bias. + exponent -= denormal ? 1022 : 1023; + + // Now correct the exponent to account for the bits to the right + // of the decimal. + exponent -= mantissaBits; + // Ordinary numbers have an implied leading `1' bit. + if (! denormal) + mantissa |= (1L << mantissaBits); + + // Shave off factors of 10. + while (exponent < 0 && (mantissa & 1) == 0) + { + ++exponent; + mantissa >>= 1; + } + + intVal = BigInteger.valueOf (bits < 0 ? - mantissa : mantissa); + if (exponent < 0) + { + // We have MANTISSA * 2 ^ (EXPONENT). + // Since (1/2)^N == 5^N * 10^-N we can easily convert this + // into a power of 10. + scale = (int) (- exponent); + BigInteger mult = BigInteger.valueOf (5).pow (scale); + intVal = intVal.multiply (mult); + } + else + { + intVal = intVal.shiftLeft ((int) exponent); + scale = 0; + } + } + + /** + * Constructs a BigDecimal from the char subarray and rounding + * according to the MathContext. + * @param in the char array + * @param offset the start of the subarray + * @param len the length of the subarray + * @param mc the MathContext for rounding + * @throws NumberFormatException if the char subarray is not a valid + * BigDecimal representation + * @throws ArithmeticException if the result is inexact but the rounding + * mode is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal(char[] in, int offset, int len, MathContext mc) + { + this(in, offset, len); + // If mc has precision other than zero then we must round. + if (mc.getPrecision() != 0) + { + BigDecimal temp = this.round(mc); + this.intVal = temp.intVal; + this.scale = temp.scale; + this.precision = temp.precision; + } + } + + /** + * Constructs a BigDecimal from the char array and rounding according + * to the MathContext. + * @param in the char array + * @param mc the MathContext + * @throws NumberFormatException if in is not a valid BigDecimal + * representation + * @throws ArithmeticException if the result is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal(char[] in, MathContext mc) + { + this(in, 0, in.length); + // If mc has precision other than zero then we must round. + if (mc.getPrecision() != 0) + { + BigDecimal temp = this.round(mc); + this.intVal = temp.intVal; + this.scale = temp.scale; + this.precision = temp.precision; + } + } + + /** + * Constructs a BigDecimal from the given char array, accepting the same + * sequence of characters as the BigDecimal(String) constructor. + * @param in the char array + * @throws NumberFormatException if in is not a valid BigDecimal + * representation + * @since 1.5 + */ + public BigDecimal(char[] in) + { + this(in, 0, in.length); + } + + /** + * Constructs a BigDecimal from a char subarray, accepting the same sequence + * of characters as the BigDecimal(String) constructor. + * @param in the char array + * @param offset the start of the subarray + * @param len the length of the subarray + * @throws NumberFormatException if in is not a valid + * BigDecimal representation. + * @since 1.5 + */ + public BigDecimal(char[] in, int offset, int len) + { + // start is the index into the char array where the significand starts + int start = offset; + // end is one greater than the index of the last character used + int end = offset + len; + // point is the index into the char array where the exponent starts + // (or, if there is no exponent, this is equal to end) + int point = offset; + // dot is the index into the char array where the decimal point is + // found, or -1 if there is no decimal point + int dot = -1; + + // The following examples show what these variables mean. Note that + // point and dot don't yet have the correct values, they will be + // properly assigned in a loop later on in this method. + // + // Example 1 + // + // + 1 0 2 . 4 6 9 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 8, start = 3, dot = 6, point = end = 10 + // + // Example 2 + // + // + 2 3 4 . 6 1 3 E - 1 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 11, start = 3, dot = 6, point = 10, end = 13 + // + // Example 3 + // + // - 1 2 3 4 5 e 7 + // __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ + // + // offset = 2, len = 8, start = 3, dot = -1, point = 8, end = 10 + + // Determine the sign of the number. + boolean negative = false; + if (in[offset] == '+') + { + ++start; + ++point; + } + else if (in[offset] == '-') + { + ++start; + ++point; + negative = true; + } + + // Check each character looking for the decimal point and the + // start of the exponent. + while (point < end) + { + char c = in[point]; + if (c == '.') + { + // If dot != -1 then we've seen more than one decimal point. + if (dot != -1) + throw new NumberFormatException("multiple `.'s in number"); + dot = point; + } + // Break when we reach the start of the exponent. + else if (c == 'e' || c == 'E') + break; + // Throw an exception if the character was not a decimal or an + // exponent and is not a digit. + else if (!Character.isDigit(c)) + throw new NumberFormatException("unrecognized character at " + point + + ": " + c); + ++point; + } + + // val is a StringBuilder from which we'll create a BigInteger + // which will be the unscaled value for this BigDecimal + CPStringBuilder val = new CPStringBuilder(point - start - 1); + if (dot != -1) + { + // If there was a decimal we must combine the two parts that + // contain only digits and we must set the scale properly. + val.append(in, start, dot - start); + val.append(in, dot + 1, point - dot - 1); + scale = point - 1 - dot; + } + else + { + // If there was no decimal then the unscaled value is just the number + // formed from all the digits and the scale is zero. + val.append(in, start, point - start); + scale = 0; + } + if (val.length() == 0) + throw new NumberFormatException("no digits seen"); + + // Prepend a negative sign if necessary. + if (negative) + val.insert(0, '-'); + intVal = new BigInteger(val.toString()); + + // Now parse exponent. + // If point < end that means we broke out of the previous loop when we + // saw an 'e' or an 'E'. + if (point < end) + { + point++; + // Ignore a '+' sign. + if (in[point] == '+') + point++; + + // Throw an exception if there were no digits found after the 'e' + // or 'E'. + if (point >= end) + throw new NumberFormatException("no exponent following e or E"); + + try + { + // Adjust the scale according to the exponent. + // Remember that the value of a BigDecimal is + // unscaledValue x Math.pow(10, -scale) + scale -= Integer.parseInt(new String(in, point, end - point)); + } + catch (NumberFormatException ex) + { + throw new NumberFormatException("malformed exponent"); + } + } + } + + public BigDecimal (String num) throws NumberFormatException + { + int len = num.length(); + int start = 0, point = 0; + int dot = -1; + boolean negative = false; + if (num.charAt(0) == '+') + { + ++start; + ++point; + } + else if (num.charAt(0) == '-') + { + ++start; + ++point; + negative = true; + } + + while (point < len) + { + char c = num.charAt (point); + if (c == '.') + { + if (dot >= 0) + throw new NumberFormatException ("multiple `.'s in number"); + dot = point; + } + else if (c == 'e' || c == 'E') + break; + else if (Character.digit (c, 10) < 0) + throw new NumberFormatException ("unrecognized character: " + c); + ++point; + } + + String val; + if (dot >= 0) + { + val = num.substring (start, dot) + num.substring (dot + 1, point); + scale = point - 1 - dot; + } + else + { + val = num.substring (start, point); + scale = 0; + } + if (val.length () == 0) + throw new NumberFormatException ("no digits seen"); + + if (negative) + val = "-" + val; + intVal = new BigInteger (val); + + // Now parse exponent. + if (point < len) + { + point++; + if (num.charAt(point) == '+') + point++; + + if (point >= len ) + throw new NumberFormatException ("no exponent following e or E"); + + try + { + scale -= Integer.parseInt (num.substring (point)); + } + catch (NumberFormatException ex) + { + throw new NumberFormatException ("malformed exponent"); + } + } + } + + public static BigDecimal valueOf (long val) + { + return valueOf (val, 0); + } + + public static BigDecimal valueOf (long val, int scale) + throws NumberFormatException + { + if ((scale == 0) && ((int)val == val)) + switch ((int) val) + { + case 0: + return ZERO; + case 1: + return ONE; + } + + return new BigDecimal (BigInteger.valueOf (val), scale); + } + + public BigDecimal add (BigDecimal val) + { + // For addition, need to line up decimals. Note that the movePointRight + // method cannot be used for this as it might return a BigDecimal with + // scale == 0 instead of the scale we need. + BigInteger op1 = intVal; + BigInteger op2 = val.intVal; + if (scale < val.scale) + op1 = op1.multiply (BigInteger.TEN.pow (val.scale - scale)); + else if (scale > val.scale) + op2 = op2.multiply (BigInteger.TEN.pow (scale - val.scale)); + + return new BigDecimal (op1.add (op2), Math.max (scale, val.scale)); + } + + /** + * Returns a BigDecimal whose value is found first by calling the + * method add(val) and then by rounding according to the MathContext mc. + * @param val the augend + * @param mc the MathContext for rounding + * @throws ArithmeticException if the value is inexact but the rounding is + * RoundingMode.UNNECESSARY + * @return this + val, rounded if need be + * @since 1.5 + */ + public BigDecimal add (BigDecimal val, MathContext mc) + { + return add(val).round(mc); + } + + public BigDecimal subtract (BigDecimal val) + { + return this.add(val.negate()); + } + + /** + * Returns a BigDecimal whose value is found first by calling the + * method subtract(val) and then by rounding according to the MathContext mc. + * @param val the subtrahend + * @param mc the MathContext for rounding + * @throws ArithmeticException if the value is inexact but the rounding is + * RoundingMode.UNNECESSARY + * @return this - val, rounded if need be + * @since 1.5 + */ + public BigDecimal subtract (BigDecimal val, MathContext mc) + { + return subtract(val).round(mc); + } + + public BigDecimal multiply (BigDecimal val) + { + return new BigDecimal (intVal.multiply (val.intVal), scale + val.scale); + } + + /** + * Returns a BigDecimal whose value is (this x val) before it is rounded + * according to the MathContext mc. + * @param val the multiplicand + * @param mc the MathContext for rounding + * @return a new BigDecimal with value approximately (this x val) + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal multiply (BigDecimal val, MathContext mc) + { + return multiply(val).round(mc); + } + + public BigDecimal divide (BigDecimal val, int roundingMode) + throws ArithmeticException, IllegalArgumentException + { + return divide (val, scale, roundingMode); + } + + /** + * Returns a BigDecimal whose value is (this / val), with the specified scale + * and rounding according to the RoundingMode + * @param val the divisor + * @param scale the scale of the BigDecimal returned + * @param roundingMode the rounding mode to use + * @return a BigDecimal whose value is approximately (this / val) + * @throws ArithmeticException if divisor is zero or the rounding mode is + * UNNECESSARY but the specified scale cannot represent the value exactly + * @since 1.5 + */ + public BigDecimal divide(BigDecimal val, + int scale, RoundingMode roundingMode) + { + return divide (val, scale, roundingMode.ordinal()); + } + + /** + * Returns a BigDecimal whose value is (this / val) rounded according to the + * RoundingMode + * @param val the divisor + * @param roundingMode the rounding mode to use + * @return a BigDecimal whose value is approximately (this / val) + * @throws ArithmeticException if divisor is zero or the rounding mode is + * UNNECESSARY but the specified scale cannot represent the value exactly + */ + public BigDecimal divide (BigDecimal val, RoundingMode roundingMode) + { + return divide (val, scale, roundingMode.ordinal()); + } + + public BigDecimal divide(BigDecimal val, int newScale, int roundingMode) + throws ArithmeticException, IllegalArgumentException + { + if (roundingMode < 0 || roundingMode > 7) + throw + new IllegalArgumentException("illegal rounding mode: " + roundingMode); + + if (intVal.signum () == 0) // handle special case of 0.0/0.0 + return newScale == 0 ? ZERO : new BigDecimal (ZERO.intVal, newScale); + + // Ensure that pow gets a non-negative value. + BigInteger valIntVal = val.intVal; + int power = newScale - (scale - val.scale); + if (power < 0) + { + // Effectively increase the scale of val to avoid an + // ArithmeticException for a negative power. + valIntVal = valIntVal.multiply (BigInteger.TEN.pow (-power)); + power = 0; + } + + BigInteger dividend = intVal.multiply (BigInteger.TEN.pow (power)); + + BigInteger parts[] = dividend.divideAndRemainder (valIntVal); + + BigInteger unrounded = parts[0]; + if (parts[1].signum () == 0) // no remainder, no rounding necessary + return new BigDecimal (unrounded, newScale); + + if (roundingMode == ROUND_UNNECESSARY) + throw new ArithmeticException ("Rounding necessary"); + + int sign = intVal.signum () * valIntVal.signum (); + + if (roundingMode == ROUND_CEILING) + roundingMode = (sign > 0) ? ROUND_UP : ROUND_DOWN; + else if (roundingMode == ROUND_FLOOR) + roundingMode = (sign < 0) ? ROUND_UP : ROUND_DOWN; + else + { + // half is -1 if remainder*2 < positive intValue (*power), 0 if equal, + // 1 if >. This implies that the remainder to round is less than, + // equal to, or greater than half way to the next digit. + BigInteger posRemainder + = parts[1].signum () < 0 ? parts[1].negate() : parts[1]; + valIntVal = valIntVal.signum () < 0 ? valIntVal.negate () : valIntVal; + int half = posRemainder.shiftLeft(1).compareTo(valIntVal); + + switch(roundingMode) + { + case ROUND_HALF_UP: + roundingMode = (half < 0) ? ROUND_DOWN : ROUND_UP; + break; + case ROUND_HALF_DOWN: + roundingMode = (half > 0) ? ROUND_UP : ROUND_DOWN; + break; + case ROUND_HALF_EVEN: + if (half < 0) + roundingMode = ROUND_DOWN; + else if (half > 0) + roundingMode = ROUND_UP; + else if (unrounded.testBit(0)) // odd, then ROUND_HALF_UP + roundingMode = ROUND_UP; + else // even, ROUND_HALF_DOWN + roundingMode = ROUND_DOWN; + break; + } + } + + if (roundingMode == ROUND_UP) + unrounded = unrounded.add (BigInteger.valueOf (sign > 0 ? 1 : -1)); + + // roundingMode == ROUND_DOWN + return new BigDecimal (unrounded, newScale); + } + + /** + * Performs division, if the resulting quotient requires rounding + * (has a nonterminating decimal expansion), + * an ArithmeticException is thrown. + * #see divide(BigDecimal, int, int) + * @since 1.5 + */ + public BigDecimal divide(BigDecimal divisor) + throws ArithmeticException, IllegalArgumentException + { + return divide(divisor, scale, ROUND_UNNECESSARY); + } + + /** + * Returns a BigDecimal whose value is the remainder in the quotient + * this / val. This is obtained by + * subtract(divideToIntegralValue(val).multiply(val)). + * @param val the divisor + * @return a BigDecimal whose value is the remainder + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal remainder(BigDecimal val) + { + return subtract(divideToIntegralValue(val).multiply(val)); + } + + /** + * Returns a BigDecimal array, the first element of which is the integer part + * of this / val, and the second element of which is the remainder of + * that quotient. + * @param val the divisor + * @return the above described BigDecimal array + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal[] divideAndRemainder(BigDecimal val) + { + BigDecimal[] result = new BigDecimal[2]; + result[0] = divideToIntegralValue(val); + result[1] = subtract(result[0].multiply(val)); + return result; + } + + /** + * Returns a BigDecimal whose value is the integer part of the quotient + * this / val. The preferred scale is this.scale - val.scale. + * @param val the divisor + * @return a BigDecimal whose value is the integer part of this / val. + * @throws ArithmeticException if val == 0 + * @since 1.5 + */ + public BigDecimal divideToIntegralValue(BigDecimal val) + { + return divide(val, ROUND_DOWN).floor().setScale(scale - val.scale, ROUND_DOWN); + } + + /** + * Mutates this BigDecimal into one with no fractional part, whose value is + * equal to the largest integer that is <= to this BigDecimal. Note that + * since this method is private it is okay to mutate this BigDecimal. + * @return the BigDecimal obtained through the floor operation on this + * BigDecimal. + */ + private BigDecimal floor() + { + if (scale <= 0) + return this; + String intValStr = intVal.toString(); + intValStr = intValStr.substring(0, intValStr.length() - scale); + intVal = new BigInteger(intValStr).multiply(BigInteger.TEN.pow(scale)); + return this; + } + + public int compareTo (BigDecimal val) + { + if (scale == val.scale) + return intVal.compareTo (val.intVal); + + BigInteger thisParts[] = + intVal.divideAndRemainder (BigInteger.TEN.pow (scale)); + BigInteger valParts[] = + val.intVal.divideAndRemainder (BigInteger.TEN.pow (val.scale)); + + int compare; + if ((compare = thisParts[0].compareTo (valParts[0])) != 0) + return compare; + + // quotients are the same, so compare remainders + + // Add some trailing zeros to the remainder with the smallest scale + if (scale < val.scale) + thisParts[1] = thisParts[1].multiply + (BigInteger.valueOf (10).pow (val.scale - scale)); + else if (scale > val.scale) + valParts[1] = valParts[1].multiply + (BigInteger.valueOf (10).pow (scale - val.scale)); + + // and compare them + return thisParts[1].compareTo (valParts[1]); + } + + public boolean equals (Object o) + { + return (o instanceof BigDecimal + && scale == ((BigDecimal) o).scale + && compareTo ((BigDecimal) o) == 0); + } + + public int hashCode() + { + return intValue() ^ scale; + } + + public BigDecimal max (BigDecimal val) + { + switch (compareTo (val)) + { + case 1: + return this; + default: + return val; + } + } + + public BigDecimal min (BigDecimal val) + { + switch (compareTo (val)) + { + case -1: + return this; + default: + return val; + } + } + + public BigDecimal movePointLeft (int n) + { + return (n < 0) ? movePointRight (-n) : new BigDecimal (intVal, scale + n); + } + + public BigDecimal movePointRight (int n) + { + if (n < 0) + return movePointLeft (-n); + + if (scale >= n) + return new BigDecimal (intVal, scale - n); + + return new BigDecimal (intVal.multiply + (BigInteger.TEN.pow (n - scale)), 0); + } + + public int signum () + { + return intVal.signum (); + } + + public int scale () + { + return scale; + } + + public BigInteger unscaledValue() + { + return intVal; + } + + public BigDecimal abs () + { + return new BigDecimal (intVal.abs (), scale); + } + + public BigDecimal negate () + { + return new BigDecimal (intVal.negate (), scale); + } + + /** + * Returns a BigDecimal whose value is found first by negating this via + * the negate() method, then by rounding according to the MathContext mc. + * @param mc the MathContext for rounding + * @return a BigDecimal whose value is approximately (-this) + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal negate(MathContext mc) + { + BigDecimal result = negate(); + if (mc.getPrecision() != 0) + result = result.round(mc); + return result; + } + + /** + * Returns this BigDecimal. This is included for symmetry with the + * method negate(). + * @return this + * @since 1.5 + */ + public BigDecimal plus() + { + return this; + } + + /** + * Returns a BigDecimal whose value is found by rounding this + * according to the MathContext. This is the same as round(MathContext). + * @param mc the MathContext for rounding + * @return a BigDecimal whose value is this before being rounded + * @throws ArithmeticException if the value is inexact but the rounding mode + * is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal plus(MathContext mc) + { + return round(mc); + } + + /** + * Returns a BigDecimal which is this BigDecimal rounded according to the + * MathContext rounding settings. + * @param mc the MathContext that tells us how to round + * @return the rounded BigDecimal + */ + public BigDecimal round(MathContext mc) + { + int mcPrecision = mc.getPrecision(); + int numToChop = precision() - mcPrecision; + // If mc specifies not to chop any digits or if we've already chopped + // enough digits (say by using a MathContext in the constructor for this + // BigDecimal) then just return this. + if (mcPrecision == 0 || numToChop <= 0) + return this; + + // Make a new BigDecimal which is the correct power of 10 to chop off + // the required number of digits and then call divide. + BigDecimal div = new BigDecimal(BigInteger.TEN.pow(numToChop)); + BigDecimal rounded = divide(div, scale, mc.getRoundingMode().ordinal()); + rounded.scale -= numToChop; + rounded.precision = mcPrecision; + return rounded; + } + + /** + * Returns the precision of this BigDecimal (the number of digits in the + * unscaled value). The precision of a zero value is 1. + * @return the number of digits in the unscaled value, or 1 if the value + * is zero. + */ + public int precision() + { + if (precision == 0) + { + String s = intVal.toString(); + precision = s.length() - (( s.charAt(0) == '-' ) ? 1 : 0); + } + return precision; + } + + /** + * Returns the String representation of this BigDecimal, using scientific + * notation if necessary. The following steps are taken to generate + * the result: + * + * 1. the BigInteger unscaledValue's toString method is called and if + * scale == 0 is returned. + * 2. an int adjExp is created which is equal to the negation + * of scale plus the number of digits in the unscaled value, + * minus one. + * 3. if scale >= 0 && adjExp >= -6 then we represent this + * BigDecimal without scientific notation. A decimal is added if the + * scale is positive and zeros are prepended as necessary. + * 4. if scale is negative or adjExp is less than -6 we use scientific + * notation. If the unscaled value has more than one digit, a decimal + * as inserted after the first digit, the character 'E' is appended + * and adjExp is appended. + */ + public String toString() + { + // bigStr is the String representation of the unscaled value. If + // scale is zero we simply return this. + String bigStr = intVal.toString(); + if (scale == 0) + return bigStr; + + boolean negative = (bigStr.charAt(0) == '-'); + int point = bigStr.length() - scale - (negative ? 1 : 0); + + CPStringBuilder val = new CPStringBuilder(); + + if (scale >= 0 && (point - 1) >= -6) + { + // Convert to character form without scientific notation. + if (point <= 0) + { + // Zeros need to be prepended to the StringBuilder. + if (negative) + val.append('-'); + // Prepend a '0' and a '.' and then as many more '0's as necessary. + val.append('0').append('.'); + while (point < 0) + { + val.append('0'); + point++; + } + // Append the unscaled value. + val.append(bigStr.substring(negative ? 1 : 0)); + } + else + { + // No zeros need to be prepended so the String is simply the + // unscaled value with the decimal point inserted. + val.append(bigStr); + val.insert(point + (negative ? 1 : 0), '.'); + } + } + else + { + // We must use scientific notation to represent this BigDecimal. + val.append(bigStr); + // If there is more than one digit in the unscaled value we put a + // decimal after the first digit. + if (bigStr.length() > 1) + val.insert( ( negative ? 2 : 1 ), '.'); + // And then append 'E' and the exponent = (point - 1). + val.append('E'); + if (point - 1 >= 0) + val.append('+'); + val.append( point - 1 ); + } + return val.toString(); + } + + /** + * Returns the String representation of this BigDecimal, using engineering + * notation if necessary. This is similar to toString() but when exponents + * are used the exponent is made to be a multiple of 3 such that the integer + * part is between 1 and 999. + * + * @return a String representation of this BigDecimal in engineering notation + * @since 1.5 + */ + public String toEngineeringString() + { + // bigStr is the String representation of the unscaled value. If + // scale is zero we simply return this. + String bigStr = intVal.toString(); + if (scale == 0) + return bigStr; + + boolean negative = (bigStr.charAt(0) == '-'); + int point = bigStr.length() - scale - (negative ? 1 : 0); + + // This is the adjusted exponent described above. + int adjExp = point - 1; + CPStringBuilder val = new CPStringBuilder(); + + if (scale >= 0 && adjExp >= -6) + { + // Convert to character form without scientific notation. + if (point <= 0) + { + // Zeros need to be prepended to the StringBuilder. + if (negative) + val.append('-'); + // Prepend a '0' and a '.' and then as many more '0's as necessary. + val.append('0').append('.'); + while (point < 0) + { + val.append('0'); + point++; + } + // Append the unscaled value. + val.append(bigStr.substring(negative ? 1 : 0)); + } + else + { + // No zeros need to be prepended so the String is simply the + // unscaled value with the decimal point inserted. + val.append(bigStr); + val.insert(point + (negative ? 1 : 0), '.'); + } + } + else + { + // We must use scientific notation to represent this BigDecimal. + // The exponent must be a multiple of 3 and the integer part + // must be between 1 and 999. + val.append(bigStr); + int zeros = adjExp % 3; + int dot = 1; + if (adjExp > 0) + { + // If the exponent is positive we just move the decimal to the + // right and decrease the exponent until it is a multiple of 3. + dot += zeros; + adjExp -= zeros; + } + else + { + // If the exponent is negative then we move the dot to the right + // and decrease the exponent (increase its magnitude) until + // it is a multiple of 3. Note that this is not adjExp -= zeros + // because the mod operator doesn't give us the distance to the + // correct multiple of 3. (-5 mod 3) is -2 but the distance from + // -5 to the correct multiple of 3 (-6) is 1, not 2. + if (zeros == -2) + { + dot += 1; + adjExp -= 1; + } + else if (zeros == -1) + { + dot += 2; + adjExp -= 2; + } + } + + // Either we have to append zeros because, for example, 1.1E+5 should + // be 110E+3, or we just have to put the decimal in the right place. + if (dot > val.length()) + { + while (dot > val.length()) + val.append('0'); + } + else if (bigStr.length() > dot) + val.insert(dot + (negative ? 1 : 0), '.'); + + // And then append 'E' and the exponent (adjExp). + val.append('E'); + if (adjExp >= 0) + val.append('+'); + val.append(adjExp); + } + return val.toString(); + } + + /** + * Returns a String representation of this BigDecimal without using + * scientific notation. This is how toString() worked for releases 1.4 + * and previous. Zeros may be added to the end of the String. For + * example, an unscaled value of 1234 and a scale of -3 would result in + * the String 1234000, but the toString() method would return + * 1.234E+6. + * @return a String representation of this BigDecimal + * @since 1.5 + */ + public String toPlainString() + { + // If the scale is zero we simply return the String representation of the + // unscaled value. + String bigStr = intVal.toString(); + if (scale == 0) + return bigStr; + + // Remember if we have to put a negative sign at the start. + boolean negative = (bigStr.charAt(0) == '-'); + + int point = bigStr.length() - scale - (negative ? 1 : 0); + + CPStringBuilder sb = new CPStringBuilder(bigStr.length() + 2 + + (point <= 0 ? (-point + 1) : 0)); + if (point <= 0) + { + // We have to prepend zeros and a decimal point. + if (negative) + sb.append('-'); + sb.append('0').append('.'); + while (point < 0) + { + sb.append('0'); + point++; + } + sb.append(bigStr.substring(negative ? 1 : 0)); + } + else if (point < bigStr.length()) + { + // No zeros need to be prepended or appended, just put the decimal + // in the right place. + sb.append(bigStr); + sb.insert(point + (negative ? 1 : 0), '.'); + } + else + { + // We must append zeros instead of using scientific notation. + sb.append(bigStr); + for (int i = bigStr.length(); i < point; i++) + sb.append('0'); + } + return sb.toString(); + } + + /** + * Converts this BigDecimal to a BigInteger. Any fractional part will + * be discarded. + * @return a BigDecimal whose value is equal to floor[this] + */ + public BigInteger toBigInteger () + { + // If scale > 0 then we must divide, if scale > 0 then we must multiply, + // and if scale is zero then we just return intVal; + if (scale > 0) + return intVal.divide (BigInteger.TEN.pow (scale)); + else if (scale < 0) + return intVal.multiply(BigInteger.TEN.pow(-scale)); + return intVal; + } + + /** + * Converts this BigDecimal into a BigInteger, throwing an + * ArithmeticException if the conversion is not exact. + * @return a BigInteger whose value is equal to the value of this BigDecimal + * @since 1.5 + */ + public BigInteger toBigIntegerExact() + { + if (scale > 0) + { + // If we have to divide, we must check if the result is exact. + BigInteger[] result = + intVal.divideAndRemainder(BigInteger.TEN.pow(scale)); + if (result[1].equals(BigInteger.ZERO)) + return result[0]; + throw new ArithmeticException("No exact BigInteger representation"); + } + else if (scale < 0) + // If we're multiplying instead, then we needn't check for exactness. + return intVal.multiply(BigInteger.TEN.pow(-scale)); + // If the scale is zero we can simply return intVal. + return intVal; + } + + public int intValue () + { + return toBigInteger ().intValue (); + } + + /** + * Returns a BigDecimal which is numerically equal to this BigDecimal but + * with no trailing zeros in the representation. For example, if this + * BigDecimal has [unscaledValue, scale] = [6313000, 4] this method returns + * a BigDecimal with [unscaledValue, scale] = [6313, 1]. As another + * example, [12400, -2] would become [124, -4]. + * @return a numerically equal BigDecimal with no trailing zeros + */ + public BigDecimal stripTrailingZeros() + { + String intValStr = intVal.toString(); + int newScale = scale; + int pointer = intValStr.length() - 1; + // This loop adjusts pointer which will be used to give us the substring + // of intValStr to use in our new BigDecimal, and also accordingly + // adjusts the scale of our new BigDecimal. + while (intValStr.charAt(pointer) == '0') + { + pointer --; + newScale --; + } + // Create a new BigDecimal with the appropriate substring and then + // set its scale. + BigDecimal result = new BigDecimal(intValStr.substring(0, pointer + 1)); + result.scale = newScale; + return result; + } + + public long longValue () + { + return toBigInteger().longValue(); + } + + public float floatValue() + { + return Float.valueOf(toString()).floatValue(); + } + + public double doubleValue() + { + return Double.valueOf(toString()).doubleValue(); + } + + public BigDecimal setScale (int scale) throws ArithmeticException + { + return setScale (scale, ROUND_UNNECESSARY); + } + + public BigDecimal setScale (int scale, int roundingMode) + throws ArithmeticException, IllegalArgumentException + { + // NOTE: The 1.5 JRE doesn't throw this, ones prior to it do and + // the spec says it should. Nevertheless, if 1.6 doesn't fix this + // we should consider removing it. + if( scale < 0 ) throw new ArithmeticException("Scale parameter < 0."); + return divide (ONE, scale, roundingMode); + } + + /** + * Returns a BigDecimal whose value is the same as this BigDecimal but whose + * representation has a scale of newScale. If the scale is + * reduced then rounding may occur, according to the RoundingMode. + * @param newScale + * @param roundingMode + * @return a BigDecimal whose scale is as given, whose value is + * this with possible rounding + * @throws ArithmeticException if the rounding mode is UNNECESSARY but + * rounding is required + * @since 1.5 + */ + public BigDecimal setScale(int newScale, RoundingMode roundingMode) + { + return setScale(newScale, roundingMode.ordinal()); + } + + /** + * Returns a new BigDecimal constructed from the BigDecimal(String) + * constructor using the Double.toString(double) method to obtain + * the String. + * @param val the double value used in Double.toString(double) + * @return a BigDecimal representation of val + * @throws NumberFormatException if val is NaN or infinite + * @since 1.5 + */ + public static BigDecimal valueOf(double val) + { + if (Double.isInfinite(val) || Double.isNaN(val)) + throw new NumberFormatException("argument cannot be NaN or infinite."); + return new BigDecimal(Double.toString(val)); + } + + /** + * Returns a BigDecimal whose numerical value is the numerical value + * of this BigDecimal multiplied by 10 to the power of n. + * @param n the power of ten + * @return the new BigDecimal + * @since 1.5 + */ + public BigDecimal scaleByPowerOfTen(int n) + { + BigDecimal result = new BigDecimal(intVal, scale - n); + result.precision = precision; + return result; + } + + /** + * Returns a BigDecimal whose value is this to the power of + * n. + * @param n the power + * @return the new BigDecimal + * @since 1.5 + */ + public BigDecimal pow(int n) + { + if (n < 0 || n > 999999999) + throw new ArithmeticException("n must be between 0 and 999999999"); + BigDecimal result = new BigDecimal(intVal.pow(n), scale * n); + return result; + } + + /** + * Returns a BigDecimal whose value is determined by first calling pow(n) + * and then by rounding according to the MathContext mc. + * @param n the power + * @param mc the MathContext + * @return the new BigDecimal + * @throws ArithmeticException if n < 0 or n > 999999999 or if the result is + * inexact but the rounding is RoundingMode.UNNECESSARY + * @since 1.5 + */ + public BigDecimal pow(int n, MathContext mc) + { + // FIXME: The specs claim to use the X3.274-1996 algorithm. We + // currently do not. + return pow(n).round(mc); + } + + /** + * Returns a BigDecimal whose value is the absolute value of this BigDecimal + * with rounding according to the given MathContext. + * @param mc the MathContext + * @return the new BigDecimal + */ + public BigDecimal abs(MathContext mc) + { + BigDecimal result = abs(); + result = result.round(mc); + return result; + } + + /** + * Returns the size of a unit in the last place of this BigDecimal. This + * returns a BigDecimal with [unscaledValue, scale] = [1, this.scale()]. + * @return the size of a unit in the last place of this. + * @since 1.5 + */ + public BigDecimal ulp() + { + return new BigDecimal(BigInteger.ONE, scale); + } + + /** + * Converts this BigDecimal to a long value. + * @return the long value + * @throws ArithmeticException if rounding occurs or if overflow occurs + * @since 1.5 + */ + public long longValueExact() + { + // Set scale will throw an exception if rounding occurs. + BigDecimal temp = setScale(0, ROUND_UNNECESSARY); + BigInteger tempVal = temp.intVal; + // Check for overflow. + long result = intVal.longValue(); + if (tempVal.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 1 + || (result < 0 && signum() == 1) || (result > 0 && signum() == -1)) + throw new ArithmeticException("this BigDecimal is too " + + "large to fit into the return type"); + + return intVal.longValue(); + } + + /** + * Converts this BigDecimal into an int by first calling longValueExact + * and then checking that the long returned from that + * method fits into an int. + * @return an int whose value is this + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into an int. + * @since 1.5 + */ + public int intValueExact() + { + long temp = longValueExact(); + int result = (int)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into an int"); + return result; + } + + /** + * Converts this BigDecimal into a byte by first calling longValueExact + * and then checking that the long returned from that + * method fits into a byte. + * @return a byte whose value is this + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into a byte. + * @since 1.5 + */ + public byte byteValueExact() + { + long temp = longValueExact(); + byte result = (byte)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into a byte"); + return result; + } + + /** + * Converts this BigDecimal into a short by first calling longValueExact + * and then checking that the long returned from that + * method fits into a short. + * @return a short whose value is this + * @throws ArithmeticException if this BigDecimal has a fractional part + * or is too large to fit into a short. + * @since 1.5 + */ + public short shortValueExact() + { + long temp = longValueExact(); + short result = (short)temp; + if (result != temp) + throw new ArithmeticException ("this BigDecimal cannot fit into a short"); + return result; + } +} diff --git a/libjava/classpath/java/math/BigInteger.java b/libjava/classpath/java/math/BigInteger.java new file mode 100644 index 000000000..953e557a8 --- /dev/null +++ b/libjava/classpath/java/math/BigInteger.java @@ -0,0 +1,2676 @@ +/* java.math.BigInteger -- Arbitary precision integers + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.math; + +import gnu.classpath.Configuration; + +import gnu.java.lang.CPStringBuilder; +import gnu.java.math.GMP; +import gnu.java.math.MPN; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Random; +import java.util.logging.Logger; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998) and + * "Applied Cryptography, Second Edition" by Bruce Schneier (Wiley, 1996). + * + * Based primarily on IntNum.java BitOps.java by Per Bothner (per@bothner.com) + * (found in Kawa 1.6.62). + * + * @author Warren Levy (warrenl@cygnus.com) + * @date December 20, 1999. + * @status believed complete and correct. + */ +public class BigInteger extends Number implements Comparable +{ + private static final Logger log = Logger.getLogger(BigInteger.class.getName()); + + /** All integers are stored in 2's-complement form. + * If words == null, the ival is the value of this BigInteger. + * Otherwise, the first ival elements of words make the value + * of this BigInteger, stored in little-endian order, 2's-complement form. */ + private transient int ival; + private transient int[] words; + + // Serialization fields. + // the first three, although not used in the code, are present for + // compatibility with older RI versions of this class. DO NOT REMOVE. + private int bitCount = -1; + private int bitLength = -1; + private int lowestSetBit = -2; + private byte[] magnitude; + private int signum; + private static final long serialVersionUID = -8287574255936472291L; + + + /** We pre-allocate integers in the range minFixNum..maxFixNum. + * Note that we must at least preallocate 0, 1, and 10. */ + private static final int minFixNum = -100; + private static final int maxFixNum = 1024; + private static final int numFixNum = maxFixNum-minFixNum+1; + private static final BigInteger[] smallFixNums; + + /** The alter-ego GMP instance for this. */ + private transient GMP mpz; + + private static final boolean USING_NATIVE = Configuration.WANT_NATIVE_BIG_INTEGER + && initializeLibrary(); + + static + { + if (USING_NATIVE) + { + smallFixNums = null; + ZERO = valueOf(0L); + ONE = valueOf(1L); + TEN = valueOf(10L); + } + else + { + smallFixNums = new BigInteger[numFixNum]; + for (int i = numFixNum; --i >= 0; ) + smallFixNums[i] = new BigInteger(i + minFixNum); + + ZERO = smallFixNums[-minFixNum]; + ONE = smallFixNums[1 - minFixNum]; + TEN = smallFixNums[10 - minFixNum]; + } + } + + /** + * The constant zero as a BigInteger. + * @since 1.2 + */ + public static final BigInteger ZERO; + + /** + * The constant one as a BigInteger. + * @since 1.2 + */ + public static final BigInteger ONE; + + /** + * The constant ten as a BigInteger. + * @since 1.5 + */ + public static final BigInteger TEN; + + /* Rounding modes: */ + private static final int FLOOR = 1; + private static final int CEILING = 2; + private static final int TRUNCATE = 3; + private static final int ROUND = 4; + + /** When checking the probability of primes, it is most efficient to + * first check the factoring of small primes, so we'll use this array. + */ + private static final int[] primes = + { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, + 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, + 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, + 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251 }; + + /** HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Table 4.4. */ + private static final int[] k = + {100,150,200,250,300,350,400,500,600,800,1250, Integer.MAX_VALUE}; + private static final int[] t = + { 27, 18, 15, 12, 9, 8, 7, 6, 5, 4, 3, 2}; + + private BigInteger() + { + super(); + + if (USING_NATIVE) + mpz = new GMP(); + } + + /* Create a new (non-shared) BigInteger, and initialize to an int. */ + private BigInteger(int value) + { + super(); + + ival = value; + } + + public BigInteger(String s, int radix) + { + this(); + + int len = s.length(); + 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; + for ( ; i < len; i++) + { + ch = s.charAt(i); + digit = Character.digit(ch, radix); + if (digit < 0) + throw new NumberFormatException("Invalid character at position #" + i); + bytes[byte_len++] = (byte) digit; + } + + if (USING_NATIVE) + { + bytes = null; + if (mpz.fromString(s, radix) != 0) + throw new NumberFormatException("String \"" + s + + "\" is NOT a valid number in base " + + radix); + } + else + { + BigInteger result; + // Testing (len < MPN.chars_per_word(radix)) would be more accurate, + // but slightly more expensive, for little practical gain. + if (len <= 15 && radix <= 16) + result = valueOf(Long.parseLong(s, radix)); + else + result = valueOf(bytes, byte_len, negative, radix); + + this.ival = result.ival; + this.words = result.words; + } + } + + public BigInteger(String val) + { + this(val, 10); + } + + /* Create a new (non-shared) BigInteger, and initialize from a byte array. */ + public BigInteger(byte[] val) + { + this(); + + if (val == null || val.length < 1) + throw new NumberFormatException(); + + if (USING_NATIVE) + mpz.fromByteArray(val); + else + { + words = byteArrayToIntArray(val, val[0] < 0 ? -1 : 0); + BigInteger result = make(words, words.length); + this.ival = result.ival; + this.words = result.words; + } + } + + public BigInteger(int signum, byte[] magnitude) + { + this(); + + if (magnitude == null || signum > 1 || signum < -1) + throw new NumberFormatException(); + + if (signum == 0) + { + int i; + for (i = magnitude.length - 1; i >= 0 && magnitude[i] == 0; --i) + ; + if (i >= 0) + throw new NumberFormatException(); + return; + } + + if (USING_NATIVE) + mpz.fromSignedMagnitude(magnitude, signum == -1); + else + { + // Magnitude is always positive, so don't ever pass a sign of -1. + words = byteArrayToIntArray(magnitude, 0); + BigInteger result = make(words, words.length); + this.ival = result.ival; + this.words = result.words; + + if (signum < 0) + setNegative(); + } + } + + public BigInteger(int numBits, Random rnd) + { + this(); + + if (numBits < 0) + throw new IllegalArgumentException(); + + init(numBits, rnd); + } + + private void init(int numBits, Random rnd) + { + if (USING_NATIVE) + { + int length = (numBits + 7) / 8; + byte[] magnitude = new byte[length]; + rnd.nextBytes(magnitude); + int discardedBitCount = numBits % 8; + if (discardedBitCount != 0) + { + discardedBitCount = 8 - discardedBitCount; + magnitude[0] = (byte)((magnitude[0] & 0xFF) >>> discardedBitCount); + } + mpz.fromSignedMagnitude(magnitude, false); + magnitude = null; + return; + } + + 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) + { + 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) + { + highbits = rnd.nextInt(); + --nwords; + } + if (nwords == 0 && highbits >= 0) + { + ival = highbits; + } + else + { + ival = highbits < 0 ? nwords + 2 : nwords + 1; + words = new int[ival]; + words[nwords] = highbits; + while (--nwords >= 0) + words[nwords] = rnd.nextInt(); + } + } + + public BigInteger(int bitLength, int certainty, Random rnd) + { + this(); + + BigInteger result = new BigInteger(); + while (true) + { + result.init(bitLength, rnd); + result = result.setBit(bitLength - 1); + if (result.isProbablePrime(certainty)) + break; + } + + if (USING_NATIVE) + mpz.fromBI(result.mpz); + else + { + this.ival = result.ival; + this.words = result.words; + } + } + + /** + * Return a BigInteger that is bitLength bits long with a + * probability < 2^-100 of being composite. + * + * @param bitLength length in bits of resulting number + * @param rnd random number generator to use + * @throws ArithmeticException if bitLength < 2 + * @since 1.4 + */ + public static BigInteger probablePrime(int bitLength, Random rnd) + { + if (bitLength < 2) + throw new ArithmeticException(); + + return new BigInteger(bitLength, 100, rnd); + } + + /** Return a (possibly-shared) BigInteger with a given long value. */ + public static BigInteger valueOf(long val) + { + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + result.mpz.fromLong(val); + return result; + } + + if (val >= minFixNum && val <= maxFixNum) + return smallFixNums[(int) val - minFixNum]; + int i = (int) val; + if ((long) i == val) + return new BigInteger(i); + BigInteger result = alloc(2); + result.ival = 2; + result.words[0] = i; + result.words[1] = (int)(val >> 32); + return result; + } + + /** + * @return true if the GMP-based native implementation library + * was successfully loaded. Returns false otherwise. + */ + private static boolean initializeLibrary() + { + boolean result; + try + { + System.loadLibrary("javamath"); + GMP.natInitializeLibrary(); + result = true; + } + catch (Throwable x) + { + result = false; + if (Configuration.DEBUG) + { + log.info("Unable to use native BigInteger: " + x); + log.info("Will use a pure Java implementation instead"); + } + } + return result; + } + + /** Make a canonicalized BigInteger from an array of words. + * The array may be reused (without copying). */ + private static BigInteger make(int[] words, int len) + { + if (words == null) + return valueOf(len); + len = BigInteger.wordsNeeded(words, len); + if (len <= 1) + return len == 0 ? ZERO : valueOf(words[0]); + BigInteger num = new BigInteger(); + num.words = words; + num.ival = len; + return num; + } + + /** Convert a big-endian byte array to a little-endian array of words. */ + private static int[] byteArrayToIntArray(byte[] bytes, int sign) + { + // Determine number of words needed. + int[] words = new int[bytes.length/4 + 1]; + int nwords = words.length; + + // Create a int out of modulo 4 high order bytes. + int bptr = 0; + int word = sign; + for (int i = bytes.length % 4; i > 0; --i, bptr++) + word = (word << 8) | (bytes[bptr] & 0xff); + words[--nwords] = word; + + // Elements remaining in byte[] are a multiple of 4. + while (nwords > 0) + words[--nwords] = bytes[bptr++] << 24 | + (bytes[bptr++] & 0xff) << 16 | + (bytes[bptr++] & 0xff) << 8 | + (bytes[bptr++] & 0xff); + return words; + } + + /** Allocate a new non-shared BigInteger. + * @param nwords number of words to allocate + */ + private static BigInteger alloc(int nwords) + { + BigInteger result = new BigInteger(); + if (nwords > 1) + result.words = new int[nwords]; + return result; + } + + /** Change words.length to nwords. + * We allow words.length to be upto nwords+2 without reallocating. + */ + private void realloc(int nwords) + { + if (nwords == 0) + { + if (words != null) + { + if (ival > 0) + ival = words[0]; + words = null; + } + } + else if (words == null + || words.length < nwords + || words.length > nwords + 2) + { + int[] new_words = new int [nwords]; + if (words == null) + { + new_words[0] = ival; + ival = 1; + } + else + { + if (nwords < ival) + ival = nwords; + System.arraycopy(words, 0, new_words, 0, ival); + } + words = new_words; + } + } + + private boolean isNegative() + { + return (words == null ? ival : words[ival - 1]) < 0; + } + + public int signum() + { + if (USING_NATIVE) + return mpz.compare(ZERO.mpz); + + if (ival == 0 && words == null) + return 0; + int top = words == null ? ival : words[ival-1]; + return top < 0 ? -1 : 1; + } + + private static int compareTo(BigInteger x, BigInteger y) + { + if (USING_NATIVE) + { + int dummy = y.signum; // force NPE check + return x.mpz.compare(y.mpz); + } + + if (x.words == null && y.words == null) + return x.ival < y.ival ? -1 : x.ival > y.ival ? 1 : 0; + boolean x_negative = x.isNegative(); + boolean y_negative = y.isNegative(); + if (x_negative != y_negative) + return x_negative ? -1 : 1; + int x_len = x.words == null ? 1 : x.ival; + int y_len = y.words == null ? 1 : y.ival; + if (x_len != y_len) + return (x_len > y_len) != x_negative ? 1 : -1; + return MPN.cmp(x.words, y.words, x_len); + } + + /** @since 1.2 */ + public int compareTo(BigInteger val) + { + return compareTo(this, val); + } + + public BigInteger min(BigInteger val) + { + return compareTo(this, val) < 0 ? this : val; + } + + public BigInteger max(BigInteger val) + { + return compareTo(this, val) > 0 ? this : val; + } + + private boolean isZero() + { + return words == null && ival == 0; + } + + private boolean isOne() + { + return words == null && ival == 1; + } + + /** Calculate how many words are significant in words[0:len-1]. + * Returns the least value x such that x>0 && words[0:x-1]==words[0:len-1], + * when words is viewed as a 2's complement integer. + */ + private static int wordsNeeded(int[] words, int len) + { + int i = len; + if (i > 0) + { + int word = words[--i]; + if (word == -1) + { + while (i > 0 && (word = words[i - 1]) < 0) + { + i--; + if (word != -1) break; + } + } + else + { + while (word == 0 && i > 0 && (word = words[i - 1]) >= 0) i--; + } + } + return i + 1; + } + + private BigInteger canonicalize() + { + if (words != null + && (ival = BigInteger.wordsNeeded(words, ival)) <= 1) + { + if (ival == 1) + ival = words[0]; + words = null; + } + if (words == null && ival >= minFixNum && ival <= maxFixNum) + return smallFixNums[ival - minFixNum]; + return this; + } + + /** Add two ints, yielding a BigInteger. */ + private static BigInteger add(int x, int y) + { + return valueOf((long) x + (long) y); + } + + /** Add a BigInteger and an int, yielding a new BigInteger. */ + private static BigInteger add(BigInteger x, int y) + { + if (x.words == null) + return BigInteger.add(x.ival, y); + BigInteger result = new BigInteger(0); + result.setAdd(x, y); + return result.canonicalize(); + } + + /** Set this to the sum of x and y. + * OK if x==this. */ + private void setAdd(BigInteger x, int y) + { + if (x.words == null) + { + set((long) x.ival + (long) y); + return; + } + int len = x.ival; + realloc(len + 1); + long carry = y; + for (int i = 0; i < len; i++) + { + carry += ((long) x.words[i] & 0xffffffffL); + words[i] = (int) carry; + carry >>= 32; + } + if (x.words[len - 1] < 0) + carry--; + words[len] = (int) carry; + ival = wordsNeeded(words, len + 1); + } + + /** Destructively add an int to this. */ + private void setAdd(int y) + { + setAdd(this, y); + } + + /** Destructively set the value of this to a long. */ + private void set(long y) + { + int i = (int) y; + if ((long) i == y) + { + ival = i; + words = null; + } + else + { + realloc(2); + words[0] = i; + words[1] = (int) (y >> 32); + ival = 2; + } + } + + /** Destructively set the value of this to the given words. + * The words array is reused, not copied. */ + private void set(int[] words, int length) + { + this.ival = length; + this.words = words; + } + + /** Destructively set the value of this to that of y. */ + private void set(BigInteger y) + { + if (y.words == null) + set(y.ival); + else if (this != y) + { + realloc(y.ival); + System.arraycopy(y.words, 0, words, 0, y.ival); + ival = y.ival; + } + } + + /** Add two BigIntegers, yielding their sum as another BigInteger. */ + private static BigInteger add(BigInteger x, BigInteger y, int k) + { + if (x.words == null && y.words == null) + return valueOf((long) k * (long) y.ival + (long) x.ival); + if (k != 1) + { + if (k == -1) + y = BigInteger.neg(y); + else + y = BigInteger.times(y, valueOf(k)); + } + if (x.words == null) + return BigInteger.add(y, x.ival); + if (y.words == null) + return BigInteger.add(x, y.ival); + // Both are big + if (y.ival > x.ival) + { // Swap so x is longer then y. + BigInteger tmp = x; x = y; y = tmp; + } + BigInteger result = alloc(x.ival + 1); + int i = y.ival; + long carry = MPN.add_n(result.words, x.words, y.words, i); + long y_ext = y.words[i - 1] < 0 ? 0xffffffffL : 0; + for (; i < x.ival; i++) + { + carry += ((long) x.words[i] & 0xffffffffL) + y_ext; + result.words[i] = (int) carry; + carry >>>= 32; + } + if (x.words[i - 1] < 0) + y_ext--; + result.words[i] = (int) (carry + y_ext); + result.ival = i+1; + return result.canonicalize(); + } + + public BigInteger add(BigInteger val) + { + if (USING_NATIVE) + { + int dummy = val.signum; // force NPE check + BigInteger result = new BigInteger(); + mpz.add(val.mpz, result.mpz); + return result; + } + + return add(this, val, 1); + } + + public BigInteger subtract(BigInteger val) + { + if (USING_NATIVE) + { + int dummy = val.signum; // force NPE check + BigInteger result = new BigInteger(); + mpz.subtract(val.mpz, result.mpz); + return result; + } + + return add(this, val, -1); + } + + private static BigInteger times(BigInteger x, int y) + { + if (y == 0) + return ZERO; + if (y == 1) + return x; + int[] xwords = x.words; + int xlen = x.ival; + if (xwords == null) + return valueOf((long) xlen * (long) y); + boolean negative; + BigInteger result = BigInteger.alloc(xlen + 1); + if (xwords[xlen - 1] < 0) + { + negative = true; + negate(result.words, xwords, xlen); + xwords = result.words; + } + else + negative = false; + if (y < 0) + { + negative = !negative; + y = -y; + } + result.words[xlen] = MPN.mul_1(result.words, xwords, xlen, y); + result.ival = xlen + 1; + if (negative) + result.setNegative(); + return result.canonicalize(); + } + + private static BigInteger times(BigInteger x, BigInteger y) + { + if (y.words == null) + return times(x, y.ival); + if (x.words == null) + return times(y, x.ival); + boolean negative = false; + int[] xwords; + int[] ywords; + int xlen = x.ival; + int ylen = y.ival; + if (x.isNegative()) + { + negative = true; + xwords = new int[xlen]; + negate(xwords, x.words, xlen); + } + else + { + negative = false; + xwords = x.words; + } + if (y.isNegative()) + { + negative = !negative; + ywords = new int[ylen]; + negate(ywords, y.words, ylen); + } + else + ywords = y.words; + // Swap if x is shorter then y. + if (xlen < ylen) + { + int[] twords = xwords; xwords = ywords; ywords = twords; + int tlen = xlen; xlen = ylen; ylen = tlen; + } + BigInteger result = BigInteger.alloc(xlen+ylen); + MPN.mul(result.words, xwords, xlen, ywords, ylen); + result.ival = xlen+ylen; + if (negative) + result.setNegative(); + return result.canonicalize(); + } + + public BigInteger multiply(BigInteger y) + { + if (USING_NATIVE) + { + int dummy = y.signum; // force NPE check + BigInteger result = new BigInteger(); + mpz.multiply(y.mpz, result.mpz); + return result; + } + + return times(this, y); + } + + private static void divide(long x, long y, + BigInteger quotient, BigInteger remainder, + int rounding_mode) + { + boolean xNegative, yNegative; + if (x < 0) + { + xNegative = true; + if (x == Long.MIN_VALUE) + { + divide(valueOf(x), valueOf(y), + quotient, remainder, rounding_mode); + return; + } + x = -x; + } + else + xNegative = false; + + if (y < 0) + { + yNegative = true; + if (y == Long.MIN_VALUE) + { + if (rounding_mode == TRUNCATE) + { // x != Long.Min_VALUE implies abs(x) < abs(y) + if (quotient != null) + quotient.set(0); + if (remainder != null) + remainder.set(x); + } + else + divide(valueOf(x), valueOf(y), + quotient, remainder, rounding_mode); + return; + } + y = -y; + } + else + yNegative = false; + + long q = x / y; + long r = x % y; + boolean qNegative = xNegative ^ yNegative; + + boolean add_one = false; + if (r != 0) + { + switch (rounding_mode) + { + case TRUNCATE: + break; + case CEILING: + case FLOOR: + if (qNegative == (rounding_mode == FLOOR)) + add_one = true; + break; + case ROUND: + add_one = r > ((y - (q & 1)) >> 1); + break; + } + } + if (quotient != null) + { + if (add_one) + q++; + if (qNegative) + q = -q; + quotient.set(q); + } + if (remainder != null) + { + // The remainder is by definition: X-Q*Y + if (add_one) + { + // Subtract the remainder from Y. + r = y - r; + // In this case, abs(Q*Y) > abs(X). + // So sign(remainder) = -sign(X). + xNegative = ! xNegative; + } + else + { + // If !add_one, then: abs(Q*Y) <= abs(X). + // So sign(remainder) = sign(X). + } + if (xNegative) + r = -r; + remainder.set(r); + } + } + + /** Divide two integers, yielding quotient and remainder. + * @param x the numerator in the division + * @param y the denominator in the division + * @param quotient is set to the quotient of the result (iff quotient!=null) + * @param remainder is set to the remainder of the result + * (iff remainder!=null) + * @param rounding_mode one of FLOOR, CEILING, TRUNCATE, or ROUND. + */ + private static void divide(BigInteger x, BigInteger y, + BigInteger quotient, BigInteger remainder, + int rounding_mode) + { + if ((x.words == null || x.ival <= 2) + && (y.words == null || y.ival <= 2)) + { + long x_l = x.longValue(); + long y_l = y.longValue(); + if (x_l != Long.MIN_VALUE && y_l != Long.MIN_VALUE) + { + divide(x_l, y_l, quotient, remainder, rounding_mode); + return; + } + } + + boolean xNegative = x.isNegative(); + boolean yNegative = y.isNegative(); + boolean qNegative = xNegative ^ yNegative; + + int ylen = y.words == null ? 1 : y.ival; + int[] ywords = new int[ylen]; + y.getAbsolute(ywords); + while (ylen > 1 && ywords[ylen - 1] == 0) ylen--; + + int xlen = x.words == null ? 1 : x.ival; + int[] xwords = new int[xlen+2]; + x.getAbsolute(xwords); + while (xlen > 1 && xwords[xlen-1] == 0) xlen--; + + int qlen, rlen; + + int cmpval = MPN.cmp(xwords, xlen, ywords, ylen); + if (cmpval < 0) // abs(x) < abs(y) + { // quotient = 0; remainder = num. + int[] rwords = xwords; xwords = ywords; ywords = rwords; + rlen = xlen; qlen = 1; xwords[0] = 0; + } + else if (cmpval == 0) // abs(x) == abs(y) + { + xwords[0] = 1; qlen = 1; // quotient = 1 + ywords[0] = 0; rlen = 1; // remainder = 0; + } + else if (ylen == 1) + { + qlen = xlen; + // Need to leave room for a word of leading zeros if dividing by 1 + // and the dividend has the high bit set. It might be safe to + // increment qlen in all cases, but it certainly is only necessary + // in the following case. + if (ywords[0] == 1 && xwords[xlen-1] < 0) + qlen++; + rlen = 1; + ywords[0] = MPN.divmod_1(xwords, xwords, xlen, ywords[0]); + } + else // abs(x) > abs(y) + { + // Normalize the denominator, i.e. make its most significant bit set by + // shifting it normalization_steps bits to the left. Also shift the + // numerator the same number of steps (to keep the quotient the same!). + + int nshift = MPN.count_leading_zeros(ywords[ylen - 1]); + if (nshift != 0) + { + // Shift up the denominator setting the most significant bit of + // the most significant word. + MPN.lshift(ywords, 0, ywords, ylen, nshift); + + // Shift up the numerator, possibly introducing a new most + // significant word. + int x_high = MPN.lshift(xwords, 0, xwords, xlen, nshift); + xwords[xlen++] = x_high; + } + + if (xlen == ylen) + xwords[xlen++] = 0; + MPN.divide(xwords, xlen, ywords, ylen); + rlen = ylen; + MPN.rshift0 (ywords, xwords, 0, rlen, nshift); + + qlen = xlen + 1 - ylen; + if (quotient != null) + { + for (int i = 0; i < qlen; i++) + xwords[i] = xwords[i+ylen]; + } + } + + if (ywords[rlen-1] < 0) + { + ywords[rlen] = 0; + rlen++; + } + + // Now the quotient is in xwords, and the remainder is in ywords. + + boolean add_one = false; + if (rlen > 1 || ywords[0] != 0) + { // Non-zero remainder i.e. in-exact quotient. + switch (rounding_mode) + { + case TRUNCATE: + break; + case CEILING: + case FLOOR: + if (qNegative == (rounding_mode == FLOOR)) + add_one = true; + break; + case ROUND: + // int cmp = compareTo(remainder<<1, abs(y)); + BigInteger tmp = remainder == null ? new BigInteger() : remainder; + tmp.set(ywords, rlen); + tmp = shift(tmp, 1); + if (yNegative) + tmp.setNegative(); + int cmp = compareTo(tmp, y); + // Now cmp == compareTo(sign(y)*(remainder<<1), y) + if (yNegative) + cmp = -cmp; + add_one = (cmp == 1) || (cmp == 0 && (xwords[0]&1) != 0); + } + } + if (quotient != null) + { + quotient.set(xwords, qlen); + if (qNegative) + { + if (add_one) // -(quotient + 1) == ~(quotient) + quotient.setInvert(); + else + quotient.setNegative(); + } + else if (add_one) + quotient.setAdd(1); + } + if (remainder != null) + { + // The remainder is by definition: X-Q*Y + remainder.set(ywords, rlen); + if (add_one) + { + // Subtract the remainder from Y: + // abs(R) = abs(Y) - abs(orig_rem) = -(abs(orig_rem) - abs(Y)). + BigInteger tmp; + if (y.words == null) + { + tmp = remainder; + tmp.set(yNegative ? ywords[0] + y.ival : ywords[0] - y.ival); + } + else + tmp = BigInteger.add(remainder, y, yNegative ? 1 : -1); + // Now tmp <= 0. + // In this case, abs(Q) = 1 + floor(abs(X)/abs(Y)). + // Hence, abs(Q*Y) > abs(X). + // So sign(remainder) = -sign(X). + if (xNegative) + remainder.setNegative(tmp); + else + remainder.set(tmp); + } + else + { + // If !add_one, then: abs(Q*Y) <= abs(X). + // So sign(remainder) = sign(X). + if (xNegative) + remainder.setNegative(); + } + } + } + + public BigInteger divide(BigInteger val) + { + if (USING_NATIVE) + { + if (val.compareTo(ZERO) == 0) + throw new ArithmeticException("divisor is zero"); + + BigInteger result = new BigInteger(); + mpz.quotient(val.mpz, result.mpz); + return result; + } + + if (val.isZero()) + throw new ArithmeticException("divisor is zero"); + + BigInteger quot = new BigInteger(); + divide(this, val, quot, null, TRUNCATE); + return quot.canonicalize(); + } + + public BigInteger remainder(BigInteger val) + { + if (USING_NATIVE) + { + if (val.compareTo(ZERO) == 0) + throw new ArithmeticException("divisor is zero"); + + BigInteger result = new BigInteger(); + mpz.remainder(val.mpz, result.mpz); + return result; + } + + if (val.isZero()) + throw new ArithmeticException("divisor is zero"); + + BigInteger rem = new BigInteger(); + divide(this, val, null, rem, TRUNCATE); + return rem.canonicalize(); + } + + public BigInteger[] divideAndRemainder(BigInteger val) + { + if (USING_NATIVE) + { + if (val.compareTo(ZERO) == 0) + throw new ArithmeticException("divisor is zero"); + + BigInteger q = new BigInteger(); + BigInteger r = new BigInteger(); + mpz.quotientAndRemainder(val.mpz, q.mpz, r.mpz); + return new BigInteger[] { q, r }; + } + + if (val.isZero()) + throw new ArithmeticException("divisor is zero"); + + BigInteger[] result = new BigInteger[2]; + result[0] = new BigInteger(); + result[1] = new BigInteger(); + divide(this, val, result[0], result[1], TRUNCATE); + result[0].canonicalize(); + result[1].canonicalize(); + return result; + } + + public BigInteger mod(BigInteger m) + { + if (USING_NATIVE) + { + int dummy = m.signum; // force NPE check + if (m.compareTo(ZERO) < 1) + throw new ArithmeticException("non-positive modulus"); + + BigInteger result = new BigInteger(); + mpz.modulo(m.mpz, result.mpz); + return result; + } + + if (m.isNegative() || m.isZero()) + throw new ArithmeticException("non-positive modulus"); + + BigInteger rem = new BigInteger(); + divide(this, m, null, rem, FLOOR); + return rem.canonicalize(); + } + + /** Calculate the integral power of a BigInteger. + * @param exponent the exponent (must be non-negative) + */ + public BigInteger pow(int exponent) + { + if (exponent <= 0) + { + if (exponent == 0) + return ONE; + throw new ArithmeticException("negative exponent"); + } + + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + mpz.pow(exponent, result.mpz); + return result; + } + + if (isZero()) + return this; + int plen = words == null ? 1 : ival; // Length of pow2. + int blen = ((bitLength() * exponent) >> 5) + 2 * plen; + boolean negative = isNegative() && (exponent & 1) != 0; + int[] pow2 = new int [blen]; + int[] rwords = new int [blen]; + int[] work = new int [blen]; + getAbsolute(pow2); // pow2 = abs(this); + int rlen = 1; + rwords[0] = 1; // rwords = 1; + for (;;) // for (i = 0; ; i++) + { + // pow2 == this**(2**i) + // prod = this**(sum(j=0..i-1, (exponent>>j)&1)) + if ((exponent & 1) != 0) + { // r *= pow2 + MPN.mul(work, pow2, plen, rwords, rlen); + int[] temp = work; work = rwords; rwords = temp; + rlen += plen; + while (rwords[rlen - 1] == 0) rlen--; + } + exponent >>= 1; + if (exponent == 0) + break; + // pow2 *= pow2; + MPN.mul(work, pow2, plen, pow2, plen); + int[] temp = work; work = pow2; pow2 = temp; // swap to avoid a copy + plen *= 2; + while (pow2[plen - 1] == 0) plen--; + } + if (rwords[rlen - 1] < 0) + rlen++; + if (negative) + negate(rwords, rwords, rlen); + return BigInteger.make(rwords, rlen); + } + + private static int[] euclidInv(int a, int b, int prevDiv) + { + if (b == 0) + throw new ArithmeticException("not invertible"); + + if (b == 1) + // Success: values are indeed invertible! + // Bottom of the recursion reached; start unwinding. + return new int[] { -prevDiv, 1 }; + + int[] xy = euclidInv(b, a % b, a / b); // Recursion happens here. + a = xy[0]; // use our local copy of 'a' as a work var + xy[0] = a * -prevDiv + xy[1]; + xy[1] = a; + return xy; + } + + private static void euclidInv(BigInteger a, BigInteger b, + BigInteger prevDiv, BigInteger[] xy) + { + if (b.isZero()) + throw new ArithmeticException("not invertible"); + + if (b.isOne()) + { + // Success: values are indeed invertible! + // Bottom of the recursion reached; start unwinding. + xy[0] = neg(prevDiv); + xy[1] = ONE; + return; + } + + // Recursion happens in the following conditional! + + // If a just contains an int, then use integer math for the rest. + if (a.words == null) + { + int[] xyInt = euclidInv(b.ival, a.ival % b.ival, a.ival / b.ival); + xy[0] = new BigInteger(xyInt[0]); + xy[1] = new BigInteger(xyInt[1]); + } + else + { + BigInteger rem = new BigInteger(); + BigInteger quot = new BigInteger(); + divide(a, b, quot, rem, FLOOR); + // quot and rem may not be in canonical form. ensure + rem.canonicalize(); + quot.canonicalize(); + euclidInv(b, rem, quot, xy); + } + + BigInteger t = xy[0]; + xy[0] = add(xy[1], times(t, prevDiv), -1); + xy[1] = t; + } + + public BigInteger modInverse(BigInteger y) + { + if (USING_NATIVE) + { + int dummy = y.signum; // force NPE check + if (mpz.compare(ZERO.mpz) < 1) + throw new ArithmeticException("non-positive modulo"); + + BigInteger result = new BigInteger(); + mpz.modInverse(y.mpz, result.mpz); + return result; + } + + if (y.isNegative() || y.isZero()) + throw new ArithmeticException("non-positive modulo"); + + // Degenerate cases. + if (y.isOne()) + return ZERO; + if (isOne()) + return ONE; + + // Use Euclid's algorithm as in gcd() but do this recursively + // rather than in a loop so we can use the intermediate results as we + // unwind from the recursion. + // Used http://www.math.nmsu.edu/~crypto/EuclideanAlgo.html as reference. + BigInteger result = new BigInteger(); + boolean swapped = false; + + if (y.words == null) + { + // The result is guaranteed to be less than the modulus, y (which is + // an int), so simplify this by working with the int result of this + // modulo y. Also, if this is negative, make it positive via modulo + // math. Note that BigInteger.mod() must be used even if this is + // already an int as the % operator would provide a negative result if + // this is negative, BigInteger.mod() never returns negative values. + int xval = (words != null || isNegative()) ? mod(y).ival : ival; + int yval = y.ival; + + // Swap values so x > y. + if (yval > xval) + { + int tmp = xval; xval = yval; yval = tmp; + swapped = true; + } + // Normally, the result is in the 2nd element of the array, but + // if originally x < y, then x and y were swapped and the result + // is in the 1st element of the array. + result.ival = + euclidInv(yval, xval % yval, xval / yval)[swapped ? 0 : 1]; + + // Result can't be negative, so make it positive by adding the + // original modulus, y.ival (not the possibly "swapped" yval). + if (result.ival < 0) + result.ival += y.ival; + } + else + { + // As above, force this to be a positive value via modulo math. + BigInteger x = isNegative() ? this.mod(y) : this; + + // Swap values so x > y. + if (x.compareTo(y) < 0) + { + result = x; x = y; y = result; // use 'result' as a work var + swapped = true; + } + // As above (for ints), result will be in the 2nd element unless + // the original x and y were swapped. + BigInteger rem = new BigInteger(); + BigInteger quot = new BigInteger(); + divide(x, y, quot, rem, FLOOR); + // quot and rem may not be in canonical form. ensure + rem.canonicalize(); + quot.canonicalize(); + BigInteger[] xy = new BigInteger[2]; + euclidInv(y, rem, quot, xy); + result = swapped ? xy[0] : xy[1]; + + // Result can't be negative, so make it positive by adding the + // original modulus, y (which is now x if they were swapped). + if (result.isNegative()) + result = add(result, swapped ? x : y, 1); + } + + return result; + } + + public BigInteger modPow(BigInteger exponent, BigInteger m) + { + if (USING_NATIVE) + { + int dummy = exponent.signum; // force NPE check + if (m.mpz.compare(ZERO.mpz) < 1) + throw new ArithmeticException("non-positive modulo"); + + BigInteger result = new BigInteger(); + mpz.modPow(exponent.mpz, m.mpz, result.mpz); + return result; + } + + if (m.isNegative() || m.isZero()) + throw new ArithmeticException("non-positive modulo"); + + if (exponent.isNegative()) + return modInverse(m).modPow(exponent.negate(), m); + if (exponent.isOne()) + return mod(m); + + // To do this naively by first raising this to the power of exponent + // and then performing modulo m would be extremely expensive, especially + // for very large numbers. The solution is found in Number Theory + // where a combination of partial powers and moduli can be done easily. + // + // We'll use the algorithm for Additive Chaining which can be found on + // p. 244 of "Applied Cryptography, Second Edition" by Bruce Schneier. + BigInteger s = ONE; + BigInteger t = this; + BigInteger u = exponent; + + while (!u.isZero()) + { + if (u.and(ONE).isOne()) + s = times(s, t).mod(m); + u = u.shiftRight(1); + t = times(t, t).mod(m); + } + + return s; + } + + /** Calculate Greatest Common Divisor for non-negative ints. */ + private static int gcd(int a, int b) + { + // Euclid's algorithm, copied from libg++. + int tmp; + if (b > a) + { + tmp = a; a = b; b = tmp; + } + for(;;) + { + if (b == 0) + return a; + if (b == 1) + return b; + tmp = b; + b = a % b; + a = tmp; + } + } + + public BigInteger gcd(BigInteger y) + { + if (USING_NATIVE) + { + int dummy = y.signum; // force NPE check + BigInteger result = new BigInteger(); + mpz.gcd(y.mpz, result.mpz); + return result; + } + + int xval = ival; + int yval = y.ival; + if (words == null) + { + if (xval == 0) + return abs(y); + if (y.words == null + && xval != Integer.MIN_VALUE && yval != Integer.MIN_VALUE) + { + if (xval < 0) + xval = -xval; + if (yval < 0) + yval = -yval; + return valueOf(gcd(xval, yval)); + } + xval = 1; + } + if (y.words == null) + { + if (yval == 0) + return abs(this); + yval = 1; + } + int len = (xval > yval ? xval : yval) + 1; + int[] xwords = new int[len]; + int[] ywords = new int[len]; + getAbsolute(xwords); + y.getAbsolute(ywords); + len = MPN.gcd(xwords, ywords, len); + BigInteger result = new BigInteger(0); + result.ival = len; + result.words = xwords; + return result.canonicalize(); + } + + /** + *

      Returns true if this BigInteger is probably prime, + * false if it's definitely composite. If certainty + * is <= 0, true is returned.

      + * + * @param certainty a measure of the uncertainty that the caller is willing + * to tolerate: if the call returns true the probability that + * this BigInteger is prime exceeds (1 - 1/2certainty). + * The execution time of this method is proportional to the value of this + * parameter. + * @return true if this BigInteger is probably prime, + * false if it's definitely composite. + */ + public boolean isProbablePrime(int certainty) + { + if (certainty < 1) + return true; + + if (USING_NATIVE) + return mpz.testPrimality(certainty) != 0; + + /** We'll use the Rabin-Miller algorithm for doing a probabilistic + * primality test. It is fast, easy and has faster decreasing odds of a + * composite passing than with other tests. This means that this + * method will actually have a probability much greater than the + * 1 - .5^certainty specified in the JCL (p. 117), but I don't think + * anyone will complain about better performance with greater certainty. + * + * The Rabin-Miller algorithm can be found on pp. 259-261 of "Applied + * Cryptography, Second Edition" by Bruce Schneier. + */ + + // First rule out small prime factors + BigInteger rem = new BigInteger(); + int i; + for (i = 0; i < primes.length; i++) + { + if (words == null && ival == primes[i]) + return true; + + divide(this, smallFixNums[primes[i] - minFixNum], null, rem, TRUNCATE); + if (rem.canonicalize().isZero()) + return false; + } + + // Now perform the Rabin-Miller test. + + // Set b to the number of times 2 evenly divides (this - 1). + // I.e. 2^b is the largest power of 2 that divides (this - 1). + BigInteger pMinus1 = add(this, -1); + int b = pMinus1.getLowestSetBit(); + + // Set m such that this = 1 + 2^b * m. + BigInteger m = pMinus1.divide(valueOf(2L).pow(b)); + + // The HAC (Handbook of Applied Cryptography), Alfred Menezes & al. Note + // 4.49 (controlling the error probability) gives the number of trials + // for an error probability of 1/2**80, given the number of bits in the + // number to test. we shall use these numbers as is if/when 'certainty' + // is less or equal to 80, and twice as much if it's greater. + int bits = this.bitLength(); + for (i = 0; i < k.length; i++) + if (bits <= k[i]) + break; + int trials = t[i]; + if (certainty > 80) + trials *= 2; + BigInteger z; + for (int t = 0; t < trials; t++) + { + // The HAC (Handbook of Applied Cryptography), Alfred Menezes & al. + // Remark 4.28 states: "...A strategy that is sometimes employed + // is to fix the bases a to be the first few primes instead of + // choosing them at random. + z = smallFixNums[primes[t] - minFixNum].modPow(m, this); + if (z.isOne() || z.equals(pMinus1)) + continue; // Passes the test; may be prime. + + for (i = 0; i < b; ) + { + if (z.isOne()) + return false; + i++; + if (z.equals(pMinus1)) + break; // Passes the test; may be prime. + + z = z.modPow(valueOf(2), this); + } + + if (i == b && !z.equals(pMinus1)) + return false; + } + return true; + } + + private void setInvert() + { + if (words == null) + ival = ~ival; + else + { + for (int i = ival; --i >= 0; ) + words[i] = ~words[i]; + } + } + + private void setShiftLeft(BigInteger x, int count) + { + int[] xwords; + int xlen; + if (x.words == null) + { + if (count < 32) + { + set((long) x.ival << count); + return; + } + xwords = new int[1]; + xwords[0] = x.ival; + xlen = 1; + } + else + { + xwords = x.words; + xlen = x.ival; + } + int word_count = count >> 5; + count &= 31; + int new_len = xlen + word_count; + if (count == 0) + { + realloc(new_len); + for (int i = xlen; --i >= 0; ) + words[i+word_count] = xwords[i]; + } + else + { + new_len++; + realloc(new_len); + int shift_out = MPN.lshift(words, word_count, xwords, xlen, count); + count = 32 - count; + words[new_len-1] = (shift_out << count) >> count; // sign-extend. + } + ival = new_len; + for (int i = word_count; --i >= 0; ) + words[i] = 0; + } + + private void setShiftRight(BigInteger x, int count) + { + if (x.words == null) + set(count < 32 ? x.ival >> count : x.ival < 0 ? -1 : 0); + else if (count == 0) + set(x); + else + { + boolean neg = x.isNegative(); + int word_count = count >> 5; + count &= 31; + int d_len = x.ival - word_count; + if (d_len <= 0) + set(neg ? -1 : 0); + else + { + if (words == null || words.length < d_len) + realloc(d_len); + MPN.rshift0 (words, x.words, word_count, d_len, count); + ival = d_len; + if (neg) + words[d_len-1] |= -2 << (31 - count); + } + } + } + + private void setShift(BigInteger x, int count) + { + if (count > 0) + setShiftLeft(x, count); + else + setShiftRight(x, -count); + } + + private static BigInteger shift(BigInteger x, int count) + { + if (x.words == null) + { + if (count <= 0) + return valueOf(count > -32 ? x.ival >> (-count) : x.ival < 0 ? -1 : 0); + if (count < 32) + return valueOf((long) x.ival << count); + } + if (count == 0) + return x; + BigInteger result = new BigInteger(0); + result.setShift(x, count); + return result.canonicalize(); + } + + public BigInteger shiftLeft(int n) + { + if (n == 0) + return this; + + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + if (n < 0) + mpz.shiftRight(-n, result.mpz); + else + mpz.shiftLeft(n, result.mpz); + return result; + } + + return shift(this, n); + } + + public BigInteger shiftRight(int n) + { + if (n == 0) + return this; + + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + if (n < 0) + mpz.shiftLeft(-n, result.mpz); + else + mpz.shiftRight(n, result.mpz); + return result; + } + + return shift(this, -n); + } + + private void format(int radix, CPStringBuilder buffer) + { + if (words == null) + buffer.append(Integer.toString(ival, radix)); + else if (ival <= 2) + buffer.append(Long.toString(longValue(), radix)); + else + { + boolean neg = isNegative(); + int[] work; + if (neg || radix != 16) + { + work = new int[ival]; + getAbsolute(work); + } + else + work = words; + int len = ival; + + if (radix == 16) + { + if (neg) + buffer.append('-'); + int buf_start = buffer.length(); + for (int i = len; --i >= 0; ) + { + int word = work[i]; + for (int j = 8; --j >= 0; ) + { + int hex_digit = (word >> (4 * j)) & 0xF; + // Suppress leading zeros: + if (hex_digit > 0 || buffer.length() > buf_start) + buffer.append(Character.forDigit(hex_digit, 16)); + } + } + } + else + { + int i = buffer.length(); + for (;;) + { + int digit = MPN.divmod_1(work, work, len, radix); + buffer.append(Character.forDigit(digit, radix)); + while (len > 0 && work[len-1] == 0) len--; + if (len == 0) + break; + } + if (neg) + buffer.append('-'); + /* Reverse buffer. */ + int j = buffer.length() - 1; + while (i < j) + { + char tmp = buffer.charAt(i); + buffer.setCharAt(i, buffer.charAt(j)); + buffer.setCharAt(j, tmp); + i++; j--; + } + } + } + } + + public String toString() + { + return toString(10); + } + + public String toString(int radix) + { + if (USING_NATIVE) + return mpz.toString(radix); + + if (words == null) + return Integer.toString(ival, radix); + if (ival <= 2) + return Long.toString(longValue(), radix); + int buf_size = ival * (MPN.chars_per_word(radix) + 1); + CPStringBuilder buffer = new CPStringBuilder(buf_size); + format(radix, buffer); + return buffer.toString(); + } + + public int intValue() + { + if (USING_NATIVE) + { + int result = mpz.absIntValue(); + return mpz.compare(ZERO.mpz) < 0 ? - result : result; + } + + if (words == null) + return ival; + return words[0]; + } + + public long longValue() + { + if (USING_NATIVE) + { + long result; + result = (abs().shiftRight(32)).mpz.absIntValue(); + result <<= 32; + result |= mpz.absIntValue() & 0xFFFFFFFFL; + return this.compareTo(ZERO) < 0 ? - result : result; + } + + if (words == null) + return ival; + if (ival == 1) + return words[0]; + return ((long)words[1] << 32) + ((long)words[0] & 0xffffffffL); + } + + public int hashCode() + { + // FIXME: May not match hashcode of JDK. + if (USING_NATIVE) + { + // TODO: profile to decide whether to make it native + byte[] bytes = this.toByteArray(); + int result = 0; + for (int i = 0; i < bytes.length; i++) + result ^= (bytes[i] & 0xFF) << (8 * (i % 4)); + return result; + } + + return words == null ? ival : (words[0] + words[ival - 1]); + } + + /* Assumes x and y are both canonicalized. */ + private static boolean equals(BigInteger x, BigInteger y) + { + if (USING_NATIVE) + return x.mpz.compare(y.mpz) == 0; + + if (x.words == null && y.words == null) + return x.ival == y.ival; + if (x.words == null || y.words == null || x.ival != y.ival) + return false; + for (int i = x.ival; --i >= 0; ) + { + if (x.words[i] != y.words[i]) + return false; + } + return true; + } + + /* Assumes this and obj are both canonicalized. */ + public boolean equals(Object obj) + { + if (! (obj instanceof BigInteger)) + return false; + return equals(this, (BigInteger) obj); + } + + private static BigInteger valueOf(byte[] digits, int byte_len, + boolean negative, int radix) + { + int chars_per_word = MPN.chars_per_word(radix); + int[] words = new int[byte_len / chars_per_word + 1]; + int size = MPN.set_str(words, digits, byte_len, radix); + if (size == 0) + return ZERO; + if (words[size-1] < 0) + words[size++] = 0; + if (negative) + negate(words, words, size); + return make(words, size); + } + + public double doubleValue() + { + if (USING_NATIVE) + return mpz.doubleValue(); + + if (words == null) + return (double) ival; + if (ival <= 2) + return (double) longValue(); + if (isNegative()) + return neg(this).roundToDouble(0, true, false); + return roundToDouble(0, false, false); + } + + public float floatValue() + { + return (float) doubleValue(); + } + + /** Return true if any of the lowest n bits are one. + * (false if n is negative). */ + private boolean checkBits(int n) + { + if (n <= 0) + return false; + if (words == null) + return n > 31 || ((ival & ((1 << n) - 1)) != 0); + int i; + for (i = 0; i < (n >> 5) ; i++) + if (words[i] != 0) + return true; + return (n & 31) != 0 && (words[i] & ((1 << (n & 31)) - 1)) != 0; + } + + /** Convert a semi-processed BigInteger to double. + * Number must be non-negative. Multiplies by a power of two, applies sign, + * and converts to double, with the usual java rounding. + * @param exp power of two, positive or negative, by which to multiply + * @param neg true if negative + * @param remainder true if the BigInteger is the result of a truncating + * division that had non-zero remainder. To ensure proper rounding in + * this case, the BigInteger must have at least 54 bits. */ + private double roundToDouble(int exp, boolean neg, boolean remainder) + { + // Compute length. + int il = bitLength(); + + // Exponent when normalized to have decimal point directly after + // leading one. This is stored excess 1023 in the exponent bit field. + exp += il - 1; + + // Gross underflow. If exp == -1075, we let the rounding + // computation determine whether it is minval or 0 (which are just + // 0x0000 0000 0000 0001 and 0x0000 0000 0000 0000 as bit + // patterns). + if (exp < -1075) + return neg ? -0.0 : 0.0; + + // gross overflow + if (exp > 1023) + return neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + + // number of bits in mantissa, including the leading one. + // 53 unless it's denormalized + int ml = (exp >= -1022 ? 53 : 53 + exp + 1022); + + // Get top ml + 1 bits. The extra one is for rounding. + long m; + int excess_bits = il - (ml + 1); + if (excess_bits > 0) + m = ((words == null) ? ival >> excess_bits + : MPN.rshift_long(words, ival, excess_bits)); + else + m = longValue() << (- excess_bits); + + // Special rounding for maxval. If the number exceeds maxval by + // any amount, even if it's less than half a step, it overflows. + if (exp == 1023 && ((m >> 1) == (1L << 53) - 1)) + { + if (remainder || checkBits(il - ml)) + return neg ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + else + return neg ? - Double.MAX_VALUE : Double.MAX_VALUE; + } + + // Normal round-to-even rule: round up if the bit dropped is a one, and + // the bit above it or any of the bits below it is a one. + if ((m & 1) == 1 + && ((m & 2) == 2 || remainder || checkBits(excess_bits))) + { + m += 2; + // Check if we overflowed the mantissa + if ((m & (1L << 54)) != 0) + { + exp++; + // renormalize + m >>= 1; + } + // Check if a denormalized mantissa was just rounded up to a + // normalized one. + else if (ml == 52 && (m & (1L << 53)) != 0) + exp++; + } + + // Discard the rounding bit + m >>= 1; + + long bits_sign = neg ? (1L << 63) : 0; + exp += 1023; + long bits_exp = (exp <= 0) ? 0 : ((long)exp) << 52; + long bits_mant = m & ~(1L << 52); + return Double.longBitsToDouble(bits_sign | bits_exp | bits_mant); + } + + /** Copy the abolute value of this into an array of words. + * Assumes words.length >= (this.words == null ? 1 : this.ival). + * Result is zero-extended, but need not be a valid 2's complement number. + */ + private void getAbsolute(int[] words) + { + int len; + if (this.words == null) + { + len = 1; + words[0] = this.ival; + } + else + { + len = this.ival; + for (int i = len; --i >= 0; ) + words[i] = this.words[i]; + } + if (words[len - 1] < 0) + negate(words, words, len); + for (int i = words.length; --i > len; ) + words[i] = 0; + } + + /** Set dest[0:len-1] to the negation of src[0:len-1]. + * Return true if overflow (i.e. if src is -2**(32*len-1)). + * Ok for src==dest. */ + private static boolean negate(int[] dest, int[] src, int len) + { + long carry = 1; + boolean negative = src[len-1] < 0; + for (int i = 0; i < len; i++) + { + carry += ((long) (~src[i]) & 0xffffffffL); + dest[i] = (int) carry; + carry >>= 32; + } + return (negative && dest[len-1] < 0); + } + + /** Destructively set this to the negative of x. + * It is OK if x==this.*/ + private void setNegative(BigInteger x) + { + int len = x.ival; + if (x.words == null) + { + if (len == Integer.MIN_VALUE) + set(- (long) len); + else + set(-len); + return; + } + realloc(len + 1); + if (negate(words, x.words, len)) + words[len++] = 0; + ival = len; + } + + /** Destructively negate this. */ + private void setNegative() + { + setNegative(this); + } + + private static BigInteger abs(BigInteger x) + { + return x.isNegative() ? neg(x) : x; + } + + public BigInteger abs() + { + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + mpz.abs(result.mpz); + return result; + } + + return abs(this); + } + + private static BigInteger neg(BigInteger x) + { + if (x.words == null && x.ival != Integer.MIN_VALUE) + return valueOf(- x.ival); + BigInteger result = new BigInteger(0); + result.setNegative(x); + return result.canonicalize(); + } + + public BigInteger negate() + { + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + mpz.negate(result.mpz); + return result; + } + + return neg(this); + } + + /** Calculates ceiling(log2(this < 0 ? -this : this+1)) + * See Common Lisp: the Language, 2nd ed, p. 361. + */ + public int bitLength() + { + if (USING_NATIVE) + return mpz.bitLength(); + + if (words == null) + return MPN.intLength(ival); + return MPN.intLength(words, ival); + } + + public byte[] toByteArray() + { + if (signum() == 0) + return new byte[1]; + + if (USING_NATIVE) + { + // the minimal number of bytes required to represent the MPI is function + // of (a) its bit-length, and (b) its sign. only when this MPI is both + // positive, and its bit-length is a multiple of 8 do we add one zero + // bit for its sign. we do this so if we construct a new MPI from the + // resulting byte array, we wouldn't mistake a positive number, whose + // bit-length is a multiple of 8, for a similar-length negative one. + int bits = bitLength(); + if (bits % 8 == 0 || this.signum() == 1) + bits++; + byte[] bytes = new byte[(bits + 7) / 8]; + mpz.toByteArray(bytes); + return bytes; + } + + // Determine number of bytes needed. The method bitlength returns + // the size without the sign bit, so add one bit for that and then + // add 7 more to emulate the ceil function using integer math. + byte[] bytes = new byte[(bitLength() + 1 + 7) / 8]; + int nbytes = bytes.length; + + int wptr = 0; + int word; + + // Deal with words array until one word or less is left to process. + // If BigInteger is an int, then it is in ival and nbytes will be <= 4. + while (nbytes > 4) + { + word = words[wptr++]; + for (int i = 4; i > 0; --i, word >>= 8) + bytes[--nbytes] = (byte) word; + } + + // Deal with the last few bytes. If BigInteger is an int, use ival. + word = (words == null) ? ival : words[wptr]; + for ( ; nbytes > 0; word >>= 8) + bytes[--nbytes] = (byte) word; + + return bytes; + } + + /** Return the boolean opcode (for bitOp) for swapped operands. + * I.e. bitOp(swappedOp(op), x, y) == bitOp(op, y, x). + */ + private static int swappedOp(int op) + { + return + "\000\001\004\005\002\003\006\007\010\011\014\015\012\013\016\017" + .charAt(op); + } + + /** Do one the the 16 possible bit-wise operations of two BigIntegers. */ + private static BigInteger bitOp(int op, BigInteger x, BigInteger y) + { + switch (op) + { + case 0: return ZERO; + case 1: return x.and(y); + case 3: return x; + case 5: return y; + case 15: return valueOf(-1); + } + BigInteger result = new BigInteger(); + setBitOp(result, op, x, y); + return result.canonicalize(); + } + + /** Do one the the 16 possible bit-wise operations of two BigIntegers. */ + private static void setBitOp(BigInteger result, int op, + BigInteger x, BigInteger y) + { + if ((y.words != null) && (x.words == null || x.ival < y.ival)) + { + BigInteger temp = x; x = y; y = temp; + op = swappedOp(op); + } + int xi; + int yi; + int xlen, ylen; + if (y.words == null) + { + yi = y.ival; + ylen = 1; + } + else + { + yi = y.words[0]; + ylen = y.ival; + } + if (x.words == null) + { + xi = x.ival; + xlen = 1; + } + else + { + xi = x.words[0]; + xlen = x.ival; + } + if (xlen > 1) + result.realloc(xlen); + int[] w = result.words; + int i = 0; + // Code for how to handle the remainder of x. + // 0: Truncate to length of y. + // 1: Copy rest of x. + // 2: Invert rest of x. + int finish = 0; + int ni; + switch (op) + { + case 0: // clr + ni = 0; + break; + case 1: // and + for (;;) + { + ni = xi & yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi < 0) finish = 1; + break; + case 2: // andc2 + for (;;) + { + ni = xi & ~yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi >= 0) finish = 1; + break; + case 3: // copy x + ni = xi; + finish = 1; // Copy rest + break; + case 4: // andc1 + for (;;) + { + ni = ~xi & yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi < 0) finish = 2; + break; + case 5: // copy y + for (;;) + { + ni = yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + break; + case 6: // xor + for (;;) + { + ni = xi ^ yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + finish = yi < 0 ? 2 : 1; + break; + case 7: // ior + for (;;) + { + ni = xi | yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi >= 0) finish = 1; + break; + case 8: // nor + for (;;) + { + ni = ~(xi | yi); + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi >= 0) finish = 2; + break; + case 9: // eqv [exclusive nor] + for (;;) + { + ni = ~(xi ^ yi); + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + finish = yi >= 0 ? 2 : 1; + break; + case 10: // c2 + for (;;) + { + ni = ~yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + break; + case 11: // orc2 + for (;;) + { + ni = xi | ~yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi < 0) finish = 1; + break; + case 12: // c1 + ni = ~xi; + finish = 2; + break; + case 13: // orc1 + for (;;) + { + ni = ~xi | yi; + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi >= 0) finish = 2; + break; + case 14: // nand + for (;;) + { + ni = ~(xi & yi); + if (i+1 >= ylen) break; + w[i++] = ni; xi = x.words[i]; yi = y.words[i]; + } + if (yi < 0) finish = 2; + break; + default: + case 15: // set + ni = -1; + break; + } + // Here i==ylen-1; w[0]..w[i-1] have the correct result; + // and ni contains the correct result for w[i+1]. + if (i+1 == xlen) + finish = 0; + switch (finish) + { + case 0: + if (i == 0 && w == null) + { + result.ival = ni; + return; + } + w[i++] = ni; + break; + case 1: w[i] = ni; while (++i < xlen) w[i] = x.words[i]; break; + case 2: w[i] = ni; while (++i < xlen) w[i] = ~x.words[i]; break; + } + result.ival = i; + } + + /** Return the logical (bit-wise) "and" of a BigInteger and an int. */ + private static BigInteger and(BigInteger x, int y) + { + if (x.words == null) + return valueOf(x.ival & y); + if (y >= 0) + return valueOf(x.words[0] & y); + int len = x.ival; + int[] words = new int[len]; + words[0] = x.words[0] & y; + while (--len > 0) + words[len] = x.words[len]; + return make(words, x.ival); + } + + /** Return the logical (bit-wise) "and" of two BigIntegers. */ + public BigInteger and(BigInteger y) + { + if (USING_NATIVE) + { + int dummy = y.signum; // force NPE check + BigInteger result = new BigInteger(); + mpz.and(y.mpz, result.mpz); + return result; + } + + if (y.words == null) + return and(this, y.ival); + else if (words == null) + return and(y, ival); + + BigInteger x = this; + if (ival < y.ival) + { + BigInteger temp = this; x = y; y = temp; + } + int i; + int len = y.isNegative() ? x.ival : y.ival; + int[] words = new int[len]; + for (i = 0; i < y.ival; i++) + words[i] = x.words[i] & y.words[i]; + for ( ; i < len; i++) + words[i] = x.words[i]; + return make(words, len); + } + + /** Return the logical (bit-wise) "(inclusive) or" of two BigIntegers. */ + public BigInteger or(BigInteger y) + { + if (USING_NATIVE) + { + int dummy = y.signum; // force NPE check + BigInteger result = new BigInteger(); + mpz.or(y.mpz, result.mpz); + return result; + } + + return bitOp(7, this, y); + } + + /** Return the logical (bit-wise) "exclusive or" of two BigIntegers. */ + public BigInteger xor(BigInteger y) + { + if (USING_NATIVE) + { + int dummy = y.signum; // force NPE check + BigInteger result = new BigInteger(); + mpz.xor(y.mpz, result.mpz); + return result; + } + + return bitOp(6, this, y); + } + + /** Return the logical (bit-wise) negation of a BigInteger. */ + public BigInteger not() + { + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + mpz.not(result.mpz); + return result; + } + + return bitOp(12, this, ZERO); + } + + public BigInteger andNot(BigInteger val) + { + if (USING_NATIVE) + { + int dummy = val.signum; // force NPE check + BigInteger result = new BigInteger(); + mpz.andNot(val.mpz, result.mpz); + return result; + } + + return and(val.not()); + } + + public BigInteger clearBit(int n) + { + if (n < 0) + throw new ArithmeticException(); + + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + mpz.setBit(n, false, result.mpz); + return result; + } + + return and(ONE.shiftLeft(n).not()); + } + + public BigInteger setBit(int n) + { + if (n < 0) + throw new ArithmeticException(); + + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + mpz.setBit(n, true, result.mpz); + return result; + } + + return or(ONE.shiftLeft(n)); + } + + public boolean testBit(int n) + { + if (n < 0) + throw new ArithmeticException(); + + if (USING_NATIVE) + return mpz.testBit(n) != 0; + + return !and(ONE.shiftLeft(n)).isZero(); + } + + public BigInteger flipBit(int n) + { + if (n < 0) + throw new ArithmeticException(); + + if (USING_NATIVE) + { + BigInteger result = new BigInteger(); + mpz.flipBit(n, result.mpz); + return result; + } + + return xor(ONE.shiftLeft(n)); + } + + public int getLowestSetBit() + { + if (USING_NATIVE) + return mpz.compare(ZERO.mpz) == 0 ? -1 : mpz.lowestSetBit(); + + if (isZero()) + return -1; + + if (words == null) + return MPN.findLowestBit(ival); + else + return MPN.findLowestBit(words); + } + + // bit4count[I] is number of '1' bits in I. + private static final byte[] bit4_count = { 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4}; + + private static int bitCount(int i) + { + int count = 0; + while (i != 0) + { + count += bit4_count[i & 15]; + i >>>= 4; + } + return count; + } + + private static int bitCount(int[] x, int len) + { + int count = 0; + while (--len >= 0) + count += bitCount(x[len]); + return count; + } + + /** Count one bits in a BigInteger. + * If argument is negative, count zero bits instead. */ + public int bitCount() + { + if (USING_NATIVE) + return mpz.bitCount(); + + int i, x_len; + int[] x_words = words; + if (x_words == null) + { + x_len = 1; + i = bitCount(ival); + } + else + { + x_len = ival; + i = bitCount(x_words, x_len); + } + return isNegative() ? x_len * 32 - i : i; + } + + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + if (USING_NATIVE) + { + mpz = new GMP(); + s.defaultReadObject(); + if (signum != 0) + mpz.fromByteArray(magnitude); + // else it's zero and we need to do nothing + } + else + { + s.defaultReadObject(); + if (magnitude.length == 0 || signum == 0) + { + this.ival = 0; + this.words = null; + } + else + { + words = byteArrayToIntArray(magnitude, signum < 0 ? -1 : 0); + BigInteger result = make(words, words.length); + this.ival = result.ival; + this.words = result.words; + } + } + } + + private void writeObject(ObjectOutputStream s) + throws IOException, ClassNotFoundException + { + signum = signum(); + magnitude = signum == 0 ? new byte[0] : toByteArray(); + s.defaultWriteObject(); + magnitude = null; // not needed anymore + } + + // inner class(es) .......................................................... + +} diff --git a/libjava/classpath/java/math/MathContext.java b/libjava/classpath/java/math/MathContext.java new file mode 100644 index 000000000..161149540 --- /dev/null +++ b/libjava/classpath/java/math/MathContext.java @@ -0,0 +1,198 @@ +/* MathContext.java -- + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.math; + +import java.io.Serializable; + +/** + * Immutable objects describing settings such as rounding mode and digit + * precision for numerical operations such as those in the BigDecimal class. + * @author Anthony Balkissoon abalkiss at redhat dot com + * + */ +public final class MathContext implements Serializable +{ + /** A MathContext for unlimited precision arithmetic * */ + public static final MathContext UNLIMITED = + new MathContext(0, RoundingMode.HALF_UP); + + /** + * A MathContext for the IEEE 754R Decimal32 format - 7 digit preicision and + * HALF_EVEN rounding. + */ + public static final MathContext DECIMAL32 = + new MathContext(7, RoundingMode.HALF_EVEN); + + /** + * A MathContext for the IEEE 754R Decimal64 format - 16 digit preicision and + * HALF_EVEN rounding. + */ + public static final MathContext DECIMAL64 = + new MathContext(16, RoundingMode.HALF_EVEN); + + /** + * A MathContext for the IEEE 754R Decimal128 format - 34 digit preicision and + * HALF_EVEN rounding. + */ + public static final MathContext DECIMAL128 = + new MathContext(34, RoundingMode.HALF_EVEN); + + /** + * This is the serialVersionUID reported here: + * java.sun.com/j2se/1.5.0/docs/api/serialized-form.html#java.math.MathContext + */ + private static final long serialVersionUID = 5579720004786848255L; + + private int precision; + + private RoundingMode roundMode; + + /** + * Constructs a new MathContext with the specified precision and with HALF_UP + * rounding. + * @param setPrecision the precision for the new MathContext + * + * @throws IllegalArgumentException if precision is < 0. + */ + public MathContext(int setPrecision) + { + this(setPrecision, RoundingMode.HALF_UP); + } + + /** + * Constructs a new MathContext with the specified precision and rounding + * mode. + * @param setPrecision the precision + * @param setRoundingMode the rounding mode + * + * @throws IllegalArgumentException if precision is < 0. + */ + public MathContext(int setPrecision, RoundingMode setRoundingMode) + { + if (setPrecision < 0) + throw new IllegalArgumentException("Precision cannot be less than zero."); + precision = setPrecision; + roundMode = setRoundingMode; + } + + /** + * Constructs a MathContext from a String that has the same form as one + * produced by the toString() method. + * @param val + * + * @throws IllegalArgumentException if the String is not in the correct + * format or if the precision specified is < 0. + */ + public MathContext(String val) + { + try + { + int roundingModeIndex = val.indexOf("roundingMode", 10); + precision = Integer.parseInt(val.substring(10, roundingModeIndex - 1)); + roundMode = RoundingMode.valueOf(val.substring(roundingModeIndex + 13)); + } + catch (NumberFormatException nfe) + { + throw new IllegalArgumentException("String not in correct format"); + } + catch (IllegalArgumentException iae) + { + throw new IllegalArgumentException("String not in correct format"); + } + if (precision < 0) + throw new IllegalArgumentException("Precision cannot be less than 0."); + } + + /** + * Returns true if x is a MathContext and has the same precision setting + * and rounding mode as this MathContext. + * + * @return true if the above conditions hold + */ + public boolean equals(Object x) + { + if (!(x instanceof MathContext)) + return false; + MathContext mc = (MathContext)x; + return mc.precision == this.precision + && mc.roundMode.equals(this.roundMode); + } + + /** + * Returns the precision setting. + * @return the precision setting. + */ + public int getPrecision() + { + return precision; + } + + /** + * Returns the rounding mode setting. This will be one of + * RoundingMode.CEILING, RoundingMode.DOWN, RoundingMode.FLOOR, + * RoundingMode.HALF_DOWN, RoundingMode.HALF_EVEN, RoundingMode.HALF_UP, + * RoundingMode.UNNECESSARY, or RoundingMode.UP. + * @return the rounding mode setting. + */ + public RoundingMode getRoundingMode() + { + return roundMode; + } + + /** + * Returns "precision=p roundingMode=MODE" where p is an int giving the + * precision and MODE is UP, DOWN, HALF_UP, HALF_DOWN, HALF_EVEN, CEILING, + * FLOOR, or UNNECESSARY corresponding to rounding modes. + * + * @return a String describing this MathContext + */ + public String toString() + { + return "precision="+precision+" roundingMode="+roundMode; + } + + /** + * Returns the hashcode for this MathContext. + * @return the hashcode for this MathContext. + */ + public int hashCode() + { + return precision ^ roundMode.hashCode(); + } +} diff --git a/libjava/classpath/java/math/RoundingMode.java b/libjava/classpath/java/math/RoundingMode.java new file mode 100644 index 000000000..140f0a36e --- /dev/null +++ b/libjava/classpath/java/math/RoundingMode.java @@ -0,0 +1,89 @@ +/* RoundingMode.java -- An Enum to replace BigDecimal rounding constants. + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.math; + +/** + * An enum to specify rounding behaviour for numerical operations that may + * discard precision. + * @author Anthony Balkissoon abalkiss at redhat dot com + * + */ +public enum RoundingMode +{ + UP, DOWN, CEILING, FLOOR, HALF_UP, HALF_DOWN, HALF_EVEN, UNNECESSARY; + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = 432302042773881265L; + + /** + * Returns the RoundingMode object corresponding to the legacy rounding modes + * in BigDecimal. + * @param rm the legacy rounding mode + * @return the corresponding RoundingMode + */ + public static RoundingMode valueOf(int rm) + { + switch (rm) + { + case BigDecimal.ROUND_CEILING: + return CEILING; + case BigDecimal.ROUND_FLOOR: + return FLOOR; + case BigDecimal.ROUND_DOWN: + return DOWN; + case BigDecimal.ROUND_UP: + return UP; + case BigDecimal.ROUND_HALF_UP: + return HALF_UP; + case BigDecimal.ROUND_HALF_DOWN: + return HALF_DOWN; + case BigDecimal.ROUND_HALF_EVEN: + return HALF_EVEN; + case BigDecimal.ROUND_UNNECESSARY: + return UNNECESSARY; + default: + throw new + IllegalArgumentException("invalid argument: " + rm + + ". Argument should be one of the " + + "rounding modes defined in BigDecimal."); + } + } +} diff --git a/libjava/classpath/java/math/package.html b/libjava/classpath/java/math/package.html new file mode 100644 index 000000000..62d12ea16 --- /dev/null +++ b/libjava/classpath/java/math/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.math + + +

      Arbitrary large decimals and integers.

      + + + diff --git a/libjava/classpath/java/net/Authenticator.java b/libjava/classpath/java/net/Authenticator.java new file mode 100644 index 000000000..229e140cc --- /dev/null +++ b/libjava/classpath/java/net/Authenticator.java @@ -0,0 +1,313 @@ +/* Authenticator.java -- Abstract class for obtaining authentication info + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This abstract class provides a model for obtaining authentication + * information (in the form of a username and password) required by + * some network operations (such as hitting a password protected + * web site). + *

      + * To make use of this feature, a programmer must create a subclass + * that knows how to obtain the necessary info. An example + * would be a class that popped up a dialog box to prompt the user. + * After creating an instance of that subclass, the static + * setDefault method of this class is called to set up + * that instance as the object to use on subsequent calls to obtain + * authorization. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status Believed to be JDK 1.4 complete + */ +public abstract class Authenticator +{ + /* + * Class Variables + */ + + /** + * This is the default Authenticator object to use for password requests + */ + private static Authenticator defaultAuthenticator; + + /* + * Instance Variables + */ + + /** + * The hostname of the site requesting authentication + */ + private String host; + + /** + * InternetAddress of the site requesting authentication + */ + private InetAddress addr; + + /** + * The port number of the site requesting authentication + */ + private int port; + + /** + * The protocol name of the site requesting authentication + */ + private String protocol; + + /** + * The prompt to display to the user when requesting authentication info + */ + private String prompt; + + /** + * The authentication scheme in use + */ + private String scheme; + + /* + * Class Methods + */ + + /** + * This method sets the default Authenticator object (an + * instance of a subclass of Authenticator) to use when + * prompting the user for + * information. Note that this method checks to see if the caller is + * allowed to set this value (the "setDefaultAuthenticator" permission) + * and throws a SecurityException if it is not. + * + * @param defAuth The new default Authenticator object to use + * + * @exception SecurityException If the caller does not have permission + * to perform this operation + */ + public static void setDefault(Authenticator defAuth) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetPermission("setDefaultAuthenticator")); + + defaultAuthenticator = defAuth; + } + + /** + * This method is called whenever a username and password for a given + * network operation is required. First, a security check is made to see + * if the caller has the "requestPasswordAuthentication" + * permission. If not, the method thows an exception. If there is no + * default Authenticator object, the method then returns + * null. Otherwise, the default authenticators's instance + * variables are initialized and it's getPasswordAuthentication + * method is called to get the actual authentication information to return. + * + * @param addr The address requesting authentication + * @param port The port requesting authentication + * @param protocol The protocol requesting authentication + * @param prompt The prompt to display to the user when requesting + * authentication info + * @param scheme The authentication scheme in use + * + * @return A PasswordAuthentication object with the user's + * authentication info. + * + * @exception SecurityException If the caller does not have permission to + * perform this operation + */ + public static PasswordAuthentication requestPasswordAuthentication(InetAddress addr, + int port, + String protocol, + String prompt, + String scheme) + throws SecurityException + { + return requestPasswordAuthentication(null, addr, port, protocol, prompt, + scheme); + } + + /** + * This method is called whenever a username and password for a given + * network operation is required. First, a security check is made to see + * if the caller has the "requestPasswordAuthentication" + * permission. If not, the method thows an exception. If there is no + * default Authenticator object, the method then returns + * null. Otherwise, the default authenticators's instance + * variables are initialized and it's getPasswordAuthentication + * method is called to get the actual authentication information to return. + * This method is the preferred one as it can be used with hostname + * when addr is unknown. + * + * @param host The hostname requesting authentication + * @param addr The address requesting authentication + * @param port The port requesting authentication + * @param protocol The protocol requesting authentication + * @param prompt The prompt to display to the user when requesting + * authentication info + * @param scheme The authentication scheme in use + * + * @return A PasswordAuthentication object with the user's + * authentication info. + * + * @exception SecurityException If the caller does not have permission to + * perform this operation + * + * @since 1.4 + */ + public static PasswordAuthentication requestPasswordAuthentication(String host, + InetAddress addr, + int port, + String protocol, + String prompt, + String scheme) + throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new NetPermission("requestPasswordAuthentication")); + + if (defaultAuthenticator == null) + return null; + + defaultAuthenticator.host = host; + defaultAuthenticator.addr = addr; + defaultAuthenticator.port = port; + defaultAuthenticator.protocol = protocol; + defaultAuthenticator.prompt = prompt; + defaultAuthenticator.scheme = scheme; + + return defaultAuthenticator.getPasswordAuthentication(); + } + + /* + * Constructors + */ + + /** + * Default, no-argument constructor for subclasses to call. + */ + public Authenticator() + { + } + + /* + * Instance Methods + */ + + /** + * This method returns the address of the site that is requesting + * authentication. + * + * @return The requesting site's address + */ + protected final InetAddress getRequestingSite() + { + return addr; + } + + /** + * Returns the hostname of the host or proxy requesting authorization, + * or null if not available. + * + * @return The name of the host requesting authentication, or + * null if it is not available. + * + * @since 1.4 + */ + protected final String getRequestingHost() + { + return host; + } + + /** + * This method returns the port of the site that is requesting + * authentication. + * + * @return The requesting port + */ + protected final int getRequestingPort() + { + return port; + } + + /** + * This method returns the requesting protocol of the operation that is + * requesting authentication + * + * @return The requesting protocol + */ + protected final String getRequestingProtocol() + { + return protocol; + } + + /** + * Returns the prompt that should be used when requesting authentication + * information from the user + * + * @return The user prompt + */ + protected final String getRequestingPrompt() + { + return prompt; + } + + /** + * This method returns the authentication scheme in use + * + * @return The authentication scheme + */ + protected final String getRequestingScheme() + { + return scheme; + } + + /** + * This method is called whenever a request for authentication is made. It + * can call the other getXXX methods to determine the information relevant + * to this request. Subclasses should override this method, which returns + * null by default. + * + * @return The PasswordAuthentication information + */ + protected PasswordAuthentication getPasswordAuthentication() + { + return null; + } +} // class Authenticator diff --git a/libjava/classpath/java/net/BindException.java b/libjava/classpath/java/net/BindException.java new file mode 100644 index 000000000..cfb509a70 --- /dev/null +++ b/libjava/classpath/java/net/BindException.java @@ -0,0 +1,74 @@ +/* BindException.java -- An exception occurred while binding to a socket + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that an error occurred while attempting to bind + * socket to a particular port. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class BindException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5945005768251722951L; + + /** + * Create a new instance without a descriptive error message. + */ + public BindException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * text from strerror(3). + * + * @param message a message describing the error that occurred + */ + public BindException(String message) + { + super(message); + } +} // class BindException diff --git a/libjava/classpath/java/net/ConnectException.java b/libjava/classpath/java/net/ConnectException.java new file mode 100644 index 000000000..c115d2fe0 --- /dev/null +++ b/libjava/classpath/java/net/ConnectException.java @@ -0,0 +1,75 @@ +/* ConnectException.java -- An exception occurred while connecting to a host + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that an error occurred while attempting to + * connect to a remote host. Often this indicates that the remote host + * refused the connection (ie, is not listening on the target socket). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class ConnectException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 3831404271622369215L; + + /** + * Create a new instance without a descriptive error message. + */ + public ConnectException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * text from strerror(3). + * + * @param message a message describing the error that occurred + */ + public ConnectException(String message) + { + super(message); + } +} // class ConnectException diff --git a/libjava/classpath/java/net/ContentHandler.java b/libjava/classpath/java/net/ContentHandler.java new file mode 100644 index 000000000..fed8f3de4 --- /dev/null +++ b/libjava/classpath/java/net/ContentHandler.java @@ -0,0 +1,126 @@ +/* ContentHandler.java -- Abstract class for handling content from URL's + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This is an abstract class that is the superclass for classes that read + * objects from URL's. Calling the getContent() method in the + * URL class or the URLConnection class will cause + * an instance of a subclass of ContentHandler to be created for + * the MIME type of the object being downloaded from the URL. Thus, this + * class is seldom needed by applications/applets directly, but only + * indirectly through methods in other classes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class ContentHandler +{ + /* + * Constructors + */ + + /** + * Default, no-argument constructor. + */ + public ContentHandler() + { + } + + /* + * Instance Methods + */ + + /** + * This method reads from the InputStream of the passed in URL + * connection and uses the data downloaded to create an Object + * represening the content. For example, if the URL is pointing to a GIF + * file, this method might return an Image object. This method + * must be implemented by subclasses. + * + * @param urlc A URLConnection object to read data from. + * + * @return An object representing the data read + * + * @exception IOException If an error occurs + */ + public abstract Object getContent(URLConnection urlc) + throws IOException; + + /** + * This method reads from the InputStream of the passed in URL + * connection and uses the data downloaded to create an Object + * represening the content. For example, if the URL is pointing to a GIF + * file, this method might return an Image object. This method + * must be implemented by subclasses. This method uses the list of + * supplied classes as candidate types. If the data read doesn't match + * any of the supplied type, null is returned. + * + * @param urlc A URLConnection object to read data from. + * @param classes An array of types of objects that are candidate types + * for the data to be read. + * + * @return An object representing the data read, or null + * if the data does not match any of the candidate types. + * + * @exception IOException If an error occurs + * + * @since 1.3 + */ + public Object getContent(URLConnection urlc, Class[] classes) + throws IOException + { + Object obj = getContent(urlc); + + for (int i = 0; i < classes.length; i++) + { + if (classes[i].isInstance(obj)) + return obj; + } + + return null; + } +} // class ContentHandler diff --git a/libjava/classpath/java/net/ContentHandlerFactory.java b/libjava/classpath/java/net/ContentHandlerFactory.java new file mode 100644 index 000000000..51a92cf15 --- /dev/null +++ b/libjava/classpath/java/net/ContentHandlerFactory.java @@ -0,0 +1,65 @@ +/* ContentHandlerFactory.java -- Interface for creating content handlers + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface maps MIME types to ContentHandler objects. + * It consists of one method that, when passed a MIME type, returns a + * handler for that type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface ContentHandlerFactory +{ + /** + * This method is passed a MIME type as a string and is responsible for + * returning the appropriate ContentHandler object. + * + * @param mimeType The MIME type to map to a ContentHandler + * + * @return The ContentHandler for the passed in MIME type + */ + ContentHandler createContentHandler(String mimeType); +} // interface ContentHandlerFactory diff --git a/libjava/classpath/java/net/DatagramPacket.java b/libjava/classpath/java/net/DatagramPacket.java new file mode 100644 index 000000000..e642f889c --- /dev/null +++ b/libjava/classpath/java/net/DatagramPacket.java @@ -0,0 +1,391 @@ +/* DatagramPacket.java -- Class to model a packet to be sent via UDP + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class models a packet of data that is to be sent across the network + * using a connectionless protocol such as UDP. It contains the data + * to be send, as well as the destination address and port. Note that + * datagram packets can arrive in any order and are not guaranteed to be + * delivered at all. + *

      + * This class can also be used for receiving data from the network. + *

      + * Note that for all method below where the buffer length passed by the + * caller cannot exceed the actually length of the byte array passed as + * the buffer, if this condition is not true, then the method silently + * reduces the length value to maximum allowable value. + * + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aarom M. Renn (arenn@urbanophile.com) (Documentation comments) + * @date April 28, 1999. + */ +public final class DatagramPacket +{ + /** + * The data buffer to send + */ + private byte[] buffer; + + /** + * This is the offset into the buffer to start sending from or receiving to. + */ + private int offset; + + /** + * The length of the data buffer to send. + */ + int length; + + /** + * The maximal length of the buffer. + */ + int maxlen; + + /** + * The address to which the packet should be sent or from which it + * was received. + */ + private InetAddress address; + + /** + * The port to which the packet should be sent or from which it was + * was received. + */ + private int port; + + /** + * This method initializes a new instance of DatagramPacket + * which has the specified buffer, offset, and length. + * + * @param buf The buffer for holding the incoming datagram. + * @param offset The offset into the buffer to start writing. + * @param length The maximum number of bytes to read. + * + * @since 1.2 + */ + public DatagramPacket(byte[] buf, int offset, int length) + { + setData(buf, offset, length); + address = null; + port = -1; + } + + /** + * Initializes a new instance of DatagramPacket for + * receiving packets from the network. + * + * @param buf A buffer for storing the returned packet data + * @param length The length of the buffer (must be <= buf.length) + */ + public DatagramPacket(byte[] buf, int length) + { + this(buf, 0, length); + } + + /** + * Initializes a new instance of DatagramPacket for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param offset The offset into the buffer to start writing from. + * @param length The length of the buffer (must be <= buf.length) + * @param address The address to send to + * @param port The port to send to + * + * @since 1.2 + */ + public DatagramPacket(byte[] buf, int offset, int length, + InetAddress address, int port) + { + setData(buf, offset, length); + setAddress(address); + setPort(port); + } + + /** + * Initializes a new instance of DatagramPacket for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param length The length of the buffer (must be <= buf.length) + * @param address The address to send to + * @param port The port to send to + */ + public DatagramPacket(byte[] buf, int length, InetAddress address, int port) + { + this(buf, 0, length, address, port); + } + + /** + * Initializes a new instance of DatagramPacket for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param offset The offset into the buffer to start writing from. + * @param length The length of the buffer (must be <= buf.length) + * @param address The socket address to send to + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public DatagramPacket(byte[] buf, int offset, int length, + SocketAddress address) throws SocketException + { + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetSocketAddress tmp = (InetSocketAddress) address; + setData(buf, offset, length); + setAddress(tmp.getAddress()); + setPort(tmp.getPort()); + } + + /** + * Initializes a new instance of DatagramPacket for + * transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param length The length of the buffer (must be <= buf.length) + * @param address The socket address to send to + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public DatagramPacket(byte[] buf, int length, SocketAddress address) + throws SocketException + { + this(buf, 0, length, address); + } + + /** + * Returns the address that this packet is being sent to or, if it was used + * to receive a packet, the address that is was received from. If the + * constructor that doesn not take an address was used to create this object + * and no packet was actually read into this object, then this method + * returns null. + * + * @return The address for this packet. + */ + public synchronized InetAddress getAddress() + { + return address; + } + + /** + * Returns the port number this packet is being sent to or, if it was used + * to receive a packet, the port that it was received from. If the + * constructor that doesn not take an address was used to create this object + * and no packet was actually read into this object, then this method + * will return 0. + * + * @return The port number for this packet + */ + public synchronized int getPort() + { + return port; + } + + /** + * Returns the data buffer for this packet + * + * @return This packet's data buffer + */ + public synchronized byte[] getData() + { + return buffer; + } + + /** + * This method returns the current offset value into the data buffer + * where data will be sent from. + * + * @return The buffer offset. + * + * @since 1.2 + */ + public synchronized int getOffset() + { + return offset; + } + + /** + * Returns the length of the data in the buffer + * + * @return The length of the data + */ + public synchronized int getLength() + { + return length; + } + + /** + * This sets the address to which the data packet will be transmitted. + * + * @param address The destination address + * + * @since 1.1 + */ + public synchronized void setAddress(InetAddress address) + { + this.address = address; + } + + /** + * This sets the port to which the data packet will be transmitted. + * + * @param port The destination port + * + * @since 1.1 + */ + public synchronized void setPort(int port) + { + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Invalid port: " + port); + + this.port = port; + } + + /** + * Sets the address of the remote host this package will be sent + * + * @param address The socket address of the remove host + * + * @exception IllegalArgumentException If address type is not supported + * + * @since 1.4 + */ + public void setSocketAddress(SocketAddress address) + throws IllegalArgumentException + { + if (address == null) + throw new IllegalArgumentException("address may not be null"); + + InetSocketAddress tmp = (InetSocketAddress) address; + this.address = tmp.getAddress(); + this.port = tmp.getPort(); + } + + /** + * Gets the socket address of the host this packet + * will be sent to/is coming from + * + * @return The socket address of the remote host + * + * @since 1.4 + */ + public SocketAddress getSocketAddress() + { + return new InetSocketAddress(address, port); + } + + /** + * Sets the data buffer for this packet. + * + * @param buf The new buffer for this packet + * + * @exception NullPointerException If the argument is null + * + * @since 1.1 + */ + public void setData(byte[] buf) + { + setData(buf, 0, buf.length); + } + + /** + * This method sets the data buffer for the packet. + * + * @param buf The byte array containing the data for this packet. + * @param offset The offset into the buffer to start reading data from. + * @param length The number of bytes of data in the buffer. + * + * @exception NullPointerException If the argument is null + * + * @since 1.2 + */ + public synchronized void setData(byte[] buf, int offset, int length) + { + // This form of setData must be used if offset is to be changed. + if (buf == null) + throw new NullPointerException("Null buffer"); + if (offset < 0) + throw new IllegalArgumentException("Invalid offset: " + offset); + + buffer = buf; + this.offset = offset; + setLength(length); + } + + /** + * Sets the length of the data in the buffer. + * + * @param length The new length. (Where len <= buf.length) + * + * @exception IllegalArgumentException If the length is negative or + * if the length is greater than the packet's data buffer length + * + * @since 1.1 + */ + public synchronized void setLength(int length) + { + if (length < 0) + throw new IllegalArgumentException("Invalid length: " + length); + if (offset + length > buffer.length) + throw new IllegalArgumentException("Potential buffer overflow - offset: " + + offset + " length: " + length); + + this.length = length; + this.maxlen = length; + } +} diff --git a/libjava/classpath/java/net/DatagramSocket.java b/libjava/classpath/java/net/DatagramSocket.java new file mode 100644 index 000000000..6ca9c42fe --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocket.java @@ -0,0 +1,968 @@ +/* DatagramSocket.java -- A class to model UDP sockets + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.classpath.SystemProperties; + +import gnu.java.net.PlainDatagramSocketImpl; +import gnu.java.nio.DatagramChannelImpl; + +import java.io.IOException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.IllegalBlockingModeException; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This class models a connectionless datagram socket that sends + * individual packets of data across the network. In the TCP/IP world, + * this means UDP. Datagram packets do not have guaranteed delivery, + * or any guarantee about the order the data will be received on the + * remote host. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @date May 3, 1999. + */ +public class DatagramSocket +{ + /** + * This is the user DatagramSocketImplFactory for this class. If this + * variable is null, a default factory is used. + */ + private static DatagramSocketImplFactory factory; + + /** + * This is the implementation object used by this socket. + */ + private DatagramSocketImpl impl; + + /** + * True if socket implementation was created. + */ + private boolean implCreated; + + /** + * This is the address we are "connected" to + */ + private InetAddress remoteAddress; + + /** + * This is the port we are "connected" to + */ + private int remotePort = -1; + + /** + * True if socket is bound. + */ + private boolean bound; + + /** + * Creates a DatagramSocket from a specified + * DatagramSocketImpl instance + * + * @param impl The DatagramSocketImpl the socket will be + * created from + * + * @since 1.4 + */ + protected DatagramSocket(DatagramSocketImpl impl) + { + if (impl == null) + throw new NullPointerException("impl may not be null"); + + this.impl = impl; + this.remoteAddress = null; + this.remotePort = -1; + } + + /** + * Initializes a new instance of DatagramSocket that binds to + * a random port and every address on the local machine. + * + * @exception SocketException If an error occurs. + * @exception SecurityException If a security manager exists and + * its checkListen method doesn't allow the operation. + */ + public DatagramSocket() throws SocketException + { + this(new InetSocketAddress(0)); + } + + /** + * Initializes a new instance of DatagramSocket that binds to + * the specified port and every address on the local machine. + * + * @param port The local port number to bind to. + * + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation. + * @exception SocketException If an error occurs. + */ + public DatagramSocket(int port) throws SocketException + { + this(new InetSocketAddress(port)); + } + + /** + * Initializes a new instance of DatagramSocket that binds to + * the specified local port and address. + * + * @param port The local port number to bind to. + * @param addr The local address to bind to. + * + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation. + * @exception SocketException If an error occurs. + */ + public DatagramSocket(int port, InetAddress addr) throws SocketException + { + this(new InetSocketAddress(addr, port)); + } + + /** + * Initializes a new instance of DatagramSocket that binds to + * the specified local port and address. + * + * @param address The local address and port number to bind to. + * + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation. + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public DatagramSocket(SocketAddress address) throws SocketException + { + String propVal = SystemProperties.getProperty("impl.prefix"); + if (propVal == null || propVal.equals("")) + { + if (factory != null) + impl = factory.createDatagramSocketImpl(); + else + { + try + { + impl = new PlainDatagramSocketImpl(); + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + } + else + try + { + impl = + (DatagramSocketImpl) Class.forName("java.net." + propVal + + "DatagramSocketImpl") + .newInstance(); + } + catch (Exception e) + { + System.err.println("Could not instantiate class: java.net." + + propVal + "DatagramSocketImpl"); + try + { + impl = new PlainDatagramSocketImpl(); + } + catch (IOException ioe) + { + SocketException se = new SocketException(); + se.initCause(ioe); + throw se; + } + } + + if (address != null) + bind(address); + } + + // This needs to be accessible from java.net.MulticastSocket + DatagramSocketImpl getImpl() throws SocketException + { + try + { + if (! implCreated) + { + impl.create(); + implCreated = true; + } + + return impl; + } + catch (IOException e) + { + SocketException se = new SocketException(); + se.initCause(e); + throw se; + } + } + + /** + * Closes this datagram socket. + */ + public void close() + { + if (isClosed()) + return; + + try + { + getImpl().close(); + } + catch (SocketException e) + { + // Ignore this case, just close the socket in finally clause. + } + finally + { + remoteAddress = null; + remotePort = -1; + impl = null; + } + + try + { + if (getChannel() != null) + getChannel().close(); + } + catch (IOException e) + { + // Do nothing. + } + } + + /** + * This method returns the remote address to which this socket is + * connected. If this socket is not connected, then this method will + * return null. + * + * @return The remote address. + * + * @since 1.2 + */ + public InetAddress getInetAddress() + { + return remoteAddress; + } + + /** + * This method returns the remote port to which this socket is + * connected. If this socket is not connected, then this method will + * return -1. + * + * @return The remote port. + * + * @since 1.2 + */ + public int getPort() + { + return remotePort; + } + + /** + * Returns the local address this datagram socket is bound to. + * + * @return The local address is the socket is bound or null + * + * @since 1.1 + */ + public InetAddress getLocalAddress() + { + if (! isBound()) + return null; + + InetAddress localAddr; + + try + { + localAddr = + (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkConnect(localAddr.getHostAddress(), -1); + } + catch (SecurityException e) + { + localAddr = InetAddress.ANY_IF; + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return null; + } + + return localAddr; + } + + /** + * Returns the local port this socket is bound to. + * + * @return The local port number. + */ + public int getLocalPort() + { + if (isClosed()) + return -1; + + try + { + return getImpl().getLocalPort(); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return 0; + } + } + + /** + * Returns the value of the socket's SO_TIMEOUT setting. If this method + * returns 0 then SO_TIMEOUT is disabled. + * + * @return The current timeout in milliseconds. + * + * @exception SocketException If an error occurs. + * + * @since 1.1 + */ + public synchronized int getSoTimeout() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_TIMEOUT); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the value of the socket's SO_TIMEOUT value. A value of 0 will + * disable SO_TIMEOUT. Any other value is the number of milliseconds + * a socket read/write will block before timing out. + * + * @param timeout The new SO_TIMEOUT value in milliseconds. + * + * @exception SocketException If an error occurs. + * + * @since 1.1 + */ + public synchronized void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("Invalid timeout: " + timeout); + + getImpl().setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); + } + + /** + * This method returns the value of the system level socket option + * SO_SNDBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The send buffer size. + * + * @exception SocketException If an error occurs. + * + * @since 1.2 + */ + public int getSendBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_SNDBUF); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_SNDBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new send buffer size. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If size is 0 or negative. + * + * @since 1.2 + */ + public void setSendBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size < 0) + throw new IllegalArgumentException("Buffer size is less than 0"); + + getImpl().setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs. + * + * @since 1.2 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_RCVBUF); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If size is 0 or negative. + * + * @since 1.2 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size < 0) + throw new IllegalArgumentException("Buffer size is less than 0"); + + getImpl().setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); + } + + /** + * This method connects this socket to the specified address and port. + * When a datagram socket is connected, it will only send or receive + * packets to and from the host to which it is connected. A multicast + * socket that is connected may only send and not receive packets. + * + * @param address The address to connect this socket to. + * @param port The port to connect this socket to. + * + * @exception IllegalArgumentException If address or port are invalid. + * @exception SecurityException If the caller is not allowed to send + * datagrams to or receive from this address and port. + * + * @since 1.2 + */ + public void connect(InetAddress address, int port) + { + if (address == null) + throw new IllegalArgumentException("Connect address may not be null"); + + if ((port < 1) || (port > 65535)) + throw new IllegalArgumentException("Port number is illegal: " + port); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(address.getHostAddress(), port); + + try + { + getImpl().connect(address, port); + remoteAddress = address; + remotePort = port; + } + catch (SocketException e) + { + // This means simply not connected or connect not implemented. + } + } + + /** + * This method disconnects this socket from the address/port it was + * connected to. If the socket was not connected in the first place, + * this method does nothing. + * + * @since 1.2 + */ + public void disconnect() + { + if (! isConnected()) + return; + + try + { + getImpl().disconnect(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + finally + { + remoteAddress = null; + remotePort = -1; + } + } + + /** + * Reads a datagram packet from the socket. Note that this method + * will block until a packet is received from the network. On return, + * the passed in DatagramPacket is populated with the data + * received and all the other information about the packet. + * + * @param p A DatagramPacket for storing the data + * + * @exception IOException If an error occurs. + * @exception SocketTimeoutException If setSoTimeout was previously called + * and the timeout has expired. + * @exception PortUnreachableException If the socket is connected to a + * currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode. + * @exception SecurityException If a security manager exists and its + * checkAccept method doesn't allow the receive. + */ + public synchronized void receive(DatagramPacket p) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (remoteAddress != null && remoteAddress.isMulticastAddress()) + throw new IOException + ("Socket connected to a multicast address my not receive"); + + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((DatagramChannelImpl) getChannel()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + DatagramPacket p2 = new DatagramPacket(p.getData(), p.getOffset(), p.maxlen); + getImpl().receive(p2); + p.length = p2.length; + if (p2.getAddress() != null) + p.setAddress(p2.getAddress()); + if (p2.getPort() != -1) + p.setPort(p2.getPort()); + + SecurityManager s = System.getSecurityManager(); + if (s != null && isConnected()) + s.checkAccept(p.getAddress().getHostAddress(), p.getPort()); + } + + /** + * Sends the specified packet. The host and port to which the packet + * are to be sent should be set inside the packet. + * + * @param p The datagram packet to send. + * + * @exception IOException If an error occurs. + * @exception SecurityException If a security manager exists and its + * checkMulticast or checkConnect method doesn't allow the send. + * @exception PortUnreachableException If the socket is connected to a + * currently unreachable destination. Note, there is no guarantee that the + * exception will be thrown. + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode. + */ + public void send(DatagramPacket p) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // JDK1.2: Don't do security checks if socket is connected; see jdk1.2 api. + SecurityManager s = System.getSecurityManager(); + if (s != null && ! isConnected()) + { + InetAddress addr = p.getAddress(); + if (addr.isMulticastAddress()) + s.checkMulticast(addr); + else + s.checkConnect(addr.getHostAddress(), p.getPort()); + } + + if (isConnected()) + { + if (p.getAddress() != null + && (remoteAddress != p.getAddress() || remotePort != p.getPort())) + throw new IllegalArgumentException + ("DatagramPacket address does not match remote address"); + } + + // FIXME: if this is a subclass of MulticastSocket, + // use getTimeToLive for TTL val. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((DatagramChannelImpl) getChannel()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + getImpl().send(p); + } + + /** + * Binds the socket to the given socket address. + * + * @param address The socket address to bind to. + * + * @exception SocketException If an error occurs. + * @exception SecurityException If a security manager exists and + * its checkListen method doesn't allow the operation. + * @exception IllegalArgumentException If address type is not supported. + * + * @since 1.4 + */ + public void bind(SocketAddress address) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (address == null) + address = new InetSocketAddress(InetAddress.ANY_IF, 0); + + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetAddress addr = ((InetSocketAddress) address).getAddress(); + int port = ((InetSocketAddress) address).getPort(); + + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Invalid port: " + port); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkListen(port); + + if (addr == null) + addr = InetAddress.ANY_IF; + + try + { + getImpl().bind(port, addr); + bound = true; + } + catch (SocketException exception) + { + getImpl().close(); + throw exception; + } + catch (RuntimeException exception) + { + getImpl().close(); + throw exception; + } + catch (Error error) + { + getImpl().close(); + throw error; + } + } + + /** + * Checks if the datagram socket is closed. + * + * @return True if socket is closed, false otherwise. + * + * @since 1.4 + */ + public boolean isClosed() + { + return impl == null; + } + + /** + * Returns the datagram channel assoziated with this datagram socket. + * + * @return The associated DatagramChannel object or null + * + * @since 1.4 + */ + public DatagramChannel getChannel() + { + return null; + } + + /** + * Connects the datagram socket to a specified socket address. + * + * @param address The socket address to connect to. + * + * @exception SocketException If an error occurs. + * @exception IllegalArgumentException If address type is not supported. + * + * @since 1.4 + */ + public void connect(SocketAddress address) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (address instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + InetSocketAddress tmp = (InetSocketAddress) address; + connect(tmp.getAddress(), tmp.getPort()); + } + + /** + * Returns the binding state of the socket. + * + * @return True if socket bound, false otherwise. + * + * @since 1.4 + */ + public boolean isBound() + { + return bound; + } + + /** + * Returns the connection state of the socket. + * + * @return True if socket is connected, false otherwise. + * + * @since 1.4 + */ + public boolean isConnected() + { + return remoteAddress != null; + } + + /** + * Returns the SocketAddress of the host this socket is conneted to + * or null if this socket is not connected. + * + * @return The socket address of the remote host if connected or null + * + * @since 1.4 + */ + public SocketAddress getRemoteSocketAddress() + { + if (! isConnected()) + return null; + + return new InetSocketAddress(remoteAddress, remotePort); + } + + /** + * Returns the local SocketAddress this socket is bound to. + * + * @return The local SocketAddress or null if the socket is not bound. + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + if (! isBound()) + return null; + + return new InetSocketAddress(getLocalAddress(), getLocalPort()); + } + + /** + * Enables/Disables SO_REUSEADDR. + * + * @param on Whether or not to have SO_REUSEADDR turned on. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public void setReuseAddress(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Checks if SO_REUSEADDR is enabled. + * + * @return True if SO_REUSEADDR is set on the socket, false otherwise. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_REUSEADDR); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Enables/Disables SO_BROADCAST + * + * @param enable True if SO_BROADCAST should be enabled, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setBroadcast(boolean enable) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(enable)); + } + + /** + * Checks if SO_BROADCAST is enabled + * + * @return Whether SO_BROADCAST is set + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getBroadcast() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_BROADCAST); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the traffic class value + * + * @param tc The traffic class + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If tc value is illegal + * + * @see DatagramSocket#getTrafficClass() + * + * @since 1.4 + */ + public void setTrafficClass(int tc) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (tc < 0 || tc > 255) + throw new IllegalArgumentException(); + + getImpl().setOption(SocketOptions.IP_TOS, Integer.valueOf(tc)); + } + + /** + * Returns the current traffic class + * + * @return The current traffic class. + * + * @see DatagramSocket#setTrafficClass(int tc) + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public int getTrafficClass() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.IP_TOS); + + if (buf instanceof Integer) + return ((Integer) buf).intValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the datagram socket implementation factory for the application + * + * @param fac The factory to set + * + * @exception IOException If an error occurs + * @exception SocketException If the factory is already defined + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static void setDatagramSocketImplFactory(DatagramSocketImplFactory fac) + throws IOException + { + if (factory != null) + throw new SocketException("DatagramSocketImplFactory already defined"); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSetFactory(); + + factory = fac; + } +} diff --git a/libjava/classpath/java/net/DatagramSocketImpl.java b/libjava/classpath/java/net/DatagramSocketImpl.java new file mode 100644 index 000000000..4f51f9f93 --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocketImpl.java @@ -0,0 +1,296 @@ +/* DatagramSocketImpl.java -- Abstract class for UDP socket implementations + Copyright (C) 1998, 1999 2000, 2001, + 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; + + +/** + * This abstract class models a datagram socket implementation. An + * actual implementation class would implement these methods, probably + * via redirecting them to native code. + *

      + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + *

      + * Status: Believed complete and correct. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + */ +public abstract class DatagramSocketImpl implements SocketOptions +{ + /** + * The local port to which this socket is bound + */ + protected int localPort; + + /** + * The FileDescriptor object for this object. + */ + protected FileDescriptor fd; + + /** + * Default, no-argument constructor for subclasses to call. + */ + public DatagramSocketImpl() + { + } + + /** + * This method binds the socket to the specified local port and address. + * + * @param lport The port number to bind to + * @param laddr The address to bind to + * + * @exception SocketException If an error occurs + */ + protected abstract void bind(int lport, InetAddress laddr) + throws SocketException; + + /** + * This methods closes the socket + */ + protected abstract void close(); + + /** + * Creates a new datagram socket. + * + * @exception SocketException If an error occurs + */ + protected abstract void create() throws SocketException; + + /** + * Takes a peek at the next packet received in order to retrieve the + * address of the sender + * + * @param i The InetAddress to fill in with the information + * about the sender if the next packet + * + * @return The port number of the sender of the packet + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract int peek(InetAddress i) throws IOException; + + /** + * Takes a peek at the next packet received. This packet is not consumed. + * With the next peekData/receive operation this packet will be read again. + * + * @param p The DatagramPacket to fill in with the data sent. + * + * @return The port number of the sender of the packet. + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + * + * @since 1.4 + */ + protected abstract int peekData(DatagramPacket p) throws IOException; + + /** + * Transmits the specified packet of data to the network. The destination + * host and port should be encoded in the packet. + * + * @param p The packet to send + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract void send(DatagramPacket p) throws IOException; + + /** + * Receives a packet of data from the network Will block until a packet + * arrives. The packet info in populated into the passed in + * DatagramPacket object. + * + * @param p A place to store the incoming packet. + * + * @exception IOException If an error occurs + * @exception PortUnreachableException May be thrown if the socket is + * connected to a currently unreachable destination. Note, there is no + * guarantee that the exception will be thrown. + */ + protected abstract void receive(DatagramPacket p) throws IOException; + + /** + * Connects the socket to a host specified by address and port. + * + * @param address The InetAddress of the host to connect to + * @param port The port number of the host to connect to + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + protected void connect(InetAddress address, int port) + throws SocketException + { + // This method has to be overwritten by real implementations + } + + /** + * Disconnects the socket. + * + * @since 1.4 + */ + protected void disconnect() + { + // This method has to be overwritten by real implementations + } + + /** + * Sets the Time to Live (TTL) setting on this socket to the specified + * value. Use setTimeToLive(int) instead. + * + * @param ttl The new Time to Live value + * + * @exception IOException If an error occurs + * @deprecated + */ + protected abstract void setTTL(byte ttl) throws IOException; + + /** + * This method returns the current Time to Live (TTL) setting on this + * socket. Use getTimeToLive() instead. + * + * @return the current time-to-live + * + * @exception IOException If an error occurs + * + * @deprecated // FIXME: when ? + */ + protected abstract byte getTTL() throws IOException; + + /** + * Sets the Time to Live (TTL) setting on this socket to the specified + * value. + * + * @param ttl The new Time to Live value + * + * @exception IOException If an error occurs + */ + protected abstract void setTimeToLive(int ttl) throws IOException; + + /** + * This method returns the current Time to Live (TTL) setting on this + * socket. + * + * @return the current time-to-live + * + * @exception IOException If an error occurs + */ + protected abstract int getTimeToLive() throws IOException; + + /** + * Causes this socket to join the specified multicast group + * + * @param inetaddr The multicast address to join with + * + * @exception IOException If an error occurs + */ + protected abstract void join(InetAddress inetaddr) throws IOException; + + /** + * Causes the socket to leave the specified multicast group. + * + * @param inetaddr The multicast address to leave + * + * @exception IOException If an error occurs + */ + protected abstract void leave(InetAddress inetaddr) throws IOException; + + /** + * Causes this socket to join the specified multicast group on a specified + * device + * + * @param mcastaddr The address to leave + * @param netIf The specified network interface to join the group at + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void joinGroup(SocketAddress mcastaddr, + NetworkInterface netIf) + throws IOException; + + /** + * Leaves a multicast group + * + * @param mcastaddr The address to join + * @param netIf The specified network interface to leave the group at + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void leaveGroup(SocketAddress mcastaddr, + NetworkInterface netIf) + throws IOException; + + /** + * Returns the FileDescriptor for this socket + * + * @return the file descriptor associated with this socket + */ + protected FileDescriptor getFileDescriptor() + { + return fd; + } + + /** + * Returns the local port this socket is bound to + * + * @return the local port + */ + protected int getLocalPort() + { + return localPort; + } +} diff --git a/libjava/classpath/java/net/DatagramSocketImplFactory.java b/libjava/classpath/java/net/DatagramSocketImplFactory.java new file mode 100644 index 000000000..014651aa8 --- /dev/null +++ b/libjava/classpath/java/net/DatagramSocketImplFactory.java @@ -0,0 +1,60 @@ +/* DatagramSocketImplFactory.java -- + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** Written using on-line Java Platform 1.4 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface defines one method which returns a + * DatagramSocketImpl object. + * This should not be needed by ordinary applications. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.3 + */ +public interface DatagramSocketImplFactory +{ + /** + * This method returns an instance of the DatagramSocketImpl object + * + * @return A DatagramSocketImpl object + */ + DatagramSocketImpl createDatagramSocketImpl(); +} // interface DatagramSocketImplFactory diff --git a/libjava/classpath/java/net/FileNameMap.java b/libjava/classpath/java/net/FileNameMap.java new file mode 100644 index 000000000..6f1d25a6a --- /dev/null +++ b/libjava/classpath/java/net/FileNameMap.java @@ -0,0 +1,65 @@ +/* FileNameMap.java -- Maps filenames to MIME types + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface has one method which, when passed a filename, returns + * the MIME type associated with that filename. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + */ +public interface FileNameMap +{ + /** + * This method is passed a filename and is responsible for determining + * the appropriate MIME type for that file. + * + * @param filename The name of the file to generate a MIME type for. + * + * @return The MIME type for the filename passed in. + */ + String getContentTypeFor(String filename); +} // interface FileNameMap diff --git a/libjava/classpath/java/net/HttpURLConnection.java b/libjava/classpath/java/net/HttpURLConnection.java new file mode 100644 index 000000000..72dd67d91 --- /dev/null +++ b/libjava/classpath/java/net/HttpURLConnection.java @@ -0,0 +1,589 @@ +/* HttpURLConnection.java -- Subclass of communications links using + Hypertext Transfer Protocol. + Copyright (C) 1998, 1999, 2000, 2002, 2003 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; +import java.security.Permission; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class provides a common abstract implementation for those + * URL connection classes that will connect using the HTTP protocol. + * In addition to the functionality provided by the URLConnection + * class, it defines constants for HTTP return code values and + * methods for setting the HTTP request method and determining whether + * or not to follow redirects. + * + * @since 1.1 + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class HttpURLConnection extends URLConnection +{ + /* HTTP Success Response Codes */ + + /** + * Indicates that the client may continue with its request. This value + * is specified as part of RFC 2068 but was not included in Sun's JDK, so + * beware of using this value + */ + static final int HTTP_CONTINUE = 100; + + /** + * Indicates the request succeeded. + */ + public static final int HTTP_OK = 200; + + /** + * The requested resource has been created. + */ + public static final int HTTP_CREATED = 201; + + /** + * The request has been accepted for processing but has not completed. + * There is no guarantee that the requested action will actually ever + * be completed succesfully, but everything is ok so far. + */ + public static final int HTTP_ACCEPTED = 202; + + /** + * The meta-information returned in the header is not the actual data + * from the original server, but may be from a local or other copy. + * Normally this still indicates a successful completion. + */ + public static final int HTTP_NOT_AUTHORITATIVE = 203; + + /** + * The server performed the request, but there is no data to send + * back. This indicates that the user's display should not be changed. + */ + public static final int HTTP_NO_CONTENT = 204; + + /** + * The server performed the request, but there is no data to sent back, + * however, the user's display should be "reset" to clear out any form + * fields entered. + */ + public static final int HTTP_RESET = 205; + + /** + * The server completed the partial GET request for the resource. + */ + public static final int HTTP_PARTIAL = 206; + + /* HTTP Redirection Response Codes */ + + /** + * There is a list of choices available for the requested resource. + */ + public static final int HTTP_MULT_CHOICE = 300; + + /** + * The resource has been permanently moved to a new location. + */ + public static final int HTTP_MOVED_PERM = 301; + + /** + * The resource requested has been temporarily moved to a new location. + */ + public static final int HTTP_MOVED_TEMP = 302; + + /** + * The response to the request issued is available at another location. + */ + public static final int HTTP_SEE_OTHER = 303; + + /** + * The document has not been modified since the criteria specified in + * a conditional GET. + */ + public static final int HTTP_NOT_MODIFIED = 304; + + /** + * The requested resource needs to be accessed through a proxy. + */ + public static final int HTTP_USE_PROXY = 305; + + /* HTTP Client Error Response Codes */ + + /** + * The request was misformed or could not be understood. + */ + public static final int HTTP_BAD_REQUEST = 400; + + /** + * The request made requires user authorization. Try again with + * a correct authentication header. + */ + public static final int HTTP_UNAUTHORIZED = 401; + + /** + * Code reserved for future use - I hope way in the future. + */ + public static final int HTTP_PAYMENT_REQUIRED = 402; + + /** + * There is no permission to access the requested resource. + */ + public static final int HTTP_FORBIDDEN = 403; + + /** + * The requested resource was not found. + */ + public static final int HTTP_NOT_FOUND = 404; + + /** + * The specified request method is not allowed for this resource. + */ + public static final int HTTP_BAD_METHOD = 405; + + /** + * Based on the input headers sent, the resource returned in response + * to the request would not be acceptable to the client. + */ + public static final int HTTP_NOT_ACCEPTABLE = 406; + + /** + * The client must authenticate with a proxy prior to attempting this + * request. + */ + public static final int HTTP_PROXY_AUTH = 407; + + /** + * The request timed out. + */ + public static final int HTTP_CLIENT_TIMEOUT = 408; + + /** + * There is a conflict between the current state of the resource and the + * requested action. + */ + public static final int HTTP_CONFLICT = 409; + + /** + * The requested resource is no longer available. This ususally indicates + * a permanent condition. + */ + public static final int HTTP_GONE = 410; + + /** + * A Content-Length header is required for this request, but was not + * supplied. + */ + public static final int HTTP_LENGTH_REQUIRED = 411; + + /** + * A client specified pre-condition was not met on the server. + */ + public static final int HTTP_PRECON_FAILED = 412; + + /** + * The request sent was too large for the server to handle. + */ + public static final int HTTP_ENTITY_TOO_LARGE = 413; + + /** + * The name of the resource specified was too long. + */ + public static final int HTTP_REQ_TOO_LONG = 414; + + /** + * The request is in a format not supported by the requested resource. + */ + public static final int HTTP_UNSUPPORTED_TYPE = 415; + + /* HTTP Server Error Response Codes */ + + /** + * This error code indicates that some sort of server error occurred. + * + * @deprecated + */ + public static final int HTTP_SERVER_ERROR = 500; + + /** + * The server encountered an unexpected error (such as a CGI script crash) + * that prevents the request from being fulfilled. + */ + public static final int HTTP_INTERNAL_ERROR = 500; + + /** + * The server does not support the requested functionality. + * @since 1.3 + */ + public static final int HTTP_NOT_IMPLEMENTED = 501; + + /** + * The proxy encountered a bad response from the server it was proxy-ing for + */ + public static final int HTTP_BAD_GATEWAY = 502; + + /** + * The HTTP service is not availalble, such as because it is overloaded + * and does not want additional requests. + */ + public static final int HTTP_UNAVAILABLE = 503; + + /** + * The proxy timed out getting a reply from the remote server it was + * proxy-ing for. + */ + public static final int HTTP_GATEWAY_TIMEOUT = 504; + + /** + * This server does not support the protocol version requested. + */ + public static final int HTTP_VERSION = 505; + + // Non-HTTP response static variables + + /** + * Flag to indicate whether or not redirects should be automatically + * followed by default. + */ + private static boolean followRedirects = true; + + /** + * This is a list of valid request methods, separated by "|" characters. + */ + private static final String valid_methods = + "|GET|POST|HEAD|OPTIONS|PUT|DELETE|TRACE|"; + + // Instance Variables + + /** + * The requested method in use for this connection. Default is GET. + */ + protected String method = "GET"; + + /** + * The response code received from the server + */ + protected int responseCode = -1; + + /** + * The response message string received from the server. + */ + protected String responseMessage; + + /** + * If this instance should follow redirect requests. + */ + protected boolean instanceFollowRedirects = followRedirects; + + /** + * Whether we already got a valid response code for this connection. + * Used by getResponseCode() and + * getResponseMessage(). + */ + private boolean gotResponseVals; + + /** + * Create an HttpURLConnection for the specified URL + * + * @param url The URL to create this connection for. + */ + protected HttpURLConnection(URL url) + { + super(url); + } + + /** + * Closes the connection to the server. + */ + public abstract void disconnect(); + + /** + * Returns a boolean indicating whether or not this connection is going + * through a proxy + * + * @return true if through a proxy, false otherwise + */ + public abstract boolean usingProxy(); + + /** + * Sets whether HTTP redirects (requests with response code 3xx) should be + * automatically followed by this class. True by default + * + * @param set true if redirects should be followed, false otherwis. + * + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static void setFollowRedirects(boolean set) + { + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + followRedirects = set; + } + + /** + * Returns a boolean indicating whether or not HTTP redirects will + * automatically be followed or not. + * + * @return true if redirects will be followed, false otherwise + */ + public static boolean getFollowRedirects() + { + return followRedirects; + } + + /** + * Returns the value of this HttpURLConnection's instanceFollowRedirects + * field + * + * @return true if following redirects is enabled, false otherwise + */ + public boolean getInstanceFollowRedirects() + { + return instanceFollowRedirects; + } + + /** + * Sets the value of this HttpURLConnection's instanceFollowRedirects field + * + * @param follow true to enable following redirects, false otherwise + */ + public void setInstanceFollowRedirects(boolean follow) + { + instanceFollowRedirects = follow; + } + + /** + * Set the method for the URL request, one of: + * GET POST HEAD OPTIONS PUT DELETE TRACE are legal + * + * @param method the method to use + * + * @exception ProtocolException If the method cannot be reset or if the + * requested method isn't valid for HTTP + */ + public void setRequestMethod(String method) throws ProtocolException + { + if (connected) + throw new ProtocolException("Already connected"); + + method = method.toUpperCase(); + if (valid_methods.indexOf("|" + method + "|") != -1) + this.method = method; + else + throw new ProtocolException("Invalid HTTP request method: " + method); + } + + /** + * The request method currently in use for this connection. + * + * @return The request method + */ + public String getRequestMethod() + { + return method; + } + + /** + * Gets the status code from an HTTP response message, or -1 if + * the response code could not be determined. + * Note that all valid response codes have class variables + * defined for them in this class. + * + * @return The response code + * + * @exception IOException If an error occurs + */ + public int getResponseCode() throws IOException + { + if (! gotResponseVals) + getResponseVals(); + return responseCode; + } + + /** + * Gets the HTTP response message, if any, returned along with the + * response code from a server. Null if no response message was set + * or an error occured while connecting. + * + * @return The response message + * + * @exception IOException If an error occurs + */ + public String getResponseMessage() throws IOException + { + if (! gotResponseVals) + getResponseVals(); + return responseMessage; + } + + private void getResponseVals() throws IOException + { + // getHeaderField() will connect for us, but do it here first in + // order to pick up IOExceptions. + if (! connected) + connect(); + + gotResponseVals = true; + + // If responseCode not yet explicitly set by subclass + if (responseCode == -1) + { + // Response is the first header received from the connection. + String respField = getHeaderField(0); + + if (respField == null || ! respField.startsWith("HTTP/")) + { + // Set to default values on failure. + responseCode = -1; + responseMessage = null; + return; + } + + int firstSpc; + int nextSpc; + firstSpc = respField.indexOf(' '); + nextSpc = respField.indexOf(' ', firstSpc + 1); + responseMessage = respField.substring(nextSpc + 1); + String codeStr = respField.substring(firstSpc + 1, nextSpc); + try + { + responseCode = Integer.parseInt(codeStr); + } + catch (NumberFormatException e) + { + // Set to default values on failure. + responseCode = -1; + responseMessage = null; + } + } + } + + /** + * Returns a permission object representing the permission necessary to make + * the connection represented by this object + * + * @return the permission necessary for this connection + * + * @exception IOException If an error occurs + */ + public Permission getPermission() throws IOException + { + URL url = getURL(); + String host = url.getHost(); + int port = url.getPort(); + if (port == -1) + port = 80; + + host = host + ":" + port; + + return new SocketPermission(host, "connect"); + } + + /** + * This method allows the caller to retrieve any data that might have + * been sent despite the fact that an error occurred. For example, the + * HTML page sent along with a 404 File Not Found error. If the socket + * is not connected, or if no error occurred or no data was returned, + * this method returns null. + * + * @return An InputStream for reading error data. + */ + public InputStream getErrorStream() + { + if (! connected) + return null; + + int code; + try + { + code = getResponseCode(); + } + catch (IOException e) + { + code = -1; + } + + if (code == -1) + return null; + + if (((code / 100) != 4) || ((code / 100) != 5)) + return null; + + try + { + PushbackInputStream pbis = new PushbackInputStream(getInputStream()); + + int i = pbis.read(); + if (i == -1) + return null; + + pbis.unread(i); + return pbis; + } + catch (IOException e) + { + return null; + } + } + + /** + * Returns the value of the named field parsed as date + * + * @param key the key of the header field + * @param value the default value if the header field is not present + * + * @return the value of the header field + */ + public long getHeaderFieldDate(String key, long value) + { + // FIXME: implement this correctly + // http://www.w3.org/Protocols/HTTP-NG/ng-notes.txt + return super.getHeaderFieldDate(key, value); + } +} diff --git a/libjava/classpath/java/net/Inet4Address.java b/libjava/classpath/java/net/Inet4Address.java new file mode 100644 index 000000000..e3cff7b13 --- /dev/null +++ b/libjava/classpath/java/net/Inet4Address.java @@ -0,0 +1,273 @@ +/* Inet4Address.java -- + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.ObjectStreamException; + +/* + * Written using on-line Java Platform 1.4 API Specification and + * RFC 1884 (http://www.ietf.org/rfc/rfc1884.txt), + * RFC 1918 (http://www.ietf.org/rfc/rfc1918.txt), + * RFC 2365 (http://www.ietf.org/rfc/rfc2365.txt) + * + * @author Michael Koch + * @status Believed complete and correct. + */ +public final class Inet4Address extends InetAddress +{ + /** + * For compatability with Sun's JDK 1.4.2 rev. 5 + */ + static final long serialVersionUID = 3286316764910316507L; + + /** + * The address family of these addresses (used for serialization). + */ + private static final int AF_INET = 2; + + /** + * Inet4Address objects are serialized as InetAddress objects. + */ + private Object writeReplace() throws ObjectStreamException + { + return new InetAddress(addr, hostName, AF_INET); + } + + /** + * Initializes this object's addr instance variable from the passed in + * byte array. Note that this constructor is protected and is called + * only by static methods in this class. + * + * @param addr The IP number of this address as an array of bytes + * @param host The hostname of this IP address. + */ + Inet4Address(byte[] addr, String host) + { + super(addr, host, AF_INET); + } + + /** + * Checks if the address is a multicast address + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + return (addr[0] & 0xf0) == 0xe0; + } + + /** + * Checks if this address is a loopback address + */ + public boolean isLoopbackAddress() + { + return (addr[0] & 0xff) == 0x7f; + } + + /** + * Checks if this address is a wildcard address + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + return equals(InetAddress.ANY_IF); + } + + /** + * Checks if this address is a link local address + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + return false; + } + + /** + * Checks if this address is a site local address + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + // 10.0.0.0/8 + if ((addr[0] & 0xff) == 0x0a) + return true; + + // 172.16.0.0/12 + if ((addr[0] & 0xff) == 0xac && (addr[1] & 0xf0) == 0x10) + return true; + + // 192.168.0.0/16 + if ((addr[0] & 0xff) == 0xc0 && (addr[1] & 0xff) == 0xa8) + return true; + + return false; + } + + /** + * Checks if this multicast address has global scope + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + return false; + } + + /** + * Checks if this multicast address has node scope + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + return false; + } + + /** + * Checks if this multicast address has link scope + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + if (! isMulticastAddress()) + return false; + + return ((addr[0] & 0xff) == 0xe0 + && (addr[1] & 0xff) == 0x00 + && (addr[2] & 0xff) == 0x00); + } + + /** + * Checks if this multicast address has site scope + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + return false; + } + + /** + * Checks if this multicast address has organization scope + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + return false; + } + + /** + * Returns the address of the current instance + */ + public byte[] getAddress() + { + return (byte[]) addr.clone(); + } + + /** + * Returns the address as string + * + * @since 1.0.2 + */ + public String getHostAddress() + { + CPStringBuilder sb = new CPStringBuilder(40); + + int len = addr.length; + int i = 0; + + for ( ; ; ) + { + sb.append(addr[i] & 0xff); + i++; + + if (i == len) + break; + + sb.append('.'); + } + + return sb.toString(); + } + + /** + * Computes the hashcode of the instance + */ + public int hashCode() + { + int hash = 0; + int len = addr.length; + int i = len > 4 ? len - 4 : 0; + + for (; i < len; i++) + hash = (hash << 8) | (addr[i] & 0xFF); + + return hash; + } + + /** + * Compare the current Inet4Address instance with obj + * + * @param obj Object to compare with + */ + public boolean equals(Object obj) + { + if (! (obj instanceof InetAddress)) + return false; + + byte[] addr1 = addr; + byte[] addr2 = ((InetAddress) obj).addr; + + if (addr1.length != addr2.length) + return false; + + for (int i = addr1.length; --i >= 0;) + if (addr1[i] != addr2[i]) + return false; + + return true; + } +} diff --git a/libjava/classpath/java/net/Inet6Address.java b/libjava/classpath/java/net/Inet6Address.java new file mode 100644 index 000000000..60c406587 --- /dev/null +++ b/libjava/classpath/java/net/Inet6Address.java @@ -0,0 +1,429 @@ +/* Inet6Address.java -- + Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.util.Arrays; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.IOException; + +/* + * Written using on-line Java Platform 1.4 API Specification and + * RFC 1884 (http://www.ietf.org/rfc/rfc1884.txt) + * + * @author Michael Koch + * @status Updated to 1.5. Serialization compatibility is tested. + */ +public final class Inet6Address extends InetAddress +{ + static final long serialVersionUID = 6880410070516793377L; + + /** + * Needed for serialization + */ + byte[] ipaddress; + + /** + * The scope ID, if any. + * @since 1.5 + * @serial + */ + private int scope_id; + + /** + * The scope ID, if any. + * @since 1.5 + * @serial + */ + private boolean scope_id_set; + + /** + * Whether ifname is set or not. + * @since 1.5 + * @serial + */ + private boolean scope_ifname_set; + + /** + * Name of the network interface, used only by the serialization methods + * @since 1.5 + * @serial + */ + private String ifname; + + /** + * Scope network interface, or null. + */ + private transient NetworkInterface nif; + + /** + * The address family of these addresses (used for serialization). + */ + private static final int AF_INET6 = 10; + + /** + * Create an Inet6Address object + * + * @param addr The IP address + * @param host The hostname + */ + Inet6Address(byte[] addr, String host) + { + super(addr, host, AF_INET6); + // Super constructor clones the addr. Get a reference to the clone. + this.ipaddress = this.addr; + ifname = null; + scope_ifname_set = scope_id_set = false; + scope_id = 0; + nif = null; + } + + /** + * Utility routine to check if the InetAddress is an IP multicast address + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + return ipaddress[0] == (byte) 0xFF; + } + + /** + * Utility routine to check if the InetAddress in a wildcard address + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + byte[] anylocal = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + return Arrays.equals(ipaddress, anylocal); + } + + /** + * Utility routine to check if the InetAddress is a loopback address + * + * @since 1.4 + */ + public boolean isLoopbackAddress() + { + byte[] loopback = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + + return Arrays.equals(ipaddress, loopback); + } + + /** + * Utility routine to check if the InetAddress is an link local address + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + return ipaddress[0] == 0xFA; + } + + /** + * Utility routine to check if the InetAddress is a site local address + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + return ipaddress[0] == 0xFB; + } + + /** + * Utility routine to check if the multicast address has global scope + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0xE; + } + + /** + * Utility routine to check if the multicast address has node scope + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x1; + } + + /** + * Utility routine to check if the multicast address has link scope + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x2; + } + + /** + * Utility routine to check if the multicast address has site scope + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x5; + } + + /** + * Utility routine to check if the multicast address has organization scope + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + if (! isMulticastAddress()) + return false; + + return (ipaddress[1] & 0x0F) == 0x8; + } + + /** + * Returns the raw IP address of this InetAddress object. The result is in + * network byte order: the highest order byte of the address is i + * n getAddress()[0] + */ + public byte[] getAddress() + { + return (byte[]) ipaddress.clone(); + } + + /** + * Creates a scoped Inet6Address where the scope has an integer id. + * + * @throws UnkownHostException if the address is an invalid number of bytes. + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + int scopeId) + throws UnknownHostException + { + if( addr.length != 16 ) + throw new UnknownHostException("Illegal address length: " + addr.length + + " bytes."); + Inet6Address ip = new Inet6Address( addr, host ); + ip.scope_id = scopeId; + ip.scope_id_set = true; + return ip; + } + + /** + * Creates a scoped Inet6Address where the scope is a given + * NetworkInterface. + * + * @throws UnkownHostException if the address is an invalid number of bytes. + * @since 1.5 + */ + public static Inet6Address getByAddress(String host, byte[] addr, + NetworkInterface nif) + throws UnknownHostException + { + if( addr.length != 16 ) + throw new UnknownHostException("Illegal address length: " + addr.length + + " bytes."); + Inet6Address ip = new Inet6Address( addr, host ); + ip.nif = nif; + + return ip; + } + + /** + * Returns the NetworkInterface of the address scope + * if it is a scoped address and the scope is given in the form of a + * NetworkInterface. + * (I.e. the address was created using the + * getByAddress(String, byte[], NetworkInterface) method) + * Otherwise this method returns null. + * @since 1.5 + */ + public NetworkInterface getScopedInterface() + { + return nif; + } + + /** + * Returns the scope ID of the address scope if it is a scoped adress using + * an integer to identify the scope. + * + * Otherwise this method returns 0. + * @since 1.5 + */ + public int getScopeId() + { + // check scope_id_set because some JDK-serialized objects seem to have + // scope_id set to a nonzero value even when scope_id_set == false + if( scope_id_set ) + return scope_id; + return 0; + } + + /** + * Returns the IP address string in textual presentation + */ + public String getHostAddress() + { + CPStringBuilder sbuf = new CPStringBuilder(40); + + for (int i = 0; i < 16; i += 2) + { + int x = ((ipaddress[i] & 0xFF) << 8) | (ipaddress[i + 1] & 0xFF); + + if (i > 0) + sbuf.append(':'); + + sbuf.append(Integer.toHexString(x)); + } + if( nif != null ) + sbuf.append( "%" + nif.getName() ); + else if( scope_id_set ) + sbuf.append( "%" + scope_id ); + + return sbuf.toString(); + } + + /** + * Returns a hashcode for this IP address + * (The hashcode is independent of scope) + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + * Compares this object against the specified object + */ + public boolean equals(Object obj) + { + if (! (obj instanceof Inet6Address)) + return false; + + Inet6Address ip = (Inet6Address)obj; + if (ipaddress.length != ip.ipaddress.length) + return false; + + for (int i = 0; i < ip.ipaddress.length; i++) + if (ipaddress[i] != ip.ipaddress[i]) + return false; + + if( ip.nif != null && nif != null ) + return nif.equals( ip.nif ); + if( ip.nif != nif ) + return false; + if( ip.scope_id_set != scope_id_set ) + return false; + if( scope_id_set ) + return (scope_id == ip.scope_id); + return true; + } + + /** + * Utility routine to check if the InetAddress is an + * IPv4 compatible IPv6 address + * + * @since 1.4 + */ + public boolean isIPv4CompatibleAddress() + { + if (ipaddress[0] != 0x00 || ipaddress[1] != 0x00 || ipaddress[2] != 0x00 + || ipaddress[3] != 0x00 || ipaddress[4] != 0x00 + || ipaddress[5] != 0x00 || ipaddress[6] != 0x00 + || ipaddress[7] != 0x00 || ipaddress[8] != 0x00 + || ipaddress[9] != 0x00 || ipaddress[10] != 0x00 + || ipaddress[11] != 0x00) + return false; + + return true; + } + + /** + * Required for 1.5-compatible serialization. + * @since 1.5 + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + try + { + if( scope_ifname_set ) + nif = NetworkInterface.getByName( ifname ); + } + catch( SocketException se ) + { + // FIXME: Ignore this? or throw an IOException? + } + } + + /** + * Required for 1.5-compatible serialization. + * @since 1.5 + */ + private void writeObject(ObjectOutputStream s) + throws IOException + { + if( nif != null ) + { + ifname = nif.getName(); + scope_ifname_set = true; + } + s.defaultWriteObject(); + } +} diff --git a/libjava/classpath/java/net/InetAddress.java b/libjava/classpath/java/net/InetAddress.java new file mode 100644 index 000000000..1a9dc6202 --- /dev/null +++ b/libjava/classpath/java/net/InetAddress.java @@ -0,0 +1,655 @@ +/* InetAddress.java -- Class to model an Internet address + Copyright (C) 1998, 1999, 2002, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; +import java.io.Serializable; + +/** + * This class models an Internet address. It does not have a public + * constructor. Instead, new instances of this objects are created + * using the static methods getLocalHost(), getByName(), and + * getAllByName(). + * + *

      This class fulfills the function of the C style functions gethostname(), + * gethostbyname(), and gethostbyaddr(). It resolves Internet DNS names + * into their corresponding numeric addresses and vice versa.

      + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * @author Gary Benson (gbenson@redhat.com) + * + * @specnote This class is not final since JDK 1.4 + */ +public class InetAddress implements Serializable +{ + private static final long serialVersionUID = 3286316764910316507L; + + /** + * Dummy InetAddress, used to bind socket to any (all) network interfaces. + */ + static InetAddress ANY_IF; + static + { + byte[] addr; + try + { + addr = VMInetAddress.lookupInaddrAny(); + } + catch (UnknownHostException e) + { + // Make one up and hope it works. + addr = new byte[] {0, 0, 0, 0}; + } + try + { + ANY_IF = getByAddress(addr); + } + catch (UnknownHostException e) + { + throw (InternalError) new InternalError().initCause(e); + } + ANY_IF.hostName = ANY_IF.getHostName(); + } + + /** + * Stores static localhost address object. + */ + static InetAddress LOCALHOST; + static + { + try + { + LOCALHOST = getByAddress("localhost", new byte[] {127, 0, 0, 1}); + } + catch (UnknownHostException e) + { + throw (InternalError) new InternalError().initCause(e); + } + } + + /** + * The Serialized Form specifies that an int 'address' is saved/restored. + * This class uses a byte array internally so we'll just do the conversion + * at serialization time and leave the rest of the algorithm as is. + */ + private int address; + + /** + * An array of octets representing an IP address. + */ + transient byte[] addr; + + /** + * The name of the host for this address. + */ + String hostName; + + /** + * Needed for serialization. + */ + private int family; + + /** + * Constructor. Prior to the introduction of IPv6 support in 1.4, + * methods such as InetAddress.getByName() would return InetAddress + * objects. From 1.4 such methods returned either Inet4Address or + * Inet6Address objects, but for compatibility Inet4Address objects + * are serialized as InetAddresses. As such, there are only two + * places where it is appropriate to invoke this constructor: within + * subclasses constructors and within Inet4Address.writeReplace(). + * + * @param ipaddr The IP number of this address as an array of bytes + * @param hostname The hostname of this IP address. + * @param family The address family of this IP address. + */ + InetAddress(byte[] ipaddr, String hostname, int family) + { + addr = (null == ipaddr) ? null : (byte[]) ipaddr.clone(); + hostName = hostname; + this.family = family; + } + + /** + * Returns true if this address is a multicast address, false otherwise. + * An address is multicast if the high four bits are "1110". These are + * also known as "Class D" addresses. + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @return true if mulitcast, false if not + * + * @since 1.1 + */ + public boolean isMulticastAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if the InetAddress in a wildcard address + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isAnyLocalAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if the InetAddress is a loopback address + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isLoopbackAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a link local address + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isLinkLocalAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a site local address + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isSiteLocalAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a global multicast address + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isMCGlobal() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a node local multicast address. + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isMCNodeLocal() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a link local multicast address. + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isMCLinkLocal() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a site local multicast address. + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isMCSiteLocal() + { + throw new UnsupportedOperationException(); + } + + /** + * Utility routine to check if InetAddress is a organization local + * multicast address. + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @since 1.4 + */ + public boolean isMCOrgLocal() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the hostname for this address. This will return the IP address + * as a String if there is no hostname available for this address + * + * @return The hostname for this address + */ + public String getHostName() + { + if (hostName == null) + hostName = getCanonicalHostName(); + + return hostName; + } + + /** + * Returns the canonical hostname represented by this InetAddress + */ + String internalGetCanonicalHostName() + { + try + { + return ResolverCache.getHostByAddr(addr); + } + catch (UnknownHostException e) + { + return getHostAddress(); + } + } + + /** + * Returns the canonical hostname represented by this InetAddress + * + * @since 1.4 + */ + public String getCanonicalHostName() + { + String hostname = internalGetCanonicalHostName(); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { + try + { + sm.checkConnect(hostname, -1); + } + catch (SecurityException e) + { + return getHostAddress(); + } + } + + return hostname; + } + + /** + * Returns the IP address of this object as a byte array. + * + * @return IP address + */ + public byte[] getAddress() + { + // An experiment shows that JDK1.2 returns a different byte array each + // time. This makes sense, in terms of security. + return (byte[]) addr.clone(); + } + + /** + * Returns the IP address of this object as a String. + * + *

      This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

      + * + * @return The IP address of this object in String form + * + * @since 1.0.2 + */ + public String getHostAddress() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns a hash value for this address. Useful for creating hash + * tables. Overrides Object.hashCode() + * + * @return A hash value for this address. + */ + public int hashCode() + { + // There hashing algorithm is not specified, but a simple experiment + // shows that it is equal to the address, as a 32-bit big-endian integer. + int hash = 0; + int len = addr.length; + int i = len > 4 ? len - 4 : 0; + + for (; i < len; i++) + hash = (hash << 8) | (addr[i] & 0xff); + + return hash; + } + + /** + * Tests this address for equality against another InetAddress. The two + * addresses are considered equal if they contain the exact same octets. + * This implementation overrides Object.equals() + * + * @param obj The address to test for equality + * + * @return true if the passed in object's address is equal to this one's, + * false otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof InetAddress)) + return false; + + // "The Java Class Libraries" 2nd edition says "If a machine has + // multiple names instances of InetAddress for different name of + // that same machine are not equal. This is because they have + // different host names." This violates the description in the + // JDK 1.2 API documentation. A little experimentation + // shows that the latter is correct. + byte[] addr2 = ((InetAddress) obj).addr; + + if (addr.length != addr2.length) + return false; + + for (int i = 0; i < addr.length; i++) + if (addr[i] != addr2[i]) + return false; + + return true; + } + + /** + * Converts this address to a String. This string contains the IP in + * dotted decimal form. For example: "127.0.0.1" This method is equivalent + * to getHostAddress() and overrides Object.toString() + * + * @return This address in String form + */ + public String toString() + { + String addr = getHostAddress(); + String host = (hostName != null) ? hostName : ""; + return host + "/" + addr; + } + + /** + * Returns an InetAddress object given the raw IP address. + * + * The argument is in network byte order: the highest order byte of the + * address is in getAddress()[0]. + * + * @param addr The IP address to create the InetAddress object from + * + * @exception UnknownHostException If IP address has illegal length + * + * @since 1.4 + */ + public static InetAddress getByAddress(byte[] addr) + throws UnknownHostException + { + return getByAddress(null, addr); + } + + /** + * Creates an InetAddress based on the provided host name and IP address. + * No name service is checked for the validity of the address. + * + * @param host The hostname of the InetAddress object to create + * @param addr The IP address to create the InetAddress object from + * + * @exception UnknownHostException If IP address is of illegal length + * + * @since 1.4 + */ + public static InetAddress getByAddress(String host, byte[] addr) + throws UnknownHostException + { + if (addr.length == 4) + return new Inet4Address(addr, host); + + if (addr.length == 16) + { + for (int i = 0; i < 12; i++) + { + if (addr[i] != (i < 10 ? 0 : (byte) 0xFF)) + return new Inet6Address(addr, host); + } + + byte[] ip4addr = new byte[4]; + ip4addr[0] = addr[12]; + ip4addr[1] = addr[13]; + ip4addr[2] = addr[14]; + ip4addr[3] = addr[15]; + return new Inet4Address(ip4addr, host); + } + + throw new UnknownHostException("IP address has illegal length"); + } + + /** + * Returns an InetAddress object representing the IP address of + * the given literal IP address in dotted decimal format such as + * "127.0.0.1". This is used by SocketPermission.setHostPort() + * to parse literal IP addresses without performing a DNS lookup. + * + * @param literal The literal IP address to create the InetAddress + * object from + * + * @return The address of the host as an InetAddress object, or + * null if the IP address is invalid. + */ + static InetAddress getByLiteral(String literal) + { + byte[] address = VMInetAddress.aton(literal); + if (address == null) + return null; + + try + { + return getByAddress(address); + } + catch (UnknownHostException e) + { + throw (InternalError) new InternalError().initCause(e); + } + } + + /** + * Returns an InetAddress object representing the IP address of the given + * hostname. This name can be either a hostname such as "www.urbanophile.com" + * or an IP address in dotted decimal format such as "127.0.0.1". If the + * hostname is null or "", the hostname of the local machine is supplied by + * default. This method is equivalent to returning the first element in + * the InetAddress array returned from GetAllByName. + * + * @param hostname The name of the desired host, or null for the local + * loopback address. + * + * @return The address of the host as an InetAddress object. + * + * @exception UnknownHostException If no IP address for the host could + * be found + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public static InetAddress getByName(String hostname) + throws UnknownHostException + { + InetAddress[] addresses = getAllByName(hostname); + return addresses[0]; + } + + /** + * Returns an array of InetAddress objects representing all the host/ip + * addresses of a given host, given the host's name. This name can be + * either a hostname such as "www.urbanophile.com" or an IP address in + * dotted decimal format such as "127.0.0.1". If the value is null, the + * hostname of the local machine is supplied by default. + * + * @param hostname The name of the desired host, or null for the + * local loopback address. + * + * @return All addresses of the host as an array of InetAddress objects. + * + * @exception UnknownHostException If no IP address for the host could + * be found + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public static InetAddress[] getAllByName(String hostname) + throws UnknownHostException + { + // If null or the empty string is supplied, the loopback address + // is returned. + if (hostname == null || hostname.length() == 0) + return new InetAddress[] {LOCALHOST}; + + // Check if hostname is an IP address + InetAddress address = getByLiteral(hostname); + if (address != null) + return new InetAddress[] {address}; + + // Perform security check before resolving + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(hostname, -1); + + // Resolve the hostname + byte[][] iplist = ResolverCache.getHostByName(hostname); + if (iplist.length == 0) + throw new UnknownHostException(hostname); + + InetAddress[] addresses = new InetAddress[iplist.length]; + for (int i = 0; i < iplist.length; i++) + addresses[i] = getByAddress(hostname, iplist[i]); + + return addresses; + } + + /** + * Returns an InetAddress object representing the address of the current + * host. + * + * @return The local host's address + * + * @exception UnknownHostException If no IP address for the host could + * be found + */ + public static InetAddress getLocalHost() throws UnknownHostException + { + String hostname = VMInetAddress.getLocalHostname(); + try + { + return getByName(hostname); + } + catch (SecurityException e) + { + return LOCALHOST; + } + } + + /** + * Inet4Address objects are serialized as InetAddress objects. + * This deserializes them back into Inet4Address objects. + */ + private Object readResolve() throws ObjectStreamException + { + return new Inet4Address(addr, hostName); + } + + private void readObject(ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + addr = new byte[4]; + addr[3] = (byte) address; + + for (int i = 2; i >= 0; --i) + addr[i] = (byte) (address >>= 8); + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + // Build a 32 bit address from the last 4 bytes of a 4 byte IPv4 address + // or a 16 byte IPv6 address. + int len = addr.length; + int i = len - 4; + + for (; i < len; i++) + address = address << 8 | (addr[i] & 0xff); + + oos.defaultWriteObject(); + } +} diff --git a/libjava/classpath/java/net/InetSocketAddress.java b/libjava/classpath/java/net/InetSocketAddress.java new file mode 100644 index 000000000..2126f7703 --- /dev/null +++ b/libjava/classpath/java/net/InetSocketAddress.java @@ -0,0 +1,261 @@ +/* InetSocketAddress.java -- + Copyright (C) 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * InetSocketAddress instances represent socket addresses + * in the java.nio package. They encapsulate a InetAddress and + * a port number. + * + * @since 1.4 + */ +public class InetSocketAddress extends SocketAddress +{ + /** + * Compatible with JDK 1.4+ + */ + private static final long serialVersionUID = 5076001401234631237L; + + /** + * Name of host. + */ + private String hostname; + + /** + * Address of host. + */ + private InetAddress addr; + + /** + * Port of host. + */ + private int port; + + /** + * Constructs an InetSocketAddress instance. + * + * @param addr Address of the socket + * @param port Port if the socket + * + * @exception IllegalArgumentException If the port number is illegel + */ + public InetSocketAddress(InetAddress addr, int port) + throws IllegalArgumentException + { + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Bad port number: " + port); + + if (addr == null) + addr = InetAddress.ANY_IF; + + this.addr = addr; + this.port = port; + } + + /** + * Constructs an InetSocketAddress instance. + * + * @param port Port if the socket + * + * @exception IllegalArgumentException If the port number is illegal + */ + public InetSocketAddress(int port) throws IllegalArgumentException + { + this((InetAddress) null, port); + } + + /** + * Constructs an InetSocketAddress instance. + * + * @param hostname The hostname for the socket address + * @param port The port for the socket address + * + * @exception IllegalArgumentException If the port number is illegal or + * the hostname argument is null + */ + public InetSocketAddress(String hostname, int port) + { + this(hostname, port, true); + } + + /** + * Constructs an InetSocketAddress instance. + * + * @param hostname The hostname for the socket address + * @param port The port for the socket address + * @param resolve true if the address has to be resolved, + * false otherwise + * + * @exception IllegalArgumentException If the port number is illegal or + * the hostname argument is null + */ + private InetSocketAddress(String hostname, int port, boolean resolve) + { + if (hostname == null) + throw new IllegalArgumentException("Null host name value"); + + if (port < 0 || port > 65535) + throw new IllegalArgumentException("Bad port number: " + port); + + this.port = port; + this.hostname = hostname; + this.addr = null; + + if (resolve) + { + try + { + this.addr = InetAddress.getByName(hostname); + } + catch (Exception e) // UnknownHostException, SecurityException + { + // Do nothing here. this.addr is already set to null. + } + } + + } + + /** + * Creates an unresolved InetSocketAddress object. + * + * @param hostname The hostname for the socket address + * @param port The port for the socket address + * + * @exception IllegalArgumentException If the port number is illegal or + * the hostname argument is null + * + * @since 1.5 + */ + public static InetSocketAddress createUnresolved(String hostname, int port) + { + return new InetSocketAddress(hostname, port, false); + } + + /** + * Test if obj is a InetSocketAddress and + * has the same address and port + * + * @param obj The obj to compare this address with. + * + * @return True if obj is equal. + */ + public final boolean equals(Object obj) + { + // InetSocketAddress objects are equal when addr and port are equal. + // The hostname may differ. + if (obj instanceof InetSocketAddress) + { + InetSocketAddress sa = (InetSocketAddress) obj; + + if (addr == null && sa.addr != null) + return false; + else if (addr == null && sa.addr == null) // we know hostname != null + return hostname.equals(sa.hostname) && sa.port == port; + else + return addr.equals(sa.addr) && sa.port == port; + } + + return false; + } + + /** + * Returns the InetAddress or + * null if its unresolved + * + * @return The IP address of this address. + */ + public final InetAddress getAddress() + { + return addr; + } + + /** + * Returns hostname + * + * @return The hostname of this address. + */ + public final String getHostName() + { + if (hostname == null) // we know addr != null + hostname = addr.getHostName(); + + return hostname; + } + + /** + * Returns the port + * + * @return The port of this address. + */ + public final int getPort() + { + return port; + } + + /** + * Returns the hashcode of the InetSocketAddress + * + * @return The hashcode for this address. + */ + public final int hashCode() + { + return port + addr.hashCode(); + } + + /** + * Checks wether the address has been resolved or not + * + * @return True if address is unresolved. + */ + public final boolean isUnresolved() + { + return addr == null; + } + + /** + * Returns the InetSocketAddress as string + * + * @return A string representation of this address. + */ + public String toString() + { + // Note: if addr is null, then hostname != null. + return (addr == null ? hostname : addr.toString()) + ":" + port; + } +} diff --git a/libjava/classpath/java/net/JarURLConnection.java b/libjava/classpath/java/net/JarURLConnection.java new file mode 100644 index 000000000..0ffd1afac --- /dev/null +++ b/libjava/classpath/java/net/JarURLConnection.java @@ -0,0 +1,229 @@ +/* JarURLConnection.java -- Class for manipulating remote jar files + Copyright (C) 1998, 2002, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + + +/** + * This abstract class represents a common superclass for implementations + * of jar URL's. A jar URL is a special type of URL that allows JAR + * files on remote systems to be accessed. It has the form: + *

      + * jar:<standard URL pointing to jar filei>!/file/within/jarfile + *

      for example: + *

      + * jar:http://www.urbanophile.com/java/foo.jar!/com/urbanophile/bar.class + *

      + * That example URL points to the file /com/urbanophile/bar.class in the + * remote JAR file http://www.urbanophile.com/java/foo.jar. The HTTP + * protocol is used only as an example. Any supported remote protocol + * can be used. + *

      + * This class currently works by retrieving the entire jar file into a + * local cache file, then performing standard jar operations on it. + * (At least this is true for the default protocol implementation). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Kresten Krab Thorup (krab@gnu.org) + * @date Aug 10, 1999. + * + * @since 1.2 + */ +public abstract class JarURLConnection extends URLConnection +{ + /** + * This is the actual URL that points the remote jar file. This is parsed + * out of the jar URL by the constructor. + */ + private final URL jarFileURL; + + /** + * The connection to the jar file itself. A JarURLConnection + * can represent an entry in a jar file or an entire jar file. In + * either case this describes just the jar file itself. + */ + protected URLConnection jarFileURLConnection; + + /** + * This is the jar file "entry name" or portion after the "!/" in the + * URL which represents the pathname inside the actual jar file. + */ + private final String entryName; + + /** + * Creates a JarURLConnection from an URL object + * + * @param url The URL object for this connection. + * + * @exception MalformedURLException If url is invalid + * + * @specnote This constructor is protected since JDK 1.4 + */ + protected JarURLConnection(URL url) throws MalformedURLException + { + super(url); + + if (! url.getProtocol().equals("jar")) + throw new MalformedURLException(url + ": Not jar protocol."); + + String spec = url.getFile(); + int bang = spec.indexOf("!/"); + if (bang == -1) + throw new MalformedURLException(url + ": No `!/' in spec."); + + // Extract the url for the jar itself. + jarFileURL = new URL(spec.substring(0, bang)); + + // Get the name of the entry, if any. + entryName = spec.length() == (bang + 2) ? null : spec.substring(bang + 2); + } + + /** + * This method returns the "real" URL where the JarFile is located. + * //****Is this right?***** + * + * @return The remote URL + */ + public URL getJarFileURL() + { + return jarFileURL; + } + + /** + * Returns the "entry name" portion of the jar URL. This is the portion + * after the "!/" in the jar URL that represents the pathname inside the + * actual jar file. + * + * @return The entry name. + */ + public String getEntryName() + { + return entryName; + } + + /** + * Returns the entry in this jar file specified by the URL. + * + * @return The jar entry + * + * @exception IOException If an error occurs + */ + public JarEntry getJarEntry() throws IOException + { + if (entryName == null) + return null; + JarFile jarFile = getJarFile(); + return jarFile != null ? jarFile.getJarEntry(entryName) : null; + } + + /** + * Returns a read-only JarFile object for the remote jar file + * + * @return The JarFile object + * + * @exception IOException If an error occurs + */ + public abstract JarFile getJarFile() throws IOException; + + /** + * Returns an array of Certificate objects for the jar file entry specified + * by this URL or null if there are none + * + * @return A Certificate array + * + * @exception IOException If an error occurs + */ + public Certificate[] getCertificates() throws IOException + { + JarEntry entry = getJarEntry(); + + return entry != null ? entry.getCertificates() : null; + } + + /** + * Returns the main Attributes for the jar file specified in the URL or + * null if there are none + * + * @return The main Attributes for the JAR file for this connection + * + * @exception IOException If an error occurs + */ + public Attributes getMainAttributes() throws IOException + { + Manifest manifest = getManifest(); + + return manifest != null ? manifest.getMainAttributes() : null; + } + + /** + * Returns the Attributes for the Jar entry specified by the URL or null + * if none + * + * @return The Attributes object for this connection if the URL for it points + * to a JAR file entry, null otherwise + * + * @exception IOException If an error occurs + */ + public Attributes getAttributes() throws IOException + { + JarEntry entry = getJarEntry(); + + return entry != null ? entry.getAttributes() : null; + } + + /** + * Returns a Manifest object for this jar file, or null if there is no + * manifest. + * + * @return The Manifest for this connection, or null if none + * + * @exception IOException If an error occurs + */ + public Manifest getManifest() throws IOException + { + JarFile file = getJarFile(); + + return file != null ? file.getManifest() : null; + } +} diff --git a/libjava/classpath/java/net/MalformedURLException.java b/libjava/classpath/java/net/MalformedURLException.java new file mode 100644 index 000000000..27e201886 --- /dev/null +++ b/libjava/classpath/java/net/MalformedURLException.java @@ -0,0 +1,74 @@ +/* MalformedURLException.java -- A URL was not in a valid format + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that a URL passed to an object was not in a + * valid format. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class MalformedURLException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -182787522200415866L; + + /** + * Create a new instance without a descriptive error message. + */ + public MalformedURLException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public MalformedURLException(String message) + { + super(message); + } +} // class MalformedURLException diff --git a/libjava/classpath/java/net/MimeTypeMapper.java b/libjava/classpath/java/net/MimeTypeMapper.java new file mode 100644 index 000000000..3e4379291 --- /dev/null +++ b/libjava/classpath/java/net/MimeTypeMapper.java @@ -0,0 +1,346 @@ +/* MimeTypeMapper.java -- A class for mapping file names to MIME types + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.classpath.SystemProperties; + +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; +import java.util.TreeMap; + + +/** + * This non-public class is used to implement the FileNameMap interface + * which defines a mechanism for mapping filenames to MIME types. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class MimeTypeMapper implements FileNameMap +{ + /** + * This array of strings is used to identify a MIME type based on a file + * extension. This is list is based on the Apache mime.types file. + */ + protected static final String[][] mime_strings = + { + { "ai", "application/postscript" } + , { "aif", "audio/x-aiff" } + , { "aifc", "audio/x-aiff" } + , { "aiff", "audio/x-aiff" } + , { "asc", "text/plain" } + , { "au", "audio/basic" } + , { "avi", "video/x-msvideo" } + , { "bcpio", "application/x-bcpio" } + , { "bin", "application/octet-stream" } + , { "bmp", "image/bmp" } + , { "bz2", "application/x-bzip2" } + , { "cdf", "application/x-netcdf" } + , { "chrt", "application/x-kchart" } + , { "class", "application/octet-stream" } + , { "cpio", "application/x-cpio" } + , { "cpt", "application/mac-compactpro" } + , { "csh", "application/x-csh" } + , { "css", "text/css" } + , { "dcr", "application/x-director" } + , { "dir", "application/x-director" } + , { "djv", "image/vnd.djvu" } + , { "djvu", "image/vnd.djvu" } + , { "dll", "application/octet-stream" } + , { "dms", "application/octet-stream" } + , { "doc", "application/msword" } + , { "dvi", "application/x-dvi" } + , { "dxr", "application/x-director" } + , { "eps", "application/postscript" } + , { "etx", "text/x-setext" } + , { "exe", "application/octet-stream" } + , { "ez", "application/andrew-inset" } + , { "gif", "image/gif" } + , { "gtar", "application/x-gtar" } + , { "gz", "application/x-gzip" } + , { "hdf", "application/x-hdf" } + , { "hqx", "application/mac-binhex40" } + , { "htm", "text/html" } + , { "html", "text/html" } + , { "ice", "x-conference/x-cooltalk" } + , { "ief", "image/ief" } + , { "iges", "model/iges" } + , { "igs", "model/iges" } + , { "img", "application/octet-stream" } + , { "iso", "application/octet-stream" } + , { "jpe", "image/jpeg" } + , { "jpeg", "image/jpeg" } + , { "jpg", "image/jpeg" } + , { "js", "application/x-javascript" } + , { "kar", "audio/midi" } + , { "kil", "application/x-killustrator" } + , { "kpr", "application/x-kpresenter" } + , { "kpt", "application/x-kpresenter" } + , { "ksp", "application/x-kspread" } + , { "kwd", "application/x-kword" } + , { "kwt", "application/x-kword" } + , { "latex", "application/x-latex" } + , { "lha", "application/octet-stream" } + , { "lzh", "application/octet-stream" } + , { "m3u", "audio/x-mpegurl" } + , { "man", "application/x-troff-man" } + , { "me", "application/x-troff-me" } + , { "mesh", "model/mesh" } + , { "mid", "audio/midi" } + , { "midi", "audio/midi" } + , { "mif", "application/vnd.mif" } + , { "mov", "video/quicktime" } + , { "movie", "video/x-sgi-movie" } + , { "mp2", "audio/mpeg" } + , { "mp3", "audio/mpeg" } + , { "mpe", "video/mpeg" } + , { "mpeg", "video/mpeg" } + , { "mpg", "video/mpeg" } + , { "mpga", "audio/mpeg" } + , { "ms", "application/x-troff-ms" } + , { "msh", "model/mesh" } + , { "mxu", "video/vnd.mpegurl" } + , { "nc", "application/x-netcdf" } + , { "ogg", "application/ogg" } + , { "pbm", "image/x-portable-bitmap" } + , { "pdb", "chemical/x-pdb" } + , { "pdf", "application/pdf" } + , { "pgm", "image/x-portable-graymap" } + , { "pgn", "application/x-chess-pgn" } + , { "png", "image/png" } + , { "pnm", "image/x-portable-anymap" } + , { "ppm", "image/x-portable-pixmap" } + , { "ppt", "application/vnd.ms-powerpoint" } + , { "ps", "application/postscript" } + , { "qt", "video/quicktime" } + , { "ra", "audio/x-realaudio" } + , { "ram", "audio/x-pn-realaudio" } + , { "ras", "image/x-cmu-raster" } + , { "rgb", "image/x-rgb" } + , { "rm", "audio/x-pn-realaudio" } + , { "roff", "application/x-troff" } + , { "rpm", "application/x-rpm" } + , { "rtf", "text/rtf" } + , { "rtx", "text/richtext" } + , { "sgm", "text/sgml" } + , { "sgml", "text/sgml" } + , { "sh", "application/x-sh" } + , { "shar", "application/x-shar" } + , { "silo", "model/mesh" } + , { "sit", "application/x-stuffit" } + , { "skd", "application/x-koan" } + , { "skm", "application/x-koan" } + , { "skp", "application/x-koan" } + , { "skt", "application/x-koan" } + , { "smi", "application/smil" } + , { "smil", "application/smil" } + , { "snd", "audio/basic" } + , { "so", "application/octet-stream" } + , { "spl", "application/x-futuresplash" } + , { "src", "application/x-wais-source" } + , { "stc", "application/vnd.sun.xml.calc.template" } + , { "std", "application/vnd.sun.xml.draw.template" } + , { "sti", "application/vnd.sun.xml.impress.template" } + , { "stw", "application/vnd.sun.xml.writer.template" } + , { "sv4cpio", "application/x-sv4cpio" } + , { "sv4crc", "application/x-sv4crc" } + , { "swf", "application/x-shockwave-flash" } + , { "sxc", "application/vnd.sun.xml.calc" } + , { "sxd", "application/vnd.sun.xml.draw" } + , { "sxg", "application/vnd.sun.xml.writer.global" } + , { "sxi", "application/vnd.sun.xml.impress" } + , { "sxm", "application/vnd.sun.xml.math" } + , { "sxw", "application/vnd.sun.xml.writer" } + , { "t", "application/x-troff" } + , { "tar", "application/x-tar" } + , { "tcl", "application/x-tcl" } + , { "tex", "application/x-tex" } + , { "texi", "application/x-texinfo" } + , { "texinfo", "application/x-texinfo" } + , { "tgz", "application/x-gzip" } + , { "tif", "image/tiff" } + , { "tiff", "image/tiff" } + , { "torrent", "application/x-bittorrent" } + , { "tr", "application/x-troff" } + , { "tsv", "text/tab-separated-values" } + , { "txt", "text/plain" } + , { "ustar", "application/x-ustar" } + , { "vcd", "application/x-cdlink" } + , { "vrml", "model/vrml" } + , { "wav", "audio/x-wav" } + , { "wbmp", "image/vnd.wap.wbmp" } + , { "wbxml", "application/vnd.wap.wbxml" } + , { "wml", "text/vnd.wap.wml" } + , { "wmlc", "application/vnd.wap.wmlc" } + , { "wmls", "text/vnd.wap.wmlscript" } + , { "wmlsc", "application/vnd.wap.wmlscriptc" } + , { "wrl", "model/vrml" } + , { "xbm", "image/x-xbitmap" } + , { "xht", "application/xhtml+xml" } + , { "xhtml", "application/xhtml+xml" } + , { "xls", "application/vnd.ms-excel" } + , { "xml", "text/xml" } + , { "xpm", "image/x-xpixmap" } + , { "xsl", "text/xml" } + , { "xwd", "image/x-xwindowdump" } + , { "xyz", "chemical/x-xyz" } + , { "zip", "application/zip" } + }; + + /** + * The MIME types above are put into this Hashtable for faster lookup. + */ + private Hashtable mime_types + = new Hashtable(150); + + /** + * Create a new MimeTypeMapper object. + */ + public MimeTypeMapper() + { + for (int i = 0; i < mime_strings.length; i++) + mime_types.put(mime_strings[i][0], mime_strings[i][1]); + + // Now read from the system mime database, if it exists. Entries found + // here override our internal ones. + try + { + // On Linux this usually means /etc/mime.types. + String file + = SystemProperties.getProperty("gnu.classpath.mime.types.file"); + if (file != null) + fillFromFile(mime_types, file); + } + catch (IOException ignore) + { + } + } + + public static void fillFromFile (Map table, String fname) + throws IOException + { + LineNumberReader reader = + new LineNumberReader (new FileReader (fname)); + + while (reader.ready ()) + { + StringTokenizer tokenizer = + new StringTokenizer (reader.readLine ()); + + try + { + String t = tokenizer.nextToken (); + + if (! t.startsWith ("#")) + { + while (true) + { + // Read the next extension + String e = tokenizer.nextToken (); + if ((e != null) && (! e.startsWith ("#"))) + table.put (e, t); + else + break; + } + } + } + catch (NoSuchElementException ex) + { + // Do nothing. + } + } + } + + /** + * The method returns the MIME type of the filename passed as an argument. + * The value returned is based on the extension of the filename. The + * default content type returned if this method cannot determine the + * actual content type is "application/octet-stream" + * + * @param filename The name of the file to return the MIME type for + * + * @return The MIME type + */ + public String getContentTypeFor(String filename) + { + int index = filename.lastIndexOf("."); + if (index != -1) + { + if (index == filename.length()) + return "application/octet-stream"; + else + filename = filename.substring(index + 1); + } + + String type = (String) mime_types.get(filename); + if (type == null) + return "application/octet-stream"; + else + return type; + } + + /** + * Run this class as a program to create a new mime_strings table. + */ + public static void main(String[] args) throws IOException + { + TreeMap map = new TreeMap(); + // It is fine to hard-code the name here. This is only ever + // used by maintainers, who can hack it if they need to re-run + // it. + fillFromFile(map, "/etc/mime.types"); + Iterator it = map.keySet().iterator(); + boolean first = true; + while (it.hasNext()) + { + String key = it.next(); + String value = map.get(key); + // Put the "," first since it is easier to make correct syntax this way. + System.out.println(" " + (first ? " " : ", ") + + "{ \"" + key + "\", \"" + value + "\" }"); + first = false; + } + } +} diff --git a/libjava/classpath/java/net/MulticastSocket.java b/libjava/classpath/java/net/MulticastSocket.java new file mode 100644 index 000000000..5014b6a8e --- /dev/null +++ b/libjava/classpath/java/net/MulticastSocket.java @@ -0,0 +1,518 @@ +/* MulticastSocket.java -- Class for using multicast sockets + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; +import java.util.Enumeration; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This class models a multicast UDP socket. A multicast address is a + * class D internet address (one whose most significant bits are 1110). + * A multicast group consists of a multicast address and a well known + * port number. All members of the group listening on that address and + * port will receive all the broadcasts to the group. + *

      + * Please note that applets are not allowed to use multicast sockets + * + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) (Documentation comments) + * @since 1.1 + * @date May 18, 1999. + */ +public class MulticastSocket extends DatagramSocket +{ + /** + * Create a MulticastSocket that this not bound to any address + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public MulticastSocket() throws IOException + { + this(new InetSocketAddress(0)); + } + + /** + * Create a multicast socket bound to the specified port + * + * @param port The port to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public MulticastSocket(int port) throws IOException + { + this(new InetSocketAddress(port)); + } + + /** + * Create a multicast socket bound to the specified SocketAddress. + * + * @param address The SocketAddress the multicast socket will be bound to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public MulticastSocket(SocketAddress address) throws IOException + { + super((SocketAddress) null); + setReuseAddress(true); + if (address != null) + bind(address); + } + + /** + * Returns the interface being used for multicast packets + * + * @return The multicast interface + * + * @exception SocketException If an error occurs + */ + public InetAddress getInterface() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + return (InetAddress) getImpl().getOption(SocketOptions.IP_MULTICAST_IF); + } + + /** + * Returns the current value of the "Time to Live" option. This is the + * number of hops a packet can make before it "expires". This method id + * deprecated. Use getTimeToLive instead. + * + * @return The TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 Replaced by getTimeToLive() + * + * @see MulticastSocket#getTimeToLive() + */ + public byte getTTL() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // Use getTTL here rather than getTimeToLive in case we're using an impl + // other than the default PlainDatagramSocketImpl and it doesn't have + // getTimeToLive yet. + return getImpl().getTTL(); + } + + /** + * Returns the current value of the "Time to Live" option. This is the + * number of hops a packet can make before it "expires". + * + * @return The TTL value + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public int getTimeToLive() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + return getImpl().getTimeToLive(); + } + + /** + * Sets the interface to use for sending multicast packets. + * + * @param addr The new interface to use. + * + * @exception SocketException If an error occurs. + * + * @since 1.4 + */ + public void setInterface(InetAddress addr) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.IP_MULTICAST_IF, addr); + } + + /** + * Sets the local network interface used to send multicast messages + * + * @param netIf The local network interface used to send multicast messages + * + * @exception SocketException If an error occurs + * + * @see MulticastSocket#getNetworkInterface() + * + * @since 1.4 + */ + public void setNetworkInterface(NetworkInterface netIf) + throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + InetAddress address; + if (netIf != null) + out: + { + Enumeration e = netIf.getInetAddresses(); + if (getLocalAddress() instanceof Inet4Address) + { + // Search for a IPv4 address. + while (e.hasMoreElements()) + { + address = (InetAddress) e.nextElement(); + if (address instanceof Inet4Address) + break out; + } + throw new SocketException("interface " + netIf.getName() + " has no IPv6 address"); + } + else if (getLocalAddress() instanceof Inet6Address) + { + // Search for a IPv6 address. + while (e.hasMoreElements()) + { + address = (InetAddress) e.nextElement(); + if (address instanceof Inet6Address) + break out; + } + throw new SocketException("interface " + netIf.getName() + " has no IPv6 address"); + } + else + throw new SocketException("interface " + netIf.getName() + " has no suitable IP address"); + } + else + address = InetAddress.ANY_IF; + + + getImpl().setOption(SocketOptions.IP_MULTICAST_IF, address); + } + + /** + * Gets the local network interface which is used to send multicast messages + * + * @return The local network interface to send multicast messages + * + * @exception SocketException If an error occurs + * + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public NetworkInterface getNetworkInterface() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + InetAddress address = + (InetAddress) getImpl().getOption(SocketOptions.IP_MULTICAST_IF); + + if (address.isAnyLocalAddress()) + return NetworkInterface.createAnyInterface(); + + NetworkInterface netIf = NetworkInterface.getByInetAddress(address); + + return netIf; + } + + /** + * Disable/Enable local loopback of multicast packets. The option is used by + * the platform's networking code as a hint for setting whether multicast + * data will be looped back to the local socket. + * + * Because this option is a hint, applications that want to verify what + * loopback mode is set to should call #getLoopbackMode + * + * @param disable True to disable loopback mode + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setLoopbackMode(boolean disable) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, + Boolean.valueOf(disable)); + } + + /** + * Checks if local loopback mode is enabled + * + * @return true if loopback mode is enabled, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getLoopbackMode() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP); + + if (buf instanceof Boolean) + return ((Boolean) buf).booleanValue(); + + throw new SocketException("unexpected type"); + } + + /** + * Sets the "Time to Live" value for a socket. The value must be between + * 1 and 255. + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + * + * @deprecated 1.2 Replaced by setTimeToLive + * + * @see MulticastSocket#setTimeToLive(int ttl) + */ + public void setTTL(byte ttl) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // Use setTTL here rather than setTimeToLive in case we're using an impl + // other than the default PlainDatagramSocketImpl and it doesn't have + // setTimeToLive yet. + getImpl().setTTL(ttl); + } + + /** + * Sets the "Time to Live" value for a socket. The value must be between + * 0 and 255, inclusive. + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + * + * @since 1.2 + */ + public void setTimeToLive(int ttl) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (ttl < 0 || ttl > 255) + throw new IllegalArgumentException("Invalid ttl: " + ttl); + + getImpl().setTimeToLive(ttl); + } + + /** + * Joins the specified multicast group. + * + * @param mcastaddr The address of the group to join + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + */ + public void joinGroup(InetAddress mcastaddr) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! mcastaddr.isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(mcastaddr); + + getImpl().join(mcastaddr); + } + + /** + * Leaves the specified multicast group + * + * @param mcastaddr The address of the group to leave + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + */ + public void leaveGroup(InetAddress mcastaddr) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! mcastaddr.isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(mcastaddr); + + getImpl().leave(mcastaddr); + } + + /** + * Joins the specified mulitcast group on a specified interface. + * + * @param mcastaddr The multicast address to join + * @param netIf The local network interface to receive the multicast + * messages on or null to defer the interface set by #setInterface or + * #setNetworkInterface + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + * + * @see MulticastSocket#setInterface(InetAddress addr) + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (mcastaddr instanceof InetSocketAddress)) + throw new IllegalArgumentException("SocketAddress type not supported"); + + InetSocketAddress tmp = (InetSocketAddress) mcastaddr; + + if (! tmp.getAddress().isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(tmp.getAddress()); + + getImpl().joinGroup(mcastaddr, netIf); + } + + /** + * Leaves the specified mulitcast group on a specified interface. + * + * @param mcastaddr The multicast address to leave + * @param netIf The local networki interface or null to defer to the + * interface set by setInterface or setNetworkInterface + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkMulticast method doesn't allow the operation + * + * @see MulticastSocket#setInterface(InetAddress addr) + * @see MulticastSocket#setNetworkInterface(NetworkInterface netIf) + * + * @since 1.4 + */ + public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + InetSocketAddress tmp = (InetSocketAddress) mcastaddr; + + if (! tmp.getAddress().isMulticastAddress()) + throw new IOException("Not a Multicast address"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkMulticast(tmp.getAddress()); + + getImpl().leaveGroup(mcastaddr, netIf); + } + + /** + * Sends a packet of data to a multicast address with a TTL that is + * different from the default TTL on this socket. The default TTL for + * the socket is not changed. + * + * @param packet The packet of data to send + * @param ttl The TTL for this packet + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect or checkMulticast method doesn't allow the operation + * + * @deprecated + */ + public synchronized void send(DatagramPacket packet, byte ttl) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + SecurityManager s = System.getSecurityManager(); + if (s != null) + { + InetAddress addr = packet.getAddress(); + if (addr.isMulticastAddress()) + s.checkPermission(new SocketPermission(addr.getHostName() + + packet.getPort(), + "accept,connect")); + else + s.checkConnect(addr.getHostAddress(), packet.getPort()); + } + + int oldttl = getImpl().getTimeToLive(); + getImpl().setTimeToLive(((int) ttl) & 0xFF); + getImpl().send(packet); + getImpl().setTimeToLive(oldttl); + } +} diff --git a/libjava/classpath/java/net/NetPermission.java b/libjava/classpath/java/net/NetPermission.java new file mode 100644 index 000000000..cabe54e06 --- /dev/null +++ b/libjava/classpath/java/net/NetPermission.java @@ -0,0 +1,90 @@ +/* NetPermission.java -- A class for basic miscellaneous network permission + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.security.BasicPermission; + + +/** + * This class is used to model miscellaneous network permissions. It is + * a subclass of BasicPermission. This means that it models a + * "boolean" permission. One that you either have or do not have. Thus + * there is no permitted action list associated with this object. + * + * The following permission names are defined for this class: + * + *

        + *
      • setDefaultAuthenticator - Grants the ability to install a facility + * to collect username and password information when requested by a + * web site or proxy server.
      • + *
      • requestPasswordAuthentication - Grants the ability to ask the + * authentication facility for the user's password.
      • + *
      • specifyStreamHandler - Grants the permission to specify the + * stream handler class used when loading from a URL.
      • + *
      + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class NetPermission extends BasicPermission +{ + static final long serialVersionUID = -8343910153355041693L; + + /** + * Initializes a new instance of NetPermission with the + * specified name. + * + * @param name The name of this permission. + */ + public NetPermission(String name) + { + super(name); + } + + /** + * Initializes a new instance of NetPermission with the + * specified name and perms. Note that the perms field is irrelevant and is + * ignored. This constructor should never need to be used. + * + * @param name The name of this permission + * @param perms The permitted actions of this permission (ignored) + */ + public NetPermission(String name, String perms) + { + super(name); + } +} diff --git a/libjava/classpath/java/net/NetworkInterface.java b/libjava/classpath/java/net/NetworkInterface.java new file mode 100644 index 000000000..127dfba78 --- /dev/null +++ b/libjava/classpath/java/net/NetworkInterface.java @@ -0,0 +1,316 @@ +/* NetworkInterface.java -- + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.CPStringBuilder; + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Vector; + +/** + * This class models a network interface on the host computer. A network + * interface contains a name (typically associated with a specific + * hardware adapter) and a list of addresses that are bound to it. + * For example, an ethernet interface may be named "eth0" and have the + * address 192.168.1.101 assigned to it. + * + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public final class NetworkInterface +{ + private final VMNetworkInterface netif; + + private NetworkInterface(VMNetworkInterface netif) + { + this.netif = netif; + } + + /** Creates an NetworkInterface instance which + * represents any interface in the system. Its only + * address is 0.0.0.0/0.0.0.0. This + * method is needed by {@link MulticastSocket#getNetworkInterface} + */ + static NetworkInterface createAnyInterface() + { + return new NetworkInterface(new VMNetworkInterface()); + } + + /** + * Returns the name of the network interface + * + * @return The name of the interface. + */ + public String getName() + { + return netif.name; + } + + /** + * Returns all available addresses of the network interface + * + * If a @see SecurityManager is available all addresses are checked + * with @see SecurityManager::checkConnect() if they are available. + * Only InetAddresses are returned where the security manager + * doesn't throw an exception. + * + * @return An enumeration of all addresses. + */ + public Enumeration getInetAddresses() + { + SecurityManager s = System.getSecurityManager(); + Vector inetAddresses + = new Vector(netif.addresses); + + if (s == null) + return inetAddresses.elements(); + + Vector tmpInetAddresses = new Vector(1, 1); + + for (Enumeration addresses = inetAddresses.elements(); + addresses.hasMoreElements();) + { + InetAddress addr = addresses.nextElement(); + try + { + s.checkConnect(addr.getHostAddress(), -1); + tmpInetAddresses.add(addr); + } + catch (SecurityException e) + { + // Ignore. + } + } + + return tmpInetAddresses.elements(); + } + + /** + * Returns the display name of the interface + * + * @return The display name of the interface + */ + public String getDisplayName() + { + return netif.name; + } + + /** + * Returns an network interface by name + * + * @param name The name of the interface to return + * + * @return a NetworkInterface object representing the interface, + * or null if there is no interface with that name. + * + * @exception SocketException If an error occurs + * @exception NullPointerException If the specified name is null + */ + public static NetworkInterface getByName(String name) + throws SocketException + { + if (name == null) + throw new NullPointerException(); + VMNetworkInterface[] netifs = VMNetworkInterface.getVMInterfaces(); + for (int i = 0; i < netifs.length; i++) + { + if (netifs[i].name.equals(name)) + return new NetworkInterface(netifs[i]); + } + return null; + } + + /** + * Return a network interface by its address + * + * @param addr The address of the interface to return + * + * @return the interface, or null if none found + * + * @exception SocketException If an error occurs + * @exception NullPointerException If the specified addess is null + */ + public static NetworkInterface getByInetAddress(InetAddress addr) + throws SocketException + { + if (addr == null) + throw new NullPointerException(); + VMNetworkInterface[] netifs = VMNetworkInterface.getVMInterfaces(); + for (int i = 0; i < netifs.length; i++) + { + if (netifs[i].addresses.contains(addr)) + return new NetworkInterface(netifs[i]); + } + return null; + } + + /** + * Return an Enumeration of all available network interfaces + * + * @return all interfaces + * + * @exception SocketException If an error occurs + */ + public static Enumeration getNetworkInterfaces() + throws SocketException + { + VMNetworkInterface[] netifs = VMNetworkInterface.getVMInterfaces(); + Vector networkInterfaces = + new Vector(netifs.length); + for (int i = 0; i < netifs.length; i++) + { + if (!netifs[i].addresses.isEmpty()) + networkInterfaces.add(new NetworkInterface(netifs[i])); + } + return networkInterfaces.elements(); + } + + /** + * Checks if the current instance is equal to obj + * + * @param obj The object to compare with + * + * @return true if equal, false otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof NetworkInterface)) + return false; + + NetworkInterface tmp = (NetworkInterface) obj; + + if (netif.name == null) + return tmp.netif.name == null; + + return (netif.name.equals(tmp.netif.name) + && (netif.addresses.equals(tmp.netif.addresses))); + } + + /** + * Returns the hashcode of the current instance + * + * @return the hashcode + */ + public int hashCode() + { + // FIXME: hash correctly + int hc = netif.addresses.hashCode(); + + if (netif.name != null) + hc += netif.name.hashCode(); + + return hc; + } + + /** + * Returns a string representation of the interface + * + * @return the string + */ + public String toString() + { + // FIXME: check if this is correct + CPStringBuilder result; + String separator = SystemProperties.getProperty("line.separator"); + + result = new CPStringBuilder(); + + result.append("name: "); + result.append(getDisplayName()); + result.append(" (").append(getName()).append(") addresses:"); + result.append(separator); + + for (Iterator it = netif.addresses.iterator(); it.hasNext(); ) + { + InetAddress address = (InetAddress) it.next(); + result.append(address.toString()).append(";").append(separator); + } + + return result.toString(); + } + + /** + * Determines whether this interface is ready to transfer data. + * + * @return whether the interface is up + */ + public boolean isUp() + throws SocketException + { + return VMNetworkInterface.isUp(netif.name); + } + + /** + * Determines whether this interface does point to point + * transmission. + * + * @return whether the interface does point to point transmission + */ + public boolean isPointToPoint() + throws SocketException + { + return VMNetworkInterface.isPointToPoint(netif.name); + } + + /** + * Determines whether this interface is the loopback interface. + * + * @return whether the interface is the loopback interface + */ + public boolean isLoopback() + throws SocketException + { + return VMNetworkInterface.isLoopback(netif.name); + } + + /** + * Determines whether this interface supports multicast transmission. + * + * @return whether the interface supports multicast transmission. + */ + public boolean supportsMulticast() + throws SocketException + { + return VMNetworkInterface.supportsMulticast(netif.name); + } + +} diff --git a/libjava/classpath/java/net/NoRouteToHostException.java b/libjava/classpath/java/net/NoRouteToHostException.java new file mode 100644 index 000000000..48c3a8e60 --- /dev/null +++ b/libjava/classpath/java/net/NoRouteToHostException.java @@ -0,0 +1,74 @@ +/* NoRouteToHostException.java -- Cannot connect to a host + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception indicates that there is no TCP/IP route to the requested + * host. This is often due to a misconfigured routing table. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class NoRouteToHostException extends SocketException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1897550894873493790L; + + /** + * Create an instance without a descriptive error message. + */ + public NoRouteToHostException() + { + } + + /** + * Create an instance with a descriptive error message, such as the text + * from strerror(3). + * + * @param message a message describing the error that occurred + */ + public NoRouteToHostException(String message) + { + super(message); + } +} // class NoRouteToHostException diff --git a/libjava/classpath/java/net/PasswordAuthentication.java b/libjava/classpath/java/net/PasswordAuthentication.java new file mode 100644 index 000000000..1d4ec8961 --- /dev/null +++ b/libjava/classpath/java/net/PasswordAuthentication.java @@ -0,0 +1,92 @@ +/* PasswordAuthentication.java -- Container class for username/password pairs + Copyright (C) 1998, 2000, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This class serves a container for username/password pairs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @since 1.2 + */ +public final class PasswordAuthentication +{ + /** + * The username + */ + private String username; + + /** + * The password + */ + private char[] password; + + /** + * Creates a new PasswordAuthentication object from the + * specified username and password. + * + * @param username The username for this object + * @param password The password for this object + */ + public PasswordAuthentication(String username, char[] password) + { + this.username = username; + this.password = password; + } + + /** + * Returns the username associated with this object + * + * @return The username + */ + public String getUserName() + { + return username; + } + + /** + * Returns the password associated with this object + * + * @return The password + */ + public char[] getPassword() + { + return password; + } +} diff --git a/libjava/classpath/java/net/PortUnreachableException.java b/libjava/classpath/java/net/PortUnreachableException.java new file mode 100644 index 000000000..49a8c9ea1 --- /dev/null +++ b/libjava/classpath/java/net/PortUnreachableException.java @@ -0,0 +1,72 @@ +/* PortUnreachableException.java -- received an ICMP port unreachable datagram + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception signals that an ICMP port unreachable datagram has been + * received. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class PortUnreachableException extends SocketException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8462541992376507323L; + + /** + * Create a new instance without a descriptive error message. + */ + public PortUnreachableException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public PortUnreachableException(String message) + { + super(message); + } +} // class PortUnreachableException diff --git a/libjava/classpath/java/net/ProtocolException.java b/libjava/classpath/java/net/ProtocolException.java new file mode 100644 index 000000000..27718a979 --- /dev/null +++ b/libjava/classpath/java/net/ProtocolException.java @@ -0,0 +1,75 @@ +/* ProtocolException.java -- a low level protocol error occurred + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that some sort of low level protocol + * exception occurred. Look in the descriptive message (if any) for + * details on what went wrong. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class ProtocolException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -6098449442062388080L; + + /** + * Create a new instance without a descriptive error message. + */ + public ProtocolException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public ProtocolException(String message) + { + super(message); + } +} // class ProtocolException diff --git a/libjava/classpath/java/net/Proxy.java b/libjava/classpath/java/net/Proxy.java new file mode 100644 index 000000000..36a25fa80 --- /dev/null +++ b/libjava/classpath/java/net/Proxy.java @@ -0,0 +1,139 @@ +/* Proxy.java -- Represends a proxy for a network connection + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Defines a proxy setting. This setting contains a type (https, socks, + * direct) and a socket address. + * + * @since 1.5 + */ +public class Proxy +{ + /** + * Represents the proxy type. + */ + public enum Type + { + DIRECT, HTTP, SOCKS; + + /** + * For compatability with Sun's JDK + */ + private static final long serialVersionUID = -2231209257930100533L; + } + + public static final Proxy NO_PROXY = new Proxy(Type.DIRECT, null); + + private Type type; + private SocketAddress address; + + /** + * Creates a new Proxy object. + * + * @param type The type for this proxy + * @param address The address of this proxy + */ + public Proxy(Type type, SocketAddress address) + { + this.type = type; + this.address = address; + } + + /** + * Returns the socket address for this proxy object. + * + * @return the socket address + */ + public SocketAddress address() + { + return address; + } + + /** + * Returns the of this proxy instance. + * + * @return the type + * + * @see Type + */ + public Type type() + { + return type; + } + + /** + * Compares the given object with this object. + * + * @return true if both objects or equals, + * false otherwise. + */ + public final boolean equals(Object obj) + { + if (! (obj instanceof Proxy)) + return false; + + Proxy tmp = (Proxy) obj; + + return (type.equals(tmp.type) + && (address == null ? tmp.address == null + : address.equals(tmp.address))); + } + + /** + * Returns the hashcode for this Proxy object. + * + * @return the hashcode + */ + public final int hashCode() + { + return type.hashCode() ^ (address == null ? 0 : address.hashCode()); + } + + /** + * Returns a string representation of this Proxy object. + * + * @return the string + */ + public String toString() + { + return type.toString() + (address == null ? "" + : (":" + address.toString())); + } +} diff --git a/libjava/classpath/java/net/ProxySelector.java b/libjava/classpath/java/net/ProxySelector.java new file mode 100644 index 000000000..6c85f99c7 --- /dev/null +++ b/libjava/classpath/java/net/ProxySelector.java @@ -0,0 +1,117 @@ +/* ProxySelector.java -- A proxy selector class + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.DefaultProxySelector; + +import java.io.IOException; +import java.util.List; + +/** + * Class for handling proxies for different connections. + * + * @since 1.5 + */ +public abstract class ProxySelector +{ + /** + * Default proxy selector. + */ + private static ProxySelector defaultSelector = new DefaultProxySelector(); + + /** + * Creates a new ProxySelector object. + */ + public ProxySelector() + { + // Do nothing here. + } + + /** + * Returns the default proxy selector. + * + * @return the default proxy selector + * + * @throws SecurityException If a security manager is installed and it + * denies NetPermission("getProxySelector") + */ + public static ProxySelector getDefault() + { + SecurityManager sm = System.getSecurityManager(); + + if (sm != null) + sm.checkPermission(new NetPermission("getProxySelector")); + + return defaultSelector; + } + + /** + * Sets the default proxy selector. + * + * @param selector the defualt proxy selector + * + * @throws SecurityException If a security manager is installed and it + * denies NetPermission("setProxySelector") + */ + public static void setDefault(ProxySelector selector) + { + SecurityManager sm = System.getSecurityManager(); + + if (sm != null) + sm.checkPermission(new NetPermission("setProxySelector")); + + defaultSelector = selector; + } + + /** + * Signals to the selector that a proxy was no available. + * + * @throws IllegalArgumentException If one argument is null + */ + public abstract void connectFailed(URI uri, SocketAddress address, + IOException exception); + + /** + * Returns the list of proxy settings for a given URI. + * + * @return list of proxy settings + * + * @throws IllegalArgumentException If uri is null + */ + public abstract List select(URI uri); +} diff --git a/libjava/classpath/java/net/ResolverCache.java b/libjava/classpath/java/net/ResolverCache.java new file mode 100644 index 000000000..d7e5ed407 --- /dev/null +++ b/libjava/classpath/java/net/ResolverCache.java @@ -0,0 +1,269 @@ +/* ResolverCache.java -- A cache of resolver lookups for InetAddress. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.security.Security; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * This class provides a cache of name service resolutions. By + * default successful resolutions are cached forever to guard + * against DNS spoofing attacks and failed resolutions are cached + * for 10 seconds to improve performance. The length of time that + * results remain in the cache is determined by the following + * security properties: + *
      + *
      networkaddress.cache.ttl
      + *
      + * This property specifies the length of time in seconds that + * successful resolutions remain in the cache. The default is + * -1, indicating to cache forever. + *
      + *
      networkaddress.cache.negative.ttl
      + *
      + * This property specifies the length of time in seconds that + * unsuccessful resolutions remain in the cache. The default + * is 10, indicating to cache for 10 seconds. + *
      + * In both cases, a value of -1 indicates to cache forever and a + * value of 0 indicates not to cache. + * + * @author Gary Benson (gbenson@redhat.com) + */ +class ResolverCache +{ + /** + * The time in seconds for which successful lookups are cached. + */ + private static final int POSITIVE_TTL = + getTTL("networkaddress.cache.ttl", -1); + + /** + * The time in seconds for which unsuccessful lookups are cached. + */ + private static final int NEGATIVE_TTL = + getTTL("networkaddress.cache.negative.ttl", 10); + + /** + * Helper function to set the TTLs. + */ + private static int getTTL(String propName, int defaultValue) + { + String propValue = Security.getProperty(propName); + if (propValue == null) + return defaultValue; + + return Integer.parseInt(propValue); + } + + /** + * The cache itself. + */ + private static HashMap cache = new HashMap(); + + /** + * List of entries which may expire. + */ + private static LinkedList killqueue = new LinkedList(); + + /** + * Return the hostname for the specified IP address. + * + * @param addr The IP address as a byte array + * + * @return The hostname + * + * @exception UnknownHostException If the reverse lookup fails + */ + public static String getHostByAddr(byte[] addr) throws UnknownHostException + { + Object key = makeHashableAddress(addr); + Entry entry = get(key); + if (entry != null) + { + if (entry.value == null) + throw new UnknownHostException(); + return (String) entry.value; + } + + try + { + String hostname = VMInetAddress.getHostByAddr(addr); + put(new Entry(key, hostname)); + return hostname; + } + catch (UnknownHostException e) + { + put(new Entry(key, null)); + throw e; + } + } + + /** + * Return a list of all IP addresses for the specified hostname. + * + * @param hostname The hostname + * + * @return An list of IP addresses as byte arrays + * + * @exception UnknownHostException If the lookup fails + */ + public static byte[][] getHostByName(String hostname) + throws UnknownHostException + { + Entry entry = get(hostname); + if (entry != null) + { + if (entry.value == null) + throw new UnknownHostException(); + return (byte[][]) entry.value; + } + + try + { + byte[][] addrs = VMInetAddress.getHostByName(hostname); + put(new Entry(hostname, addrs)); + return addrs; + } + catch (UnknownHostException e) + { + put(new Entry(hostname, null)); + throw e; + } + } + + /** + * Convert an IP address expressed as a byte array into something + * we can use as a hashtable key. + */ + private static Object makeHashableAddress(byte[] addr) + { + char[] chars = new char[addr.length]; + for (int i = 0; i < addr.length; i++) + chars[i] = (char) addr[i]; + return new String(chars); + } + + /** + * Return the entry in the cache associated with the supplied key, + * or null if the cache does not contain an entry + * associated with this key. + */ + private static synchronized Entry get(Object key) + { + reap(); + return (Entry) cache.get(key); + } + + /** + * Insert the supplied entry into the cache. + */ + private static synchronized void put(Entry entry) + { + reap(); + if (entry.expires != 0) + { + if (entry.expires != -1) + killqueue.add(entry); + cache.put(entry.key, entry); + } + } + + /** + * Clear expired entries. This method is not synchronized, so + * it must only be called by methods that are. + */ + private static void reap() + { + if (!killqueue.isEmpty()) + { + long now = System.currentTimeMillis(); + + Iterator iter = killqueue.iterator(); + while (iter.hasNext()) + { + Entry entry = (Entry) iter.next(); + if (entry.expires > now) + break; + cache.remove(entry.key); + iter.remove(); + } + } + } + + /** + * An entry in the cache. + */ + private static class Entry + { + /** + * The key by which this entry is referenced. + */ + public final Object key; + + /** + * The entry itself. A null value indicates a failed lookup. + */ + public final Object value; + + /** + * The time when this cache entry expires. If set to -1 then + * this entry will never expire. If set to 0 then this entry + * expires immediately and will not be inserted into the cache. + */ + public final long expires; + + /** + * Constructor. + */ + public Entry(Object key, Object value) + { + this.key = key; + this.value = value; + + int ttl = value != null ? POSITIVE_TTL : NEGATIVE_TTL; + if (ttl < 1) + expires = ttl; + else + expires = System.currentTimeMillis() + ttl * 1000; + } + } +} diff --git a/libjava/classpath/java/net/STATUS b/libjava/classpath/java/net/STATUS new file mode 100644 index 000000000..25dff963e --- /dev/null +++ b/libjava/classpath/java/net/STATUS @@ -0,0 +1,48 @@ +X ContentHandlerFactory +X FileNameMap +X SocketImplFactory +X URLStreamHandlerFactory +* Authenticator +* ContentHandler ++ DatagramPacket ++ DatagramSocket ++ DatagramSocketImpl ++ HttpURLConnection ++ InetAddress +* JarURLConnection ++ MulticastSocket +* NetPermission +* PasswordAuthentication ++ PlainDatagramSocketImpl ++ PlainSocketImpl (internal) ++ ServerSocket ++ Socket ++ SocketImpl ++ SocketInputStream (internal) ++ SocketOptions (internal) ++ SocketOutputStream (internal) +* SocketPermission ++ URL + URLClassLoader ++ URLConnection +* URLEncoder ++ URLStreamHandler +X BindException +X ConnectException +X MalformedURLException +X NoRouteToHostException +X ProtocolException +X SocketException +X UnknownHostException +X UnknownServiceException + +--------------- ++ Native InetAddress ++ Native SocketImpl ++ Native DatagramSocketImpl ++ Protocol Handler for HTTP + Protocol Handler for FTP ++ ContentHandler for text + ContentHandler for gif + ContentHandler for jpeg + diff --git a/libjava/classpath/java/net/ServerSocket.java b/libjava/classpath/java/net/ServerSocket.java new file mode 100644 index 000000000..1dbd7636c --- /dev/null +++ b/libjava/classpath/java/net/ServerSocket.java @@ -0,0 +1,627 @@ +/* ServerSocket.java -- Class for implementing server side sockets + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.PlainSocketImpl; + +import java.io.IOException; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.ServerSocketChannel; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Status: I believe all methods are implemented. + */ + +/** + * This class models server side sockets. The basic model is that the + * server socket is created and bound to some well known port. It then + * listens for and accepts connections. At that point the client and + * server sockets are ready to communicate with one another utilizing + * whatever application layer protocol they desire. + * + * As with the Socket class, most instance methods of this class + * simply redirect their calls to an implementation class. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class ServerSocket +{ + /** + * This is the user defined SocketImplFactory, if one is supplied + */ + private static SocketImplFactory factory; + + /** + * This is the SocketImp object to which most instance methods in this + * class are redirected + */ + private SocketImpl impl; + + /** + * We need to retain the local address even after the socket is closed. + */ + private InetSocketAddress local; + private int port; + + /* + * This constructor is only used by java.nio. + */ + + ServerSocket(PlainSocketImpl impl) throws IOException + { + if (impl == null) + throw new NullPointerException("impl may not be null"); + + this.impl = impl; + this.impl.create(true); + setReuseAddress(true); + } + + /* + * This method is only used by java.nio. + */ + + SocketImpl getImpl() + { + return impl; + } + + /** + * Constructor that simply sets the implementation. + * + * @exception IOException If an error occurs + * + * @specnote This constructor is public since JDK 1.4 + */ + public ServerSocket() throws IOException + { + if (factory != null) + impl = factory.createSocketImpl(); + else + impl = new PlainSocketImpl(); + + impl.create(true); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to 50. + * + * @param port The port number to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public ServerSocket(int port) throws IOException + { + this(port, 50); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to the value passed as + * arg2. + * + * @param port The port number to bind to + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + */ + public ServerSocket(int port, int backlog) throws IOException + { + this(port, backlog, null); + } + + /** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to the value passed as + * backlog. The third argument specifies a particular local address to + * bind t or null to bind to all local address. + * + * @param port The port number to bind to + * @param backlog The length of the pending connection queue + * @param bindAddr The address to bind to, or null to bind to all addresses + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.1 + */ + public ServerSocket(int port, int backlog, InetAddress bindAddr) + throws IOException + { + this(); + + // bind/listen socket + bind(new InetSocketAddress(bindAddr, port), backlog); + } + + /** + * Binds the server socket to a specified socket address + * + * @param endpoint The socket address to bind to + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public void bind(SocketAddress endpoint) throws IOException + { + bind(endpoint, 50); + } + + /** + * Binds the server socket to a specified socket address + * + * @param endpoint The socket address to bind to + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If address type is not supported + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * + * @since 1.4 + */ + public void bind(SocketAddress endpoint, int backlog) + throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (isBound()) + throw new SocketException("Already bound"); + + InetAddress addr; + int port; + + if (endpoint == null) + { + addr = InetAddress.ANY_IF; + port = 0; + } + else if (! (endpoint instanceof InetSocketAddress)) + { + throw new IllegalArgumentException("Address type not supported"); + } + else + { + InetSocketAddress tmp = (InetSocketAddress) endpoint; + if (tmp.isUnresolved()) + throw new SocketException("Unresolved address"); + addr = tmp.getAddress(); + port = tmp.getPort(); + } + + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkListen(port); + + try + { + impl.bind(addr, port); + impl.listen(backlog); + this.port = port; + local = new InetSocketAddress( + (InetAddress) impl.getOption(SocketOptions.SO_BINDADDR), + impl.getLocalPort()); + } + finally + { + try + { + if (local == null) + close(); + } + catch (IOException _) + { + } + } + } + + /** + * This method returns the local address to which this socket is bound + * + * @return The socket's local address + */ + public InetAddress getInetAddress() + { + if (local == null) + return null; + + return local.getAddress(); + } + + /** + * This method returns the local port number to which this socket is bound + * + * @return The socket's port number + */ + public int getLocalPort() + { + if (local == null) + return -1; + + return local.getPort(); + } + + /** + * Returns the local socket address + * + * @return the local socket address, null if not bound + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + return local; + } + + /** + * Accepts a new connection and returns a connected Socket + * instance representing that connection. This method will block until a + * connection is available. + * + * @return socket object for the just accepted connection + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkListen method doesn't allow the operation + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * @exception SocketTimeoutException If a timeout was previously set with + * setSoTimeout and the timeout has been reached + */ + public Socket accept() throws IOException + { + Socket socket = new Socket(); + + try + { + implAccept(socket); + } + catch (IOException e) + { + try + { + socket.close(); + } + catch (IOException e2) + { + // Ignore. + } + + throw e; + } + catch (SecurityException e) + { + try + { + socket.close(); + } + catch (IOException e2) + { + // Ignore. + } + + throw e; + } + + return socket; + } + + /** + * This protected method is used to help subclasses override + * ServerSocket.accept(). The passed in socket will be + * connected when this method returns. + * + * @param socket The socket that is used for the accepted connection + * + * @exception IOException If an error occurs + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * + * @since 1.1 + */ + protected final void implAccept(Socket socket) throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + // The Sun spec says that if we have an associated channel and + // it is in non-blocking mode, we throw an IllegalBlockingModeException. + // However, in our implementation if the channel itself initiated this + // operation, then we must honor it regardless of its blocking mode. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((PlainSocketImpl) getImpl()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + impl.accept(socket.impl); + socket.bound = true; + socket.implCreated = true; + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkAccept(socket.getInetAddress().getHostAddress(), + socket.getPort()); + } + + /** + * Closes this socket and stops listening for connections + * + * @exception IOException If an error occurs + */ + public void close() throws IOException + { + if (impl != null) + { + impl.close(); + impl = null; + } + } + + /** + * Returns the unique ServerSocketChannel object + * associated with this socket, if any. + * + *

      The socket only has a ServerSocketChannel if its created + * by ServerSocketChannel.open().

      + * + * @return the associated socket channel, null if none exists + * + * @since 1.4 + */ + public ServerSocketChannel getChannel() + { + return null; + } + + /** + * Returns true when the socket is bound, otherwise false + * + * @return true if socket is bound, false otherwise + * + * @since 1.4 + */ + public boolean isBound() + { + return local != null; + } + + /** + * Returns true if the socket is closed, otherwise false + * + * @return true if socket is closed, false otherwise + * + * @since 1.4 + */ + public boolean isClosed() + { + ServerSocketChannel channel = getChannel(); + return impl == null || (channel != null && ! channel.isOpen()); + } + + /** + * Sets the value of SO_TIMEOUT. A value of 0 implies that SO_TIMEOUT is + * disabled (ie, operations never time out). This is the number of + * milliseconds a socket operation can block before an + * InterruptedIOException is thrown. + * + * @param timeout The new SO_TIMEOUT value + * + * @exception SocketException If an error occurs + * + * @since 1.1 + */ + public void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("SO_TIMEOUT value must be >= 0"); + + impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); + } + + /** + * Retrieves the current value of the SO_TIMEOUT setting. A value of 0 + * implies that SO_TIMEOUT is disabled (ie, operations never time out). + * This is the number of milliseconds a socket operation can block before + * an InterruptedIOException is thrown. + * + * @return The value of SO_TIMEOUT + * + * @exception IOException If an error occurs + * + * @since 1.1 + */ + public int getSoTimeout() throws IOException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object timeout = impl.getOption(SocketOptions.SO_TIMEOUT); + + if (! (timeout instanceof Integer)) + throw new IOException("Internal Error"); + + return ((Integer) timeout).intValue(); + } + + /** + * Enables/Disables the SO_REUSEADDR option + * + * @param on true if SO_REUSEADDR should be enabled, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setReuseAddress(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on)); + } + + /** + * Checks if the SO_REUSEADDR option is enabled + * + * @return true if SO_REUSEADDR is set, false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object reuseaddr = impl.getOption(SocketOptions.SO_REUSEADDR); + + if (! (reuseaddr instanceof Boolean)) + throw new SocketException("Internal Error"); + + return ((Boolean) reuseaddr).booleanValue(); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.4 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_RCVBUF value must be > 0"); + + impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.4 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("ServerSocket is closed"); + + Object buf = impl.getOption(SocketOptions.SO_RCVBUF); + + if (! (buf instanceof Integer)) + throw new SocketException("Internal Error: Unexpected type"); + + return ((Integer) buf).intValue(); + } + + /** + * Returns the value of this socket as a String. + * + * @return This socket represented as a String. + */ + public String toString() + { + if (! isBound()) + return "ServerSocket[unbound]"; + + return ("ServerSocket[addr=" + getInetAddress() + ",port=" + + port + ",localport=" + getLocalPort() + "]"); + } + + /** + * Sets the SocketImplFactory for all + * ServerSocket's. This may only be done + * once per virtual machine. Subsequent attempts will generate an + * exception. Note that a SecurityManager check is made prior + * to setting the factory. If insufficient privileges exist to set the + * factory, an exception will be thrown + * + * @param fac the factory to set + * + * @exception SecurityException If this operation is not allowed by the + * SecurityManager. + * @exception SocketException If the factory object is already defined + * @exception IOException If any other error occurs + */ + public static synchronized void setSocketFactory(SocketImplFactory fac) + throws IOException + { + if (factory != null) + throw new SocketException("SocketFactory already defined"); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSetFactory(); + + factory = fac; + } +} diff --git a/libjava/classpath/java/net/Socket.java b/libjava/classpath/java/net/Socket.java new file mode 100644 index 000000000..d61e81f5e --- /dev/null +++ b/libjava/classpath/java/net/Socket.java @@ -0,0 +1,1288 @@ +/* Socket.java -- Client socket implementation + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2006, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.net.PlainSocketImpl; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Status: I believe all methods are implemented. + */ + +/** + * This class models a client site socket. A socket is a TCP/IP endpoint + * for network communications conceptually similar to a file handle. + *

      + * This class does not actually do any work. Instead, it redirects all of + * its calls to a socket implementation object which implements the + * SocketImpl interface. The implementation class is + * instantiated by factory class that implements the + * SocketImplFactory interface. A default + * factory is provided, however the factory may be set by a call to + * the setSocketImplFactory method. Note that this may only be + * done once per virtual machine. If a subsequent attempt is made to set the + * factory, a SocketException will be thrown. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class Socket +{ + /** + * This is the user SocketImplFactory for this class. If this variable is + * null, a default factory is used. + */ + static SocketImplFactory factory; + + /** + * The implementation object to which calls are redirected + */ + // package-private because ServerSocket.implAccept() needs to access it. + SocketImpl impl; + + /** + * True if impl.create() has been called. + */ + // package-private because ServerSocket.implAccept() needs to access it. + boolean implCreated; + + /** + * True if the socket is bound. + * Package private so it can be set from ServerSocket when accept is called. + */ + boolean bound; + + /** + * True if input is shutdown. + */ + private boolean inputShutdown; + + /** + * True if output is shutdown. + */ + private boolean outputShutdown; + + /** + * Initializes a new instance of Socket object without + * connecting to a remote host. This useful for subclasses of socket that + * might want this behavior. + * + * @specnote This constructor is public since JDK 1.4 + * @since 1.1 + */ + public Socket() + { + if (factory != null) + impl = factory.createSocketImpl(); + else + impl = new PlainSocketImpl(); + } + + /** + * Initializes a new instance of Socket object without + * connecting to a remote host. This is useful for subclasses of socket + * that might want this behavior. + *

      + * Additionally, this socket will be created using the supplied + * implementation class instead the default class or one returned by a + * factory. If this value is null, the default Socket + * implementation is used. + * + * @param impl The SocketImpl to use for this + * Socket + * + * @exception SocketException If an error occurs + * + * @since 1.1 + */ + protected Socket(SocketImpl impl) throws SocketException + { + if (impl == null) + this.impl = new PlainSocketImpl(); + else + this.impl = impl; + } + + /** + * Initializes a new instance of Socket and connects to the + * hostname and port specified as arguments. + * + * @param host The name of the host to connect to + * @param port The port number to connect to + * + * @exception UnknownHostException If the hostname cannot be resolved to a + * network address. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public Socket(String host, int port) + throws UnknownHostException, IOException + { + this(InetAddress.getByName(host), port, null, 0, true); + } + + /** + * Initializes a new instance of Socket and connects to the + * address and port number specified as arguments. + * + * @param address The address to connect to + * @param port The port number to connect to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + public Socket(InetAddress address, int port) throws IOException + { + this(address, port, null, 0, true); + } + + /** + * Initializes a new instance of Socket that connects to the + * named host on the specified port and binds to the specified local address + * and port. + * + * @param host The name of the remote host to connect to. + * @param port The remote port to connect to. + * @param localAddr The local address to bind to. + * @param localPort The local port to bind to. + * + * @exception SecurityException If the SecurityManager + * exists and does not allow a connection to the specified host/port or + * binding to the specified local host/port. + * @exception IOException If a connection error occurs. + * + * @since 1.1 + */ + public Socket(String host, int port, InetAddress localAddr, int localPort) + throws IOException + { + this(InetAddress.getByName(host), port, localAddr, localPort, true); + } + + /** + * Initializes a new instance of Socket and connects to the + * address and port number specified as arguments, plus binds to the + * specified local address and port. + * + * @param address The remote address to connect to + * @param port The remote port to connect to + * @param localAddr The local address to connect to + * @param localPort The local port to connect to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @since 1.1 + */ + public Socket(InetAddress address, int port, InetAddress localAddr, + int localPort) throws IOException + { + this(address, port, localAddr, localPort, true); + } + + /** + * Initializes a new instance of Socket and connects to the + * hostname and port specified as arguments. If the stream argument is set + * to true, then a stream socket is created. If it is + * false, a datagram socket is created. + * + * @param host The name of the host to connect to + * @param port The port to connect to + * @param stream true for a stream socket, false + * for a datagram socket + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @deprecated Use the DatagramSocket class to create + * datagram oriented sockets. + */ + public Socket(String host, int port, boolean stream) + throws IOException + { + this(InetAddress.getByName(host), port, null, 0, stream); + } + + /** + * Initializes a new instance of Socket and connects to the + * address and port number specified as arguments. If the stream param is + * true, a stream socket will be created, otherwise a datagram + * socket is created. + * + * @param host The address to connect to + * @param port The port number to connect to + * @param stream true to create a stream socket, + * false to create a datagram socket. + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * + * @deprecated Use the DatagramSocket class to create + * datagram oriented sockets. + */ + public Socket(InetAddress host, int port, boolean stream) + throws IOException + { + this(host, port, null, 0, stream); + } + + /** + * This constructor is where the real work takes place. Connect to the + * specified address and port. Use default local values if not specified, + * otherwise use the local host and port passed in. Create as stream or + * datagram based on "stream" argument. + *

      + * + * @param raddr The remote address to connect to + * @param rport The remote port to connect to + * @param laddr The local address to connect to + * @param lport The local port to connect to + * @param stream true for a stream socket, false for a datagram socket + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + */ + private Socket(InetAddress raddr, int rport, InetAddress laddr, int lport, + boolean stream) throws IOException + { + this(); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(raddr.getHostAddress(), rport); + + // bind socket + SocketAddress bindaddr = + laddr == null ? null : new InetSocketAddress(laddr, lport); + bind(bindaddr); + + // Connect socket in case of Exceptions we must close the socket + // because an exception in the constructor means that the caller will + // not have a reference to this instance. + // Note: You may have the idea that the exception treatment + // should be moved into connect() but there is a Mauve test which + // shows that a failed connect should not close the socket. + try + { + connect(new InetSocketAddress(raddr, rport)); + } + catch (IOException ioe) + { + impl.close(); + throw ioe; + } + catch (RuntimeException re) + { + impl.close(); + throw re; + } + + // FIXME: JCL p. 1586 says if localPort is unspecified, bind to any port, + // i.e. '0' and if localAddr is unspecified, use getLocalAddress() as + // that default. JDK 1.2 doc infers not to do a bind. + } + + private SocketImpl getImpl() throws SocketException + { + if (! implCreated) + { + try + { + impl.create(true); + } + catch (IOException x) + { + throw (SocketException) new SocketException().initCause(x); + } + implCreated = true; + } + return impl; + } + + /** + * Binds the socket to the given local address/port + * + * @param bindpoint The address/port to bind to + * + * @exception IOException If an error occurs + * @exception SecurityException If a security manager exists and its + * checkConnect method doesn't allow the operation + * @exception IllegalArgumentException If the address type is not supported + * + * @since 1.4 + */ + public void bind(SocketAddress bindpoint) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + // XXX: JDK 1.4.1 API documentation says that if bindpoint is null the + // socket will be bound to an ephemeral port and a valid local address. + if (bindpoint == null) + bindpoint = new InetSocketAddress(InetAddress.ANY_IF, 0); + + if (! (bindpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException(); + + InetSocketAddress tmp = (InetSocketAddress) bindpoint; + + // bind to address/port + try + { + getImpl().bind(tmp.getAddress(), tmp.getPort()); + bound = true; + } + catch (IOException exception) + { + close(); + throw exception; + } + catch (RuntimeException exception) + { + close(); + throw exception; + } + catch (Error error) + { + close(); + throw error; + } + } + + /** + * Connects the socket with a remote address. + * + * @param endpoint The address to connect to + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If the addess type is not supported + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * + * @since 1.4 + */ + public void connect(SocketAddress endpoint) throws IOException + { + connect(endpoint, 0); + } + + /** + * Connects the socket with a remote address. A timeout of zero is + * interpreted as an infinite timeout. The connection will then block + * until established or an error occurs. + * + * @param endpoint The address to connect to + * @param timeout The length of the timeout in milliseconds, or + * 0 to indicate no timeout. + * + * @exception IOException If an error occurs + * @exception IllegalArgumentException If the address type is not supported + * @exception IllegalBlockingModeException If this socket has an associated + * channel, and the channel is in non-blocking mode + * @exception SocketTimeoutException If the timeout is reached + * + * @since 1.4 + */ + public void connect(SocketAddress endpoint, int timeout) + throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! (endpoint instanceof InetSocketAddress)) + throw new IllegalArgumentException("unsupported address type"); + + // The Sun spec says that if we have an associated channel and + // it is in non-blocking mode, we throw an IllegalBlockingModeException. + // However, in our implementation if the channel itself initiated this + // operation, then we must honor it regardless of its blocking mode. + if (getChannel() != null && ! getChannel().isBlocking() + && ! ((PlainSocketImpl) getImpl()).isInChannelOperation()) + throw new IllegalBlockingModeException(); + + if (! isBound()) + bind(null); + + getImpl().connect(endpoint, timeout); + } + + /** + * Returns the address of the remote end of the socket. If this socket + * is not connected, then null is returned. + * + * @return The remote address this socket is connected to + */ + public InetAddress getInetAddress() + { + if (! isConnected()) + return null; + + try + { + return getImpl().getInetAddress(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return null; + } + + /** + * Returns the local address to which this socket is bound. If this socket + * is not connected, then a wildcard address, for which + * @see InetAddress#isAnyLocalAddress() is true, is returned. + * + * @return The local address + * + * @since 1.1 + */ + public InetAddress getLocalAddress() + { + if (! isBound()) + return InetAddress.ANY_IF; + + InetAddress addr = null; + + if (impl instanceof PlainSocketImpl) + addr = ((PlainSocketImpl) impl).getLocalAddress().getAddress(); + + if (addr == null) + { + try + { + addr = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR); + } + catch (SocketException e) + { + // (hopefully) shouldn't happen + // throw new java.lang.InternalError + // ("Error in PlainSocketImpl.getOption"); + return null; + } + } + + // FIXME: According to libgcj, checkConnect() is supposed to be called + // before performing this operation. Problems: 1) We don't have the + // addr until after we do it, so we do a post check. 2). The docs I + // see don't require this in the Socket case, only DatagramSocket, but + // we'll assume they mean both. + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(addr.getHostName(), getLocalPort()); + + return addr; + } + + /** + * Returns the port number of the remote end of the socket connection. If + * this socket is not connected, then 0 is returned. + * + * @return The remote port this socket is connected to + */ + public int getPort() + { + if (! isConnected()) + return 0; + + try + { + return getImpl().getPort(); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return 0; + } + + /** + * Returns the local port number to which this socket is bound. If this + * socket is not connected, then -1 is returned. + * + * @return The local port + */ + public int getLocalPort() + { + if (! isBound()) + return -1; + + try + { + if (getImpl() != null) + return getImpl().getLocalPort(); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + } + + return -1; + } + + /** + * Returns local socket address. + * + * @return the local socket address, null if not bound + * + * @since 1.4 + */ + public SocketAddress getLocalSocketAddress() + { + if (! isBound()) + return null; + + InetAddress addr = getLocalAddress(); + + try + { + return new InetSocketAddress(addr, getImpl().getLocalPort()); + } + catch (SocketException e) + { + // This cannot happen as we are bound. + return null; + } + } + + /** + * Returns the remote socket address. + * + * @return the remote socket address, null of not connected + * + * @since 1.4 + */ + public SocketAddress getRemoteSocketAddress() + { + if (! isConnected()) + return null; + + try + { + return new InetSocketAddress(getImpl().getInetAddress(), + getImpl().getPort()); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + return null; + } + } + + /** + * Returns an InputStream for reading from this socket. + * + * @return The InputStream object + * + * @exception IOException If an error occurs or Socket is not connected + */ + public InputStream getInputStream() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! isConnected()) + throw new IOException("not connected"); + + return getImpl().getInputStream(); + } + + /** + * Returns an OutputStream for writing to this socket. + * + * @return The OutputStream object + * + * @exception IOException If an error occurs or Socket is not connected + */ + public OutputStream getOutputStream() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (! isConnected()) + throw new IOException("not connected"); + + return getImpl().getOutputStream(); + } + + /** + * Sets the TCP_NODELAY option on the socket. + * + * @param on true to enable, false to disable + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.1 + */ + public void setTcpNoDelay(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on)); + } + + /** + * Tests whether or not the TCP_NODELAY option is set on the socket. + * Returns true if enabled, false if disabled. When on it disables the + * Nagle algorithm which means that packets are always send immediatly and + * never merged together to reduce network trafic. + * + * @return Whether or not TCP_NODELAY is set + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public boolean getTcpNoDelay() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object on = getImpl().getOption(SocketOptions.TCP_NODELAY); + + if (on instanceof Boolean) + return (((Boolean) on).booleanValue()); + else + throw new SocketException("Internal Error"); + } + + /** + * Sets the value of the SO_LINGER option on the socket. If the + * SO_LINGER option is set on a socket and there is still data waiting to + * be sent when the socket is closed, then the close operation will block + * until either that data is delivered or until the timeout period + * expires. The linger interval is specified in hundreths of a second + * (platform specific?) + * + * @param on true to enable SO_LINGER, false to disable + * @param linger The SO_LINGER timeout in hundreths of a second or -1 if + * SO_LINGER not set. + * + * @exception SocketException If an error occurs or Socket not connected + * @exception IllegalArgumentException If linger is negative + * + * @since 1.1 + */ + public void setSoLinger(boolean on, int linger) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (on) + { + if (linger < 0) + throw new IllegalArgumentException("SO_LINGER must be >= 0"); + + if (linger > 65535) + linger = 65535; + + getImpl().setOption(SocketOptions.SO_LINGER, Integer.valueOf(linger)); + } + else + getImpl().setOption(SocketOptions.SO_LINGER, Integer.valueOf(-1)); + } + + /** + * Returns the value of the SO_LINGER option on the socket. If the + * SO_LINGER option is set on a socket and there is still data waiting to + * be sent when the socket is closed, then the close operation will block + * until either that data is delivered or until the timeout period + * expires. This method either returns the timeouts (in hundredths of + * of a second (platform specific?)) if SO_LINGER is set, or -1 if + * SO_LINGER is not set. + * + * @return The SO_LINGER timeout in hundreths of a second or -1 + * if SO_LINGER not set + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.1 + */ + public int getSoLinger() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object linger = getImpl().getOption(SocketOptions.SO_LINGER); + + if (linger instanceof Integer) + return (((Integer) linger).intValue()); + else + return -1; + } + + /** + * Sends urgent data through the socket + * + * @param data The data to send. + * Only the lowest eight bits of data are sent + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + public void sendUrgentData(int data) throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().sendUrgentData(data); + } + + /** + * Enables/disables the SO_OOBINLINE option + * + * @param on True if SO_OOBLINE should be enabled + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setOOBInline(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_OOBINLINE, Boolean.valueOf(on)); + } + + /** + * Returns the current setting of the SO_OOBINLINE option for this socket + * + * @return True if SO_OOBINLINE is set, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getOOBInline() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_OOBINLINE); + + if (buf instanceof Boolean) + return (((Boolean) buf).booleanValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * Sets the value of the SO_TIMEOUT option on the socket. If this value + * is set, and an read/write is performed that does not complete within + * the timeout period, a short count is returned (or an EWOULDBLOCK signal + * would be sent in Unix if no data had been read). A value of 0 for + * this option implies that there is no timeout (ie, operations will + * block forever). On systems that have separate read and write timeout + * values, this method returns the read timeout. This + * value is in milliseconds. + * + * @param timeout The length of the timeout in milliseconds, or + * 0 to indicate no timeout. + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public synchronized void setSoTimeout(int timeout) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (timeout < 0) + throw new IllegalArgumentException("SO_TIMEOUT value must be >= 0"); + + getImpl().setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); + } + + /** + * Returns the value of the SO_TIMEOUT option on the socket. If this value + * is set, and an read/write is performed that does not complete within + * the timeout period, a short count is returned (or an EWOULDBLOCK signal + * would be sent in Unix if no data had been read). A value of 0 for + * this option implies that there is no timeout (ie, operations will + * block forever). On systems that have separate read and write timeout + * values, this method returns the read timeout. This + * value is in thousandths of a second (implementation specific?). + * + * @return The length of the timeout in thousandth's of a second or 0 + * if not set + * + * @exception SocketException If an error occurs or Socket not connected + * + * @since 1.1 + */ + public synchronized int getSoTimeout() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object timeout = getImpl().getOption(SocketOptions.SO_TIMEOUT); + if (timeout instanceof Integer) + return (((Integer) timeout).intValue()); + else + return 0; + } + + /** + * This method sets the value for the system level socket option + * SO_SNDBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new send buffer size. + * + * @exception SocketException If an error occurs or Socket not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.2 + */ + public void setSendBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_SNDBUF value must be > 0"); + + getImpl().setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_SNDBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The send buffer size. + * + * @exception SocketException If an error occurs or socket not connected + * + * @since 1.2 + */ + public int getSendBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_SNDBUF); + + if (buf instanceof Integer) + return (((Integer) buf).intValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * This method sets the value for the system level socket option + * SO_RCVBUF to the specified value. Note that valid values for this + * option are specific to a given operating system. + * + * @param size The new receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * @exception IllegalArgumentException If size is 0 or negative + * + * @since 1.2 + */ + public void setReceiveBufferSize(int size) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (size <= 0) + throw new IllegalArgumentException("SO_RCVBUF value must be > 0"); + + getImpl().setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); + } + + /** + * This method returns the value of the system level socket option + * SO_RCVBUF, which is used by the operating system to tune buffer + * sizes for data transfers. + * + * @return The receive buffer size. + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.2 + */ + public int getReceiveBufferSize() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_RCVBUF); + + if (buf instanceof Integer) + return (((Integer) buf).intValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * This method sets the value for the socket level socket option + * SO_KEEPALIVE. + * + * @param on True if SO_KEEPALIVE should be enabled + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.3 + */ + public void setKeepAlive(boolean on) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_KEEPALIVE, Boolean.valueOf(on)); + } + + /** + * This method returns the value of the socket level socket option + * SO_KEEPALIVE. + * + * @return The setting + * + * @exception SocketException If an error occurs or Socket is not connected + * + * @since 1.3 + */ + public boolean getKeepAlive() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object buf = getImpl().getOption(SocketOptions.SO_KEEPALIVE); + + if (buf instanceof Boolean) + return (((Boolean) buf).booleanValue()); + else + throw new SocketException("Internal Error: Unexpected type"); + } + + /** + * Closes the socket. + * + * @exception IOException If an error occurs + */ + public synchronized void close() throws IOException + { + if (isClosed()) + return; + + impl.close(); + impl = null; + } + + /** + * Converts this Socket to a String. + * + * @return The String representation of this Socket + */ + public String toString() + { + try + { + if (isConnected()) + return (super.toString() + + " [addr=" + getImpl().getInetAddress() + ",port=" + + getImpl().getPort() + ",localport=" + + getImpl().getLocalPort() + "]"); + } + catch (SocketException e) + { + // This cannot happen as we are connected. + } + + return super.toString() + " [unconnected]"; + } + + /** + * Sets the SocketImplFactory. This may be done only once per + * virtual machine. Subsequent attempts will generate a + * SocketException. Note that a SecurityManager + * check is made prior to setting the factory. If + * insufficient privileges exist to set the factory, then an + * IOException will be thrown. + * + * @param fac the factory to set + * + * @exception SecurityException If the SecurityManager does + * not allow this operation. + * @exception SocketException If the SocketImplFactory is already defined + * @exception IOException If any other error occurs + */ + public static synchronized void setSocketImplFactory(SocketImplFactory fac) + throws IOException + { + // See if already set + if (factory != null) + throw new SocketException("SocketImplFactory already defined"); + + // Check permissions + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSetFactory(); + + if (fac == null) + throw new SocketException("SocketImplFactory cannot be null"); + + factory = fac; + } + + /** + * Closes the input side of the socket stream. + * + * @exception IOException If an error occurs. + * + * @since 1.3 + */ + public void shutdownInput() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().shutdownInput(); + inputShutdown = true; + } + + /** + * Closes the output side of the socket stream. + * + * @exception IOException If an error occurs. + * + * @since 1.3 + */ + public void shutdownOutput() throws IOException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().shutdownOutput(); + outputShutdown = true; + } + + /** + * Returns the socket channel associated with this socket. + * + * @return the associated socket channel, + * null if no associated channel exists + * + * @since 1.4 + */ + public SocketChannel getChannel() + { + return null; + } + + /** + * Checks if the SO_REUSEADDR option is enabled + * + * @return True if SO_REUSEADDR is set, false otherwise. + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public boolean getReuseAddress() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object reuseaddr = getImpl().getOption(SocketOptions.SO_REUSEADDR); + + if (! (reuseaddr instanceof Boolean)) + throw new SocketException("Internal Error"); + + return ((Boolean) reuseaddr).booleanValue(); + } + + /** + * Enables/Disables the SO_REUSEADDR option + * + * @param reuseAddress true if SO_REUSEADDR should be enabled, + * false otherwise + * + * @exception SocketException If an error occurs + * + * @since 1.4 + */ + public void setReuseAddress(boolean reuseAddress) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + getImpl().setOption(SocketOptions.SO_REUSEADDR, + Boolean.valueOf(reuseAddress)); + } + + /** + * Returns the current traffic class + * + * @return The current traffic class. + * + * @exception SocketException If an error occurs + * + * @see Socket#setTrafficClass(int tc) + * + * @since 1.4 + */ + public int getTrafficClass() throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + Object obj = getImpl().getOption(SocketOptions.IP_TOS); + + if (obj instanceof Integer) + return ((Integer) obj).intValue(); + else + throw new SocketException("Unexpected type"); + } + + /** + * Sets the traffic class value + * + * @param tc The traffic class + * + * @exception SocketException If an error occurs + * @exception IllegalArgumentException If tc value is illegal + * + * @see Socket#getTrafficClass() + * + * @since 1.4 + */ + public void setTrafficClass(int tc) throws SocketException + { + if (isClosed()) + throw new SocketException("socket is closed"); + + if (tc < 0 || tc > 255) + throw new IllegalArgumentException(); + + getImpl().setOption(SocketOptions.IP_TOS, Integer.valueOf(tc)); + } + + /** + * Checks if the socket is connected + * + * @return True if socket is connected, false otherwise. + * + * @since 1.4 + */ + public boolean isConnected() + { + if (impl == null) + return false; + + return impl.getInetAddress() != null; + } + + /** + * Checks if the socket is already bound. + * + * @return True if socket is bound, false otherwise. + * + * @since 1.4 + */ + public boolean isBound() + { + if (isClosed()) + return false; + if (impl instanceof PlainSocketImpl) + { + InetSocketAddress addr = ((PlainSocketImpl) impl).getLocalAddress(); + return addr != null && addr.getAddress() != null; + } + return bound; + } + + /** + * Checks if the socket is closed. + * + * @return True if socket is closed, false otherwise. + * + * @since 1.4 + */ + public boolean isClosed() + { + SocketChannel channel = getChannel(); + + return impl == null || (channel != null && ! channel.isOpen()); + } + + /** + * Checks if the socket's input stream is shutdown + * + * @return True if input is shut down. + * + * @since 1.4 + */ + public boolean isInputShutdown() + { + return inputShutdown; + } + + /** + * Checks if the socket's output stream is shutdown + * + * @return True if output is shut down. + * + * @since 1.4 + */ + public boolean isOutputShutdown() + { + return outputShutdown; + } +} diff --git a/libjava/classpath/java/net/SocketAddress.java b/libjava/classpath/java/net/SocketAddress.java new file mode 100644 index 000000000..48ab01009 --- /dev/null +++ b/libjava/classpath/java/net/SocketAddress.java @@ -0,0 +1,63 @@ +/* SocketAddress.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.Serializable; + + +/** + * Abstract base class for InetSocketAddress. + * InetSocketAddress is to my knowledge the only derived + * class. [Ronald] + * + * @since 1.4 + */ +public abstract class SocketAddress implements Serializable +{ + /** + * Compatible with JDK 1.4+ + */ + static final long serialVersionUID = 5215720748342549866L; + + /** + * Initializes the socket address. + */ + public SocketAddress() + { + } +} diff --git a/libjava/classpath/java/net/SocketException.java b/libjava/classpath/java/net/SocketException.java new file mode 100644 index 000000000..37b2f6fba --- /dev/null +++ b/libjava/classpath/java/net/SocketException.java @@ -0,0 +1,75 @@ +/* SocketException.java -- An exception occurred while performing a socket op + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that a generic error occurred related to an + * operation on a socket. Check the descriptive message (if any) for + * details on the nature of this error + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * @status updated to 1.4 + */ +public class SocketException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -5935874303556886934L; + + /** + * Create a new instance without a descriptive error message. + */ + public SocketException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public SocketException(String message) + { + super(message); + } +} // class SocketException diff --git a/libjava/classpath/java/net/SocketImpl.java b/libjava/classpath/java/net/SocketImpl.java new file mode 100644 index 000000000..77f470be3 --- /dev/null +++ b/libjava/classpath/java/net/SocketImpl.java @@ -0,0 +1,321 @@ +/* SocketImpl.java -- Abstract socket implementation class + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + +/* Written using on-line Java Platform 1.2 API Specification. + * Believed complete and correct. + */ + +/** + * This abstract class serves as the parent class for socket implementations. + * The implementation class serves an intermediary to native routines that + * perform system specific socket operations. + *

      + * A default implementation is provided by the system, but this can be + * changed via installing a SocketImplFactory (through a call + * to the static method Socket.setSocketImplFactory). A + * subclass of Socket can also pass in a SocketImpl + * to the Socket(SocketImpl) constructor to use an + * implementation different from the system default without installing + * a factory. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public abstract class SocketImpl implements SocketOptions +{ + /** + * The address of the remote end of the socket connection + */ + protected InetAddress address; + + /** + * A FileDescriptor object representing this socket connection. + */ + protected FileDescriptor fd; + + /** + * The port number the socket is bound to locally + */ + protected int localport = -1; + + /** + * The port number of the remote end of the socket connection + */ + protected int port; + + /** + * Default, no-argument constructor for use by subclasses. + */ + public SocketImpl() + { + } + + /** + * Creates a new socket that is not bound to any local address/port and + * is not connected to any remote address/port. This will be created as + * a stream socket if the stream parameter is true, or a datagram socket + * if the stream parameter is false. + * + * @param stream true for a stream socket, false for a datagram socket + * + * @exception IOException If an error occurs + */ + protected abstract void create(boolean stream) throws IOException; + + /** + * Connects to the remote hostname and port specified as arguments. + * + * @param host The remote hostname to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected abstract void connect(String host, int port) + throws IOException; + + /** + * Connects to the remote address and port specified as arguments. + * + * @param host The remote address to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ + protected abstract void connect(InetAddress host, int port) + throws IOException; + + /** + * Connects to the socket to the host specified in address. This + * method blocks until successful connected or the timeout occurs. + * A timeout of zero means no timout. + * + * @param address Data of remote host + * @param timeout time to wait to stop connecting + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void connect(SocketAddress address, int timeout) + throws IOException; + + /** + * Binds to the specified port on the specified addr. Note that this addr + * must represent a local IP address. + *

      + * Note that it is unspecified how to bind to all interfaces on the localhost + * (INADDR_ANY). + * + * @param host The address to bind to + * @param port The port number to bind to + * + * @exception IOException If an error occurs + */ + protected abstract void bind(InetAddress host, int port) + throws IOException; + + /** + * Starts listening for connections on a socket. The backlog parameter + * is how many pending connections will queue up waiting to be serviced + * before being accept'ed. If the queue of pending requests exceeds this + * number, additional connections will be refused. + * + * @param backlog The length of the pending connection queue + * + * @exception IOException If an error occurs + */ + protected abstract void listen(int backlog) throws IOException; + + /** + * Accepts a connection on this socket. + * + * @param s The implementation object for the accepted connection. + * + * @exception IOException If an error occurs + */ + protected abstract void accept(SocketImpl s) throws IOException; + + /** + * Returns an InputStream object for reading from this socket. + * + * @return An InputStream for reading from this socket. + * + * @exception IOException If an error occurs + */ + protected abstract InputStream getInputStream() throws IOException; + + /** + * Returns an OutputStream object for writing to this socket + * + * @return An OutputStream for writing to this socket. + * + * @exception IOException If an error occurs. + */ + protected abstract OutputStream getOutputStream() throws IOException; + + /** + * Returns the number of bytes that the caller can read from this socket + * without blocking. + * + * @return The number of readable bytes before blocking + * + * @exception IOException If an error occurs + */ + protected abstract int available() throws IOException; + + /** + * Closes the socket. This will normally cause any resources, such as the + * InputStream, OutputStream and associated file descriptors to be freed. + *

      + * Note that if the SO_LINGER option is set on this socket, then the + * operation could block. + * + * @exception IOException If an error occurs + */ + protected abstract void close() throws IOException; + + /** + * Returns the FileDescriptor objects for this socket. + * + * @return A FileDescriptor for this socket. + */ + protected FileDescriptor getFileDescriptor() + { + return fd; + } + + /** + * Returns the remote address this socket is connected to + * + * @return The remote address + */ + protected InetAddress getInetAddress() + { + return address; + } + + /** + * Returns the remote port this socket is connected to + * + * @return The remote port + */ + protected int getPort() + { + return port; + } + + /** + * Returns true or false when this socket supports sending urgent data + * or not. + * + * @return true if the socket implementation supports sending urgent data, + * false otherwise + * + * @since 1.4 + */ + protected boolean supportsUrgentData() + { + // This method has to be overwritten by socket classes that support + // sending urgend data. + return false; + } + + /** + * Sends one byte of urgent data to the socket. + * + * @param data The byte to send, the low eight bits of it + * + * @exception IOException If an error occurs + * + * @since 1.4 + */ + protected abstract void sendUrgentData(int data) throws IOException; + + /** + * Returns the local port this socket is bound to + * + * @return The local port + */ + protected int getLocalPort() + { + return localport; + } + + /** + * Returns a String representing the remote host and port of + * this socket. + * + * @return A String for this socket. + */ + public String toString() + { + return "[addr=" + + ((address == null) ? "0.0.0.0/0.0.0.0" : address.toString()) + + ",port=" + port + ",localport=" + localport + "]"; + } + + /** + * Shut down the input side of this socket. Subsequent reads will + * return end-of-file. + * + * @exception IOException if an error occurs + */ + protected void shutdownInput() throws IOException + { + throw new IOException("Not implemented in this socket class"); + } + + /** + * Shut down the output side of this socket. Subsequent writes will + * fail with an IOException. + * + * @exception IOException if an error occurs + */ + protected void shutdownOutput() throws IOException + { + throw new IOException("Not implemented in this socket class"); + } +} diff --git a/libjava/classpath/java/net/SocketImplFactory.java b/libjava/classpath/java/net/SocketImplFactory.java new file mode 100644 index 000000000..b7cb10ca6 --- /dev/null +++ b/libjava/classpath/java/net/SocketImplFactory.java @@ -0,0 +1,59 @@ +/* SocketImplFactory.java -- Interface to create a SocketImpl object + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** Written using on-line Java Platform 1.2 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface defines one method which returns a SocketImpl + * object. This should not be needed by ordinary applications. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public interface SocketImplFactory +{ + /** + * This method returns an instance of the SocketImpl object + * + * @return A SocketImpl object + */ + SocketImpl createSocketImpl(); +} // interface SocketImplFactory diff --git a/libjava/classpath/java/net/SocketOptions.java b/libjava/classpath/java/net/SocketOptions.java new file mode 100644 index 000000000..659bf750c --- /dev/null +++ b/libjava/classpath/java/net/SocketOptions.java @@ -0,0 +1,166 @@ +/* SocketOptions.java -- Implements options for sockets (duh!) + Copyright (C) 1998, 1999, 2000, 2001, + 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification. + * Status: Believed complete and correct. + */ +/** + * This interface is used by SocketImpl and + * DatagramSocketImpl to implement options + * on sockets. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status should be completely JDK 1.4 compatible + */ +public interface SocketOptions +{ + /** + * Option id for the SO_KEEPALIVE value + * @since 1.3 + */ + int SO_KEEPALIVE = 0x8; + + /** + * Option id for the SO_LINGER value + */ + int SO_LINGER = 0x80; // 128 + + /** + * Option id for the SO_TIMEOUT value + */ + int SO_TIMEOUT = 0x1006; // 4102 + + /** + * Retrieve the local address to which the socket is bound. + */ + int SO_BINDADDR = 0x0F; // 15 + + /** + * Option id for the send buffer size + * @since 1.2 + */ + int SO_SNDBUF = 0x1001; // 4097 + + /** + * Option id for the receive buffer size + * @since 1.2 + */ + int SO_RCVBUF = 0x1002; // 4098 + + /** + * Sets the SO_REUSEADDR parameter on a socket + */ + int SO_REUSEADDR = 0x04; // 4 + + /** + * Sets SO_BROADCAST for a socket + * @since 1.4 + */ + int SO_BROADCAST = 0x20; // 32 + + /** + * Sets SO_OOBINLINE for a socket + * @since 1.4 + */ + int SO_OOBINLINE = 0x1003; // 4099 + + /** + * Option id for the TCP_NODELAY value + */ + int TCP_NODELAY = 0x01; // 1 + + /** + * Options id for the IP_MULTICAST_IF value + */ + int IP_MULTICAST_IF = 0x10; // 16 + + /** + * same as above + * @since 1.4 + */ + int IP_MULTICAST_IF2 = 0x1F; // 31 + + /** + * This option enables or disables local loopback of multicast datagrams. + * @since 1.4 + */ + int IP_MULTICAST_LOOP = 0x12; // 18 + + /** + * This option sets the type-of-service or traffic class field in the + * IP header for a TCP or UDP socket. + * @since 1.4 + */ + int IP_TOS = 0x03; // 3 + + /** + * Sets the specified option on a socket to the passed in object. For + * options that take an integer argument, the passed in object is an + * Integer. For options that are set to on or off, the + * value passed will be a Boolean. The optionId + * parameter is one of the defined constants in this interface. + * + * @param optionId The identifier of the option + * @param val The value to set the option to + * + * @exception SocketException If an error occurs + */ + void setOption(int optionId, Object val) throws SocketException; + + /** + * Returns the current setting of the specified option. The + * Object returned will be an Integer for options + * that have integer values. For options that are set to on or off, a + * Boolean will be returned. The optionId + * parameter is one of the defined constants in this interface. + * + * @param optionId The option identifier + * + * @return The current value of the option + * + * @exception SocketException If an error occurs + */ + Object getOption(int optionId) throws SocketException; +} // interface SocketOptions diff --git a/libjava/classpath/java/net/SocketPermission.java b/libjava/classpath/java/net/SocketPermission.java new file mode 100644 index 000000000..ce16a79a0 --- /dev/null +++ b/libjava/classpath/java/net/SocketPermission.java @@ -0,0 +1,636 @@ +/* SocketPermission.java -- Class modeling permissions for socket operations + Copyright (C) 1998, 2000, 2001, 2002, 2004, 2006 Free Software + Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.StringTokenizer; + + +/** + * This class models a specific set of permssions for connecting to a + * host. There are two elements to this, the host/port combination and + * the permission list. + *

      + * The host/port combination is specified as followed + *

      + *

      + * hostname[:[-]port[-[port]]]
      + * 
      + *

      + * The hostname portion can be either a hostname or IP address. If it is + * a hostname, a wildcard is allowed in hostnames. This wildcard is a "*" + * and matches one or more characters. Only one "*" may appear in the + * host and it must be the leftmost character. For example, + * "*.urbanophile.com" matches all hosts in the "urbanophile.com" domain. + *

      + * The port portion can be either a single value, or a range of values + * treated as inclusive. The first or the last port value in the range + * can be omitted in which case either the minimum or maximum legal + * value for a port (respectively) is used by default. Here are some + * examples: + *

        + *
      • 8080 - Represents port 8080 only
      • + *
      • 2000-3000 - Represents ports 2000 through 3000 inclusive
      • + *
      • -4000 - Represents ports 0 through 4000 inclusive
      • + *
      • 1024- - Represents ports 1024 through 65535 inclusive
      • + *

      + * The permission list is a comma separated list of individual permissions. + * These individual permissions are: + *

      + *

      + * accept
      + * connect
      + * listen
      + * resolve
      + * 
      + *

      + * The "listen" permission is only relevant if the host is localhost. If + * any permission at all is specified, then resolve permission is implied to + * exist. + *

      + * Here are a variety of examples of how to create SocketPermission's + *

      + * SocketPermission("www.urbanophile.com", "connect");
      + *   Can connect to any port on www.urbanophile.com
      + * SocketPermission("www.urbanophile.com:80", "connect,accept");
      + *   Can connect to or accept connections from www.urbanophile.com on port 80
      + * SocketPermission("localhost:1024-", "listen,accept,connect");
      + *   Can connect to, accept from, an listen on any local port number 1024
      + *   and up.
      + * SocketPermission("*.edu", "connect");
      + *   Can connect to any host in the edu domain
      + * SocketPermission("197.197.20.1", "accept");
      + *   Can accept connections from 197.197.20.1
      + * 

      + * + * This class also supports IPv6 addresses. These should be specified + * in either RFC 2732 format or in full uncompressed form. + * + * @since 1.2 + * + * @author Written by Aaron M. Renn (arenn@urbanophile.com) + * @author Extensively modified by Gary Benson (gbenson@redhat.com) + */ +public final class SocketPermission extends Permission implements Serializable +{ + static final long serialVersionUID = -7204263841984476862L; + + /** + * A hostname (possibly wildcarded). Will be set if and only if + * this object was initialized with a hostname. + */ + private transient String hostname = null; + + /** + * An IP address (IPv4 or IPv6). Will be set if and only if this + * object was initialized with a single literal IP address. + */ + private transient InetAddress address = null; + + /** + * A range of ports. + */ + private transient int minport; + private transient int maxport; + + /** + * Values used for minimum and maximum ports when one or both bounds + * are omitted. This class is essentially independent of the + * networking code it describes, so we do not limit ports to the + * usual network limits of 1 and 65535. + */ + private static final int MIN_PORT = 0; + private static final int MAX_PORT = Integer.MAX_VALUE; + + /** + * The actions for which we have permission. This field is present + * to make the serialized form correct and should not be used by + * anything other than writeObject: everything else should use + * actionmask. + */ + private String actions; + + /** + * A bitmask representing the actions for which we have permission. + */ + private transient int actionmask; + + /** + * The available actions, in the canonical order required for getActions(). + */ + private static final String[] ACTIONS = new String[] { + "connect", "listen", "accept", "resolve"}; + + /** + * Initializes a new instance of SocketPermission with the + * specified host/port combination and actions string. + * + * @param hostport The hostname/port number combination + * @param actions The actions string + */ + public SocketPermission(String hostport, String actions) + { + super(processHostport(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; + boolean colon_allowed = true; + for (int i = 0; i < hostport.length(); i++) + { + if (hostport.charAt(i) == ':') + { + if (!colon_allowed) + throw new IllegalArgumentException("Ambiguous hostport part"); + colons++; + colon_allowed = false; + } + else + colon_allowed = true; + } + + 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 + int last_colon = hostport.lastIndexOf(':'); + 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 host, ports; + if (hostport.charAt(0) == '[') + { + // host is a bracketed IPv6 address + int end = hostport.indexOf("]"); + if (end == -1) + throw new IllegalArgumentException("Unmatched '['"); + host = hostport.substring(1, end); + + address = InetAddress.getByLiteral(host); + if (address == null) + throw new IllegalArgumentException("Bad IPv6 address"); + + if (end == hostport.length() - 1) + ports = ""; + else if (hostport.charAt(end + 1) == ':') + ports = hostport.substring(end + 2); + else + throw new IllegalArgumentException("Bad character after ']'"); + } + else + { + // host is a hostname or IPv4 address + int sep = hostport.indexOf(":"); + if (sep == -1) + { + host = hostport; + ports = ""; + } + else + { + host = hostport.substring(0, sep); + ports = hostport.substring(sep + 1); + } + + address = InetAddress.getByLiteral(host); + if (address == null) + { + if (host.lastIndexOf('*') > 0) + throw new IllegalArgumentException("Bad hostname"); + + hostname = host; + } + } + + // Parse and validate the ports + if (ports.length() == 0) + { + minport = MIN_PORT; + maxport = MAX_PORT; + } + else + { + int sep = ports.indexOf("-"); + if (sep == -1) + { + // a single port + minport = maxport = Integer.parseInt(ports); + } + else + { + if (ports.indexOf("-", sep + 1) != -1) + throw new IllegalArgumentException("Unexpected '-'"); + + if (sep == 0) + { + // an upper bound + minport = MIN_PORT; + maxport = Integer.parseInt(ports.substring(1)); + } + else if (sep == ports.length() - 1) + { + // a lower bound + minport = + Integer.parseInt(ports.substring(0, ports.length() - 1)); + maxport = MAX_PORT; + } + else + { + // a range with two bounds + minport = Integer.parseInt(ports.substring(0, sep)); + maxport = Integer.parseInt(ports.substring(sep + 1)); + } + } + } + } + + /** + * Parse the actions argument to the constructor. + */ + private void setActions(String actionstring) + { + actionmask = 0; + + boolean resolve_needed = false; + boolean resolve_present = false; + + StringTokenizer t = new StringTokenizer(actionstring, ","); + while (t.hasMoreTokens()) + { + String action = t.nextToken(); + action = action.trim().toLowerCase(); + setAction(action); + + if (action.equals("resolve")) + resolve_present = true; + else + resolve_needed = true; + } + + if (resolve_needed && !resolve_present) + setAction("resolve"); + } + + /** + * Parse one element of the actions argument to the constructor. + */ + private void setAction(String action) + { + for (int i = 0; i < ACTIONS.length; i++) + { + if (action.equals(ACTIONS[i])) + { + actionmask |= 1 << i; + return; + } + } + throw new IllegalArgumentException("Unknown action " + action); + } + + /** + * Tests this object for equality against another. This will be true if + * and only if the passed object is an instance of + * SocketPermission and both its hostname/port combination + * and permissions string are identical. + * + * @param obj The object to test against for equality + * + * @return true if object is equal to this object, + * false otherwise. + */ + public boolean equals(Object obj) + { + SocketPermission p; + + if (obj instanceof SocketPermission) + p = (SocketPermission) obj; + else + return false; + + if (p.actionmask != actionmask || + p.minport != minport || + p.maxport != maxport) + return false; + + if (address != null) + { + if (p.address == null) + return false; + else + return p.address.equals(address); + } + else + { + if (p.hostname == null) + return false; + else + return p.hostname.equals(hostname); + } + } + + /** + * Returns a hash code value for this object. Overrides the + * Permission.hashCode(). + * + * @return A hash code + */ + public int hashCode() + { + int code = actionmask + minport + maxport; + if (address != null) + code += address.hashCode(); + else + code += hostname.hashCode(); + return code; + } + + /** + * Returns the list of permission actions in this object in canonical + * order. The canonical order is "connect,listen,accept,resolve" + * + * @return The permitted action string. + */ + public String getActions() + { + CPStringBuilder sb = new CPStringBuilder(""); + + for (int i = 0; i < ACTIONS.length; i++) + { + if ((actionmask & (1 << i)) != 0) + { + if (sb.length() != 0) + sb.append(","); + sb.append(ACTIONS[i]); + } + } + + return sb.toString(); + } + + /** + * Returns a new PermissionCollection object that can hold + * SocketPermission's. + * + * @return A new PermissionCollection. + */ + public PermissionCollection newPermissionCollection() + { + // FIXME: Implement + + return null; + } + + /** + * Returns an array of all IP addresses represented by this object. + */ + private InetAddress[] getAddresses() + { + if (address != null) + return new InetAddress[] {address}; + + try + { + return InetAddress.getAllByName(hostname); + } + catch (UnknownHostException e) + { + return new InetAddress[0]; + } + } + + /** + * Returns the canonical hostname represented by this object, + * or null if this object represents a wildcarded domain. + */ + private String getCanonicalHostName() + { + if (address != null) + return address.internalGetCanonicalHostName(); + if (hostname.charAt(0) == '*') + return null; + try + { + return InetAddress.getByName(hostname).internalGetCanonicalHostName(); + } + catch (UnknownHostException e) + { + return null; + } + } + + /** + * Returns true if the permission object passed it is implied by the + * this permission. This will be true if: + * + *

        + *
      • The argument is of type SocketPermission
      • + *
      • The actions list of the argument are in this object's actions
      • + *
      • The port range of the argument is within this objects port range
      • + *
      • The hostname is equal to or a subset of this objects hostname
      • + *
      + * + *

      The argument's hostname will be a subset of this object's hostname if:

      + * + *
        + *
      • The argument's hostname or IP address is equal to this object's.
      • + *
      • The argument's canonical hostname is equal to this object's.
      • + *
      • The argument's canonical name matches this domains hostname with + * wildcards
      • + *
      + * + * @param perm The Permission to check against + * + * @return true if the Permission is implied by + * this object, false otherwise. + */ + public boolean implies(Permission perm) + { + SocketPermission p; + + // First make sure we are the right object type + if (perm instanceof SocketPermission) + p = (SocketPermission) perm; + else + return false; + + // If p was initialised with an empty hostname then we do not + // imply it. This is not part of the spec, but it seems necessary. + if (p.hostname != null && p.hostname.length() == 0) + return false; + + // Next check the actions + if ((p.actionmask & actionmask) != p.actionmask) + return false; + + // Then check the ports + if ((p.minport < minport) || (p.maxport > maxport)) + return false; + + // Finally check the hosts + String p_canon = null; + + // Return true if this object was initialized with a single + // IP address which one of p's IP addresses is equal to. + if (address != null) + { + InetAddress[] addrs = p.getAddresses(); + for (int i = 0; i < addrs.length; i++) + { + if (address.equals(addrs[i])) + return true; + } + } + + // Return true if this object is a wildcarded domain that + // p's canonical name matches. + if (hostname != null && hostname.charAt(0) == '*') + { + p_canon = p.getCanonicalHostName(); + if (p_canon != null && p_canon.endsWith(hostname.substring(1))) + return true; + + } + + // Return true if this one of this object's IP addresses + // is equal to one of p's. + if (address == null) + { + InetAddress[] addrs = p.getAddresses(); + InetAddress[] p_addrs = p.getAddresses(); + + for (int i = 0; i < addrs.length; i++) + { + for (int j = 0; j < p_addrs.length; j++) + { + if (addrs[i].equals(p_addrs[j])) + return true; + } + } + } + + // Return true if this object's canonical name equals p's. + String canon = getCanonicalHostName(); + if (canon != null) + { + if (p_canon == null) + p_canon = p.getCanonicalHostName(); + if (p_canon != null && canon.equals(p_canon)) + return true; + } + + // Didn't make it + return false; + } + + /** + * Deserializes a SocketPermission object from + * an input stream. + * + * @param input the input stream. + * @throws IOException if an I/O error occurs in the stream. + * @throws ClassNotFoundException if the class of the + * serialized object could not be found. + */ + private void readObject(ObjectInputStream input) + throws IOException, ClassNotFoundException + { + input.defaultReadObject(); + setHostPort(getName()); + setActions(actions); + } + + /** + * Serializes a SocketPermission object to an + * output stream. + * + * @param output the output stream. + * @throws IOException if an I/O error occurs in the stream. + */ + private void writeObject(ObjectOutputStream output) + throws IOException + { + actions = getActions(); + output.defaultWriteObject(); + } +} diff --git a/libjava/classpath/java/net/SocketTimeoutException.java b/libjava/classpath/java/net/SocketTimeoutException.java new file mode 100644 index 000000000..21b0dcd86 --- /dev/null +++ b/libjava/classpath/java/net/SocketTimeoutException.java @@ -0,0 +1,73 @@ +/* SocketTimeoutException.java -- the socket timed out + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.InterruptedIOException; + + +/** + * This exception signals that a socket read or accept timed out. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public class SocketTimeoutException extends InterruptedIOException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -8846654841826352300L; + + /** + * Create a new instance without a descriptive error message. + */ + public SocketTimeoutException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public SocketTimeoutException(String message) + { + super(message); + } +} // class SocketTimeoutException diff --git a/libjava/classpath/java/net/TODO b/libjava/classpath/java/net/TODO new file mode 100644 index 000000000..e48969d3a --- /dev/null +++ b/libjava/classpath/java/net/TODO @@ -0,0 +1,24 @@ +-- DNS cache purging. + +-- Implement ContentHandler chaining (non-JDK feature) + +-- Implement MIME type by file determination chaining using external + disk files. (non-JDK feature) + +-- Implement determining MIME type from an InputStream + +-- Datagram peek()'s + +-- Finalize methods for sockets + +-- HTTP - caching (supported by JDK?) + +-- HTTP - all protocol support beyond basic GET functionality + +-- Fix call to Date(String) in URLConnection.getHeaderFieldDate() when + I figure out why DateFormat isn't working. + +-- Finish off all JDK 1.2 permissions stuff + +-- Write URLClassLoader + diff --git a/libjava/classpath/java/net/URI.java b/libjava/classpath/java/net/URI.java new file mode 100644 index 000000000..b5fb9654f --- /dev/null +++ b/libjava/classpath/java/net/URI.java @@ -0,0 +1,1450 @@ +/* URI.java -- An URI class + Copyright (C) 2002, 2004, 2005, 2006, 2008 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + *

      + * A URI instance represents that defined by + * RFC3986, + * with some deviations. + *

      + *

      + * At its highest level, a URI consists of: + *

      + * [scheme:]scheme-specific-part + * [#fragment] + *

      + *

      + * where # and : are literal characters, + * and those parts enclosed in square brackets are optional. + *

      + *

      + * There are two main types of URI. An opaque URI is one + * which just consists of the above three parts, and is not further + * defined. An example of such a URI would be mailto: URI. + * In contrast, hierarchical URIs give further definition + * to the scheme-specific part, so as represent some part of a hierarchical + * structure. + *

      + *

      + * [//authority][path] + * [?query] + *

      + *

      + * with / and ? being literal characters. + * When server-based, the authority section is further subdivided into: + *

      + *

      + * [user-info@]host + * [:port] + *

      + *

      + * with @ and : as literal characters. + * Authority sections that are not server-based are said to be registry-based. + *

      + *

      + * Hierarchical URIs can be either relative or absolute. Absolute URIs + * always start with a `/', while relative URIs don't + * specify a scheme. Opaque URIs are always absolute. + *

      + *

      + * Each part of the URI may have one of three states: undefined, empty + * or containing some content. The former two of these are represented + * by null and the empty string in Java, respectively. + * The scheme-specific part may never be undefined. It also follows from + * this that the path sub-part may also not be undefined, so as to ensure + * the former. + *

      + *

      Character Escaping and Quoting

      + *

      + * The characters that can be used within a valid URI are restricted. + * There are two main classes of characters which can't be used as is + * within the URI: + *

      + *
        + *
      1. Characters outside the US-ASCII character set. + * These have to be escaped in order to create + * an RFC-compliant URI; this means replacing the character with the + * appropriate hexadecimal value, preceded by a `%'.
      2. + *
      3. Illegal characters (e.g. space characters, + * control characters) are quoted, which results in them being encoded + * in the same way as non-US-ASCII characters.
      4. + *
      + *

      + * The set of valid characters differs depending on the section of the URI: + *

      + *
        + *
      • Scheme: Must be an alphanumeric, `-', `.' or '+'.
      • + *
      • Authority:Composed of the username, host, port, `@' + * and `:'.
      • + *
      • Username: Allows unreserved or percent-encoded + * characters, sub-delimiters and `:'.
      • + *
      • Host: Allows unreserved or percent-encoded + * characters, sub-delimiters and square brackets (`[' and `]') for IPv6 + * addresses.
      • + *
      • Port: Digits only.
      • + *
      • Path: Allows the path characters and `/'. + *
      • Query: Allows the path characters, `?' and '/'. + *
      • Fragment: Allows the path characters, `?' and '/'. + *
      + *

      + * These definitions reference the following sets of characters: + *

      + *
        + *
      • Unreserved characters: The alphanumerics plus + * `-', `.', `_', and `~'.
      • + *
      • Sub-delimiters: `!', `$', `&', `(', `)', `*', + * `+', `,', `;', `=' and the single-quote itself.
      • + *
      • Path characters: Unreserved and percent-encoded + * characters and the sub-delimiters along with `@' and `:'.
      • + *
      + *

      + * The constructors and accessor methods allow the use and retrieval of + * URI components which contain non-US-ASCII characters directly. + * They are only escaped when the toASCIIString() method + * is used. In contrast, illegal characters are always quoted, with the + * exception of the return values of the non-raw accessors. + *

      + * + * @author Ito Kazumitsu (ito.kazumitsu@hitachi-cable.co.jp) + * @author Dalibor Topic (robilad@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.4 + */ +public final class URI + implements Comparable, Serializable +{ + /** + * For serialization compatability. + */ + static final long serialVersionUID = -6052424284110960213L; + + /** + * Regular expression for parsing URIs. + * + * Taken from RFC 2396, Appendix B. + * This expression doesn't parse IPv6 addresses. + */ + private static final String URI_REGEXP = + "^(([^:/?#]+):)?((//([^/?#]*))?([^?#]*)(\\?([^#]*))?)?(#(.*))?"; + + /** + * Regular expression for parsing the authority segment. + */ + private static final String AUTHORITY_REGEXP = + "(([^?#]*)@)?([^?#:]*)(:([0-9]*))?"; + + /** + * Valid characters (taken from rfc2396/3986) + */ + private static final String RFC2396_DIGIT = "0123456789"; + private static final String RFC2396_LOWALPHA = "abcdefghijklmnopqrstuvwxyz"; + private static final String RFC2396_UPALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String RFC2396_ALPHA = + RFC2396_LOWALPHA + RFC2396_UPALPHA; + private static final String RFC2396_ALPHANUM = RFC2396_DIGIT + RFC2396_ALPHA; + private static final String RFC3986_UNRESERVED = RFC2396_ALPHANUM + "-._~"; + private static final String RFC3986_SUBDELIMS = "!$&'()*+,;="; + private static final String RFC3986_REG_NAME = + RFC3986_UNRESERVED + RFC3986_SUBDELIMS + "%"; + private static final String RFC3986_PCHAR = RFC3986_UNRESERVED + + RFC3986_SUBDELIMS + ":@%"; + private static final String RFC3986_SEGMENT = RFC3986_PCHAR; + private static final String RFC3986_PATH_SEGMENTS = RFC3986_SEGMENT + "/"; + private static final String RFC3986_SSP = RFC3986_PCHAR + "?/"; + private static final String RFC3986_HOST = RFC3986_REG_NAME + "[]"; + private static final String RFC3986_USERINFO = RFC3986_REG_NAME + ":"; + + /** + * Index of scheme component in parsed URI. + */ + private static final int SCHEME_GROUP = 2; + + /** + * Index of scheme-specific-part in parsed URI. + */ + private static final int SCHEME_SPEC_PART_GROUP = 3; + + /** + * Index of authority component in parsed URI. + */ + private static final int AUTHORITY_GROUP = 5; + + /** + * Index of path component in parsed URI. + */ + private static final int PATH_GROUP = 6; + + /** + * Index of query component in parsed URI. + */ + private static final int QUERY_GROUP = 8; + + /** + * Index of fragment component in parsed URI. + */ + private static final int FRAGMENT_GROUP = 10; + + /** + * Index of userinfo component in parsed authority section. + */ + private static final int AUTHORITY_USERINFO_GROUP = 2; + + /** + * Index of host component in parsed authority section. + */ + private static final int AUTHORITY_HOST_GROUP = 3; + + /** + * Index of port component in parsed authority section. + */ + private static final int AUTHORITY_PORT_GROUP = 5; + + /** + * The compiled version of the URI regular expression. + */ + private static final Pattern URI_PATTERN; + + /** + * The compiled version of the authority regular expression. + */ + private static final Pattern AUTHORITY_PATTERN; + + /** + * The set of valid hexadecimal characters. + */ + private static final String HEX = "0123456789ABCDEF"; + + private transient String scheme; + private transient String rawSchemeSpecificPart; + private transient String schemeSpecificPart; + private transient String rawAuthority; + private transient String authority; + private transient String rawUserInfo; + private transient String userInfo; + private transient String rawHost; + private transient String host; + private transient int port = -1; + private transient String rawPath; + private transient String path; + private transient String rawQuery; + private transient String query; + private transient String rawFragment; + private transient String fragment; + private String string; + + /** + * Static initializer to pre-compile the regular expressions. + */ + static + { + URI_PATTERN = Pattern.compile(URI_REGEXP); + AUTHORITY_PATTERN = Pattern.compile(AUTHORITY_REGEXP); + } + + private void readObject(ObjectInputStream is) + throws ClassNotFoundException, IOException + { + this.string = (String) is.readObject(); + try + { + parseURI(this.string); + } + catch (URISyntaxException x) + { + // Should not happen. + throw new RuntimeException(x); + } + } + + private void writeObject(ObjectOutputStream os) throws IOException + { + if (string == null) + string = toString(); + os.writeObject(string); + } + + /** + *

      + * Returns the string content of the specified group of the supplied + * matcher. The returned value is modified according to the following: + *

      + *
        + *
      • If the resulting string has a length greater than 0, then + * that string is returned.
      • + *
      • If a string of zero length, is matched, then the content + * of the preceding group is considered. If this is also an empty + * string, then null is returned to indicate an undefined + * value. Otherwise, the value is truly the empty string and this is + * the returned value.
      • + *
      + *

      + * This method is used for matching against all parts of the URI + * that may be either undefined or empty (i.e. all those but the + * scheme-specific part and the path). In each case, the preceding + * group is the content of the original group, along with some + * additional distinguishing feature. For example, the preceding + * group for the query includes the preceding question mark, + * while that of the fragment includes the hash symbol. The presence + * of these features enables disambiguation between the two cases + * of a completely unspecified value and a simple non-existant value. + * The scheme differs in that it will never return an empty string; + * the delimiter follows the scheme rather than preceding it, so + * it becomes part of the following section. The same is true + * of the user information. + *

      + * + * @param match the matcher, which contains the results of the URI + * matched against the URI regular expression. + * @return either the matched content, null for undefined + * values, or an empty string for a URI part with empty content. + */ + private static String getURIGroup(Matcher match, int group) + { + String matched = match.group(group); + if (matched == null || matched.length() == 0) + { + String prevMatched = match.group(group -1); + if (prevMatched == null || prevMatched.length() == 0) + return null; + else + return ""; + } + return matched; + } + + /** + * Sets fields of this URI by parsing the given string. + * + * @param str The string to parse + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + private void parseURI(String str) throws URISyntaxException + { + Matcher matcher = URI_PATTERN.matcher(str); + + if (matcher.matches()) + { + scheme = getURIGroup(matcher, SCHEME_GROUP); + rawSchemeSpecificPart = matcher.group(SCHEME_SPEC_PART_GROUP); + schemeSpecificPart = unquote(rawSchemeSpecificPart); + if (!isOpaque()) + { + rawAuthority = getURIGroup(matcher, AUTHORITY_GROUP); + rawPath = matcher.group(PATH_GROUP); + rawQuery = getURIGroup(matcher, QUERY_GROUP); + } + rawFragment = getURIGroup(matcher, FRAGMENT_GROUP); + } + else + throw new URISyntaxException(str, + "doesn't match URI regular expression"); + parseServerAuthority(); + + // We must eagerly unquote the parts, because this is the only time + // we may throw an exception. + authority = unquote(rawAuthority); + userInfo = unquote(rawUserInfo); + host = unquote(rawHost); + path = unquote(rawPath); + query = unquote(rawQuery); + fragment = unquote(rawFragment); + } + + /** + * Unquote "%" + hex quotes characters + * + * @param str The string to unquote or null. + * + * @return The unquoted string or null if str was null. + * + * @exception URISyntaxException If the given string contains invalid + * escape sequences. + */ + private static String unquote(String str) throws URISyntaxException + { + if (str == null) + return null; + byte[] buf = new byte[str.length()]; + int pos = 0; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (c == '%') + { + if (i + 2 >= str.length()) + throw new URISyntaxException(str, "Invalid quoted character"); + int hi = Character.digit(str.charAt(++i), 16); + int lo = Character.digit(str.charAt(++i), 16); + if (lo < 0 || hi < 0) + throw new URISyntaxException(str, "Invalid quoted character"); + buf[pos++] = (byte) (hi * 16 + lo); + } + else + buf[pos++] = (byte) c; + } + try + { + return new String(buf, 0, pos, "utf-8"); + } + catch (java.io.UnsupportedEncodingException x2) + { + throw (Error) new InternalError().initCause(x2); + } + } + + /** + * Quote characters illegal in URIs in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quote(String str) + { + return quote(str, RFC3986_SSP); + } + + /** + * Quote characters illegal in URI authorities in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteAuthority(String str) + { + // Technically, we should be using RFC2396_AUTHORITY, but + // it contains no additional characters. + return quote(str, RFC3986_REG_NAME); + } + + /** + * Quotes the characters in the supplied string that are not part of + * the specified set of legal characters. + * + * @param str the string to quote + * @param legalCharacters the set of legal characters + * + * @return the quoted string. + */ + private static String quote(String str, String legalCharacters) + { + CPStringBuilder sb = new CPStringBuilder(str.length()); + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if ((legalCharacters.indexOf(c) == -1) + && (c <= 127)) + { + sb.append('%'); + sb.append(HEX.charAt(c / 16)); + sb.append(HEX.charAt(c % 16)); + } + else + sb.append(c); + } + return sb.toString(); + } + + /** + * Quote characters illegal in URI hosts in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteHost(String str) + { + return quote(str, RFC3986_HOST); + } + + /** + * Quote characters illegal in URI paths in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quotePath(String str) + { + // Technically, we should be using RFC2396_PATH, but + // it contains no additional characters. + return quote(str, RFC3986_PATH_SEGMENTS); + } + + /** + * Quote characters illegal in URI user infos in given string. + * + * Replace illegal characters by encoding their UTF-8 + * representation as "%" + hex code for each resulting + * UTF-8 character. + * + * @param str The string to quote + * + * @return The quoted string. + */ + private static String quoteUserInfo(String str) + { + return quote(str, RFC3986_USERINFO); + } + + /** + * Creates an URI from the given string + * + * @param str The string to create the URI from + * + * @exception URISyntaxException If the given string violates RFC 2396 + * @exception NullPointerException If str is null + */ + public URI(String str) throws URISyntaxException + { + this.string = str; + parseURI(str); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param userInfo The username and authorization info + * @param host The hostname + * @param port The port number + * @param path The path + * @param query The query + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String userInfo, String host, int port, + String path, String query, String fragment) + throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (userInfo == null && host == null && port == -1 ? "" : "//") + + (userInfo == null ? "" : quoteUserInfo(userInfo) + "@") + + (host == null ? "" : quoteHost(host)) + + (port == -1 ? "" : ":" + String.valueOf(port)) + + (path == null ? "" : quotePath(path)) + + (query == null ? "" : "?" + quote(query)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param authority The authority + * @param path The apth + * @param query The query + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String authority, String path, String query, + String fragment) throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (authority == null ? "" : "//" + quoteAuthority(authority)) + + (path == null ? "" : quotePath(path)) + + (query == null ? "" : "?" + quote(query)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param host The hostname + * @param path The path + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String host, String path, String fragment) + throws URISyntaxException + { + this(scheme, null, host, -1, path, null, fragment); + } + + /** + * Create an URI from the given components + * + * @param scheme The scheme name + * @param ssp The scheme specific part + * @param fragment The fragment + * + * @exception URISyntaxException If the given string violates RFC 2396 + */ + public URI(String scheme, String ssp, String fragment) + throws URISyntaxException + { + this((scheme == null ? "" : scheme + ":") + + (ssp == null ? "" : quote(ssp)) + + (fragment == null ? "" : "#" + quote(fragment))); + } + + /** + * Create an URI from the given string + * + * @param str The string to create the URI from + * + * @exception IllegalArgumentException If the given string violates RFC 2396 + * @exception NullPointerException If str is null + */ + public static URI create(String str) + { + try + { + return new URI(str); + } + catch (URISyntaxException e) + { + throw (IllegalArgumentException) new IllegalArgumentException() + .initCause(e); + } + } + + /** + * Attempts to parse this URI's authority component, if defined, + * into user-information, host, and port components. The purpose + * of this method was to disambiguate between some authority sections, + * which form invalid server-based authories, but valid registry + * based authorities. In the updated RFC 3986, the authority section + * is defined differently, with registry-based authorities part of + * the host section. Thus, this method is now simply an explicit + * way of parsing any authority section. + * + * @return the URI, with the authority section parsed into user + * information, host and port components. + * @throws URISyntaxException if the given string violates RFC 2396 + */ + public URI parseServerAuthority() throws URISyntaxException + { + if (rawAuthority != null) + { + Matcher matcher = AUTHORITY_PATTERN.matcher(rawAuthority); + + if (matcher.matches()) + { + rawUserInfo = getURIGroup(matcher, AUTHORITY_USERINFO_GROUP); + rawHost = getURIGroup(matcher, AUTHORITY_HOST_GROUP); + + String portStr = getURIGroup(matcher, AUTHORITY_PORT_GROUP); + + if (portStr != null && ! portStr.isEmpty()) + try + { + port = Integer.parseInt(portStr); + } + catch (NumberFormatException e) + { + URISyntaxException use = + new URISyntaxException + (string, "doesn't match URI regular expression"); + use.initCause(e); + throw use; + } + } + else + throw new URISyntaxException(string, + "doesn't match URI regular expression"); + } + return this; + } + + /** + *

      + * Returns a normalized version of the URI. If the URI is opaque, + * or its path is already in normal form, then this URI is simply + * returned. Otherwise, the following transformation of the path + * element takes place: + *

      + *
        + *
      1. All `.' segments are removed.
      2. + *
      3. Each `..' segment which can be paired with a prior non-`..' segment + * is removed along with the preceding segment.
      4. + *
      5. A `.' segment is added to the front if the first segment contains + * a colon (`:'). This is a deviation from the RFC, which prevents + * confusion between the path and the scheme.
      6. + *
      + *

      + * The resulting URI will be free of `.' and `..' segments, barring those + * that were prepended or which couldn't be paired, respectively. + *

      + * + * @return the normalized URI. + */ + public URI normalize() + { + if (isOpaque() || path.indexOf("/./") == -1 && path.indexOf("/../") == -1) + return this; + try + { + return new URI(scheme, authority, normalizePath(path), query, + fragment); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Normalized URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + *

      + * Normalize the given path. The following transformation takes place: + *

      + *
        + *
      1. All `.' segments are removed.
      2. + *
      3. Each `..' segment which can be paired with a prior non-`..' segment + * is removed along with the preceding segment.
      4. + *
      5. A `.' segment is added to the front if the first segment contains + * a colon (`:'). This is a deviation from the RFC, which prevents + * confusion between the path and the scheme.
      6. + *
      + *

      + * The resulting URI will be free of `.' and `..' segments, barring those + * that were prepended or which couldn't be paired, respectively. + *

      + * + * @param relativePath the relative path to be normalized. + * @return the normalized path. + */ + private String normalizePath(String relativePath) + { + /* + This follows the algorithm in section 5.2.4. of RFC3986, + but doesn't modify the input buffer. + */ + CPStringBuilder input = new CPStringBuilder(relativePath); + CPStringBuilder output = new CPStringBuilder(); + int start = 0; + while (start < input.length()) + { + /* A */ + if (input.indexOf("../",start) == start) + { + start += 3; + continue; + } + if (input.indexOf("./",start) == start) + { + start += 2; + continue; + } + /* B */ + if (input.indexOf("/./",start) == start) + { + start += 2; + continue; + } + if (input.indexOf("/.",start) == start + && input.charAt(start + 2) != '.') + { + start += 1; + input.setCharAt(start,'/'); + continue; + } + /* C */ + if (input.indexOf("/../",start) == start) + { + start += 3; + removeLastSegment(output); + continue; + } + if (input.indexOf("/..",start) == start) + { + start += 2; + input.setCharAt(start,'/'); + removeLastSegment(output); + continue; + } + /* D */ + if (start == input.length() - 1 && input.indexOf(".",start) == start) + { + input.delete(0,1); + continue; + } + if (start == input.length() - 2 && input.indexOf("..",start) == start) + { + input.delete(0,2); + continue; + } + /* E */ + int indexOfSlash = input.indexOf("/",start); + while (indexOfSlash == start) + { + output.append("/"); + ++start; + indexOfSlash = input.indexOf("/",start); + } + if (indexOfSlash == -1) + indexOfSlash = input.length(); + output.append(input.substring(start, indexOfSlash)); + start = indexOfSlash; + } + return output.toString(); + } + + /** + * Removes the last segment of the path from the specified buffer. + * + * @param buffer the buffer containing the path. + */ + private void removeLastSegment(CPStringBuilder buffer) + { + int lastSlash = buffer.lastIndexOf("/"); + if (lastSlash == -1) + buffer.setLength(0); + else + buffer.setLength(lastSlash); + } + + /** + * Resolves the given URI against this URI + * + * @param uri The URI to resolve against this URI + * + * @return The resulting URI, or null when it couldn't be resolved + * for some reason. + * + * @throws NullPointerException if uri is null + */ + public URI resolve(URI uri) + { + if (uri.isAbsolute()) + return uri; + if (uri.isOpaque()) + return uri; + + String scheme = uri.getScheme(); + String schemeSpecificPart = uri.getSchemeSpecificPart(); + String authority = uri.getAuthority(); + String path = uri.getPath(); + String query = uri.getQuery(); + String fragment = uri.getFragment(); + + try + { + if (fragment != null && path != null && path.equals("") + && scheme == null && authority == null && query == null) + return new URI(this.scheme, this.schemeSpecificPart, fragment); + + if (authority == null) + { + authority = this.authority; + if (path == null) + path = ""; + if (! (path.startsWith("/"))) + { + CPStringBuilder basepath = new CPStringBuilder(this.path); + int i = this.path.lastIndexOf('/'); + + if (i >= 0) + basepath.delete(i + 1, basepath.length()); + + basepath.append(path); + path = normalizePath(basepath.toString()); + } + } + return new URI(this.scheme, authority, path, query, fragment); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Resolved URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + * Resolves the given URI string against this URI + * + * @param str The URI as string to resolve against this URI + * + * @return The resulting URI + * + * @throws IllegalArgumentException If the given URI string + * violates RFC 2396 + * @throws NullPointerException If uri is null + */ + public URI resolve(String str) throws IllegalArgumentException + { + return resolve(create(str)); + } + + /** + *

      + * Relativizes the given URI against this URI. The following + * algorithm is used: + *

      + *
        + *
      • If either URI is opaque, the given URI is returned.
      • + *
      • If the schemes of the URIs differ, the given URI is returned.
      • + *
      • If the authority components of the URIs differ, then the given + * URI is returned.
      • + *
      • If the path of this URI is not a prefix of the supplied URI, + * then the given URI is returned.
      • + *
      • If all the above conditions hold, a new URI is created using the + * query and fragment components of the given URI, along with a path + * computed by removing the path of this URI from the start of the path + * of the supplied URI.
      • + *
      + * + * @param uri the URI to relativize agsint this URI + * @return the resulting URI + * @throws NullPointerException if the uri is null + */ + public URI relativize(URI uri) + { + if (isOpaque() || uri.isOpaque()) + return uri; + if (scheme == null && uri.getScheme() != null) + return uri; + if (scheme != null && !(scheme.equals(uri.getScheme()))) + return uri; + if (rawAuthority == null && uri.getRawAuthority() != null) + return uri; + if (rawAuthority != null && !(rawAuthority.equals(uri.getRawAuthority()))) + return uri; + String basePath = rawPath; + if (!(uri.getRawPath().equals(rawPath))) + { + if (!(basePath.endsWith("/"))) + basePath = basePath.concat("/"); + if (!(uri.getRawPath().startsWith(basePath))) + return uri; + } + try + { + return new URI(null, null, + uri.getRawPath().substring(basePath.length()), + uri.getRawQuery(), uri.getRawFragment()); + } + catch (URISyntaxException e) + { + throw (Error) new InternalError("Relativized URI variant could not "+ + "be constructed").initCause(e); + } + } + + /** + * Creates an URL from an URI + * + * @throws MalformedURLException If a protocol handler for the URL could + * not be found, or if some other error occurred while constructing the URL + * @throws IllegalArgumentException If the URI is not absolute + */ + public URL toURL() throws IllegalArgumentException, MalformedURLException + { + if (isAbsolute()) + return new URL(this.toString()); + + throw new IllegalArgumentException("not absolute"); + } + + /** + * Returns the scheme of the URI + */ + public String getScheme() + { + return scheme; + } + + /** + * Tells whether this URI is absolute or not + */ + public boolean isAbsolute() + { + return scheme != null; + } + + /** + * Tell whether this URI is opaque or not + */ + public boolean isOpaque() + { + return ((scheme != null) && ! (schemeSpecificPart.startsWith("/"))); + } + + /** + * Returns the raw scheme specific part of this URI. + * The scheme-specific part is never undefined, though it may be empty + */ + public String getRawSchemeSpecificPart() + { + return rawSchemeSpecificPart; + } + + /** + * Returns the decoded scheme specific part of this URI. + */ + public String getSchemeSpecificPart() + { + return schemeSpecificPart; + } + + /** + * Returns the raw authority part of this URI + */ + public String getRawAuthority() + { + return rawAuthority; + } + + /** + * Returns the decoded authority part of this URI + */ + public String getAuthority() + { + return authority; + } + + /** + * Returns the raw user info part of this URI + */ + public String getRawUserInfo() + { + return rawUserInfo; + } + + /** + * Returns the decoded user info part of this URI + */ + public String getUserInfo() + { + return userInfo; + } + + /** + * Returns the hostname of the URI + */ + public String getHost() + { + return host; + } + + /** + * Returns the port number of the URI + */ + public int getPort() + { + return port; + } + + /** + * Returns the raw path part of this URI + */ + public String getRawPath() + { + return rawPath; + } + + /** + * Returns the path of the URI + */ + public String getPath() + { + return path; + } + + /** + * Returns the raw query part of this URI + */ + public String getRawQuery() + { + return rawQuery; + } + + /** + * Returns the query of the URI + */ + public String getQuery() + { + return query; + } + + /** + * Return the raw fragment part of this URI + */ + public String getRawFragment() + { + return rawFragment; + } + + /** + * Returns the fragment of the URI + */ + public String getFragment() + { + return fragment; + } + + /** + *

      + * Compares the URI with the given object for equality. If the + * object is not a URI, then the method returns false. + * Otherwise, the following criteria are observed: + *

      + *
        + *
      • The scheme of the URIs must either be null (undefined) in both cases, + * or equal, ignorant of case.
      • + *
      • The raw fragment of the URIs must either be null (undefined) in both + * cases, or equal, ignorant of case.
      • + *
      • Both URIs must be of the same type (opaque or hierarchial)
      • + *
      • For opaque URIs:
      • + *
          + *
        • The raw scheme-specific parts must be equal.
        • + *
        + *
      • For hierarchical URIs:
      • + *
          + *
        • The raw paths must be equal, ignorant of case.
        • + *
        • The raw queries are either both undefined or both equal, ignorant + * of case.
        • + *
        • The raw authority sections are either both undefined or:
        • + *
        • For registry-based authorities:
        • + *
          • they are equal.
          + *
        • For server-based authorities:
        • + *
            + *
          • the hosts are equal, ignoring case
          • + *
          • the ports are equal
          • + *
          • the user information components are equal
          • + *
          + *
        + *
      + * + * @param obj the obj to compare the URI with. + * @return true if the objects are equal, according to + * the specification above. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof URI)) + return false; + URI uriObj = (URI) obj; + if (scheme == null) + { + if (uriObj.getScheme() != null) + return false; + } + else + if (!(scheme.equalsIgnoreCase(uriObj.getScheme()))) + return false; + if (rawFragment == null) + { + if (uriObj.getRawFragment() != null) + return false; + } + else + if (!(rawFragment.equalsIgnoreCase(uriObj.getRawFragment()))) + return false; + boolean opaqueThis = isOpaque(); + boolean opaqueObj = uriObj.isOpaque(); + if (opaqueThis && opaqueObj) + return rawSchemeSpecificPart.equals(uriObj.getRawSchemeSpecificPart()); + else if (!opaqueThis && !opaqueObj) + { + boolean common = rawPath.equalsIgnoreCase(uriObj.getRawPath()) + && ((rawQuery == null && uriObj.getRawQuery() == null) + || rawQuery.equalsIgnoreCase(uriObj.getRawQuery())); + if (rawAuthority == null && uriObj.getRawAuthority() == null) + return common; + if (host == null) + return common + && rawAuthority.equalsIgnoreCase(uriObj.getRawAuthority()); + return common + && host.equalsIgnoreCase(uriObj.getHost()) + && port == uriObj.getPort() + && (rawUserInfo == null ? + uriObj.getRawUserInfo() == null : + rawUserInfo.equalsIgnoreCase(uriObj.getRawUserInfo())); + } + else + return false; + } + + /** + * Computes the hashcode of the URI + */ + public int hashCode() + { + return (getScheme() == null ? 0 : 13 * getScheme().hashCode()) + + 17 * getRawSchemeSpecificPart().hashCode() + + (getRawFragment() == null ? 0 : 21 + getRawFragment().hashCode()); + } + + /** + * Compare the URI with another URI. + * Undefined components are taken to be less than any other component. + * The following criteria are observed: + *

      + *
        + *
      • Two URIs with different schemes are compared according to their + * scheme, regardless of case.
      • + *
      • A hierarchical URI is less than an opaque URI with the same + * scheme.
      • + *
      • For opaque URIs:
      • + *
          + *
        • URIs with differing scheme-specific parts are ordered according + * to the ordering of the scheme-specific part.
        • + *
        • URIs with the same scheme-specific part are ordered by the + * raw fragment.
        • + *
        + *
      • For hierarchical URIs:
      • + *
          + *
        • URIs are ordered according to their raw authority sections, + * if they are unequal.
        • + *
        • For registry-based authorities:
        • + *
          • they are ordered according to the ordering of the authority + * component.
          + *
        • For server-based authorities:
        • + *
            + *
          • URIs are ordered according to the raw user information.
          • + *
          • URIs with the same user information are ordered by the host, + * ignoring case.
          • + *
          • URIs with the same host are ordered by the port.
          • + *
          + *
        • URIs with the same authority section are ordered by the raw path.
        • + *
        • URIs with the same path are ordered by their raw query.
        • + *
        • URIs with the same query are ordered by their raw fragments.
        • + *
        + *
      + * + * @param uri The other URI to compare this URI with + * @return a negative integer, zero or a positive integer depending + * on whether this URI is less than, equal to or greater + * than that supplied, respectively. + */ + public int compareTo(URI uri) + throws ClassCastException + { + if (scheme == null && uri.getScheme() != null) + return -1; + if (scheme != null) + { + int sCompare = scheme.compareToIgnoreCase(uri.getScheme()); + if (sCompare != 0) + return sCompare; + } + boolean opaqueThis = isOpaque(); + boolean opaqueObj = uri.isOpaque(); + if (opaqueThis && !opaqueObj) + return 1; + if (!opaqueThis && opaqueObj) + return -1; + if (opaqueThis) + { + int ssCompare = + rawSchemeSpecificPart.compareTo(uri.getRawSchemeSpecificPart()); + if (ssCompare == 0) + return compareFragments(uri); + else + return ssCompare; + } + if (rawAuthority == null && uri.getRawAuthority() != null) + return -1; + if (rawAuthority != null) + { + int aCompare = rawAuthority.compareTo(uri.getRawAuthority()); + if (aCompare != 0) + { + if (host == null) + return aCompare; + if (rawUserInfo == null && uri.getRawUserInfo() != null) + return -1; + int uCompare = rawUserInfo.compareTo(uri.getRawUserInfo()); + if (uCompare != 0) + return uCompare; + if (host == null && uri.getHost() != null) + return -1; + int hCompare = host.compareTo(uri.getHost()); + if (hCompare != 0) + return hCompare; + int uriPort = uri.getPort(); + return (uriPort == port) ? 0 : (uriPort > port) ? -1 : 1; + } + } + if (rawPath == null && uri.getRawPath() != null) + return -1; + if (rawPath != null) + { + int pCompare = rawPath.compareTo(uri.getRawPath()); + if (pCompare != 0) + return pCompare; + } + if (rawQuery == null && uri.getRawQuery() != null) + return -1; + if (rawQuery != null) + { + int qCompare = rawQuery.compareTo(uri.getRawQuery()); + if (qCompare != 0) + return qCompare; + } + return compareFragments(uri); + } + + /** + * Compares the fragment of this URI with that of the supplied URI. + * + * @param uri the URI to compare with this one. + * @return a negative integer, zero or a positive integer depending + * on whether this uri's fragment is less than, equal to + * or greater than the fragment of the uri supplied, respectively. + */ + private int compareFragments(URI uri) + { + if (rawFragment == null && uri.getRawFragment() != null) + return -1; + else if (rawFragment == null) + return 0; + else + return rawFragment.compareTo(uri.getRawFragment()); + } + + /** + * Returns the URI as a String. If the URI was created using a constructor, + * then this will be the same as the original input string. + * + * @return a string representation of the URI. + */ + public String toString() + { + return (scheme == null ? "" : scheme + ":") + + rawSchemeSpecificPart + + (rawFragment == null ? "" : "#" + rawFragment); + } + + /** + * Returns the URI as US-ASCII string. This is the same as the result + * from toString() for URIs that don't contain any non-US-ASCII + * characters. Otherwise, the non-US-ASCII characters are replaced + * by their percent-encoded representations. + * + * @return a string representation of the URI, containing only US-ASCII + * characters. + */ + public String toASCIIString() + { + String strRep = toString(); + boolean inNonAsciiBlock = false; + CPStringBuilder buffer = new CPStringBuilder(); + CPStringBuilder encBuffer = null; + for (int i = 0; i < strRep.length(); i++) + { + char c = strRep.charAt(i); + if (c <= 127) + { + if (inNonAsciiBlock) + { + buffer.append(escapeCharacters(encBuffer.toString())); + inNonAsciiBlock = false; + } + buffer.append(c); + } + else + { + if (!inNonAsciiBlock) + { + encBuffer = new CPStringBuilder(); + inNonAsciiBlock = true; + } + encBuffer.append(c); + } + } + return buffer.toString(); + } + + /** + * Converts the non-ASCII characters in the supplied string + * to their equivalent percent-encoded representations. + * That is, they are replaced by "%" followed by their hexadecimal value. + * + * @param str a string including non-ASCII characters. + * @return the string with the non-ASCII characters converted to their + * percent-encoded representations. + */ + private static String escapeCharacters(String str) + { + try + { + CPStringBuilder sb = new CPStringBuilder(); + // this is far from optimal, but it works + byte[] utf8 = str.getBytes("utf-8"); + for (int j = 0; j < utf8.length; j++) + { + sb.append('%'); + sb.append(HEX.charAt((utf8[j] & 0xff) / 16)); + sb.append(HEX.charAt((utf8[j] & 0xff) % 16)); + } + return sb.toString(); + } + catch (java.io.UnsupportedEncodingException x) + { + throw (Error) new InternalError("Escaping error").initCause(x); + } + } + +} diff --git a/libjava/classpath/java/net/URISyntaxException.java b/libjava/classpath/java/net/URISyntaxException.java new file mode 100644 index 000000000..27a70bdb7 --- /dev/null +++ b/libjava/classpath/java/net/URISyntaxException.java @@ -0,0 +1,144 @@ +/* URISyntaxException.java -- a string could not be parsed as a URI + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * This exception is thrown when a String cannot be parsed as a URI. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see URI + * @since 1.4 + * @status updated to 1.4 + */ +public class URISyntaxException extends Exception +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 2137979680897488891L; + + /** + * The failed input. + * + * @serial the bad URI + */ + private final String input; + + /** + * The index of failure. + * + * @serial the location of the problem + */ + private final int index; + + /** + * Create an exception from the invalid string, with the index set to -1. + * + * @param input the bad URI + * @param msg the descriptive error message + * @throws NullPointerException if input or msg are null + */ + public URISyntaxException(String input, String msg) + { + this(input, msg, -1); + } + + /** + * Create an exception from the invalid string, with the index of the + * point of failure. + * + * @param input the bad URI + * @param msg the descriptive error message + * @param index the index of the parse error, or -1 + * @throws NullPointerException if input or msg are null + * @throws IllegalArgumentException if index < -1 + */ + public URISyntaxException(String input, String msg, int index) + { + // The toString() hack checks for null. + super(msg.toString()); + this.input = input.toString(); + this.index = index; + if (index < -1) + throw new IllegalArgumentException(); + } + + /** + * Returns the bad input string. + * + * @return the bad URI, guaranteed non-null + */ + public String getInput() + { + return input; + } + + /** + * Returns the reason for the failure. + * + * @return the message, guaranteed non-null + */ + public String getReason() + { + return super.getMessage(); + } + + /** + * Returns the index of the failure, or -1. + * + * @return the index of failure + */ + public int getIndex() + { + return index; + } + + /** + * Returns a message describing the parse error, as if by + * getReason() + (getIndex() >= 0 ? " at index " + getIndex() : "") + * + ": " + getInput(). + * + * @return the message string + */ + public String getMessage() + { + return (super.getMessage() + (index >= 0 ? " at index " + index : "") + + ": " + input); + } +} diff --git a/libjava/classpath/java/net/URL.java b/libjava/classpath/java/net/URL.java new file mode 100644 index 000000000..1b778b40a --- /dev/null +++ b/libjava/classpath/java/net/URL.java @@ -0,0 +1,1010 @@ +/* URL.java -- Uniform Resource Locator Class + Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.classpath.SystemProperties; +import gnu.java.net.URLParseError; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.StringTokenizer; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This final class represents an Internet Uniform Resource Locator (URL). + * For details on the syntax of URL's and what they can be used for, + * refer to RFC 1738, available from + * http://ds.internic.net/rfcs/rfc1738.txt + *

      + * There are a great many protocols supported by URL's such as "http", + * "ftp", and "file". This object can handle any arbitrary URL for which + * a URLStreamHandler object can be written. Default protocol handlers + * are provided for the "http" and "ftp" protocols. Additional protocols + * handler implementations may be provided in the future. In any case, + * an application or applet can install its own protocol handlers that + * can be "chained" with other protocol hanlders in the system to extend + * the base functionality provided with this class. (Note, however, that + * unsigned applets cannot access properties by default or install their + * own protocol handlers). + *

      + * This chaining is done via the system property java.protocol.handler.pkgs + * If this property is set, it is assumed to be a "|" separated list of + * package names in which to attempt locating protocol handlers. The + * protocol handler is searched for by appending the string + * ".<protocol>.Handler" to each packed in the list until a hander is + * found. If a protocol handler is not found in this list of packages, or if + * the property does not exist, then the default protocol handler of + * "gnu.java.net.<protocol>.Handler" is tried. If this is + * unsuccessful, a MalformedURLException is thrown. + *

      + * All of the constructor methods of URL attempt to load a protocol + * handler and so any needed protocol handlers must be installed when + * the URL is constructed. + *

      + * Here is an example of how URL searches for protocol handlers. Assume + * the value of java.protocol.handler.pkgs is "com.foo|com.bar" and the + * URL is "news://comp.lang.java.programmer". URL would looking the + * following places for protocol handlers: + *

      +  * com.foo.news.Handler
      +  * com.bar.news.Handler
      +  * gnu.java.net.news.Handler
      +  * 

      + * If the protocol handler is not found in any of those locations, a + * MalformedURLException would be thrown. + *

      + * Please note that a protocol handler must be a subclass of + * URLStreamHandler. + *

      + * Normally, this class caches protocol handlers. Once it finds a handler + * for a particular protocol, it never tries to look up a new handler + * again. However, if the system property + * gnu.java.net.nocache_protocol_handlers is set, then this + * caching behavior is disabled. This property is specific to this + * implementation. Sun's JDK may or may not do protocol caching, but it + * almost certainly does not examine this property. + *

      + * Please also note that an application can install its own factory for + * loading protocol handlers (see setURLStreamHandlerFactory). If this is + * done, then the above information is superseded and the behavior of this + * class in loading protocol handlers is dependent on that factory. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * + * @see URLStreamHandler + */ +public final class URL implements Serializable +{ + private static final String DEFAULT_SEARCH_PATH = + "gnu.java.net.protocol|gnu.inet"; + + // Cached System ClassLoader + private static ClassLoader systemClassLoader; + + /** + * The name of the protocol for this URL. + * The protocol is always stored in lower case. + */ + private String protocol; + + /** + * The "authority" portion of the URL. + */ + private String authority; + + /** + * The hostname or IP address of this protocol. + * This includes a possible user. For example joe@some.host.net. + */ + private String host; + + /** + * The user information necessary to establish the connection. + */ + private String userInfo; + + /** + * The port number of this protocol or -1 if the port number used is + * the default for this protocol. + */ + private int port = -1; // Initialize for constructor using context. + + /** + * The "file" portion of the URL. It is defined as path[?query]. + */ + private String file; + + /** + * The anchor portion of the URL. + */ + private String ref; + + /** + * This is the hashCode for this URL + */ + private int hashCode; + + /** + * The protocol handler in use for this URL + */ + transient URLStreamHandler ph; + + /** + * If an application installs its own protocol handler factory, this is + * where we keep track of it. + */ + private static URLStreamHandlerFactory factory; + private static final long serialVersionUID = -7627629688361524110L; + + /** + * This a table where we cache protocol handlers to avoid the overhead + * of looking them up each time. + */ + private static HashMap ph_cache + = new HashMap(); + + /** + * Whether or not to cache protocol handlers. + */ + private static boolean cache_handlers; + + static + { + String s = SystemProperties.getProperty("gnu.java.net.nocache_protocol_handlers"); + + if (s == null) + cache_handlers = true; + else + cache_handlers = false; + } + + /** + * Constructs a URL and loads a protocol handler for the values passed as + * arguments. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address to connect to + * @param port The port number to use, or -1 to use the protocol's + * default port + * @param file The "file" portion of the URL. + * + * @exception MalformedURLException If a protocol handler cannot be loaded or + * a parse error occurs. + */ + public URL(String protocol, String host, int port, String file) + throws MalformedURLException + { + this(protocol, host, port, file, null); + } + + /** + * Constructs a URL and loads a protocol handler for the values passed in + * as arugments. Uses the default port for the protocol. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address for this URL + * @param file The "file" portion of this URL. + * + * @exception MalformedURLException If a protocol handler cannot be loaded or + * a parse error occurs. + */ + public URL(String protocol, String host, String file) + throws MalformedURLException + { + this(protocol, host, -1, file, null); + } + + /** + * This method initializes a new instance of URL with the + * specified protocol, host, port, and file. Additionally, this method + * allows the caller to specify a protocol handler to use instead of + * the default. If this handler is specified, the caller must have + * the "specifyStreamHandler" permission (see NetPermission) + * or a SecurityException will be thrown. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address to connect to + * @param port The port number to use, or -1 to use the protocol's default + * port + * @param file The "file" portion of the URL. + * @param ph The protocol handler to use with this URL. + * + * @exception MalformedURLException If no protocol handler can be loaded + * for the specified protocol. + * @exception SecurityException If the SecurityManager exists + * and does not allow the caller to specify its own protocol handler. + * + * @since 1.2 + */ + public URL(String protocol, String host, int port, String file, + URLStreamHandler ph) throws MalformedURLException + { + if (protocol == null) + throw new MalformedURLException("null protocol"); + protocol = protocol.toLowerCase(); + this.protocol = protocol; + + if (ph != null) + { + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkPermission(new NetPermission("specifyStreamHandler")); + + this.ph = ph; + } + else + this.ph = getURLStreamHandler(protocol); + + if (this.ph == null) + throw new MalformedURLException("Protocol handler not found: " + + protocol); + + this.host = host; + this.port = port; + this.authority = (host != null) ? host : ""; + if (port >= 0 && host != null) + this.authority += ":" + port; + + int hashAt = file.indexOf('#'); + if (hashAt < 0) + { + this.file = file; + this.ref = null; + } + else + { + this.file = file.substring(0, hashAt); + this.ref = file.substring(hashAt + 1); + } + hashCode = hashCode(); // Used for serialization. + } + + /** + * Initializes a URL from a complete string specification such as + * "http://www.urbanophile.com/arenn/". First the protocol name is parsed + * out of the string. Then a handler is located for that protocol and + * the parseURL() method of that protocol handler is used to parse the + * remaining fields. + * + * @param spec The complete String representation of a URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * or the URL cannot be parsed + */ + public URL(String spec) throws MalformedURLException + { + this((URL) null, spec != null ? spec : "", (URLStreamHandler) null, + false); + } + + /** + * This method parses a String representation of a URL within the + * context of an existing URL. Principally this means that any + * fields not present the URL are inheritied from the context URL. + * This allows relative URL's to be easily constructed. If the + * context argument is null, then a complete URL must be specified + * in the URL string. If the protocol parsed out of the URL is + * different from the context URL's protocol, then then URL String + * is also expected to be a complete URL. + * + * @param context The context on which to parse the specification + * @param spec The string to parse an URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * for the URL cannot be parsed + */ + public URL(URL context, String spec) throws MalformedURLException + { + this(context, spec, + (context == null) ? (URLStreamHandler) null : context.ph, + false); + } + + /** + * Creates an URL from given arguments + * This method parses a String representation of a URL within the + * context of an existing URL. Principally this means that any fields + * not present the URL are inheritied from the context URL. This allows + * relative URL's to be easily constructed. If the context argument is + * null, then a complete URL must be specified in the URL string. + * If the protocol parsed out of the URL is different + * from the context URL's protocol, then then URL String is also + * expected to be a complete URL. + *

      + * Additionally, this method allows the caller to specify a protocol handler + * to use instead of the default. If this handler is specified, the caller + * must have the "specifyStreamHandler" permission + * (see NetPermission) or a SecurityException + * will be thrown. + * + * @param context The context in which to parse the specification + * @param spec The string to parse as an URL + * @param ph The stream handler for the URL + * + * @exception MalformedURLException If a protocol handler cannot be found + * or the URL cannot be parsed + * @exception SecurityException If the SecurityManager exists + * and does not allow the caller to specify its own protocol handler. + * + * @since 1.2 + */ + public URL(URL context, String spec, URLStreamHandler ph) + throws MalformedURLException + { + this(context, spec, ph, true); + } + + /** + * Private constructor called by all other constructors taking + * a context and spec. + * + * @param context The context in which to parse the specification + * @param spec The string to parse as an URL + * @param ph The stream handler for the URL + * @param phFromUser Whether or not the user supplied the URLStreamHandler + * + */ + private URL(URL context, String spec, URLStreamHandler ph, + boolean phFromUser) + throws MalformedURLException + { + /* A protocol is defined by the doc as the substring before a ':' + * as long as the ':' occurs before any '/'. + * + * If context is null, then spec must be an absolute URL. + * + * The relative URL need not specify all the components of a URL. + * If the protocol, host name, or port number is missing, the value + * is inherited from the context. A bare file component is appended + * to the context's file. The optional anchor is not inherited. + */ + + // If this is an absolute URL, then ignore context completely. + // An absolute URL must have chars prior to "://" but cannot have a colon + // right after the "://". The second colon is for an optional port value + // and implies that the host from the context is used if available. + int colon; + int slash = spec.indexOf('/'); + if ((colon = spec.indexOf("://", 1)) > 0 + && ((colon < slash || slash < 0)) + && ! spec.regionMatches(colon, "://:", 0, 4)) + { + context = null; + if (! phFromUser) + ph = null; + } + + boolean protocolSpecified = false; + + if ((colon = spec.indexOf(':')) > 0 + && (colon < slash || slash < 0)) + { + // Protocol may have been specified in spec string. + protocolSpecified = true; + protocol = spec.substring(0, colon).toLowerCase(); + if (context != null) + { + if (context.protocol.equals(protocol)) + { + // The 1.2 doc specifically says these are copied to the new URL. + host = context.host; + port = context.port; + userInfo = context.userInfo; + authority = context.authority; + } + else + { + // There was a colon in the spec. Check to see if + // what precedes it is a valid protocol. If it was + // not, assume that it is relative to the context. + URLStreamHandler specPh = getURLStreamHandler(protocol.trim()); + if (null == specPh) + protocolSpecified = false; + } + } + } + + if (!protocolSpecified) + { + if (context != null) + { + // Protocol NOT specified in spec string. + // Use context fields (except ref) as a foundation for relative URLs. + colon = -1; + protocol = context.protocol; + host = context.host; + port = context.port; + userInfo = context.userInfo; + if (spec.indexOf(":/", 1) < 0) + { + file = context.file; + if (file == null || file.length() == 0) + file = "/"; + } + authority = context.authority; + } + else // Protocol NOT specified in spec. and no context available. + throw new MalformedURLException("Absolute URL required with null" + + " context: " + spec); + } + + protocol = protocol.trim(); + + if (ph != null) + { + SecurityManager s = System.getSecurityManager(); + if (s != null && phFromUser) + s.checkPermission(new NetPermission("specifyStreamHandler")); + + this.ph = ph; + } + else + this.ph = getURLStreamHandler(protocol); + + if (this.ph == null) + throw new MalformedURLException("Protocol handler not found: " + + protocol); + + // JDK 1.2 doc for parseURL specifically states that any '#' ref + // is to be excluded by passing the 'limit' as the indexOf the '#' + // if one exists, otherwise pass the end of the string. + int hashAt = spec.indexOf('#', colon + 1); + + try + { + this.ph.parseURL(this, spec, colon + 1, + hashAt < 0 ? spec.length() : hashAt); + } + catch (URLParseError e) + { + MalformedURLException mue = new MalformedURLException(e.getMessage()); + mue.initCause(e); + throw mue; + } + catch (RuntimeException e) + { + // This isn't documented, but the JDK also catches + // RuntimeExceptions here. + MalformedURLException mue = new MalformedURLException(e.getMessage()); + mue.initCause(e); + throw mue; + } + + if (hashAt >= 0) + ref = spec.substring(hashAt + 1); + + hashCode = hashCode(); // Used for serialization. + } + + /** + * Test another URL for equality with this one. This will be true only if + * the argument is non-null and all of the fields in the URL's match + * exactly (ie, protocol, host, port, file, and ref). Overrides + * Object.equals(), implemented by calling the equals method of the handler. + * + * @param obj The URL to compare with + * + * @return true if the URL is equal, false otherwise + */ + public boolean equals(Object obj) + { + if (! (obj instanceof URL)) + return false; + + return ph.equals(this, (URL) obj); + } + + /** + * Returns the contents of this URL as an object by first opening a + * connection, then calling the getContent() method against the connection + * + * @return A content object for this URL + * @exception IOException If opening the connection or getting the + * content fails. + * + * @since 1.3 + */ + public Object getContent() throws IOException + { + return openConnection().getContent(); + } + + /** + * Gets the contents of this URL + * + * @param classes The allow classes for the content object. + * + * @return a context object for this URL. + * + * @exception IOException If an error occurs + */ + public Object getContent(Class[] classes) throws IOException + { + return openConnection().getContent(classes); + } + + /** + * Returns the file portion of the URL. + * Defined as path[?query]. + * Returns the empty string if there is no file portion. + * + * @return The filename specified in this URL, or an empty string if empty. + */ + public String getFile() + { + return file == null ? "" : file; + } + + /** + * Returns the path of the URL. This is the part of the file before any '?' + * character. + * + * @return The path specified in this URL, or null if empty. + * + * @since 1.3 + */ + public String getPath() + { + // The spec says we need to return an empty string, but some + // applications depends on receiving null when the path is empty. + if (file == null) + return null; + int quest = file.indexOf('?'); + return quest < 0 ? getFile() : file.substring(0, quest); + } + + /** + * Returns the authority of the URL + * + * @return The authority specified in this URL. + * + * @since 1.3 + */ + public String getAuthority() + { + return authority; + } + + /** + * Returns the host of the URL + * + * @return The host specified in this URL. + */ + public String getHost() + { + int at = (host == null) ? -1 : host.indexOf('@'); + return at < 0 ? host : host.substring(at + 1, host.length()); + } + + /** + * Returns the port number of this URL or -1 if the default port number is + * being used. + * + * @return The port number + * + * @see #getDefaultPort() + */ + public int getPort() + { + return port; + } + + /** + * Returns the default port of the URL. If the StreamHandler for the URL + * protocol does not define a default port it returns -1. + * + * @return The default port of the current protocol. + */ + public int getDefaultPort() + { + return ph.getDefaultPort(); + } + + /** + * Returns the protocol of the URL + * + * @return The specified protocol. + */ + public String getProtocol() + { + return protocol; + } + + /** + * Returns the ref (sometimes called the "# reference" or "anchor") portion + * of the URL. + * + * @return The ref + */ + public String getRef() + { + return ref; + } + + /** + * Returns the user information of the URL. This is the part of the host + * name before the '@'. + * + * @return the user at a particular host or null when no user defined. + */ + public String getUserInfo() + { + if (userInfo != null) + return userInfo; + int at = (host == null) ? -1 : host.indexOf('@'); + return at < 0 ? null : host.substring(0, at); + } + + /** + * Returns the query of the URL. This is the part of the file before the + * '?'. + * + * @return the query part of the file, or null when there is no query part. + */ + public String getQuery() + { + int quest = (file == null) ? -1 : file.indexOf('?'); + return quest < 0 ? null : file.substring(quest + 1, file.length()); + } + + /** + * Returns a hashcode computed by the URLStreamHandler of this URL + * + * @return The hashcode for this URL. + */ + public int hashCode() + { + if (hashCode != 0) + return hashCode; // Use cached value if available. + else + return ph.hashCode(this); + } + + /** + * Returns a URLConnection object that represents a connection to the remote + * object referred to by the URL. The URLConnection is created by calling the + * openConnection() method of the protocol handler + * + * @return A URLConnection for this URL + * + * @exception IOException If an error occurs + */ + public URLConnection openConnection() throws IOException + { + return ph.openConnection(this); + } + + /** + * Opens a connection to this URL and returns an InputStream for reading + * from that connection + * + * @return An InputStream for this URL. + * + * @exception IOException If an error occurs + */ + public InputStream openStream() throws IOException + { + return openConnection().getInputStream(); + } + + /** + * Tests whether or not another URL refers to the same "file" as this one. + * This will be true if and only if the passed object is not null, is a + * URL, and matches all fields but the ref (ie, protocol, host, port, + * and file); + * + * @param url The URL object to test with + * + * @return true if URL matches this URL's file, false otherwise + */ + public boolean sameFile(URL url) + { + return ph.sameFile(this, url); + } + + /** + * Sets the specified fields of the URL. This is not a public method so + * that only URLStreamHandlers can modify URL fields. This might be called + * by the parseURL() method in that class. URLs are otherwise + * constant. If the given protocol does not exist, it will keep the previously + * set protocol. + * + * @param protocol The protocol name for this URL + * @param host The hostname or IP address for this URL + * @param port The port number of this URL + * @param file The "file" portion of this URL. + * @param ref The anchor portion of this URL. + */ + protected void set(String protocol, String host, int port, String file, + String ref) + { + URLStreamHandler protocolHandler = null; + protocol = protocol.toLowerCase(); + if (! this.protocol.equals(protocol)) + protocolHandler = getURLStreamHandler(protocol); + + // It is an hidden feature of the JDK. If the protocol does not exist, + // we keep the previously initialized protocol. + if (protocolHandler != null) + { + this.ph = protocolHandler; + this.protocol = protocol; + } + this.authority = ""; + this.port = port; + this.host = host; + this.file = file; + this.ref = ref; + + if (host != null) + this.authority += host; + if (port >= 0) + this.authority += ":" + port; + + hashCode = hashCode(); // Used for serialization. + } + + /** + * Sets the specified fields of the URL. This is not a public method so + * that only URLStreamHandlers can modify URL fields. URLs are otherwise + * constant. If the given protocol does not exist, it will keep the previously + * set protocol. + * + * @param protocol The protocol name for this URL. + * @param host The hostname or IP address for this URL. + * @param port The port number of this URL. + * @param authority The authority of this URL. + * @param userInfo The user and password (if needed) of this URL. + * @param path The "path" portion of this URL. + * @param query The query of this URL. + * @param ref The anchor portion of this URL. + * + * @since 1.3 + */ + protected void set(String protocol, String host, int port, String authority, + String userInfo, String path, String query, String ref) + { + URLStreamHandler protocolHandler = null; + protocol = protocol.toLowerCase(); + if (! this.protocol.equals(protocol)) + protocolHandler = getURLStreamHandler(protocol); + + // It is an hidden feature of the JDK. If the protocol does not exist, + // we keep the previously initialized protocol. + if (protocolHandler != null) + { + this.ph = protocolHandler; + this.protocol = protocol; + } + this.host = host; + this.userInfo = userInfo; + this.port = port; + this.authority = authority; + if (query == null) + this.file = path; + else + this.file = path + "?" + query; + this.ref = ref; + hashCode = hashCode(); // Used for serialization. + } + + /** + * Sets the URLStreamHandlerFactory for this class. This factory is + * responsible for returning the appropriate protocol handler for + * a given URL. + * + * @param fac The URLStreamHandlerFactory class to use + * + * @exception Error If the factory is alread set. + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) + { + if (factory != null) + throw new Error("URLStreamHandlerFactory already set"); + + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + factory = fac; + } + + /** + * Returns a String representing this URL. The String returned is + * created by calling the protocol handler's toExternalForm() method. + * + * @return A string for this URL + */ + public String toExternalForm() + { + // Identical to toString(). + return ph.toExternalForm(this); + } + + /** + * Returns a String representing this URL. Identical to toExternalForm(). + * The value returned is created by the protocol handler's + * toExternalForm method. Overrides Object.toString() + * + * @return A string for this URL + */ + public String toString() + { + // Identical to toExternalForm(). + return ph.toExternalForm(this); + } + + /** + * This internal method is used in two different constructors to load + * a protocol handler for this URL. + * + * @param protocol The protocol to load a handler for + * + * @return A URLStreamHandler for this protocol, or null when not found. + */ + private static synchronized URLStreamHandler getURLStreamHandler(String protocol) + { + URLStreamHandler ph = null; + + // First, see if a protocol handler is in our cache. + if (cache_handlers) + { + if ((ph = ph_cache.get(protocol)) != null) + return ph; + } + + // If a non-default factory has been set, use it to find the protocol. + if (factory != null) + { + ph = factory.createURLStreamHandler(protocol); + } + + // Non-default factory may have returned null or a factory wasn't set. + // Use the default search algorithm to find a handler for this protocol. + if (ph == null) + { + // Get the list of packages to check and append our default handler + // to it, along with the JDK specified default as a last resort. + // Except in very unusual environments the JDK specified one shouldn't + // ever be needed (or available). + String ph_search_path = + SystemProperties.getProperty("java.protocol.handler.pkgs"); + + // Tack our default package on at the ends. + if (ph_search_path != null) + ph_search_path += "|" + DEFAULT_SEARCH_PATH; + else + ph_search_path = DEFAULT_SEARCH_PATH; + + // Finally loop through our search path looking for a match. + StringTokenizer pkgPrefix = new StringTokenizer(ph_search_path, "|"); + + // Cache the systemClassLoader + if (systemClassLoader == null) + { + systemClassLoader = AccessController.doPrivileged + (new PrivilegedAction() { + public ClassLoader run() + { + return ClassLoader.getSystemClassLoader(); + } + }); + } + + do + { + try + { + // Try to get a class from the system/application + // classloader, initialize it, make an instance + // and try to cast it to a URLStreamHandler. + String clsName = + (pkgPrefix.nextToken() + "." + protocol + ".Handler"); + Class c = Class.forName(clsName, true, systemClassLoader); + ph = (URLStreamHandler) c.newInstance(); + } + catch (ThreadDeath death) + { + throw death; + } + catch (Throwable t) + { + // Ignored. + } + } + while (ph == null && pkgPrefix.hasMoreTokens()); + } + + // Update the hashtable with the new protocol handler. + if (ph != null && cache_handlers) + ph_cache.put(protocol, ph); + else + ph = null; + + return ph; + } + + private void readObject(ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + this.ph = getURLStreamHandler(protocol); + if (this.ph == null) + throw new IOException("Handler for protocol " + protocol + " not found"); + } + + private void writeObject(ObjectOutputStream oos) throws IOException + { + oos.defaultWriteObject(); + } + + /** + * Returns the equivalent URI object for this URL. + * This is the same as calling new URI(this.toString()). + * RFC2396-compliant URLs are guaranteed a successful conversion to + * a URI instance. However, there are some values which + * form valid URLs, but which do not also form RFC2396-compliant URIs. + * + * @throws URISyntaxException if this URL is not RFC2396-compliant, + * and thus can not be successfully converted to a URI. + */ + public URI toURI() + throws URISyntaxException + { + return new URI(toString()); + } + +} diff --git a/libjava/classpath/java/net/URLClassLoader.java b/libjava/classpath/java/net/URLClassLoader.java new file mode 100644 index 000000000..418ee77f3 --- /dev/null +++ b/libjava/classpath/java/net/URLClassLoader.java @@ -0,0 +1,860 @@ +/* URLClassLoader.java -- ClassLoader that loads classes from one or more URLs + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.net.loader.FileURLLoader; +import gnu.java.net.loader.JarURLLoader; +import gnu.java.net.loader.RemoteURLLoader; +import gnu.java.net.loader.Resource; +import gnu.java.net.loader.URLLoader; +import gnu.java.net.loader.URLStreamHandlerCache; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.PrivilegedAction; +import java.security.SecureClassLoader; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Vector; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + + +/** + * A secure class loader that can load classes and resources from + * multiple locations. Given an array of URLs this class + * loader will retrieve classes and resources by fetching them from + * possible remote locations. Each URL is searched in + * order in which it was added. If the file portion of the + * URL ends with a '/' character then it is interpreted + * as a base directory, otherwise it is interpreted as a jar file from + * which the classes/resources are resolved. + * + *

      New instances can be created by two static + * newInstance() methods or by three public + * contructors. Both ways give the option to supply an initial array + * of URLs and (optionally) a parent classloader (that is + * different from the standard system class loader).

      + * + *

      Normally creating a URLClassLoader throws a + * SecurityException if a SecurityManager is + * installed and the checkCreateClassLoader() method does + * not return true. But the newInstance() methods may be + * used by any code as long as it has permission to acces the given + * URLs. URLClassLoaders created by the + * newInstance() methods also explicitly call the + * checkPackageAccess() method of + * SecurityManager if one is installed before trying to + * load a class. Note that only subclasses of + * URLClassLoader can add new URLs after the + * URLClassLoader had been created. But it is always possible to get + * an array of all URLs that the class loader uses to resolve classes + * and resources by way of the getURLs() method.

      + * + *

      Open issues: + *

        + * + *
      • Should the URLClassLoader actually add the locations found in + * the manifest or is this the responsibility of some other + * loader/(sub)class? (see + * Extension Mechanism Architecture - Bundles Extensions)
      • + * + *
      • How does definePackage() and sealing work + * precisely?
      • + * + *
      • We save and use the security context (when a created by + * newInstance() but do we have to use it in more + * places?
      • + * + *
      • The use of URLStreamHandlers has not been tested.
      • + * + *
      + *

      + * + * @since 1.2 + * + * @author Mark Wielaard (mark@klomp.org) + * @author Wu Gansha (gansha.wu@intel.com) + */ +public class URLClassLoader extends SecureClassLoader +{ + // Class Variables + + /** + * A cache to store mappings between handler factory and its + * private protocol handler cache (also a HashMap), so we can avoid + * creating handlers each time the same protocol comes. + */ + private static URLStreamHandlerCache factoryCache + = new URLStreamHandlerCache(); + + /** + * The prefix for URL loaders. + */ + private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_"; + + // Instance variables + + /** Locations to load classes from */ + private final Vector urls = new Vector(); + + /** + * Store pre-parsed information for each url into this vector: each + * element is a URL loader. A jar file has its own class-path + * attribute which adds to the URLs that will be searched, but this + * does not add to the list of urls. + */ + private final Vector urlinfos = new Vector(); + + /** Factory used to get the protocol handlers of the URLs */ + private final URLStreamHandlerFactory factory; + + /** + * The security context when created from newInstance() + * or null when created through a normal constructor or when no + * SecurityManager was installed. + */ + private final AccessControlContext securityContext; + + // Helper classes + + /** + * Creates a URLClassLoader that gets classes from the supplied URLs. + * To determine if this classloader may be created the constructor of + * the super class (SecureClassLoader) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the default parent ClassLoader). + * + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls) throws SecurityException + { + super(); + this.factory = null; + this.securityContext = null; + addURLs(urls); + } + + /** + * Creates a URLClassLoader that gets classes from the supplied + * URLs. + * To determine if this classloader may be created the constructor of + * the super class (SecureClassLoader) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the supplied parent ClassLoader). + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @param parent The parent class loader used before trying this class + * loader. + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @exception SecurityException + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls, ClassLoader parent) + throws SecurityException + { + super(parent); + this.factory = null; + this.securityContext = null; + addURLs(urls); + } + + // Package-private to avoid a trampoline constructor. + /** + * Package-private constructor used by the static + * newInstance(URL[]) method. Creates an + * URLClassLoader with the given parent but without any + * URLs yet. This is used to bypass the normal security + * check for creating classloaders, but remembers the security + * context which will be used when defining classes. The + * URLs to load from must be added by the + * newInstance() method in the security context of the + * caller. + * + * @param securityContext the security context of the unprivileged code. + */ + URLClassLoader(ClassLoader parent, AccessControlContext securityContext) + { + super(parent); + this.factory = null; + this.securityContext = securityContext; + } + + /** + * Creates a URLClassLoader that gets classes from the supplied URLs. + * To determine if this classloader may be created the constructor of + * the super class (SecureClassLoader) is called first, which + * can throw a SecurityException. Then the supplied URLs are added + * in the order given to the URLClassLoader which uses these URLs to + * load classes and resources (after using the supplied parent ClassLoader). + * It will use the supplied URLStreamHandlerFactory to get the + * protocol handlers of the supplied URLs. + * @param urls Locations that should be searched by this ClassLoader when + * resolving Classes or Resources. + * @param parent The parent class loader used before trying this class + * loader. + * @param factory Used to get the protocol handler for the URLs. + * @exception SecurityException if the SecurityManager disallows the + * creation of a ClassLoader. + * @exception SecurityException + * @see SecureClassLoader + */ + public URLClassLoader(URL[] urls, ClassLoader parent, + URLStreamHandlerFactory factory) + throws SecurityException + { + super(parent); + this.securityContext = null; + this.factory = factory; + // If this factory is not yet in factoryCache, add it. + factoryCache.add(factory); + addURLs(urls); + } + + // Methods + + /** + * Adds a new location to the end of the internal URL store. + * @param newUrl the location to add + */ + protected void addURL(URL newUrl) + { + urls.add(newUrl); + addURLImpl(newUrl); + } + + private void addURLImpl(URL newUrl) + { + synchronized (this) + { + if (newUrl == null) + return; // Silently ignore... + + // Reset the toString() value. + thisString = null; + + // Create a loader for this URL. + URLLoader loader = null; + String file = newUrl.getFile(); + String protocol = newUrl.getProtocol(); + + // If we have a file: URL, we want to make it absolute + // here, before we decide whether it is really a jar. + URL absoluteURL; + if ("file".equals (protocol)) + { + File dir = new File(file); + try + { + absoluteURL = dir.getCanonicalFile().toURL(); + } + catch (IOException ignore) + { + try + { + absoluteURL = dir.getAbsoluteFile().toURL(); + } + catch (MalformedURLException _) + { + // This really should not happen. + absoluteURL = newUrl; + } + } + } + else + { + // This doesn't hurt, and it simplifies the logic a + // little. + absoluteURL = newUrl; + } + + // First see if we can find a handler with the correct name. + try + { + Class handler = Class.forName(URL_LOADER_PREFIX + protocol); + Class[] argTypes = new Class[] { URLClassLoader.class, + URLStreamHandlerCache.class, + URLStreamHandlerFactory.class, + URL.class, + URL.class }; + Constructor k = handler.getDeclaredConstructor(argTypes); + loader + = (URLLoader) k.newInstance(new Object[] { this, + factoryCache, + factory, + newUrl, + absoluteURL }); + } + catch (ClassNotFoundException ignore) + { + // Fall through. + } + catch (NoSuchMethodException nsme) + { + // Programming error in the class library. + InternalError vme + = new InternalError("couldn't find URLLoader constructor"); + vme.initCause(nsme); + throw vme; + } + catch (InstantiationException inste) + { + // Programming error in the class library. + InternalError vme + = new InternalError("couldn't instantiate URLLoader"); + vme.initCause(inste); + throw vme; + } + catch (InvocationTargetException ite) + { + // Programming error in the class library. + InternalError vme + = new InternalError("error instantiating URLLoader"); + vme.initCause(ite); + throw vme; + } + catch (IllegalAccessException illae) + { + // Programming error in the class library. + InternalError vme + = new InternalError("invalid access to URLLoader"); + vme.initCause(illae); + throw vme; + } + + if (loader == null) + { + // If it is not a directory, use the jar loader. + if (! (file.endsWith("/") || file.endsWith(File.separator))) + loader = new JarURLLoader(this, factoryCache, factory, + newUrl, absoluteURL); + else if ("file".equals(protocol)) + loader = new FileURLLoader(this, factoryCache, factory, + newUrl, absoluteURL); + else + loader = new RemoteURLLoader(this, factoryCache, factory, + newUrl); + } + + urlinfos.add(loader); + ArrayList extra = loader.getClassPath(); + if (extra != null) + urlinfos.addAll(extra); + } + } + + /** + * Adds an array of new locations to the end of the internal URL + * store. Called from the the constructors. Should not call to the + * protected addURL() method since that can be overridden and + * subclasses are not yet in a good state at this point. + * jboss 4.0.3 for example depends on this. + * + * @param newUrls the locations to add + */ + private void addURLs(URL[] newUrls) + { + for (int i = 0; i < newUrls.length; i++) + { + urls.add(newUrls[i]); + addURLImpl(newUrls[i]); + } + } + + /** + * Look in both Attributes for a given value. The first Attributes + * object, if not null, has precedence. + */ + private String getAttributeValue(Attributes.Name name, Attributes first, + Attributes second) + { + String result = null; + if (first != null) + result = first.getValue(name); + if (result == null) + result = second.getValue(name); + return result; + } + + /** + * Defines a Package based on the given name and the supplied manifest + * information. The manifest indicates the title, version and + * vendor information of the specification and implementation and whether the + * package is sealed. If the Manifest indicates that the package is sealed + * then the Package will be sealed with respect to the supplied URL. + * + * @param name The name of the package + * @param manifest The manifest describing the specification, + * implementation and sealing details of the package + * @param url the code source url to seal the package + * @return the defined Package + * @throws IllegalArgumentException If this package name already exists + * in this class loader + */ + protected Package definePackage(String name, Manifest manifest, URL url) + throws IllegalArgumentException + { + // Compute the name of the package as it may appear in the + // Manifest. + CPStringBuilder xform = new CPStringBuilder(name); + for (int i = xform.length () - 1; i >= 0; --i) + if (xform.charAt(i) == '.') + xform.setCharAt(i, '/'); + xform.append('/'); + String xformName = xform.toString(); + + Attributes entryAttr = manifest.getAttributes(xformName); + Attributes attr = manifest.getMainAttributes(); + + String specTitle + = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE, + entryAttr, attr); + String specVersion + = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION, + entryAttr, attr); + String specVendor + = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR, + entryAttr, attr); + String implTitle + = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE, + entryAttr, attr); + String implVersion + = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION, + entryAttr, attr); + String implVendor + = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR, + entryAttr, attr); + + // Look if the Manifest indicates that this package is sealed + // XXX - most likely not completely correct! + // Shouldn't we also check the sealed attribute of the complete jar? + // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled + // But how do we get that jar manifest here? + String sealed = attr.getValue(Attributes.Name.SEALED); + if ("false".equals(sealed)) + // make sure that the URL is null so the package is not sealed + url = null; + + return definePackage(name, + specTitle, specVendor, specVersion, + implTitle, implVendor, implVersion, + url); + } + + /** + * Finds (the first) class by name from one of the locations. The locations + * are searched in the order they were added to the URLClassLoader. + * + * @param className the classname to find + * @exception ClassNotFoundException when the class could not be found or + * loaded + * @return a Class object representing the found class + */ + protected Class findClass(final String className) + throws ClassNotFoundException + { + // Just try to find the resource by the (almost) same name + String resourceName = className.replace('.', '/') + ".class"; + int max = urlinfos.size(); + Resource resource = null; + for (int i = 0; i < max && resource == null; i++) + { + URLLoader loader = (URLLoader)urlinfos.elementAt(i); + if (loader == null) + continue; + + Class k = loader.getClass(className); + if (k != null) + return k; + + resource = loader.getResource(resourceName); + } + if (resource == null) + throw new ClassNotFoundException(className + " not found in " + this); + + // Try to read the class data, create the CodeSource, Package and + // construct the class (and watch out for those nasty IOExceptions) + try + { + byte[] data; + InputStream in = resource.getInputStream(); + try + { + int length = resource.getLength(); + if (length != -1) + { + // We know the length of the data. + // Just try to read it in all at once + data = new byte[length]; + int pos = 0; + while (length - pos > 0) + { + int len = in.read(data, pos, length - pos); + if (len == -1) + throw new EOFException("Not enough data reading from: " + + in); + pos += len; + } + } + else + { + // We don't know the data length. + // Have to read it in chunks. + ByteArrayOutputStream out = new ByteArrayOutputStream(4096); + byte[] b = new byte[4096]; + int l = 0; + while (l != -1) + { + l = in.read(b); + if (l != -1) + out.write(b, 0, l); + } + data = out.toByteArray(); + } + } + finally + { + in.close(); + } + final byte[] classData = data; + + // Now get the CodeSource + final CodeSource source = resource.getCodeSource(); + + // Find out package name + String packageName = null; + int lastDot = className.lastIndexOf('.'); + if (lastDot != -1) + packageName = className.substring(0, lastDot); + + if (packageName != null && getPackage(packageName) == null) + { + // define the package + Manifest manifest = resource.getLoader().getManifest(); + if (manifest == null) + definePackage(packageName, null, null, null, null, null, null, + null); + else + definePackage(packageName, manifest, + resource.getLoader().getBaseURL()); + } + + // And finally construct the class! + SecurityManager sm = System.getSecurityManager(); + Class result = null; + if (sm != null && securityContext != null) + { + result = AccessController.doPrivileged + (new PrivilegedAction() + { + public Class run() + { + return defineClass(className, classData, + 0, classData.length, + source); + } + }, securityContext); + } + else + result = defineClass(className, classData, 0, classData.length, source); + + // Avoid NullPointerExceptions. + Certificate[] resourceCertificates = resource.getCertificates(); + if(resourceCertificates != null) + super.setSigners(result, resourceCertificates); + + return result; + } + catch (IOException ioe) + { + throw new ClassNotFoundException(className + " not found in " + this, ioe); + } + } + + // Cached String representation of this URLClassLoader + private String thisString; + + /** + * Returns a String representation of this URLClassLoader giving the + * actual Class name, the URLs that are searched and the parent + * ClassLoader. + */ + public String toString() + { + synchronized (this) + { + if (thisString == null) + { + CPStringBuilder sb = new CPStringBuilder(); + sb.append(this.getClass().getName()); + sb.append("{urls=[" ); + URL[] thisURLs = getURLs(); + for (int i = 0; i < thisURLs.length; i++) + { + sb.append(thisURLs[i]); + if (i < thisURLs.length - 1) + sb.append(','); + } + sb.append(']'); + sb.append(", parent="); + sb.append(getParent()); + sb.append('}'); + thisString = sb.toString(); + } + return thisString; + } + } + + /** + * Finds the first occurrence of a resource that can be found. The locations + * are searched in the order they were added to the URLClassLoader. + * + * @param resourceName the resource name to look for + * @return the URLResource for the resource if found, null otherwise + */ + private Resource findURLResource(String resourceName) + { + int max = urlinfos.size(); + for (int i = 0; i < max; i++) + { + URLLoader loader = (URLLoader) urlinfos.elementAt(i); + if (loader == null) + continue; + + Resource resource = loader.getResource(resourceName); + if (resource != null) + return resource; + } + return null; + } + + /** + * Finds the first occurrence of a resource that can be found. + * + * @param resourceName the resource name to look for + * @return the URL if found, null otherwise + */ + public URL findResource(String resourceName) + { + Resource resource = findURLResource(resourceName); + if (resource != null) + return resource.getURL(); + + // Resource not found + return null; + } + + /** + * Finds all the resources with a particular name from all the locations. + * + * @param resourceName the name of the resource to lookup + * @return a (possible empty) enumeration of URLs where the resource can be + * found + * @exception IOException when an error occurs accessing one of the + * locations + */ + public Enumeration findResources(String resourceName) + throws IOException + { + Vector resources = new Vector(); + int max = urlinfos.size(); + for (int i = 0; i < max; i++) + { + URLLoader loader = (URLLoader) urlinfos.elementAt(i); + Resource resource = loader.getResource(resourceName); + if (resource != null) + resources.add(resource.getURL()); + } + return resources.elements(); + } + + /** + * Returns the permissions needed to access a particular code + * source. These permissions includes those returned by + * SecureClassLoader.getPermissions() and the actual + * permissions to access the objects referenced by the URL of the + * code source. The extra permissions added depend on the protocol + * and file portion of the URL in the code source. If the URL has + * the "file" protocol ends with a '/' character then it must be a + * directory and a file Permission to read everything in that + * directory and all subdirectories is added. If the URL had the + * "file" protocol and doesn't end with a '/' character then it must + * be a normal file and a file permission to read that file is + * added. If the URL has any other protocol then a + * socket permission to connect and accept connections from the host + * portion of the URL is added. + * + * @param source The codesource that needs the permissions to be accessed + * @return the collection of permissions needed to access the code resource + * @see java.security.SecureClassLoader#getPermissions(CodeSource) + */ + protected PermissionCollection getPermissions(CodeSource source) + { + // XXX - This implementation does exactly as the Javadoc describes. + // But maybe we should/could use URLConnection.getPermissions()? + // First get the permissions that would normally be granted + PermissionCollection permissions = super.getPermissions(source); + + // Now add any extra permissions depending on the URL location. + URL url = source.getLocation(); + String protocol = url.getProtocol(); + if (protocol.equals("file")) + { + String file = url.getFile(); + + // If the file end in / it must be an directory. + if (file.endsWith("/") || file.endsWith(File.separator)) + { + // Grant permission to read everything in that directory and + // all subdirectories. + permissions.add(new FilePermission(file + "-", "read")); + } + else + { + // It is a 'normal' file. + // Grant permission to access that file. + permissions.add(new FilePermission(file, "read")); + } + } + else + { + // Grant permission to connect to and accept connections from host + String host = url.getHost(); + if (host != null) + permissions.add(new SocketPermission(host, "connect,accept")); + } + + return permissions; + } + + /** + * Returns all the locations that this class loader currently uses the + * resolve classes and resource. This includes both the initially supplied + * URLs as any URLs added later by the loader. + * @return All the currently used URLs + */ + public URL[] getURLs() + { + return (URL[]) urls.toArray(new URL[urls.size()]); + } + + /** + * Creates a new instance of a URLClassLoader that gets + * classes from the supplied URLs. This class loader + * will have as parent the standard system class loader. + * + * @param urls the initial URLs used to resolve classes and + * resources + * + * @return the class loader + * + * @exception SecurityException when the calling code does not have + * permission to access the given URLs + */ + public static URLClassLoader newInstance(URL[] urls) + throws SecurityException + { + return newInstance(urls, null); + } + + /** + * Creates a new instance of a URLClassLoader that gets + * classes from the supplied URLs and with the supplied + * loader as parent class loader. + * + * @param urls the initial URLs used to resolve classes and + * resources + * @param parent the parent class loader + * + * @return the class loader + * + * @exception SecurityException when the calling code does not have + * permission to access the given URLs + */ + public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent) + throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) + return new URLClassLoader(urls, parent); + else + { + final Object securityContext = sm.getSecurityContext(); + + // XXX - What to do with anything else then an AccessControlContext? + if (! (securityContext instanceof AccessControlContext)) + throw new SecurityException("securityContext must be AccessControlContext: " + + securityContext); + + URLClassLoader loader = + AccessController.doPrivileged(new PrivilegedAction() + { + public URLClassLoader run() + { + return new URLClassLoader(parent, + (AccessControlContext) securityContext); + } + }); + loader.addURLs(urls); + return loader; + } + } +} diff --git a/libjava/classpath/java/net/URLConnection.java b/libjava/classpath/java/net/URLConnection.java new file mode 100644 index 000000000..e9365a3e0 --- /dev/null +++ b/libjava/classpath/java/net/URLConnection.java @@ -0,0 +1,1150 @@ +/* URLConnection.java -- Abstract superclass for reading from URL's + Copyright (C) 1998, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import gnu.classpath.SystemProperties; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.AllPermission; +import java.security.Permission; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: One guessContentTypeFrom... methods not implemented. + * getContent method assumes content type from response; see comment there. + */ +/** + * This class models a connection that retrieves the information pointed + * to by a URL object. This is typically a connection to a remote node + * on the network, but could be a simple disk read. + *

      + * A URLConnection object is normally created by calling the openConnection() + * method of a URL object. This method is somewhat misnamed because it does + * not actually open the connection. Instead, it return an unconnected + * instance of this object. The caller then has the opportunity to set + * various connection options prior to calling the actual connect() method. + *

      + * After the connection has been opened, there are a number of methods in + * this class that access various attributes of the data, typically + * represented by headers sent in advance of the actual data itself. + *

      + * Also of note are the getInputStream and getContent() methods which allow + * the caller to retrieve the actual data from the connection. Note that + * for some types of connections, writing is also allowed. The setDoOutput() + * method must be called prior to connecing in order to enable this, then + * the getOutputStream method called after the connection in order to + * obtain a stream to write the output to. + *

      + * The getContent() method is of particular note. This method returns an + * Object that encapsulates the data returned. There is no way do determine + * the type of object that will be returned in advance. This is determined + * by the actual content handlers as described in the description of that + * method. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public abstract class URLConnection +{ + /** + * This is an object that maps filenames to MIME types. The interface + * to do this is implemented by this class, so just create an empty + * instance and store it here. + */ + private static FileNameMap fileNameMap; + + /** + * This is the ContentHandlerFactory set by the caller, if any + */ + private static ContentHandlerFactory factory; + + /** + * This is the default value that will be used to determine whether or + * not user interaction should be allowed. + */ + private static boolean defaultAllowUserInteraction; + + /** + * This is the default flag indicating whether or not to use caches to + * store the data returned from a server + */ + private static boolean defaultUseCaches = true; + + /** + * Default internal content handler factory. + */ + private static ContentHandlerFactory defaultFactory + = new gnu.java.net.DefaultContentHandlerFactory(); + + /** + * This variable determines whether or not interaction is allowed with + * the user. For example, to prompt for a username and password. + */ + protected boolean allowUserInteraction; + + /** + * Indicates whether or not a connection has been established to the + * destination specified in the URL + */ + protected boolean connected; + + /** + * Indicates whether or not input can be read from this URL + */ + protected boolean doInput = true; + + /** + * Indicates whether or not output can be sent to this URL + */ + protected boolean doOutput; + + /** + * If this flag is set, the protocol is allowed to cache data whenever + * it can (caching is not guaranteed). If it is not set, the protocol + * must a get a fresh copy of the data. + *

      + * This field is set by the setUseCaches method and returned by the + * getUseCaches method. + * + * Its default value is that determined by the last invocation of + * setDefaultUseCaches + */ + protected boolean useCaches; + + /** + * If this value is non-zero, then the connection will only attempt to + * fetch the document pointed to by the URL if the document has been + * modified more recently than the date set in this variable. That date + * should be specified as the number of seconds since 1/1/1970 GMT. + */ + protected long ifModifiedSince; + + /** + * This is the URL associated with this connection + */ + protected URL url; + + private static SimpleDateFormat[] dateFormats; + private static boolean dateformats_initialized; + + /** + * The connection timeout period. + */ + private int connectTimeout; + + /** + * The read timeout period. + */ + private int readTimeout; + + /* Cached ParsePosition, used when parsing dates. */ + private ParsePosition position; + + /** + * Creates a URL connection to a given URL. A real connection is not made. + * Use connect() to do this. + * + * @param url The Object to create the URL connection to + * + * @see URLConnection#connect() + */ + protected URLConnection(URL url) + { + // Set up all our instance variables + this.url = url; + allowUserInteraction = defaultAllowUserInteraction; + useCaches = defaultUseCaches; + } + + /** + * Establishes the actual connection to the URL associated with this + * connection object + * + * @exception IOException if an error occurs + */ + public abstract void connect() throws IOException; + + /** + * Returns the URL object associated with this connection + * + * @return The URL for this connection. + */ + public URL getURL() + { + return url; + } + + /** + * Returns the connection timeout speed, in milliseconds, or zero if + * the timeout is infinite or not set. + * + * @return The timeout. + * + * @since 1.5 + */ + public int getConnectTimeout() + { + return connectTimeout; + } + + /** + * Set the connection timeout speed, in milliseconds, or zero if the timeout + * is to be considered infinite. Note that in certain socket + * implementations/platforms this method may not have any effect. + * + * Throws an IllegalArgumentException if timeout < 0. + * + * @param timeout the timeout, in milliseconds. + * + * @since 1.5 + */ + public void setConnectTimeout(int timeout) + throws IllegalArgumentException + { + if( timeout < 0 ) + throw new IllegalArgumentException("Timeout must be 0 or positive."); + connectTimeout = timeout; + } + + /** + * Returns the read timeout, in milliseconds, or zero if the timeout + * is infinite or not set. + * + * @return The timeout. + * + * @see #setReadTimeout + * + * @since 1.5 + */ + public int getReadTimeout() + { + return readTimeout; + } + + /** + * Set the read timeout, in milliseconds, or zero if the timeout + * is to be considered infinite. Note that in certain socket + * implementations/platforms this method may not have any effect. + * + * Throws an IllegalArgumentException if timeout < 0. + * + * @param timeout - The timeout, in milliseconds. + * + * @throws IllegalArgumentException if timeout is negative. + * + * @see #getReadTimeout + * + * @since 1.5 + */ + public void setReadTimeout(int timeout) + throws IllegalArgumentException + { + if( timeout < 0 ) + throw new IllegalArgumentException("Timeout must be 0 or positive."); + readTimeout = timeout; + } + + /** + * Returns the value of the content-length header field or -1 if the value + * is not known or not present. + * + * @return The content-length field + */ + public int getContentLength() + { + return getHeaderFieldInt("content-length", -1); + } + + /** + * Returns the the content-type of the data pointed to by the URL. This + * method first tries looking for a content-type header. If that is not + * present, it attempts to use the file name to determine the content's + * MIME type. If that is unsuccessful, the method returns null. The caller + * may then still attempt to determine the MIME type by a call to + * guessContentTypeFromStream() + * + * @return The content MIME type + */ + public String getContentType() + { + return getHeaderField("content-type"); + } + + /** + * Returns the value of the content-encoding field or null if it is not + * known or not present. + * + * @return The content-encoding field + */ + public String getContentEncoding() + { + return getHeaderField("content-encoding"); + } + + /** + * Returns the value of the expires header or 0 if not known or present. + * If populated, the return value is number of seconds since midnight + * on 1/1/1970 GMT. + * + * @return The expiration time. + */ + public long getExpiration() + { + return getHeaderFieldDate("expires", 0L); + } + + /** + * Returns the date of the document pointed to by the URL as reported in + * the date field of the header or 0 if the value is not present or not + * known. If populated, the return value is number of seconds since + * midnight on 1/1/1970 GMT. + * + * @return The document date + */ + public long getDate() + { + return getHeaderFieldDate("date", 0L); + } + + /** + * Returns the value of the last-modified header field or 0 if not known known + * or not present. If populated, the return value is the number of seconds + * since midnight on 1/1/1970. + * + * @return The last modified time + */ + public long getLastModified() + { + return getHeaderFieldDate("last-modified", 0L); + } + + /** + * Return a String representing the header value at the specified index. + * This allows the caller to walk the list of header fields. The analogous + * {@link #getHeaderField(int)} method allows access to the corresponding + * key for this header field + * + * @param index The index into the header field list to retrieve the value for + * + * @return The header value or null if index is past the end of the headers + */ + public String getHeaderField(int index) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * Returns a String representing the value of the header field having + * the named key. Returns null if the header field does not exist. + * + * @param name The key of the header field + * + * @return The value of the header field as a String + */ + public String getHeaderField(String name) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * Returns an unmodifiable Map containing all sent header fields. + * + * @return The map of header fields. The map consists of String keys with + * an unmodifiable List of String objects as value. + * + * @since 1.4 + */ + public Map> getHeaderFields() + { + // Subclasses for specific protocols override this. + return Collections.emptyMap(); + } + + /** + * Returns the value of the named header field as an int. If the field + * is not present or cannot be parsed as an integer, the default value + * will be returned. + * + * @param name The header field key to lookup + * @param defaultValue The defaule value if the header field is not found + * or can't be parsed. + * + * @return The value of the header field or the default value if the field + * is missing or malformed + */ + public int getHeaderFieldInt(String name, int defaultValue) + { + String value = getHeaderField(name); + + if (value == null) + return defaultValue; + + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException e) + { + return defaultValue; + } + } + + /** + * Returns the value of the named header field as a date. This date will + * be the number of seconds since midnight 1/1/1970 GMT or the default + * value if the field is not present or cannot be converted to a date. + * + * @param name The name of the header field + * @param defaultValue The default date if the header field is not found + * or can't be converted. + * + * @return The date value of the header filed or the default value + * if the field is missing or malformed + */ + public long getHeaderFieldDate(String name, long defaultValue) + { + if (! dateformats_initialized) + initializeDateFormats(); + + if (position == null) + position = new ParsePosition(0); + + long result = defaultValue; + String str = getHeaderField(name); + + if (str != null) + { + for (int i = 0; i < dateFormats.length; i++) + { + SimpleDateFormat df = dateFormats[i]; + position.setIndex(0); + position.setErrorIndex(0); + Date date = df.parse(str, position); + if (date != null) + return date.getTime(); + } + } + + return result; + } + + /** + * Returns a String representing the header key at the specified index. + * This allows the caller to walk the list of header fields. The analogous + * {@link #getHeaderField(int)} method allows access to the corresponding + * value for this tag. + * + * @param index The index into the header field list to retrieve the key for. + * + * @return The header field key or null if index is past the end + * of the headers. + */ + public String getHeaderFieldKey(int index) + { + // Subclasses for specific protocols override this. + return null; + } + + /** + * This method returns the content of the document pointed to by the + * URL as an Object. The type of object depends on the MIME type of + * the object and particular content hander loaded. Most text type + * content handlers will return a subclass of + * InputStream. Images usually return a class that + * implements ImageProducer. There is not guarantee + * what type of object will be returned, however. + * + *

      This class first determines the MIME type of the content, then + * creates a ContentHandler object to process the input. If the + * ContentHandlerFactory is set, then that object is + * called to load a content handler, otherwise a class called + * gnu.java.net.content.<content_type> is tried. If this + * handler does not exist, the method will simple return the + * InputStream returned by + * getInputStream(). Note that the default + * implementation of getInputStream() throws a + * UnknownServiceException so subclasses are encouraged + * to override this method.

      + * + * @return the content + * + * @exception IOException If an error with the connection occurs. + * @exception UnknownServiceException If the protocol does not support the + * content type at all. + */ + public Object getContent() throws IOException + { + if (!connected) + connect(); + + // FIXME: Doc indicates that other criteria should be applied as + // heuristics to determine the true content type, e.g. see + // guessContentTypeFromName() and guessContentTypeFromStream methods + // as well as FileNameMap class & fileNameMap field & get/set methods. + String type = getContentType(); + ContentHandler ch = getContentHandler(type); + + if (ch != null) + return ch.getContent(this); + + return getInputStream(); + } + + /** + * Retrieves the content of this URLConnection + * + * @param classes The allowed classes for the content + * + * @return the content + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support the + * content type + */ + public Object getContent(Class[] classes) + throws IOException + { + if (! connected) + connect(); + String type = getContentType(); + ContentHandler ch = getContentHandler(type); + if (ch != null) + return ch.getContent(this, classes); + throw new UnknownServiceException("protocol does not support the content type"); + } + + /** + * This method returns a Permission object representing the + * permissions required to access this URL. This method returns + * java.security.AllPermission by default. Subclasses should + * override it to return a more specific permission. For example, an + * HTTP URL should return an instance of SocketPermission + * for the appropriate host and port. + *

      + * Note that because of items such as HTTP redirects, the permission + * object returned might be different before and after connecting. + * + * @return A Permission object + * + * @exception IOException If the computation of the permission requires + * network or file I/O and an exception occurs while computing it + */ + public Permission getPermission() throws IOException + { + // Subclasses may override this. + return new AllPermission(); + } + + /** + * Returns an InputStream for this connection. As this default + * implementation returns null, subclasses should override this method + * + * @return An InputStream for this connection + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support input + */ + public InputStream getInputStream() throws IOException + { + // Subclasses for specific protocols override this. + throw new UnknownServiceException("Protocol " + url.getProtocol() + + " does not support input."); + } + + /** + * Returns an OutputStream for this connection. As this default + * implementation returns null, subclasses should override this method + * + * @return An OutputStream for this connection + * + * @exception IOException If an error occurs + * @exception UnknownServiceException If the protocol does not support output + */ + public OutputStream getOutputStream() throws IOException + { + // Subclasses for specific protocols override this. + throw new UnknownServiceException("Protocol " + url.getProtocol() + + " does not support output."); + } + + /** + * The methods prints the value of this object as a String by calling the + * toString() method of its associated URL. Overrides Object.toString() + * + * @return A String representation of this object + */ + public String toString() + { + return this.getClass().getName() + ":" + url.toString(); + } + + /** + * Sets the value of a flag indicating whether or not input is going + * to be done for this connection. This default to true unless the + * doOutput flag is set to false, in which case this defaults to false. + * + * @param input true if input is to be done, + * false otherwise + * + * @exception IllegalStateException If already connected + */ + public void setDoInput(boolean input) + { + if (connected) + throw new IllegalStateException("Already connected"); + + doInput = input; + } + + /** + * Returns the value of a flag indicating whether or not input is going + * to be done for this connection. This default to true unless the + * doOutput flag is set to false, in which case this defaults to false. + * + * @return true if input is to be done, false otherwise + */ + public boolean getDoInput() + { + return doInput; + } + + /** + * Sets a boolean flag indicating whether or not output will be done + * on this connection. The default value is false, so this method can + * be used to override the default + * + * @param output ture if output is to be done, false otherwise + * + * @exception IllegalStateException If already connected + */ + public void setDoOutput(boolean output) + { + if (connected) + throw new IllegalStateException("Already connected"); + + doOutput = output; + } + + /** + * Returns a boolean flag indicating whether or not output will be done + * on this connection. This defaults to false. + * + * @return true if output is to be done, false otherwise + */ + public boolean getDoOutput() + { + return doOutput; + } + + /** + * Sets a boolean flag indicating whether or not user interaction is + * allowed for this connection. (For example, in order to prompt for + * username and password info. + * + * @param allow true if user interaction should be allowed, false otherwise. + * + * @exception IllegalStateException If already connected + */ + public void setAllowUserInteraction(boolean allow) + { + if (connected) + throw new IllegalStateException("Already connected"); + + allowUserInteraction = allow; + } + + /** + * Returns a boolean flag indicating whether or not user interaction is + * allowed for this connection. (For example, in order to prompt for + * username and password info. + * + * @return true if user interaction is allowed, false otherwise + */ + public boolean getAllowUserInteraction() + { + return allowUserInteraction; + } + + /** + * Sets the default flag for whether or not interaction with a user + * is allowed. This will be used for all connections unless overridden + * + * @param allow true to allow user interaction, false otherwise + */ + public static void setDefaultAllowUserInteraction(boolean allow) + { + defaultAllowUserInteraction = allow; + } + + /** + * Returns the default flag for whether or not interaction with a user + * is allowed. This will be used for all connections unless overridden + * + * @return true if user interaction is allowed, false otherwise + */ + public static boolean getDefaultAllowUserInteraction() + { + return defaultAllowUserInteraction; + } + + /** + * Sets a boolean flag indicating whether or not caching will be used + * (if possible) to store data downloaded via the connection. + * + * @param usecaches The new value + * + * @exception IllegalStateException If already connected + */ + public void setUseCaches(boolean usecaches) + { + if (connected) + throw new IllegalStateException("Already connected"); + + useCaches = usecaches; + } + + /** + * Returns a boolean flag indicating whether or not caching will be used + * (if possible) to store data downloaded via the connection. + * + * @return true if caching should be used if possible, false otherwise + */ + public boolean getUseCaches() + { + return useCaches; + } + + /** + * Sets the ifModified since instance variable. If this value is non + * zero and the underlying protocol supports it, the actual document will + * not be fetched unless it has been modified since this time. The value + * passed should be 0 if this feature is to be disabled or the time expressed + * as the number of seconds since midnight 1/1/1970 GMT otherwise. + * + * @param ifmodifiedsince The new value in milliseconds + * since January 1, 1970 GMT + * + * @exception IllegalStateException If already connected + */ + public void setIfModifiedSince(long ifmodifiedsince) + { + if (connected) + throw new IllegalStateException("Already connected"); + + ifModifiedSince = ifmodifiedsince; + } + + /** + * Returns the ifModified since instance variable. If this value is non + * zero and the underlying protocol supports it, the actual document will + * not be fetched unless it has been modified since this time. The value + * returned will be 0 if this feature is disabled or the time expressed + * as the number of seconds since midnight 1/1/1970 GMT otherwise + * + * @return The ifModifiedSince value + */ + public long getIfModifiedSince() + { + return ifModifiedSince; + } + + /** + * Returns the default value used to determine whether or not caching + * of documents will be done when possible. + * + * @return true if caches will be used, false otherwise + */ + public boolean getDefaultUseCaches() + { + return defaultUseCaches; + } + + /** + * Sets the default value used to determine whether or not caching + * of documents will be done when possible. + * + * @param use true to use caches if possible by default, false otherwise + */ + public void setDefaultUseCaches(boolean use) + { + defaultUseCaches = use; + } + + /** + * Sets the value of the named request property. + * This method does overwrite the value of existing properties with + * the new value. + * + * @param key The name of the property + * @param value The value of the property + * + * @exception IllegalStateException If already connected + * @exception NullPointerException If key is null + * + * @see URLConnection#getRequestProperty(String key) + * @see URLConnection#addRequestProperty(String key, String value) + * + * @since 1.4 + */ + public void setRequestProperty(String key, String value) + { + if (connected) + throw new IllegalStateException("Already connected"); + + if (key == null) + throw new NullPointerException("key is null"); + + // Do nothing unless overridden by subclasses that support setting + // header fields in the request. + } + + /** + * Adds a new request property by a key/value pair. + * This method does not overwrite existing properties with the same key. + * + * @param key Key of the property to add + * @param value Value of the Property to add + * + * @exception IllegalStateException If already connected + * @exception NullPointerException If key is null + * + * @see URLConnection#getRequestProperty(String) + * @see URLConnection#setRequestProperty(String, String) + * + * @since 1.4 + */ + public void addRequestProperty(String key, String value) + { + if (connected) + throw new IllegalStateException("Already connected"); + + if (key == null) + throw new NullPointerException("key is null"); + + // Do nothing unless overridden by subclasses that support adding + // header fields in the request. + } + + /** + * Returns the value of the named request property. + * + * @param key The name of the property + * + * @return Value of the property, or null if key is null. + * + * @exception IllegalStateException If already connected + * + * @see URLConnection#setRequestProperty(String, String) + * @see URLConnection#addRequestProperty(String, String) + */ + public String getRequestProperty(String key) + { + if (connected) + throw new IllegalStateException("Already connected"); + + // Overridden by subclasses that support reading header fields from the + // request. + return null; + } + + /** + * Returns an unmodifiable Map containing the request properties. + * + * @return The map of properties. The map consists of String keys with an + * unmodifiable List of String objects as value. + * + * @exception IllegalStateException If already connected + * + * @since 1.4 + */ + public Map> getRequestProperties() + { + if (connected) + throw new IllegalStateException("Already connected"); + + // Overridden by subclasses that support reading header fields from the + // request. + return Collections.emptyMap(); + } + + /** + * Sets the default value of a request property. This will be used + * for all connections unless the value of the property is manually + * overridden. + * + * @param key The request property name the default is being set for + * @param value The value to set the default to + * + * @deprecated 1.3 The method setRequestProperty should be used instead. + * This method does nothing now. + * + * @see URLConnection#setRequestProperty(String, String) + */ + public static void setDefaultRequestProperty(String key, String value) + { + // This method does nothing since JDK 1.3. + } + + /** + * Returns the default value of a request property. This will be used + * for all connections unless the value of the property is manually + * overridden. + * + * @param key The request property to return the default value of + * + * @return The value of the default property or null if not available + * + * @deprecated 1.3 The method getRequestProperty should be used instead. + * This method does nothing now. + * + * @see URLConnection#getRequestProperty(String) + */ + public static String getDefaultRequestProperty(String key) + { + // This method does nothing since JDK 1.3. + return null; + } + + /** + * Sets the ContentHandlerFactory for an application. This can be called + * once and only once. If it is called again, then an Error is thrown. + * Unlike for other set factory methods, this one does not do a security + * check prior to setting the factory. + * + * @param factory The ContentHandlerFactory for this application + * + * @exception Error If the factory has already been defined + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + */ + public static synchronized void setContentHandlerFactory(ContentHandlerFactory factory) + { + if (URLConnection.factory != null) + throw new Error("ContentHandlerFactory already set"); + + // Throw an exception if an extant security mgr precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + URLConnection.factory = factory; + } + + /** + * Returns the MIME type of a file based on the name of the file. This + * works by searching for the file's extension in a list of file extensions + * and returning the MIME type associated with it. If no type is found, + * then a MIME type of "application/octet-stream" will be returned. + * + * @param filename The filename to determine the MIME type for + * + * @return The MIME type String + * + * @specnote public since JDK 1.4 + */ + public static String guessContentTypeFromName(String filename) + { + return getFileNameMap().getContentTypeFor(filename.toLowerCase()); + } + + /** + * Returns the MIME type of a stream based on the first few characters + * at the beginning of the stream. This routine can be used to determine + * the MIME type if a server is believed to be returning an incorrect + * MIME type. This method returns "application/octet-stream" if it + * cannot determine the MIME type. + *

      + * NOTE: Overriding MIME types sent from the server can be obnoxious + * to user's. See Internet Exploder 4 if you don't believe me. + * + * @param is The InputStream to determine the MIME type from + * + * @return The MIME type + * + * @exception IOException If an error occurs + */ + public static String guessContentTypeFromStream(InputStream is) + throws IOException + { + String result = VMURLConnection.guessContentTypeFromStream(is); + if (result == null) + return "application/octet-stream"; + return result; + } + + /** + * This method returns the FileNameMap object being used + * to decode MIME types by file extension. + * + * @return The FileNameMap. + * + * @since 1.2 + */ + public static synchronized FileNameMap getFileNameMap() + { + // Delayed initialization. + if (fileNameMap == null) + fileNameMap = new MimeTypeMapper(); + + return fileNameMap; + } + + /** + * This method sets the FileNameMap object being used + * to decode MIME types by file extension. + * + * @param map The FileNameMap. + * + * @exception SecurityException If a security manager exists and its + * checkSetFactory method doesn't allow the operation + * + * @since 1.2 + */ + public static synchronized void setFileNameMap(FileNameMap map) + { + // Throw an exception if an extant security manager precludes + // setting the factory. + SecurityManager s = System.getSecurityManager(); + if (s != null) + s.checkSetFactory(); + + fileNameMap = map; + } + + private ContentHandler getContentHandler(String contentType) + { + // No content type so just handle it as the default. + if (contentType == null || contentType.equals("")) + return null; + + ContentHandler handler = null; + + // If a non-default factory has been set, use it. + if (factory != null) + handler = factory.createContentHandler(contentType); + + // Now try default factory. Using this factory to instantiate built-in + // content handlers is preferable + if (handler == null) + handler = defaultFactory.createContentHandler(contentType); + + // User-set factory has not returned a handler. Use the default search + // algorithm. + if (handler == null) + { + // Get the list of packages to check and append our default handler + // to it, along with the JDK specified default as a last resort. + // Except in very unusual environments the JDK specified one shouldn't + // ever be needed (or available). + String propVal = SystemProperties.getProperty("java.content.handler.pkgs"); + propVal = (((propVal == null) ? "" : (propVal + "|")) + + "gnu.java.net.content|sun.net.www.content"); + + // Deal with "Content-Type: text/html; charset=ISO-8859-1". + int parameterBegin = contentType.indexOf(';'); + if (parameterBegin >= 1) + contentType = contentType.substring(0, parameterBegin); + contentType = contentType.trim(); + + // Replace the '/' character in the content type with '.' and + // all other non-alphabetic, non-numeric characters with '_'. + char[] cArray = contentType.toCharArray(); + for (int i = 0; i < cArray.length; i++) + { + if (cArray[i] == '/') + cArray[i] = '.'; + else if (! ((cArray[i] >= 'A' && cArray[i] <= 'Z') || + (cArray[i] >= 'a' && cArray[i] <= 'z') || + (cArray[i] >= '0' && cArray[i] <= '9'))) + cArray[i] = '_'; + } + String contentClass = new String(cArray); + + // See if a class of this content type exists in any of the packages. + StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|"); + do + { + String facName = pkgPrefix.nextToken() + "." + contentClass; + try + { + handler = + (ContentHandler) Class.forName(facName).newInstance(); + } + catch (Exception e) + { + // Can't instantiate; handler still null, go on to next element. + } + } while (handler == null && pkgPrefix.hasMoreTokens()); + } + + return handler; + } + + // We don't put these in a static initializer, because it creates problems + // with initializer co-dependency: SimpleDateFormat's constructors + // eventually depend on URLConnection (via the java.text.*Symbols classes). + private static synchronized void initializeDateFormats() + { + if (dateformats_initialized) + return; + + Locale locale = new Locale("En", "Us", "Unix"); + dateFormats = new SimpleDateFormat[3]; + dateFormats[0] = + new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'", locale); + dateFormats[1] = + new SimpleDateFormat("EEEE, dd-MMM-yy hh:mm:ss 'GMT'", locale); + dateFormats[2] = new SimpleDateFormat("EEE MMM d hh:mm:ss yyyy", locale); + dateformats_initialized = true; + } +} diff --git a/libjava/classpath/java/net/URLDecoder.java b/libjava/classpath/java/net/URLDecoder.java new file mode 100644 index 000000000..73cedea5c --- /dev/null +++ b/libjava/classpath/java/net/URLDecoder.java @@ -0,0 +1,182 @@ +/* URLDecoder.java -- Class to decode URL's from encoded form. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.UnsupportedEncodingException; + + +/** + * This utility class contains static methods that converts a + * string encoded in the x-www-form-urlencoded format to the original + * text. The x-www-form-urlencoded format replaces certain disallowed + * characters with encoded equivalents. All upper case and lower case + * letters in the US alphabet remain as is, the space character (' ') + * is replaced with '+' sign, and all other characters are converted to a + * "%XX" format where XX is the hexadecimal representation of that character + * in a given character encoding (default is "UTF-8"). + *

      + * This method is very useful for decoding strings sent to CGI scripts + * + * Written using on-line Java Platform 1.2/1.4 API Specification. + * Status: Believed complete and correct. + * + * @since 1.2 + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) (documentation comments) + * @author Mark Wielaard (mark@klomp.org) + */ +public class URLDecoder +{ + /** + * Public contructor. Note that this class has only static methods. + */ + public URLDecoder() + { + } + + /** + * This method translates the passed in string from x-www-form-urlencoded + * format using the default encoding "UTF-8" to decode the hex encoded + * unsafe characters. + * + * @param s the String to convert + * + * @return the converted String + * + * @deprecated + */ + public static String decode(String s) + { + try + { + return decode(s, "UTF-8"); + } + catch (UnsupportedEncodingException uee) + { + // Should never happen since UTF-8 encoding should always be supported + return s; + } + } + + /** + * This method translates the passed in string from x-www-form-urlencoded + * format using the given character encoding to decode the hex encoded + * unsafe characters. + * + * This implementation will decode the string even if it contains + * unsafe characters (characters that should have been encoded) or if the + * two characters following a % do not represent a hex encoded byte. + * In those cases the unsafe character or the % character will be added + * verbatim to the decoded result. + * + * @param s the String to convert + * @param encoding the character encoding to use the decode the hex encoded + * unsafe characters + * + * @return the converted String + * + * @exception UnsupportedEncodingException If the named encoding is not + * supported + * + * @since 1.4 + */ + public static String decode(String s, String encoding) + throws UnsupportedEncodingException + { + // First convert all '+' characters to spaces. + String str = s.replace('+', ' '); + + // Then go through the whole string looking for byte encoded characters + int i; + int start = 0; + byte[] bytes = null; + int length = str.length(); + CPStringBuilder result = new CPStringBuilder(length); + while ((i = str.indexOf('%', start)) >= 0) + { + // Add all non-encoded characters to the result buffer + result.append(str.substring(start, i)); + start = i; + + // Get all consecutive encoded bytes + while ((i + 2 < length) && (str.charAt(i) == '%')) + i += 3; + + // Decode all these bytes + if ((bytes == null) || (bytes.length < ((i - start) / 3))) + bytes = new byte[((i - start) / 3)]; + + int index = 0; + try + { + while (start < i) + { + String sub = str.substring(start + 1, start + 3); + bytes[index] = (byte) Integer.parseInt(sub, 16); + index++; + start += 3; + } + } + catch (NumberFormatException nfe) + { + // One of the hex encoded strings was bad + } + + // Add the bytes as characters according to the given encoding + result.append(new String(bytes, 0, index, encoding)); + + // Make sure we skip to just after a % sign + // There might not have been enough encoded characters after the % + // or the hex chars were not actually hex chars (NumberFormatException) + if (start < length && s.charAt(start) == '%') + { + result.append('%'); + start++; + } + } + + // Add any characters left + if (start < str.length()) + result.append(str.substring(start)); + + return result.toString(); + } +} // class URLDecoder diff --git a/libjava/classpath/java/net/URLEncoder.java b/libjava/classpath/java/net/URLEncoder.java new file mode 100644 index 000000000..09c0f3de0 --- /dev/null +++ b/libjava/classpath/java/net/URLEncoder.java @@ -0,0 +1,186 @@ +/* URLEncoder.java -- Class to convert strings to a properly encoded URL + Copyright (C) 1998, 1999, 2001, 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.UnsupportedEncodingException; + + +/* + * Written using on-line Java Platform 1.2/1.4 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This utility class contains static methods that converts a + * string into a fully encoded URL string in x-www-form-urlencoded + * format. This format replaces certain disallowed characters with + * encoded equivalents. All upper case and lower case letters in the + * US alphabet remain as is, the space character (' ') is replaced with + * '+' sign, and all other characters are converted to a "%XX" format + * where XX is the hexadecimal representation of that character in a + * certain encoding (by default, the platform encoding, though the + * standard is "UTF-8"). + *

      + * This method is very useful for encoding strings to be sent to CGI scripts + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @author Mark Wielaard (mark@klomp.org) + */ +public class URLEncoder +{ + /** + * This method translates the passed in string into x-www-form-urlencoded + * format using the default encoding. The standard encoding is + * "UTF-8", and the two-argument form of this method should be used + * instead. + * + * @param s The String to convert + * + * @return The converted String + * + * @deprecated + */ + public static String encode(String s) + { + try + { + // We default to 8859_1 for compatibility with the same + // default elsewhere in the library. + return encode(s, System.getProperty("file.encoding", "8859_1")); + } + catch (UnsupportedEncodingException uee) + { + // Should never happen since default should always be supported + return s; + } + } + + /** + * This method translates the passed in string into x-www-form-urlencoded + * format using the character encoding to hex-encode the unsafe characters. + * + * @param s The String to convert + * @param encoding The encoding to use for unsafe characters + * + * @return The converted String + * + * @exception UnsupportedEncodingException If the named encoding is not + * supported + * + * @since 1.4 + */ + public static String encode(String s, String encoding) + throws UnsupportedEncodingException + { + int length = s.length(); + int start = 0; + int i = 0; + + CPStringBuilder result = new CPStringBuilder(length); + while (true) + { + while (i < length && isSafe(s.charAt(i))) + i++; + + // Safe character can just be added + result.append(s.substring(start, i)); + + // Are we done? + if (i >= length) + return result.toString(); + else if (s.charAt(i) == ' ') + { + result.append('+'); // Replace space char with plus symbol. + i++; + } + else + { + // Get all unsafe characters + start = i; + char c; + while (i < length && (c = s.charAt(i)) != ' ' && ! isSafe(c)) + i++; + + // Convert them to %XY encoded strings + String unsafe = s.substring(start, i); + byte[] bytes = unsafe.getBytes(encoding); + for (int j = 0; j < bytes.length; j++) + { + result.append('%'); + int val = bytes[j]; + result.append(hex.charAt((val & 0xf0) >> 4)); + result.append(hex.charAt(val & 0x0f)); + } + } + start = i; + } + } + + /** + * Private static method that returns true if the given char is either + * a uppercase or lowercase letter from 'a' till 'z', or a digit froim + * '0' till '9', or one of the characters '-', '_', '.' or '*'. Such + * 'safe' character don't have to be url encoded. + */ + private static boolean isSafe(char c) + { + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' + || c == '*'); + } + + /** + * Private constructor that does nothing. Included to avoid a default + * public constructor being created by the compiler. + */ + private URLEncoder() + { + } + + /** + * Used to convert to hex. We don't use Integer.toHexString, since + * it converts to lower case (and the Sun docs pretty clearly + * specify upper case here), and because it doesn't provide a + * leading 0. + */ + private static final String hex = "0123456789ABCDEF"; +} diff --git a/libjava/classpath/java/net/URLStreamHandler.java b/libjava/classpath/java/net/URLStreamHandler.java new file mode 100644 index 000000000..5433aedd9 --- /dev/null +++ b/libjava/classpath/java/net/URLStreamHandler.java @@ -0,0 +1,541 @@ +/* URLStreamHandler.java -- Abstract superclass for all protocol handlers + Copyright (C) 1998, 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import gnu.java.lang.CPStringBuilder; + +import java.io.File; +import java.io.IOException; + + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * This class is the superclass of all URL protocol handlers. The URL + * class loads the appropriate protocol handler to establish a connection + * to a (possibly) remote service (eg, "http", "ftp") and to do protocol + * specific parsing of URL's. Refer to the URL class documentation for + * details on how that class locates and loads protocol handlers. + *

      + * A protocol handler implementation should override the openConnection() + * method, and optionally override the parseURL() and toExternalForm() + * methods if necessary. (The default implementations will parse/write all + * URL's in the same form as http URL's). A protocol specific subclass + * of URLConnection will most likely need to be created as well. + *

      + * Note that the instance methods in this class are called as if they + * were static methods. That is, a URL object to act on is passed with + * every call rather than the caller assuming the URL is stored in an + * instance variable of the "this" object. + *

      + * The methods in this class are protected and accessible only to subclasses. + * URLStreamConnection objects are intended for use by the URL class only, + * not by other classes (unless those classes are implementing protocols). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * + * @see URL + */ +public abstract class URLStreamHandler +{ + /** + * Creates a URLStreamHander + */ + public URLStreamHandler() + { + } + + /** + * Returns a URLConnection for the passed in URL. Note that this should + * not actually create the connection to the (possibly) remote host, but + * rather simply return a URLConnection object. The connect() method of + * URL connection is used to establish the actual connection, possibly + * after the caller sets up various connection options. + * + * @param url The URL to get a connection object for + * + * @return A URLConnection object for the given URL + * + * @exception IOException If an error occurs + */ + protected abstract URLConnection openConnection(URL url) + throws IOException; + + /** + * This method parses the string passed in as a URL and set's the + * instance data fields in the URL object passed in to the various values + * parsed out of the string. The start parameter is the position to start + * scanning the string. This is usually the position after the ":" which + * terminates the protocol name. The end parameter is the position to + * stop scanning. This will be either the end of the String, or the + * position of the "#" character, which separates the "file" portion of + * the URL from the "anchor" portion. + *

      + * This method assumes URL's are formatted like http protocol URL's, so + * subclasses that implement protocols with URL's the follow a different + * syntax should override this method. The lone exception is that if + * the protocol name set in the URL is "file", this method will accept + * an empty hostname (i.e., "file:///"), which is legal for that protocol + * + * @param url The URL object in which to store the results + * @param spec The String-ized URL to parse + * @param start The position in the string to start scanning from + * @param end The position in the string to stop scanning + */ + protected void parseURL(URL url, String spec, int start, int end) + { + String host = url.getHost(); + int port = url.getPort(); + String file = url.getFile(); + String ref = url.getRef(); + String userInfo = url.getUserInfo(); + String authority = url.getAuthority(); + String query = null; + + // On Windows we need to change \ to / for file URLs + char separator = File.separatorChar; + if (url.getProtocol().equals("file") && separator != '/') + { + file = file.replace(separator, '/'); + spec = spec.replace(separator, '/'); + } + + if (spec.regionMatches(start, "//", 0, 2)) + { + String genuineHost; + int hostEnd; + int colon; + int at_host; + + start += 2; + int slash = spec.indexOf('/', start); + if (slash >= 0) + hostEnd = slash; + else + hostEnd = end; + + authority = host = spec.substring(start, hostEnd); + + // We first need a genuine host name (with userinfo). + // So we check for '@': if it's present check the port in the + // section after '@' in the other case check it in the full string. + // P.S.: We don't care having '@' at the beginning of the string. + if ((at_host = host.indexOf('@')) >= 0) + { + genuineHost = host.substring(at_host); + userInfo = host.substring(0, at_host); + } + else + genuineHost = host; + + // Look for optional port number. It is valid for the non-port + // part of the host name to be null (e.g. a URL "http://:80"). + // TBD: JDK 1.2 in this case sets host to null rather than ""; + // this is undocumented and likely an unintended side effect in 1.2 + // so we'll be simple here and stick with "". Note that + // "http://" or "http:///" produce a "" host in JDK 1.2. + if ((colon = genuineHost.indexOf(':')) >= 0) + { + try + { + port = Integer.parseInt(genuineHost.substring(colon + 1)); + } + catch (NumberFormatException e) + { + // Ignore invalid port values; port is already set to u's + // port. + } + + // Now we must cut the port number in the original string. + if (at_host >= 0) + host = host.substring(0, at_host + colon); + else + host = host.substring(0, colon); + } + file = null; + start = hostEnd; + } + else if (host == null) + host = ""; + + if (file == null || file.length() == 0 + || (start < end && spec.charAt(start) == '/')) + { + // No file context available; just spec for file. + // Or this is an absolute path name; ignore any file context. + file = spec.substring(start, end); + ref = null; + } + else if (start < end) + { + // Context is available, but only override it if there is a new file. + int lastSlash = file.lastIndexOf('/'); + if (lastSlash < 0) + file = spec.substring(start, end); + else + file = (file.substring(0, lastSlash) + + '/' + spec.substring(start, end)); + + // For URLs constructed relative to a context, we + // need to canonicalise the file path. + file = canonicalizeFilename(file); + + ref = null; + } + + if (ref == null) + { + // Normally there should be no '#' in the file part, + // but we are nice. + int hash = file.indexOf('#'); + if (hash != -1) + { + ref = file.substring(hash + 1, file.length()); + file = file.substring(0, hash); + } + } + + // We care about the query tag only if there is no reference at all. + if (ref == null) + { + int queryTag = file.indexOf('?'); + if (queryTag != -1) + { + query = file.substring(queryTag + 1); + file = file.substring(0, queryTag); + } + } + + // XXX - Classpath used to call PlatformHelper.toCanonicalForm() on + // the file part. It seems like overhead, but supposedly there is some + // benefit in windows based systems (it also lowercased the string). + setURL(url, url.getProtocol(), host, port, authority, userInfo, file, query, ref); + } + + /* + * Canonicalize a filename. + */ + private static String canonicalizeFilename(String file) + { + // XXX - GNU Classpath has an implementation that might be more appropriate + // for Windows based systems (gnu.java.io.PlatformHelper.toCanonicalForm) + int index; + + // Replace "/./" with "/". This probably isn't very efficient in + // the general case, but it's probably not bad most of the time. + while ((index = file.indexOf("/./")) >= 0) + file = file.substring(0, index) + file.substring(index + 2); + + // Process "/../" correctly. This probably isn't very efficient in + // the general case, but it's probably not bad most of the time. + while ((index = file.indexOf("/../")) >= 0) + { + // Strip of the previous directory - if it exists. + int previous = file.lastIndexOf('/', index - 1); + if (previous >= 0) + file = file.substring(0, previous) + file.substring(index + 3); + else + break; + } + return file; + } + + /** + * Compares two URLs, excluding the fragment component + * + * @param url1 The first url + * @param url2 The second url to compare with the first + * + * @return True if both URLs point to the same file, false otherwise. + * + * @specnote Now protected + */ + protected boolean sameFile(URL url1, URL url2) + { + if (url1 == url2) + return true; + + // This comparison is very conservative. It assumes that any + // field can be null. + if (url1 == null || url2 == null) + return false; + int p1 = url1.getPort(); + if (p1 == -1) + p1 = url1.ph.getDefaultPort(); + int p2 = url2.getPort(); + if (p2 == -1) + p2 = url2.ph.getDefaultPort(); + if (p1 != p2) + return false; + String s1; + String s2; + s1 = url1.getProtocol(); + s2 = url2.getProtocol(); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + s1 = url1.getHost(); + s2 = url2.getHost(); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + s1 = canonicalizeFilename(url1.getFile()); + s2 = canonicalizeFilename(url2.getFile()); + if (s1 != s2 && (s1 == null || ! s1.equals(s2))) + return false; + return true; + } + + /** + * This methods sets the instance variables representing the various fields + * of the URL to the values passed in. + * + * @param u The URL to modify + * @param protocol The protocol to set + * @param host The host name to et + * @param port The port number to set + * @param file The filename to set + * @param ref The reference + * + * @exception SecurityException If the protocol handler of the URL is + * different from this one + * + * @deprecated 1.2 Please use + * #setURL(URL,String,String,int,String,String,String,String); + */ + protected void setURL(URL u, String protocol, String host, int port, + String file, String ref) + { + u.set(protocol, host, port, file, ref); + } + + /** + * Sets the fields of the URL argument to the indicated values + * + * @param u The URL to modify + * @param protocol The protocol to set + * @param host The host name to set + * @param port The port number to set + * @param authority The authority to set + * @param userInfo The user information to set + * @param path The path/filename to set + * @param query The query part to set + * @param ref The reference + * + * @exception SecurityException If the protocol handler of the URL is + * different from this one + */ + protected void setURL(URL u, String protocol, String host, int port, + String authority, String userInfo, String path, + String query, String ref) + { + u.set(protocol, host, port, authority, userInfo, path, query, ref); + } + + /** + * This is the default method for computing whether two URLs are + * equivalent. This method assumes that neither URL is null. + * + * @param url1 An URL object + * @param url2 Another URL object + * + * @return True if both given URLs are equal, false otherwise. + */ + protected boolean equals(URL url1, URL url2) + { + // This comparison is very conservative. It assumes that any + // field can be null. + int port1 = url1.getPort(); + if (port1 == -1) + port1 = url1.getDefaultPort(); + int port2 = url2.getPort(); + if (port2 == -1) + port2 = url2.getDefaultPort(); + // Note that we don't bother checking the 'authority'; it is + // redundant. + return (port1 == port2 + && ((url1.getProtocol() == null && url2.getProtocol() == null) + || (url1.getProtocol() != null + && url1.getProtocol().equals(url2.getProtocol()))) + && ((url1.getUserInfo() == null && url2.getUserInfo() == null) + || (url1.getUserInfo() != null + && url1.getUserInfo().equals(url2.getUserInfo()))) + && ((url1.getHost() == null && url2.getHost() == null) + || (url1.getHost() != null && url1.getHost().equals(url2.getHost()))) + && ((url1.getPath() == null && url2.getPath() == null) + || (url1.getPath() != null && url1.getPath().equals(url2.getPath()))) + && ((url1.getQuery() == null && url2.getQuery() == null) + || (url1.getQuery() != null + && url1.getQuery().equals(url2.getQuery()))) + && ((url1.getRef() == null && url2.getRef() == null) + || (url1.getRef() != null && url1.getRef().equals(url2.getRef())))); + } + + /** + * Compares the host components of two URLs. + * + * @param url1 The first URL. + * @param url2 The second URL. + * + * @return True if both URLs contain the same host. + */ + protected boolean hostsEqual(URL url1, URL url2) + { + InetAddress addr1 = getHostAddress(url1); + InetAddress addr2 = getHostAddress(url2); + + if (addr1 != null && addr2 != null) + return addr1.equals(addr2); + + String host1 = url1.getHost(); + String host2 = url2.getHost(); + + if (host1 != null && host2 != null) + return host1.equalsIgnoreCase(host2); + + return host1 == null && host2 == null; + } + + /** + * Get the IP address of our host. An empty host field or a DNS failure will + * result in a null return. + * + * @param url The URL to return the host address for. + * + * @return The address of the hostname in url. + */ + protected InetAddress getHostAddress(URL url) + { + String hostname = url.getHost(); + + if (hostname.equals("")) + return null; + + try + { + return InetAddress.getByName(hostname); + } + catch (UnknownHostException e) + { + return null; + } + } + + /** + * Returns the default port for a URL parsed by this handler. This method is + * meant to be overidden by handlers with default port numbers. + * + * @return The default port number. + */ + protected int getDefaultPort() + { + return -1; + } + + /** + * Provides the default hash calculation. May be overidden by handlers for + * other protocols that have different requirements for hashCode calculation. + * + * @param url The URL to calc the hashcode for. + * + * @return The hashcode for the given URL. + */ + protected int hashCode(URL url) + { + return url.getProtocol().hashCode() + + ((url.getHost() == null) ? 0 : url.getHost().hashCode()) + + url.getFile().hashCode() + url.getPort(); + } + + /** + * This method converts a URL object into a String. This method creates + * Strings in the mold of http URL's, so protocol handlers which use URL's + * that have a different syntax should override this method + * + * @param url The URL object to convert + * + * @return A string representation of the url + */ + protected String toExternalForm(URL url) + { + String protocol; + String file; + String ref; + String authority; + + protocol = url.getProtocol(); + authority = url.getAuthority(); + if (authority == null) + authority = ""; + + file = url.getFile(); + ref = url.getRef(); + + // Guess a reasonable size for the string buffer so we have to resize + // at most once. + int size = protocol.length() + authority.length() + file.length() + 24; + CPStringBuilder sb = new CPStringBuilder(size); + + if (protocol.length() > 0) + { + sb.append(protocol); + sb.append(":"); + } + + // If we have superfluous leading slashes (that means, at least 2) + // we always add the authority component ("//" + host) to + // avoid ambiguity. Otherwise we would generate an URL like + // proto://home/foo + // where we meant: + // host: - file: //home/foo + // but URL spec says it is: + // host: home - file: /foo + if (authority.length() != 0 || file.startsWith("//") ) + sb.append("//").append(authority).append(file); + else + sb.append(file); + + if (ref != null) + sb.append('#').append(ref); + + return sb.toString(); + } +} diff --git a/libjava/classpath/java/net/URLStreamHandlerFactory.java b/libjava/classpath/java/net/URLStreamHandlerFactory.java new file mode 100644 index 000000000..c92c71fb2 --- /dev/null +++ b/libjava/classpath/java/net/URLStreamHandlerFactory.java @@ -0,0 +1,65 @@ +/* URLStreamHandlerFactory.java -- Maps protocols to URLStreamHandlers + Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + + +/** + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ +/** + * This interface contains one method which maps the protocol portion of + * a URL (eg, "http" in "http://www.urbanophile.com/arenn/") to a + * URLStreamHandler object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + */ +public interface URLStreamHandlerFactory +{ + /** + * This method maps the protocol portion of a URL to a + * URLStreamHandler object. + * + * @param protocol The protocol name to map ("http", "ftp", etc). + * + * @return The URLStreamHandler for the specified protocol + */ + URLStreamHandler createURLStreamHandler(String protocol); +} // interface URLStreamHandlerFactory diff --git a/libjava/classpath/java/net/UnknownHostException.java b/libjava/classpath/java/net/UnknownHostException.java new file mode 100644 index 000000000..c5ba18330 --- /dev/null +++ b/libjava/classpath/java/net/UnknownHostException.java @@ -0,0 +1,77 @@ +/* UnknownHostException.java -- The hostname is unknown + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * This exception indicates that an attempt was made to reference a hostname + * or IP address that is not valid. This could possibly indicate that a + * DNS problem has occurred, but most often means that the host was not + * correctly specified. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner + * @status updated to 1.4 + */ +public class UnknownHostException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4639126076052875403L; + + /** + * Create a new instance without a descriptive error message. + */ + public UnknownHostException() + { + } + + /** + * Create a new instance with a descriptive error message, such as the + * name of the host that could not be resolved. + * + * @param message a message describing the error that occurred + */ + public UnknownHostException(String message) + { + super(message); + } +} // class UnknownHostException diff --git a/libjava/classpath/java/net/UnknownServiceException.java b/libjava/classpath/java/net/UnknownServiceException.java new file mode 100644 index 000000000..65cc8f592 --- /dev/null +++ b/libjava/classpath/java/net/UnknownServiceException.java @@ -0,0 +1,76 @@ +/* UnknownServiceException.java -- A service error occurred + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.net; + +import java.io.IOException; + + +/** + * Contrary to what you might think, this does not indicate that the + * TCP/IP service name specified was invalid. Instead it indicates that + * the MIME type returned from a URL could not be determined or that an + * attempt was made to write to a read-only URL. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class UnknownServiceException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -4169033248853639508L; + + /** + * Create a new instance without a descriptive error message. + */ + public UnknownServiceException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param message a message describing the error that occurred + */ + public UnknownServiceException(String message) + { + super(message); + } +} // class UnknownServiceException diff --git a/libjava/classpath/java/net/package.html b/libjava/classpath/java/net/package.html new file mode 100644 index 000000000..133ee716f --- /dev/null +++ b/libjava/classpath/java/net/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.net + + +

      Network communication through TCP and UDP sockets or URLs.

      + + + diff --git a/libjava/classpath/java/nio/Buffer.java b/libjava/classpath/java/nio/Buffer.java new file mode 100644 index 000000000..023ab0e1d --- /dev/null +++ b/libjava/classpath/java/nio/Buffer.java @@ -0,0 +1,429 @@ +/* Buffer.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +import gnu.classpath.Pointer; + +/** + * @since 1.4 + */ +public abstract class Buffer +{ + private final int cap; + int limit; + int pos; + int mark; + final Pointer address; + + /** + * Creates a new Buffer. + * + * Should be package private. + */ + Buffer (int capacity, int limit, int position, int mark, + Pointer address) + { + this.address = address; + + if (capacity < 0) + throw new IllegalArgumentException (); + + cap = capacity; + limit (limit); + position (position); + + if (mark >= 0) + { + if (mark > pos) + throw new IllegalArgumentException (); + + this.mark = mark; + } + else + { + this.mark = -1; + } + } + + /** + * Retrieves the capacity of the buffer. + * + * @return the capacity of the buffer + */ + public final int capacity () + { + return cap; + } + + /** + * Clears the buffer. + * + * @return this buffer + */ + public final Buffer clear () + { + limit = cap; + pos = 0; + mark = -1; + return this; + } + + /** + * Flips the buffer. + * + * @return this buffer + */ + public final Buffer flip () + { + limit = pos; + pos = 0; + mark = -1; + return this; + } + + /** + * Tells whether the buffer has remaining data to read or not. + * + * @return true if the buffer contains remaining data to read, + * false otherwise + */ + public final boolean hasRemaining () + { + return remaining() > 0; + } + + /** + * Tells whether this buffer is read only or not. + * + * @return true if the buffer is read only, false otherwise + */ + public abstract boolean isReadOnly (); + + /** + * Retrieves the current limit of the buffer. + * + * @return the limit of the buffer + */ + public final int limit () + { + return limit; + } + + /** + * Sets this buffer's limit. + * + * @param newLimit The new limit value; must be non-negative and no larger + * than this buffer's capacity. + * + * @return this buffer + * + * @exception IllegalArgumentException If the preconditions on newLimit + * do not hold. + */ + public final Buffer limit (int newLimit) + { + if ((newLimit < 0) || (newLimit > cap)) + throw new IllegalArgumentException (); + + if (newLimit < mark) + mark = -1; + + if (pos > newLimit) + pos = newLimit; + + limit = newLimit; + return this; + } + + /** + * Sets this buffer's mark at its position. + * + * @return this buffer + */ + public final Buffer mark () + { + mark = pos; + return this; + } + + /** + * Retrieves the current position of this buffer. + * + * @return the current position of this buffer + */ + public final int position () + { + return pos; + } + + /** + * Sets this buffer's position. If the mark is defined and larger than the + * new position then it is discarded. + * + * @param newPosition The new position value; must be non-negative and no + * larger than the current limit. + * + * @return this buffer + * + * @exception IllegalArgumentException If the preconditions on newPosition + * do not hold + */ + public final Buffer position (int newPosition) + { + if ((newPosition < 0) || (newPosition > limit)) + throw new IllegalArgumentException (); + + if (newPosition <= mark) + mark = -1; + + pos = newPosition; + return this; + } + + /** + * Returns the number of elements between the current position and the limit. + * + * @return the number of remaining elements + */ + public final int remaining() + { + return limit - pos; + } + + /** + * Resets this buffer's position to the previously-marked position. + * + * @return this buffer + * + * @exception InvalidMarkException If the mark has not been set. + */ + public final Buffer reset() + { + if (mark == -1) + throw new InvalidMarkException (); + + pos = mark; + return this; + } + + /** + * Rewinds this buffer. The position is set to zero and the mark + * is discarded. + * + * @return this buffer + */ + public final Buffer rewind() + { + pos = 0; + mark = -1; + return this; + } + + /** + * Checks for underflow. This method is used internally to check + * whether a buffer has enough elements left to satisfy a read + * request. + * + * @exception BufferUnderflowException If there are no remaining + * elements in this buffer. + */ + final void checkForUnderflow() + { + if (!hasRemaining()) + throw new BufferUnderflowException(); + } + + /** + * Checks for underflow. This method is used internally to check + * whether a buffer has enough elements left to satisfy a read + * request for a given number of elements. + * + * @param length The length of a sequence of elements. + * + * @exception BufferUnderflowException If there are not enough + * remaining elements in this buffer. + */ + final void checkForUnderflow(int length) + { + if (remaining() < length) + throw new BufferUnderflowException(); + } + + /** + * Checks for overflow. This method is used internally to check + * whether a buffer has enough space left to satisfy a write + * request. + * + * @exception BufferOverflowException If there is no remaining + * space in this buffer. + */ + final void checkForOverflow() + { + if (!hasRemaining()) + throw new BufferOverflowException(); + } + + /** + * Checks for overflow. This method is used internally to check + * whether a buffer has enough space left to satisfy a write + * request for a given number of elements. + * + * @param length The length of a sequence of elements. + * + * @exception BufferUnderflowException If there is not enough + * remaining space in this buffer. + */ + final void checkForOverflow(int length) + { + if (remaining() < length) + throw new BufferOverflowException(); + } + + /** + * Checks if index is negative or not smaller than the buffer's + * limit. This method is used internally to check whether + * an indexed request can be fulfilled. + * + * @param index The requested position in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + final void checkIndex(int index) + { + if (index < 0 + || index >= limit ()) + throw new IndexOutOfBoundsException (); + } + + /** + * Checks if buffer is read-only. This method is used internally to + * check if elements can be put into a buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + final void checkIfReadOnly() + { + if (isReadOnly()) + throw new ReadOnlyBufferException (); + } + + /** + * Checks whether an array is large enough to hold the given number of + * elements at the given offset. This method is used internally to + * check if an array is big enough. + * + * @param arraylength The length of the array. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than arraylength. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than arraylength - offset. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + static final void checkArraySize(int arraylength, int offset, int length) + { + if ((offset < 0) || + (length < 0) || + (arraylength < length + offset)) + throw new IndexOutOfBoundsException (); + } + + /** + * Returns the backing array of this buffer, if this buffer has one. + * Modification to the array are directly visible in this buffer and vice + * versa. + * + *

      + * If this is a read-only buffer, then a {@link ReadOnlyBufferException} is + * thrown because exposing the array would allow to circumvent the read-only + * property. If this buffer doesn't have an array, then an + * {@link UnsupportedOperationException} is thrown. Applications should check + * if this buffer supports a backing array by calling {@link #hasArray} + * first.

      + * + * @return the backing array of this buffer + * + * @throws ReadOnlyBufferException when this buffer is read only + * @throws UnsupportedOperationException when this buffer does not provide + * a backing array + * + * @since 1.6 + */ + public abstract Object array(); + + /** + * Returns true if this buffer can provide a backing array, + * false otherwise. When true, application code + * can call {@link #array()} to access this backing array. + * + * @return true if this buffer can provide a backing array, + * false otherwise + * + * @since 1.6 + */ + public abstract boolean hasArray(); + + /** + * For buffers that are backed by a Java array, this returns the offset + * into that array at which the buffer content starts. + * + * @return the offset into the backing array at which the buffer content + * starts + * @throws ReadOnlyBufferException when this buffer is read only + * @throws UnsupportedOperationException when this buffer does not provide + * a backing array + * + * @since 1.6 + */ + public abstract int arrayOffset(); + + /** + * Returns true when this buffer is direct, false + * otherwise. A direct buffer is usually backed by a raw memory area instead + * of a Java array. + * + * @return true when this buffer is direct, false + * otherwise + * + * @since 1.6 + */ + public abstract boolean isDirect(); +} diff --git a/libjava/classpath/java/nio/BufferOverflowException.java b/libjava/classpath/java/nio/BufferOverflowException.java new file mode 100644 index 000000000..eac79ed75 --- /dev/null +++ b/libjava/classpath/java/nio/BufferOverflowException.java @@ -0,0 +1,53 @@ +/* BufferOverflowException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @since 1.4 + */ +public class BufferOverflowException extends RuntimeException +{ + private static final long serialVersionUID = - 5484897634319144535L; + + /** + * Creates the exception + */ + public BufferOverflowException () + { + } +} diff --git a/libjava/classpath/java/nio/BufferUnderflowException.java b/libjava/classpath/java/nio/BufferUnderflowException.java new file mode 100644 index 000000000..76581371b --- /dev/null +++ b/libjava/classpath/java/nio/BufferUnderflowException.java @@ -0,0 +1,53 @@ +/* BufferUnderflowException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @since 1.4 + */ +public class BufferUnderflowException extends RuntimeException +{ + private static final long serialVersionUID = - 1713313658691622206L; + + /** + * Creates the exception + */ + public BufferUnderflowException () + { + } +} diff --git a/libjava/classpath/java/nio/ByteBuffer.java b/libjava/classpath/java/nio/ByteBuffer.java new file mode 100644 index 000000000..00db5bf4f --- /dev/null +++ b/libjava/classpath/java/nio/ByteBuffer.java @@ -0,0 +1,655 @@ +/* ByteBuffer.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +// GCJ LOCAL: Change gnu.classpath.Pointer to RawData +import gnu.gcj.RawData; +import gnu.classpath.Pointer; + +/** + * @since 1.4 + */ +public abstract class ByteBuffer extends Buffer + implements Comparable +{ + ByteOrder endian = ByteOrder.BIG_ENDIAN; + final byte[] backing_buffer; + final int array_offset; + + ByteBuffer (int capacity, int limit, int position, int mark, + RawData address, byte[] backing_buffer, int array_offset) + { + super (capacity, limit, position, mark, address); + this.backing_buffer = backing_buffer; + this.array_offset = array_offset; + } + + /** + * Allocates a new direct byte buffer. + */ + public static ByteBuffer allocateDirect (int capacity) + { + return DirectByteBufferImpl.allocate (capacity); + } + + /** + * Allocates a new ByteBuffer object with a given capacity. + */ + public static ByteBuffer allocate (int capacity) + { + return wrap(new byte[capacity], 0, capacity); + } + + /** + * Wraps a byte array into a ByteBuffer + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final ByteBuffer wrap (byte[] array, int offset, int length) + { + // FIXME: In GCJ and other implementations where arrays may not + // move we might consider, at least when offset==0: + // return new DirectByteBufferImpl(array, + // address_of_data(array) + offset, + // length, length, 0, false); + // This may be more efficient, mainly because we can then use the + // same logic for all ByteBuffers. + + return new ByteBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a byte array into a ByteBuffer + * object. + */ + public static final ByteBuffer wrap (byte[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers bytes from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length bytes remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first byte + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * bytes remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public ByteBuffer get (byte[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers bytes from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * bytes remaining in this buffer. + */ + public ByteBuffer get (byte[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the ByteBUFFER src + * into the buffer. Before the transfer, it checks if there is fewer than + * src.remaining() space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining bytes in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ByteBuffer put (ByteBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining()); + + if (src.remaining () > 0) + { + byte[] toPut = new byte [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the byte array src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining bytes in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ByteBuffer put (byte[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the byte array src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining bytes in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final ByteBuffer put (byte[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * byte array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the byte array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final byte[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with int arithmetic, + * where ** represents exponentiation, by this formula:
      + * s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1). + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + * + * @return the hash code + */ + public int hashCode () + { + int hashCode = get(position()) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof ByteBuffer) + { + return compareTo ((ByteBuffer) obj) == 0; + } + + return false; + } + + /** + * Compares two ByteBuffer objects. + * + * @exception ClassCastException If obj is not an object derived from + * ByteBuffer. + */ + public int compareTo (ByteBuffer other) + { + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + byte a = get(pos_this++); + byte b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public final ByteOrder order () + { + return endian; + } + + /** + * Modifies this buffer's byte order. + */ + public final ByteBuffer order (ByteOrder endian) + { + this.endian = endian; + return this; + } + + /** + * Reads the byte at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * bytes in this buffer. + */ + public abstract byte get (); + + /** + * Writes the byte at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * bytes in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ByteBuffer put (byte b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract byte get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ByteBuffer put (int index, byte b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ByteBuffer compact (); + + void shiftDown (int dst_offset, int src_offset, int count) + { + for (int i = 0; i < count; i++) + put(dst_offset + i, get(src_offset + i)); + } + + /** + * Tells whether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new ByteBuffer whose content is a shared + * subsequence of this buffer's content. + */ + public abstract ByteBuffer slice (); + + /** + * Creates a new ByteBuffer that shares this buffer's + * content. + */ + public abstract ByteBuffer duplicate (); + + /** + * Creates a new read-only ByteBuffer that shares this + * buffer's content. + */ + public abstract ByteBuffer asReadOnlyBuffer (); + + /** + * Creates a view of this byte buffer as a short buffer. + */ + public abstract ShortBuffer asShortBuffer (); + + /** + * Creates a view of this byte buffer as a char buffer. + */ + public abstract CharBuffer asCharBuffer (); + + /** + * Creates a view of this byte buffer as an integer buffer. + */ + public abstract IntBuffer asIntBuffer (); + + /** + * Creates a view of this byte buffer as a long buffer. + */ + public abstract LongBuffer asLongBuffer (); + + /** + * Creates a view of this byte buffer as a float buffer. + */ + public abstract FloatBuffer asFloatBuffer (); + + /** + * Creates a view of this byte buffer as a double buffer. + */ + public abstract DoubleBuffer asDoubleBuffer (); + + /** + * Relative get method for reading a character value. + * + * @exception BufferUnderflowException If there are fewer than two bytes + * remaining in this buffer. + */ + public abstract char getChar (); + + /** + * Relative put method for writing a character value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putChar (char value); + + /** + * Absolute get method for reading a character value. + * + * @exception IndexOutOfBoundsException If there are fewer than two bytes + * remaining in this buffer + */ + public abstract char getChar (int index); + + /** + * Absolute put method for writing a character value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus one. + */ + public abstract ByteBuffer putChar (int index, char value); + + /** + * Relative get method for reading a short value. + * + * @exception BufferUnderflowException If index is negative or not smaller + * than the buffer's limit, minus one. + */ + public abstract short getShort (); + + /** + * Relative put method for writing a short value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putShort (short value); + + /** + * Absolute get method for reading a short value. + * + * @exception IndexOutOfBoundsException If there are fewer than two bytes + * remaining in this buffer + */ + public abstract short getShort (int index); + + /** + * Absolute put method for writing a short value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus one. + */ + public abstract ByteBuffer putShort (int index, short value); + + /** + * Relative get method for reading an integer value. + * + * @exception BufferUnderflowException If there are fewer than four bytes + * remaining in this buffer. + */ + public abstract int getInt (); + + /** + * Relative put method for writing an integer value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putInt (int value); + + /** + * Absolute get method for reading an integer value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus three. + */ + public abstract int getInt (int index); + + /** + * Absolute put method for writing an integer value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus three. + */ + public abstract ByteBuffer putInt (int index, int value); + + /** + * Relative get method for reading a long value. + * + * @exception BufferUnderflowException If there are fewer than eight bytes + * remaining in this buffer. + */ + public abstract long getLong (); + + /** + * Relative put method for writing a long value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putLong (long value); + + /** + * Absolute get method for reading a long value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus seven. + */ + public abstract long getLong (int index); + + /** + * Absolute put method for writing a float value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus seven. + */ + public abstract ByteBuffer putLong (int index, long value); + + /** + * Relative get method for reading a float value. + * + * @exception BufferUnderflowException If there are fewer than four bytes + * remaining in this buffer. + */ + public abstract float getFloat (); + + /** + * Relative put method for writing a float value. + * + * @exception BufferOverflowException If there are fewer than four bytes + * remaining in this buffer. + */ + public abstract ByteBuffer putFloat (float value); + + /** + * Absolute get method for reading a float value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus three. + */ + public abstract float getFloat (int index); + + /** + * Relative put method for writing a float value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus three. + */ + public abstract ByteBuffer putFloat (int index, float value); + + /** + * Relative get method for reading a double value. + * + * @exception BufferUnderflowException If there are fewer than eight bytes + * remaining in this buffer. + */ + public abstract double getDouble (); + + /** + * Relative put method for writing a double value. + * + * @exception BufferOverflowException If this buffer's current position is + * not smaller than its limit. + */ + public abstract ByteBuffer putDouble (double value); + + /** + * Absolute get method for reading a double value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus seven. + */ + public abstract double getDouble (int index); + + /** + * Absolute put method for writing a double value. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit, minus seven. + */ + public abstract ByteBuffer putDouble (int index, double value); + + /** + * Returns a string summarizing the state of this buffer. + */ + public String toString () + { + return getClass ().getName () + + "[pos=" + position () + + " lim=" + limit () + + " cap=" + capacity () + "]"; + } +} diff --git a/libjava/classpath/java/nio/ByteBufferHelper.java b/libjava/classpath/java/nio/ByteBufferHelper.java new file mode 100644 index 000000000..aee007b50 --- /dev/null +++ b/libjava/classpath/java/nio/ByteBufferHelper.java @@ -0,0 +1,343 @@ +/* ByteBufferImpl.java -- + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @author Michael Koch (konqueror@gmx.de) + */ +final class ByteBufferHelper +{ + public static char getChar (ByteBuffer buffer, ByteOrder order) + { + return (char) getShort (buffer, order); + } + + public static void putChar (ByteBuffer buffer, char value, ByteOrder order) + { + putShort (buffer, (short) value, order); + } + + public static char getChar (ByteBuffer buffer, int index, ByteOrder order) + { + return (char) getShort (buffer, index, order); + } + + public static void putChar (ByteBuffer buffer, int index, + char value, ByteOrder order) + { + putShort (buffer, index, (short) value, order); + } + + public static short getShort (ByteBuffer buffer, ByteOrder order) + { + buffer.checkForUnderflow(2); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + return (short) ((buffer.get() & 0xff) + + (buffer.get() << 8)); + } + + return (short) ((buffer.get() << 8) + + (buffer.get() & 0xff)); + } + + public static void putShort (ByteBuffer buffer, short value, ByteOrder order) + { + buffer.checkForOverflow(2); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put ((byte) value); + buffer.put ((byte) (value >> 8)); + } + else + { + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) value); + } + } + + public static short getShort (ByteBuffer buffer, + int index, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + return (short) ((buffer.get (index) & 0xff) + + (buffer.get (++index) << 8)); + } + + return (short) ((buffer.get (index) << 8) + + (buffer.get (++index) & 0xff)); + } + + public static void putShort (ByteBuffer buffer, int index, + short value, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put (index, (byte) value); + buffer.put (++index, (byte) (value >> 8)); + } + else + { + buffer.put (index, (byte) (value >> 8)); + buffer.put (++index, (byte) value); + } + } + + public static int getInt (ByteBuffer buffer, ByteOrder order) + { + buffer.checkForUnderflow(4); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + return ((buffer.get() & 0xff) + + ((buffer.get() & 0xff) << 8) + + ((buffer.get() & 0xff) << 16) + + (buffer.get() << 24)); + } + + return (int) ((buffer.get() << 24) + + ((buffer.get() & 0xff) << 16) + + ((buffer.get() & 0xff) << 8) + + (buffer.get() & 0xff)); + } + + public static void putInt (ByteBuffer buffer, int value, ByteOrder order) + { + buffer.checkForOverflow(4); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put ((byte) value); + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) (value >> 16)); + buffer.put ((byte) (value >> 24)); + } + else + { + buffer.put ((byte) (value >> 24)); + buffer.put ((byte) (value >> 16)); + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) value); + } + } + + public static int getInt (ByteBuffer buffer, int index, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + return ((buffer.get (index) & 0xff) + + ((buffer.get (++index) & 0xff) << 8) + + ((buffer.get (++index) & 0xff) << 16) + + (buffer.get (++index) << 24)); + } + + return ((buffer.get (index) << 24) + + ((buffer.get (++index) & 0xff) << 16) + + ((buffer.get (++index) & 0xff) << 8) + + (buffer.get (++index) & 0xff)); + } + + public static void putInt (ByteBuffer buffer, int index, + int value, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put (index, (byte) value); + buffer.put (++index, (byte) (value >> 8)); + buffer.put (++index, (byte) (value >> 16)); + buffer.put (++index, (byte) (value >> 24)); + } + else + { + buffer.put (index, (byte) (value >> 24)); + buffer.put (++index, (byte) (value >> 16)); + buffer.put (++index, (byte) (value >> 8)); + buffer.put (++index, (byte) value); + } + } + + public static long getLong (ByteBuffer buffer, ByteOrder order) + { + buffer.checkForUnderflow(8); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + return ((buffer.get() & 0xff) + + (((buffer.get() & 0xff)) << 8) + + (((buffer.get() & 0xff)) << 16) + + (((buffer.get() & 0xffL)) << 24) + + (((buffer.get() & 0xffL)) << 32) + + (((buffer.get() & 0xffL)) << 40) + + (((buffer.get() & 0xffL)) << 48) + + (((long) buffer.get()) << 56)); + } + + return ((((long) buffer.get()) << 56) + + ((buffer.get() & 0xffL) << 48) + + ((buffer.get() & 0xffL) << 40) + + ((buffer.get() & 0xffL) << 32) + + ((buffer.get() & 0xffL) << 24) + + ((buffer.get() & 0xff) << 16) + + ((buffer.get() & 0xff) << 8) + + (buffer.get() & 0xff)); + } + + public static void putLong (ByteBuffer buffer, long value, ByteOrder order) + { + buffer.checkForOverflow(8); + + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put ((byte) value); + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) (value >> 16)); + buffer.put ((byte) (value >> 24)); + buffer.put ((byte) (value >> 32)); + buffer.put ((byte) (value >> 40)); + buffer.put ((byte) (value >> 48)); + buffer.put ((byte) (value >> 56)); + } + else + { + buffer.put ((byte) (value >> 56)); + buffer.put ((byte) (value >> 48)); + buffer.put ((byte) (value >> 40)); + buffer.put ((byte) (value >> 32)); + buffer.put ((byte) (value >> 24)); + buffer.put ((byte) (value >> 16)); + buffer.put ((byte) (value >> 8)); + buffer.put ((byte) value); + } + } + + public static long getLong (ByteBuffer buffer, int index, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + return ((buffer.get (index) & 0xff) + + ((buffer.get (++index) & 0xff) << 8) + + ((buffer.get (++index) & 0xff) << 16) + + ((buffer.get (++index) & 0xffL) << 24) + + ((buffer.get (++index) & 0xffL) << 32) + + ((buffer.get (++index) & 0xffL) << 40) + + ((buffer.get (++index) & 0xffL) << 48) + + (((long) buffer.get (++index)) << 56)); + } + + return ((((long) buffer.get (index)) << 56) + + ((buffer.get (++index) & 0xffL) << 48) + + ((buffer.get (++index) & 0xffL) << 40) + + ((buffer.get (++index) & 0xffL) << 32) + + ((buffer.get (++index) & 0xffL) << 24) + + ((buffer.get (++index) & 0xff) << 16) + + ((buffer.get (++index) & 0xff) << 8) + + (buffer.get (++index) & 0xff)); + } + + public static void putLong (ByteBuffer buffer, int index, + long value, ByteOrder order) + { + if (order == ByteOrder.LITTLE_ENDIAN) + { + buffer.put (index, (byte) value); + buffer.put (++index, (byte) (value >> 8)); + buffer.put (++index, (byte) (value >> 16)); + buffer.put (++index, (byte) (value >> 24)); + buffer.put (++index, (byte) (value >> 32)); + buffer.put (++index, (byte) (value >> 40)); + buffer.put (++index, (byte) (value >> 48)); + buffer.put (++index, (byte) (value >> 56)); + } + else + { + buffer.put (index, (byte) (value >> 56)); + buffer.put (++index, (byte) (value >> 48)); + buffer.put (++index, (byte) (value >> 40)); + buffer.put (++index, (byte) (value >> 32)); + buffer.put (++index, (byte) (value >> 24)); + buffer.put (++index, (byte) (value >> 16)); + buffer.put (++index, (byte) (value >> 8)); + buffer.put (++index, (byte) value); + } + } + + public static float getFloat (ByteBuffer buffer, ByteOrder order) + { + return Float.intBitsToFloat (getInt (buffer, order)); + } + + public static void putFloat (ByteBuffer buffer, float value, ByteOrder order) + { + putInt (buffer, Float.floatToRawIntBits (value), order); + } + + public static float getFloat (ByteBuffer buffer, int index, ByteOrder order) + { + return Float.intBitsToFloat (getInt (buffer, index, order)); + } + + public static void putFloat (ByteBuffer buffer, int index, + float value, ByteOrder order) + { + putInt (buffer, index, Float.floatToRawIntBits (value), order); + } + + public static double getDouble (ByteBuffer buffer, ByteOrder order) + { + return Double.longBitsToDouble (getLong (buffer, order)); + } + + public static void putDouble (ByteBuffer buffer, double value, ByteOrder order) + { + putLong (buffer, Double.doubleToRawLongBits (value), order); + } + + public static double getDouble (ByteBuffer buffer, int index, ByteOrder order) + { + return Double.longBitsToDouble (getLong (buffer, index, order)); + } + + public static void putDouble (ByteBuffer buffer, int index, + double value, ByteOrder order) + { + putLong (buffer, index, Double.doubleToRawLongBits (value), order); + } +} // ByteBufferHelper diff --git a/libjava/classpath/java/nio/ByteBufferImpl.java b/libjava/classpath/java/nio/ByteBufferImpl.java new file mode 100644 index 000000000..2bf319220 --- /dev/null +++ b/libjava/classpath/java/nio/ByteBufferImpl.java @@ -0,0 +1,374 @@ +/* ByteBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class ByteBufferImpl extends ByteBuffer +{ + private final boolean readOnly; + + ByteBufferImpl (byte[] buffer, int offset, int capacity, int limit, + int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark, null, buffer, offset); + this.readOnly = readOnly; + } + + public CharBuffer asCharBuffer () + { + return new CharViewBufferImpl (this, remaining() >> 1); + } + + public ShortBuffer asShortBuffer () + { + return new ShortViewBufferImpl (this, remaining() >> 1); + } + + public IntBuffer asIntBuffer () + { + return new IntViewBufferImpl (this, remaining() >> 2); + } + + public LongBuffer asLongBuffer () + { + return new LongViewBufferImpl (this, remaining() >> 3); + } + + public FloatBuffer asFloatBuffer () + { + return new FloatViewBufferImpl (this, remaining() >> 2); + } + + public DoubleBuffer asDoubleBuffer () + { + return new DoubleViewBufferImpl (this, remaining() >> 3); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public ByteBuffer slice () + { + return new ByteBufferImpl (backing_buffer, array_offset + position (), + remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public ByteBuffer duplicate () + { + return new ByteBufferImpl (backing_buffer, array_offset, capacity (), + limit (), position (), mark, isReadOnly ()); + } + + public ByteBuffer asReadOnlyBuffer () + { + return new ByteBufferImpl (backing_buffer, array_offset, capacity (), + limit (), position (), mark, true); + } + + void shiftDown (int dst_offset, int src_offset, int count) + { + System.arraycopy(backing_buffer, array_offset + src_offset, + backing_buffer, array_offset + dst_offset, + count); + } + + public ByteBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int pos = position(); + int n = limit() - pos; + if (n > 0) + shiftDown(0, pos, n); + position(n); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the byte at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * bytes in this buffer. + */ + public byte get () + { + if (pos >= limit) + throw new BufferUnderflowException(); + + return backing_buffer [(pos++) + array_offset]; + } + + /** + * Bulk get + */ + public ByteBuffer get (byte[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + if ( (limit - pos) < length) // check for overflow + throw new BufferUnderflowException(); + + System.arraycopy(backing_buffer, pos + array_offset, + dst, offset, length); + pos += length; + + return this; + } + + /** + * Relative bulk put(), overloads the ByteBuffer impl. + */ + public ByteBuffer put (byte[] src, int offset, int length) + { + if ( (limit - pos) < length) // check for overflow + throw new BufferOverflowException(); + checkArraySize(src.length, offset, length); + + System.arraycopy(src, offset, backing_buffer, pos + array_offset, length); + pos += length; + + return this; + } + + /** + * Relative put method. Writes value to the next position + * in the buffer. + * + * @exception BufferOverflowException If there is no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ByteBuffer put (byte value) + { + if (readOnly) + throw new ReadOnlyBufferException(); + if (pos >= limit) + throw new BufferOverflowException(); + + backing_buffer [(pos++) + array_offset] = value; + return this; + } + + /** + * Absolute get method. Reads the byte at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public byte get (int index) + { + checkIndex(index); + + return backing_buffer [index + array_offset]; + } + + /** + * Absolute put method. Writes value to position + * index in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ByteBuffer put (int index, byte value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index + array_offset] = value; + return this; + } + + public char getChar () + { + return ByteBufferHelper.getChar(this, order()); + } + + public ByteBuffer putChar (char value) + { + if (readOnly) + throw new ReadOnlyBufferException (); + if ( (limit-pos) < 2) + throw new BufferOverflowException(); + + if (endian == ByteOrder.LITTLE_ENDIAN) + { + backing_buffer [(pos++) + array_offset] = (byte)(value&0xFF); + backing_buffer [(pos++) + array_offset] = (byte)(value>>8); + } + else + { + backing_buffer [(pos++) + array_offset] = (byte)(value>>8); + backing_buffer [(pos++) + array_offset] = (byte)(value&0xFF); + } + return this; + } + + public char getChar (int index) + { + return ByteBufferHelper.getChar(this, index, order()); + } + + public ByteBuffer putChar (int index, char value) + { + ByteBufferHelper.putChar(this, index, value, order()); + return this; + } + + public short getShort () + { + return ByteBufferHelper.getShort(this, order()); + } + + public ByteBuffer putShort (short value) + { + ByteBufferHelper.putShort(this, value, order()); + return this; + } + + public short getShort (int index) + { + return ByteBufferHelper.getShort(this, index, order()); + } + + public ByteBuffer putShort (int index, short value) + { + ByteBufferHelper.putShort(this, index, value, order()); + return this; + } + + public int getInt () + { + return ByteBufferHelper.getInt(this, order()); + } + + public ByteBuffer putInt (int value) + { + ByteBufferHelper.putInt(this, value, order()); + return this; + } + + public int getInt (int index) + { + return ByteBufferHelper.getInt(this, index, order()); + } + + public ByteBuffer putInt (int index, int value) + { + ByteBufferHelper.putInt(this, index, value, order()); + return this; + } + + public long getLong () + { + return ByteBufferHelper.getLong(this, order()); + } + + public ByteBuffer putLong (long value) + { + ByteBufferHelper.putLong (this, value, order()); + return this; + } + + public long getLong (int index) + { + return ByteBufferHelper.getLong (this, index, order()); + } + + public ByteBuffer putLong (int index, long value) + { + ByteBufferHelper.putLong (this, index, value, order()); + return this; + } + + public float getFloat () + { + return ByteBufferHelper.getFloat (this, order()); + } + + public ByteBuffer putFloat (float value) + { + ByteBufferHelper.putFloat (this, value, order()); + return this; + } + + public float getFloat (int index) + { + return ByteBufferHelper.getFloat (this, index, order()); + } + + public ByteBuffer putFloat (int index, float value) + { + ByteBufferHelper.putFloat (this, index, value, order()); + return this; + } + + public double getDouble () + { + return ByteBufferHelper.getDouble (this, order()); + } + + public ByteBuffer putDouble (double value) + { + ByteBufferHelper.putDouble (this, value, order()); + return this; + } + + public double getDouble (int index) + { + return ByteBufferHelper.getDouble (this, index, order()); + } + + public ByteBuffer putDouble (int index, double value) + { + ByteBufferHelper.putDouble (this, index, value, order()); + return this; + } +} diff --git a/libjava/classpath/java/nio/ByteOrder.java b/libjava/classpath/java/nio/ByteOrder.java new file mode 100644 index 000000000..364aa1829 --- /dev/null +++ b/libjava/classpath/java/nio/ByteOrder.java @@ -0,0 +1,84 @@ +/* ByteOrder.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public final class ByteOrder +{ + /** + * Constant indicating big endian byte order. + */ + public static final ByteOrder BIG_ENDIAN = new ByteOrder(); + + /** + * Constant indicating little endian byte order. + */ + public static final ByteOrder LITTLE_ENDIAN = new ByteOrder(); + + /** + * Returns the native byte order of the platform currently running. + * + * @return the native byte order + */ + public static ByteOrder nativeOrder() + { + // Let this fail with an NPE when the property is not set correctly. + // Otherwise we risk that NIO is silently working wrong. + return (System.getProperty("gnu.cpu.endian").equals("big") + ? BIG_ENDIAN : LITTLE_ENDIAN); + } + + /** + * Returns a string representation of the byte order. + * + * @return the string + */ + public String toString() + { + return this == BIG_ENDIAN ? "BIG_ENDIAN" : "LITTLE_ENDIAN"; + } + + // This class can only be instantiated here. + private ByteOrder() + { + } +} diff --git a/libjava/classpath/java/nio/CharBuffer.java b/libjava/classpath/java/nio/CharBuffer.java new file mode 100644 index 000000000..a1729e6e7 --- /dev/null +++ b/libjava/classpath/java/nio/CharBuffer.java @@ -0,0 +1,532 @@ +/* CharBuffer.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +// GCJ LOCAL: Use RawData instead of gnu.classpath.Pointer +import gnu.gcj.RawData; + +import java.io.IOException; + +/** + * @since 1.4 + */ +public abstract class CharBuffer extends Buffer + implements Comparable, CharSequence, Readable, Appendable +{ + final int array_offset; + final char[] backing_buffer; + + CharBuffer (int capacity, int limit, int position, int mark, + RawData address, char[] backing_buffer, int array_offset) + { + super (capacity, limit, position, mark, address); + this.backing_buffer = backing_buffer; + this.array_offset = array_offset; + } + + /** + * Allocates a new CharBuffer object with a given capacity. + */ + public static CharBuffer allocate (int capacity) + { + return new CharBufferImpl (capacity); + } + + /** + * Wraps a char array into a CharBuffer + * object. + * + * @param array the array to wrap + * @param offset the offset of the region in the array to wrap + * @param length the length of the region in the array to wrap + * + * @return a new CharBuffer object + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final CharBuffer wrap(char[] array, int offset, int length) + { + return new CharBufferImpl(array, 0, array.length, offset + length, offset, + -1, false); + } + + /** + * Wraps a character sequence into a CharBuffer object. + * + * @param seq the sequence to wrap + * + * @return a new CharBuffer object + */ + public static final CharBuffer wrap(CharSequence seq) + { + return wrap(seq, 0, seq.length()); + } + + /** + * Wraps a character sequence into a CharBuffer object. + * + * @param seq the sequence to wrap + * @param start the index of the first character to wrap + * @param end the index of the first character not to wrap + * + * @return a new CharBuffer object + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final CharBuffer wrap(CharSequence seq, int start, int end) + { + return new CharSequenceBuffer(seq, start, end); + } + + /** + * Wraps a char array into a CharBuffer + * object. + * + * @param array the array to wrap + * + * @return a new CharBuffer object + */ + public static final CharBuffer wrap(char[] array) + { + return wrap(array, 0, array.length); + } + + /** + * This method transfers chars from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length chars remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first char + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * chars remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public CharBuffer get (char[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** @since 1.5 */ + public int read(CharBuffer buffer) throws IOException + { + // We want to call put(), so we don't manipulate the CharBuffer + // directly. + int rem = Math.min(buffer.remaining(), remaining()); + char[] buf = new char[rem]; + get(buf); + buffer.put(buf); + return rem; + } + + /** + * This method transfers chars from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * chars remaining in this buffer. + */ + public CharBuffer get (char[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the CharBUFFER src + * into the buffer. Before the transfer, it checks if there is fewer than + * src.remaining() space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining chars in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (CharBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining()); + + if (src.remaining () > 0) + { + char[] toPut = new char [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the char array src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining chars in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (char[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the char array src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining chars in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final CharBuffer put (char[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * char array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the char array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final char[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with int arithmetic, + * where ** represents exponentiation, by this formula:
      + * s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1). + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + */ + public int hashCode () + { + int hashCode = get(position()) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof CharBuffer) + { + return compareTo ((CharBuffer) obj) == 0; + } + + return false; + } + + /** + * Compares two CharBuffer objects. + * + * @exception ClassCastException If obj is not an object derived from + * CharBuffer. + */ + public int compareTo (CharBuffer other) + { + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + char a = get(pos_this++); + char b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the char at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * chars in this buffer. + */ + public abstract char get (); + + /** + * Writes the char at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * chars in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract CharBuffer put (char b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract char get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract CharBuffer put (int index, char b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract CharBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new CharBuffer whose content is a shared + * subsequence of this buffer's content. + */ + public abstract CharBuffer slice (); + + /** + * Creates a new CharBuffer that shares this buffer's + * content. + */ + public abstract CharBuffer duplicate (); + + /** + * Creates a new read-only CharBuffer that shares this + * buffer's content. + */ + public abstract CharBuffer asReadOnlyBuffer (); + + /** + * Returns the remaining content of the buffer as a string. + */ + public String toString () + { + if (hasArray ()) + return new String (array (), position (), length ()); + + char[] buf = new char [length ()]; + int pos = position (); + get (buf, 0, buf.length); + position (pos); + return new String (buf); + } + + /** + * Returns the length of the remaining chars in this buffer. + */ + public final int length () + { + return remaining (); + } + + /** + * Creates a new character buffer that represents the specified subsequence + * of this buffer, relative to the current position. + * + * @exception IndexOutOfBoundsException If the preconditions on start and + * end do not hold. + */ + public abstract CharSequence subSequence (int start, int length); + + /** + * Relative put method. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer. + * @exception IndexOutOfBoundsException If the preconditions on the start + * and end parameters do not hold. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (String str, int start, int length) + { + return put (str.toCharArray (), start, length); + } + + /** + * Relative put method. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final CharBuffer put (String str) + { + return put (str.toCharArray (), 0, str.length ()); + } + + /** + * Returns the character at position() + index. + * + * @exception IndexOutOfBoundsException If index is negative not smaller than + * remaining(). + */ + public final char charAt (int index) + { + if (index < 0 + || index >= remaining ()) + throw new IndexOutOfBoundsException (); + + return get (position () + index); + } + + /** @since 1.5 */ + public CharBuffer append(char c) + { + put(c); + return this; + } + + /** @since 1.5 */ + public CharBuffer append(CharSequence cs) + { + put(cs == null ? "null" : cs.toString()); + return this; + } + + /** @since 1.5 */ + public CharBuffer append(CharSequence cs, int start, int end) + { + put(cs == null ? "null" : cs.subSequence(start, end).toString()); + return this; + } +} diff --git a/libjava/classpath/java/nio/CharBufferImpl.java b/libjava/classpath/java/nio/CharBufferImpl.java new file mode 100644 index 000000000..8df9dbbea --- /dev/null +++ b/libjava/classpath/java/nio/CharBufferImpl.java @@ -0,0 +1,214 @@ +/* CharBufferImpl.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class CharBufferImpl extends CharBuffer +{ + private final boolean readOnly; + + CharBufferImpl (int capacity) + { + this (new char [capacity], 0, capacity, capacity, 0, -1, false); + } + + CharBufferImpl (char[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark, null, buffer, offset); + this.readOnly = readOnly; + } + + public CharBufferImpl (CharBufferImpl copy) + { + super (copy.capacity (), copy.limit (), copy.position (), 0, null, copy.backing_buffer, copy.array_offset); + this.readOnly = copy.isReadOnly (); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public CharBuffer slice () + { + return new CharBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public CharBuffer duplicate () + { + return new CharBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public CharBuffer asReadOnlyBuffer () + { + return new CharBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public CharBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int p = position(); + int n = limit() - p; + if (n > 0) + { + System.arraycopy(backing_buffer, array_offset + p, + backing_buffer, array_offset, n); + } + position(n); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + public CharSequence subSequence (int start, int end) + { + if (start < 0 + || start > length () + || end < start + || end > length ()) + throw new IndexOutOfBoundsException (); + + return new CharBufferImpl (backing_buffer, array_offset, capacity (), position () + end, position () + start, -1, isReadOnly ()); + } + + /** + * Reads the char at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * chars in this buffer. + */ + public char get () + { + if (pos >= limit) + throw new BufferUnderflowException(); + + return backing_buffer [(pos++) + array_offset]; + } + + /** + * Relative put method. Writes value to the next position + * in the buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (char value) + { + if (readOnly) + throw new ReadOnlyBufferException(); + if (pos >= limit) + throw new BufferOverflowException(); + + backing_buffer [(pos++) + array_offset] = value; + return this; + } + + /** + * Absolute get method. Reads the char at position + * index. + * + * @param index Position to read the char from. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public char get (int index) + { + checkIndex(index); + + return backing_buffer [index + array_offset]; + } + + /** + * Bulk get, overloaded for speed. + */ + public CharBuffer get (char[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + System.arraycopy(backing_buffer, pos + array_offset, + dst, offset, length); + pos += length; + return this; + } + + /** + * Bulk put, overloaded for speed. + */ + public CharBuffer put (char[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + System.arraycopy(src, offset, + backing_buffer, pos + array_offset, length); + pos += length; + return this; + } + + /** + * Absolute put method. Writes value to position + * index in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public CharBuffer put (int index, char value) + { + checkIndex(index); + checkIfReadOnly(); + + backing_buffer [index + array_offset] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/CharSequenceBuffer.java b/libjava/classpath/java/nio/CharSequenceBuffer.java new file mode 100644 index 000000000..95c37cc97 --- /dev/null +++ b/libjava/classpath/java/nio/CharSequenceBuffer.java @@ -0,0 +1,207 @@ +/* CharBuffer.java -- + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * A CharBuffer that wraps a {@link CharSequence}. + */ +final class CharSequenceBuffer + extends CharBuffer +{ + + /** + * The wrapped char sequence. + */ + private final CharSequence charSequence; + + /** + * Creates a new CharSequenceBuffer. + * + * @param charSeq the CharSequence to wrap + * @param capacity the capacity + * @param limit the limit + * @param position the position + * @param mark the mark + * @param offs the offset + */ + CharSequenceBuffer(CharSequence charSeq, int capacity, int limit, + int position, int mark, int offs) + { + super(capacity, limit, position, mark, null, null, offs); + this.charSequence = charSeq; + } + + /** + * Creates a new instance of CharSequenceBuffer, wrapping the specified + * {@link CharSequence}. + * + * @param charSeq the char sequence to wrap + * @param start the start index in the char sequence + * @param end the end index in the char sequence + */ + CharSequenceBuffer(CharSequence charSeq, int start, int end) + { + this(charSeq, charSeq.length(), end, start, -1, 0); + } + + /** + * Returns a read-only view of this buffer. + */ + public CharBuffer asReadOnlyBuffer() + { + return duplicate(); + } + + /** + * This buffer class is not writable by definition and thus throws + * a ReadOnlyBufferException here. + */ + public CharBuffer compact() + { + throw new ReadOnlyBufferException(); + } + + /** + * Returns a duplicate of this buffer. + * + * @return a duplicate of this buffer + */ + public CharBuffer duplicate() + { + return new CharSequenceBuffer(charSequence, capacity(), limit, pos, mark, 0); + } + + /** + * Returns the character at the current position. + * + * @return the character at the current position + */ + public char get() + { + if (pos >= limit) + throw new BufferUnderflowException(); + + return charSequence.charAt(array_offset + pos++); + } + + /** + * Returns the character at the specified position. + * + * @return the character at the specified position + */ + public char get(int index) + { + if (index < 0 || index >= limit) + throw new IndexOutOfBoundsException(); + + return charSequence.charAt(array_offset + index); + } + + /** + * Cannot be direct, return false here. + * + * @return false + */ + public boolean isDirect() + { + return false; + } + + /** + * Returns the byte order of this buffer. This is always the native byte + * order. + * + * @return the byte order of this buffer + */ + public ByteOrder order() + { + return ByteOrder.nativeOrder(); + } + + /** + * This buffer class is not writable by definition and thus throws + * a ReadOnlyBufferException here. + */ + public CharBuffer put(char b) + { + throw new ReadOnlyBufferException(); + } + + /** + * This buffer class is not writable by definition and thus throws + * a ReadOnlyBufferException here. + */ + public CharBuffer put(int index, char b) + { + throw new ReadOnlyBufferException(); + } + + /** + * Returns a slice of this buffer, exposing the current position and limit. + */ + public CharBuffer slice() + { + int newCapacity = limit - pos; + return new CharSequenceBuffer(charSequence, newCapacity, newCapacity, 0, + -1, pos); + } + + /** + * Returns a sub sequence from the specified start index and with the + * specified length. + * + * @param start the start index + * @param length the length of the sub sequence + */ + public CharSequence subSequence(int start, int length) + { + int begin = array_offset + start + pos; + return charSequence.subSequence(begin, begin + length); + } + + /** + * This kind of CharBuffer is read-only, so we return true + * here. + */ + public boolean isReadOnly() + { + return true; + } + +} diff --git a/libjava/classpath/java/nio/CharViewBufferImpl.java b/libjava/classpath/java/nio/CharViewBufferImpl.java new file mode 100644 index 000000000..72e344d66 --- /dev/null +++ b/libjava/classpath/java/nio/CharViewBufferImpl.java @@ -0,0 +1,187 @@ +/* CharViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +class CharViewBufferImpl extends CharBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private final int offset; + private final ByteBuffer bb; + private final boolean readOnly; + private final ByteOrder endian; + + CharViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, bb.position()) : null, + null, 0); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + } + + public CharViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, offset) : null, + null, 0); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + } + + /** + * Reads the char at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * chars in this buffer. + */ + public char get () + { + int p = position(); + char result = ByteBufferHelper.getChar(bb, (p << 1) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the char at position + * index. + * + * @param index Position to read the char from. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public char get (int index) + { + return ByteBufferHelper.getChar(bb, (index << 1) + offset, endian); + } + + public CharBuffer put (char value) + { + int p = position(); + ByteBufferHelper.putChar(bb, (p << 1) + offset, value, endian); + position(p + 1); + return this; + } + + public CharBuffer put (int index, char value) + { + ByteBufferHelper.putChar(bb, (index << 1) + offset, value, endian); + return this; + } + + public CharBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 2 * position(), 2 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public CharBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new CharViewBufferImpl (bb, (position () << 1) + offset, + remaining (), remaining (), 0, -1, + isReadOnly (), endian); + } + + CharBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new CharViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public CharBuffer duplicate () + { + return duplicate(readOnly); + } + + public CharBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public CharSequence subSequence (int start, int end) + { + if (start < 0 + || end < start + || end > length ()) + throw new IndexOutOfBoundsException (); + + return new CharViewBufferImpl (bb, array_offset, capacity (), + position () + end, position () + start, + -1, isReadOnly (), endian); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/DirectByteBufferImpl.java b/libjava/classpath/java/nio/DirectByteBufferImpl.java new file mode 100644 index 000000000..4f34732a8 --- /dev/null +++ b/libjava/classpath/java/nio/DirectByteBufferImpl.java @@ -0,0 +1,441 @@ +/* DirectByteBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +import gnu.classpath.Pointer; + +abstract class DirectByteBufferImpl extends ByteBuffer +{ + /** + * The owner is used to keep alive the object that actually owns the + * memory. There are three possibilities: + * 1) owner == this: We allocated the memory and we should free it, + * but *only* in finalize (if we've been sliced + * other objects will also have access to the + * memory). + * 2) owner == null: The byte buffer was created thru + * JNI.NewDirectByteBuffer. The JNI code is + * responsible for freeing the memory. + * 3) owner == some other object: The other object allocated the + * memory and should free it. + */ + private final Object owner; + + static final class ReadOnly extends DirectByteBufferImpl + { + ReadOnly(Object owner, Pointer address, + int capacity, int limit, + int position) + { + super(owner, address, capacity, limit, position); + } + + public ByteBuffer put(byte value) + { + throw new ReadOnlyBufferException (); + } + + public ByteBuffer put(int index, byte value) + { + throw new ReadOnlyBufferException (); + } + + public boolean isReadOnly() + { + return true; + } + } + + static final class ReadWrite extends DirectByteBufferImpl + { + ReadWrite(int capacity) + { + super(capacity); + } + + ReadWrite(Pointer address, int capacity) + { + super(address, capacity); + } + + ReadWrite(Object owner, Pointer address, + int capacity, int limit, + int position) + { + super(owner, address, capacity, limit, position); + } + + public boolean isReadOnly() + { + return false; + } + } + + DirectByteBufferImpl(int capacity) + { + super(capacity, capacity, 0, -1, + VMDirectByteBuffer.allocate(capacity), null, 0); + this.owner = this; + } + + DirectByteBufferImpl(Pointer address, int capacity) + { + super(capacity, capacity, 0, -1, address, null, 0); + this.owner = null; + } + + DirectByteBufferImpl(Object owner, Pointer address, + int capacity, int limit, + int position) + { + super(capacity, limit, position, -1, address, null, 0); + this.owner = owner; + } + + /** + * Allocates a new direct byte buffer. + */ + public static ByteBuffer allocate(int capacity) + { + return new DirectByteBufferImpl.ReadWrite(capacity); + } + + protected void finalize() throws Throwable + { + if (owner == this) + VMDirectByteBuffer.free(address); + } + + public byte get() + { + checkForUnderflow(); + + int pos = position(); + byte result = VMDirectByteBuffer.get(address, pos); + position(pos + 1); + return result; + } + + public byte get(int index) + { + checkIndex(index); + + return VMDirectByteBuffer.get(address, index); + } + + public ByteBuffer get(byte[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + int index = position(); + VMDirectByteBuffer.get(address, index, dst, offset, length); + position(index+length); + + return this; + } + + public ByteBuffer put(byte value) + { + checkForOverflow(); + + int pos = position(); + VMDirectByteBuffer.put(address, pos, value); + position(pos + 1); + return this; + } + + public ByteBuffer put(int index, byte value) + { + checkIndex(index); + + VMDirectByteBuffer.put(address, index, value); + return this; + } + + public ByteBuffer put (byte[] src, int offset, int length) + { + checkArraySize (src.length, offset, length); + checkForUnderflow (length); + int index = position (); + VMDirectByteBuffer.put (address, index, src, offset, length); + position (index + length); + + return this; + } + + void shiftDown(int dst_offset, int src_offset, int count) + { + VMDirectByteBuffer.shiftDown(address, dst_offset, src_offset, count); + } + + public ByteBuffer compact() + { + checkIfReadOnly(); + mark = -1; + int pos = position(); + if (pos > 0) + { + int count = remaining(); + VMDirectByteBuffer.shiftDown(address, 0, pos, count); + position(count); + limit(capacity()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public ByteBuffer slice() + { + int rem = remaining(); + if (isReadOnly()) + return new DirectByteBufferImpl.ReadOnly + (owner, VMDirectByteBuffer.adjustAddress(address, position()), + rem, rem, 0); + else + return new DirectByteBufferImpl.ReadWrite + (owner, VMDirectByteBuffer.adjustAddress(address, position()), + rem, rem, 0); + } + + private ByteBuffer duplicate(boolean readOnly) + { + int pos = position(); + if (this.mark != -1) + reset(); + int mark = position(); + position(pos); + DirectByteBufferImpl result; + if (readOnly) + result = new DirectByteBufferImpl.ReadOnly(owner, address, capacity(), + limit(), pos); + else + result = new DirectByteBufferImpl.ReadWrite(owner, address, capacity(), + limit(), pos); + + if (mark != pos) + { + result.position(mark); + result.mark(); + result.position(pos); + } + return result; + } + + public ByteBuffer duplicate() + { + return duplicate(isReadOnly()); + } + + public ByteBuffer asReadOnlyBuffer() + { + return duplicate(true); + } + + public boolean isDirect() + { + return true; + } + + public CharBuffer asCharBuffer() + { + return new CharViewBufferImpl(this, remaining() >> 1); + } + + public ShortBuffer asShortBuffer() + { + return new ShortViewBufferImpl(this, remaining() >> 1); + } + + public IntBuffer asIntBuffer() + { + return new IntViewBufferImpl(this, remaining() >> 2); + } + + public LongBuffer asLongBuffer() + { + return new LongViewBufferImpl(this, remaining() >> 3); + } + + public FloatBuffer asFloatBuffer() + { + return new FloatViewBufferImpl(this, remaining() >> 2); + } + + public DoubleBuffer asDoubleBuffer() + { + return new DoubleViewBufferImpl(this, remaining() >> 3); + } + + public char getChar() + { + return ByteBufferHelper.getChar(this, order()); + } + + public ByteBuffer putChar(char value) + { + ByteBufferHelper.putChar(this, value, order()); + return this; + } + + public char getChar(int index) + { + return ByteBufferHelper.getChar(this, index, order()); + } + + public ByteBuffer putChar(int index, char value) + { + ByteBufferHelper.putChar(this, index, value, order()); + return this; + } + + public short getShort() + { + return ByteBufferHelper.getShort(this, order()); + } + + public ByteBuffer putShort(short value) + { + ByteBufferHelper.putShort(this, value, order()); + return this; + } + + public short getShort(int index) + { + return ByteBufferHelper.getShort(this, index, order()); + } + + public ByteBuffer putShort(int index, short value) + { + ByteBufferHelper.putShort(this, index, value, order()); + return this; + } + + public int getInt() + { + return ByteBufferHelper.getInt(this, order()); + } + + public ByteBuffer putInt(int value) + { + ByteBufferHelper.putInt(this, value, order()); + return this; + } + + public int getInt(int index) + { + return ByteBufferHelper.getInt(this, index, order()); + } + + public ByteBuffer putInt(int index, int value) + { + ByteBufferHelper.putInt(this, index, value, order()); + return this; + } + + public long getLong() + { + return ByteBufferHelper.getLong(this, order()); + } + + public ByteBuffer putLong(long value) + { + ByteBufferHelper.putLong(this, value, order()); + return this; + } + + public long getLong(int index) + { + return ByteBufferHelper.getLong(this, index, order()); + } + + public ByteBuffer putLong(int index, long value) + { + ByteBufferHelper.putLong(this, index, value, order()); + return this; + } + + public float getFloat() + { + return ByteBufferHelper.getFloat(this, order()); + } + + public ByteBuffer putFloat(float value) + { + ByteBufferHelper.putFloat(this, value, order()); + return this; + } + + public float getFloat(int index) + { + return ByteBufferHelper.getFloat(this, index, order()); + } + + public ByteBuffer putFloat(int index, float value) + { + ByteBufferHelper.putFloat(this, index, value, order()); + return this; + } + + public double getDouble() + { + return ByteBufferHelper.getDouble(this, order()); + } + + public ByteBuffer putDouble(double value) + { + ByteBufferHelper.putDouble(this, value, order()); + return this; + } + + public double getDouble(int index) + { + return ByteBufferHelper.getDouble(this, index, order()); + } + + public ByteBuffer putDouble(int index, double value) + { + ByteBufferHelper.putDouble(this, index, value, order()); + return this; + } +} diff --git a/libjava/classpath/java/nio/DoubleBuffer.java b/libjava/classpath/java/nio/DoubleBuffer.java new file mode 100644 index 000000000..c17058c44 --- /dev/null +++ b/libjava/classpath/java/nio/DoubleBuffer.java @@ -0,0 +1,386 @@ +/* DoubleBuffer.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +// GCJ LOCAL: Change gnu.classpath.Pointer to RawData +import gnu.gcj.RawData; + +/** + * @since 1.4 + */ +public abstract class DoubleBuffer extends Buffer + implements Comparable +{ + final int array_offset; + final double[] backing_buffer; + + DoubleBuffer (int capacity, int limit, int position, int mark, + RawData address, double[] backing_buffer, int array_offset) + { + super (capacity, limit, position, mark, address); + this.backing_buffer = backing_buffer; + this.array_offset = array_offset; + } + + /** + * Allocates a new DoubleBuffer object with a given capacity. + */ + public static DoubleBuffer allocate (int capacity) + { + return new DoubleBufferImpl (capacity); + } + + /** + * Wraps a double array into a DoubleBuffer + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final DoubleBuffer wrap (double[] array, int offset, int length) + { + return new DoubleBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a double array into a DoubleBuffer + * object. + */ + public static final DoubleBuffer wrap (double[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers doubles from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length doubles remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first double + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * doubles remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public DoubleBuffer get (double[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers doubles from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * doubles remaining in this buffer. + */ + public DoubleBuffer get (double[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the DoubleBUFFER src + * into the buffer. Before the transfer, it checks if there is fewer than + * src.remaining() space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining doubles in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public DoubleBuffer put (DoubleBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining ()); + + if (src.remaining () > 0) + { + double[] toPut = new double [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the double array src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining doubles in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public DoubleBuffer put (double[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the double array src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining doubles in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final DoubleBuffer put (double[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * double array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the double array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final double[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with long arithmetic, + * where ** represents exponentiation, by this formula:
      + * s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1). + * Where s is the buffer data, in Double.doubleToLongBits() form + * Note that the hashcode is dependent on buffer content, + * and therefore is not useful if the buffer content may change. + * + * @return the hash code (casted to int) + */ + public int hashCode () + { + long hashCode = Double.doubleToLongBits(get(position())) + 31; + long multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (Double.doubleToLongBits(get(i)) + 30)*multiplier; + } + return ((int)hashCode); + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof DoubleBuffer) + { + return compareTo ((DoubleBuffer) obj) == 0; + } + + return false; + } + + /** + * Compares two DoubleBuffer objects. + * + * @exception ClassCastException If obj is not an object derived from + * DoubleBuffer. + */ + public int compareTo (DoubleBuffer other) + { + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + double a = get(pos_this++); + double b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the double at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * doubles in this buffer. + */ + public abstract double get (); + + /** + * Writes the double at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * doubles in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract DoubleBuffer put (double b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract double get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract DoubleBuffer put (int index, double b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract DoubleBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new DoubleBuffer whose content is a shared + * subsequence of this buffer's content. + */ + public abstract DoubleBuffer slice (); + + /** + * Creates a new DoubleBuffer that shares this buffer's + * content. + */ + public abstract DoubleBuffer duplicate (); + + /** + * Creates a new read-only DoubleBuffer that shares this + * buffer's content. + */ + public abstract DoubleBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/DoubleBufferImpl.java b/libjava/classpath/java/nio/DoubleBufferImpl.java new file mode 100644 index 000000000..160841353 --- /dev/null +++ b/libjava/classpath/java/nio/DoubleBufferImpl.java @@ -0,0 +1,173 @@ +/* DoubleBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class DoubleBufferImpl extends DoubleBuffer +{ + private final boolean readOnly; + + DoubleBufferImpl (int capacity) + { + this (new double [capacity], 0, capacity, capacity, 0, -1, false); + } + + DoubleBufferImpl (double[] buffer, int offset, int capacity, int limit, + int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark, null, buffer, offset); + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public DoubleBuffer slice () + { + return new DoubleBufferImpl (backing_buffer, array_offset + position (), + remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public DoubleBuffer duplicate () + { + return new DoubleBufferImpl (backing_buffer, array_offset, capacity (), + limit (), position (), mark, isReadOnly ()); + } + + public DoubleBuffer asReadOnlyBuffer () + { + return new DoubleBufferImpl (backing_buffer, array_offset, capacity (), + limit (), position (), mark, true); + } + + public DoubleBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int p = position(); + int n = limit() - p; + if (n > 0) + { + System.arraycopy(backing_buffer, array_offset + p, + backing_buffer, array_offset, n); + } + position(n); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the double at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * doubles in this buffer. + */ + public double get () + { + checkForUnderflow(); + + double result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes value to the next position + * in the buffer. + * + * @exception BufferOverflowException If there no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public DoubleBuffer put (double value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the double at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public double get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes value to position + * index in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public DoubleBuffer put (int index, double value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/DoubleViewBufferImpl.java b/libjava/classpath/java/nio/DoubleViewBufferImpl.java new file mode 100644 index 000000000..a78bf2738 --- /dev/null +++ b/libjava/classpath/java/nio/DoubleViewBufferImpl.java @@ -0,0 +1,170 @@ +/* DoubleViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class DoubleViewBufferImpl extends DoubleBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private final int offset; + private final ByteBuffer bb; + private final boolean readOnly; + private final ByteOrder endian; + + DoubleViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, bb.position()) : null, null, 0); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + } + + public DoubleViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, bb.position()) : null, null, 0); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + } + + /** + * Reads the double at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * doubles in this buffer. + */ + public double get () + { + int p = position(); + double result = ByteBufferHelper.getDouble(bb, (p << 3) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the double at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public double get (int index) + { + return ByteBufferHelper.getDouble(bb, (index << 3) + offset, endian); + } + + public DoubleBuffer put (double value) + { + int p = position(); + ByteBufferHelper.putDouble(bb, (p << 3) + offset, value, endian); + position(p + 1); + return this; + } + + public DoubleBuffer put (int index, double value) + { + ByteBufferHelper.putDouble(bb, (index << 3) + offset, value, endian); + return this; + } + + public DoubleBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 8 * position(), 8 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public DoubleBuffer slice () + { + return new DoubleViewBufferImpl (bb, (position () << 3) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + DoubleBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new DoubleViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public DoubleBuffer duplicate () + { + return duplicate(readOnly); + } + + public DoubleBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/FloatBuffer.java b/libjava/classpath/java/nio/FloatBuffer.java new file mode 100644 index 000000000..3459cf12b --- /dev/null +++ b/libjava/classpath/java/nio/FloatBuffer.java @@ -0,0 +1,386 @@ +/* FloatBuffer.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +// GCJ LOCAL: Change gnu.classpath.Pointer to RawData +import gnu.gcj.RawData; + +/** + * @since 1.4 + */ +public abstract class FloatBuffer extends Buffer + implements Comparable +{ + final int array_offset; + final float[] backing_buffer; + + FloatBuffer (int capacity, int limit, int position, int mark, + RawData address, float[] backing_buffer, int array_offset) + { + super (capacity, limit, position, mark, address); + this.backing_buffer = backing_buffer; + this.array_offset = array_offset; + } + + /** + * Allocates a new FloatBuffer object with a given capacity. + */ + public static FloatBuffer allocate (int capacity) + { + return new FloatBufferImpl (capacity); + } + + /** + * Wraps a float array into a FloatBuffer + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final FloatBuffer wrap (float[] array, int offset, int length) + { + return new FloatBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a float array into a FloatBuffer + * object. + */ + public static final FloatBuffer wrap (float[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers floats from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length floats remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first float + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * floats remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public FloatBuffer get (float[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers floats from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * floats remaining in this buffer. + */ + public FloatBuffer get (float[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the FloatBUFFER src + * into the buffer. Before the transfer, it checks if there is fewer than + * src.remaining() space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining floats in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public FloatBuffer put (FloatBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining()); + + if (src.remaining () > 0) + { + float[] toPut = new float [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the float array src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining floats in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public FloatBuffer put (float[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the float array src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining floats in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final FloatBuffer put (float[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * float array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the float array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final float[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with int arithmetic, + * where ** represents exponentiation, by this formula:
      + * s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1). + * Where s is the buffer data, in Float.floatToIntBits() form + * Note that the hashcode is dependent on buffer content, + * and therefore is not useful if the buffer content may change. + * + * @return the hash code + */ + public int hashCode () + { + int hashCode = Float.floatToIntBits(get(position())) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (Float.floatToIntBits(get(i)) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof FloatBuffer) + { + return compareTo ((FloatBuffer) obj) == 0; + } + + return false; + } + + /** + * Compares two FloatBuffer objects. + * + * @exception ClassCastException If obj is not an object derived from + * FloatBuffer. + */ + public int compareTo (FloatBuffer other) + { + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + float a = get(pos_this++); + float b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the float at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * floats in this buffer. + */ + public abstract float get (); + + /** + * Writes the float at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * floats in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract FloatBuffer put (float b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract float get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract FloatBuffer put (int index, float b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract FloatBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new FloatBuffer whose content is a shared + * subsequence of this buffer's content. + */ + public abstract FloatBuffer slice (); + + /** + * Creates a new FloatBuffer that shares this buffer's + * content. + */ + public abstract FloatBuffer duplicate (); + + /** + * Creates a new read-only FloatBuffer that shares this + * buffer's content. + */ + public abstract FloatBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/FloatBufferImpl.java b/libjava/classpath/java/nio/FloatBufferImpl.java new file mode 100644 index 000000000..603832d7a --- /dev/null +++ b/libjava/classpath/java/nio/FloatBufferImpl.java @@ -0,0 +1,170 @@ +/* FloatBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class FloatBufferImpl extends FloatBuffer +{ + private final boolean readOnly; + + FloatBufferImpl (int capacity) + { + this (new float [capacity], 0, capacity, capacity, 0, -1, false); + } + + FloatBufferImpl (float[] buffer, int offset, int capacity, int limit, + int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark, null, buffer, offset); + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public FloatBuffer slice () + { + return new FloatBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public FloatBuffer duplicate () + { + return new FloatBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public FloatBuffer asReadOnlyBuffer () + { + return new FloatBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public FloatBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int p = position(); + int n = limit() - p; + if (n > 0) + { + System.arraycopy(backing_buffer, array_offset + p, + backing_buffer, array_offset, n); + } + position(n); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the float at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * floats in this buffer. + */ + public float get () + { + checkForUnderflow(); + + float result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes value to the next position + * in the buffer. + * + * @exception BufferOverflowException If there no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public FloatBuffer put (float value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the float at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public float get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes value to position + * index in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public FloatBuffer put (int index, float value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/FloatViewBufferImpl.java b/libjava/classpath/java/nio/FloatViewBufferImpl.java new file mode 100644 index 000000000..663b65c4e --- /dev/null +++ b/libjava/classpath/java/nio/FloatViewBufferImpl.java @@ -0,0 +1,171 @@ +/* FloatViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class FloatViewBufferImpl extends FloatBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private final int offset; + private final ByteBuffer bb; + private final boolean readOnly; + private final ByteOrder endian; + + FloatViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, bb.position()):null, null, 0); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + } + + public FloatViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, offset):null, null, 0); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + } + + /** + * Reads the float at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * floats in this buffer. + */ + public float get () + { + int p = position(); + float result = ByteBufferHelper.getFloat(bb, (p << 2) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the float at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public float get (int index) + { + return ByteBufferHelper.getFloat(bb, (index << 2) + offset, endian); + } + + public FloatBuffer put (float value) + { + int p = position(); + ByteBufferHelper.putFloat(bb, (p << 2) + offset, value, endian); + position(p + 1); + return this; + } + + public FloatBuffer put (int index, float value) + { + ByteBufferHelper.putFloat(bb, (index << 2) + offset, value, endian); + return this; + } + + public FloatBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 4 * position(), 4 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public FloatBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new FloatViewBufferImpl (bb, (position () << 2) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + FloatBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new FloatViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public FloatBuffer duplicate () + { + return duplicate(readOnly); + } + + public FloatBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/IntBuffer.java b/libjava/classpath/java/nio/IntBuffer.java new file mode 100644 index 000000000..597886d9a --- /dev/null +++ b/libjava/classpath/java/nio/IntBuffer.java @@ -0,0 +1,387 @@ +/* IntBuffer.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +// GCJ LOCAL: Change gnu.classpath.Pointer to RawData +import gnu.gcj.RawData; + +/** + * @since 1.4 + */ +public abstract class IntBuffer extends Buffer + implements Comparable +{ + final int array_offset; + final int[] backing_buffer; + + IntBuffer (int capacity, int limit, int position, int mark, + RawData address, int[] backing_buffer, int array_offset) + { + super (capacity, limit, position, mark, address); + this.backing_buffer = backing_buffer; + this.array_offset = array_offset; + } + + /** + * Allocates a new IntBuffer object with a given capacity. + */ + public static IntBuffer allocate (int capacity) + { + return new IntBufferImpl (capacity); + } + + /** + * Wraps a int array into a IntBuffer + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final IntBuffer wrap (int[] array, int offset, int length) + { + return new IntBufferImpl (array, 0, array.length, offset + length, offset, + -1, false); + } + + /** + * Wraps a int array into a IntBuffer + * object. + */ + public static final IntBuffer wrap (int[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers ints from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length ints remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first int + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * ints remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public IntBuffer get (int[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers ints from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * ints remaining in this buffer. + */ + public IntBuffer get (int[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the IntBUFFER src + * into the buffer. Before the transfer, it checks if there is fewer than + * src.remaining() space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining ints in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public IntBuffer put (IntBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining ()); + + if (src.remaining () > 0) + { + int[] toPut = new int [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the int array src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining ints in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public IntBuffer put (int[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the int array src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining ints in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final IntBuffer put (int[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * int array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the int array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with int arithmetic, + * where ** represents exponentiation, by this formula:
      + * s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1). + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + * + * @return the hash code + */ + public int hashCode () + { + int hashCode = get(position()) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof IntBuffer) + { + return compareTo ((IntBuffer) obj) == 0; + } + + return false; + } + + /** + * Compares two IntBuffer objects. + * + * @exception ClassCastException If obj is not an object derived from + * IntBuffer. + */ + public int compareTo (IntBuffer other) + { + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + int a = get(pos_this++); + int b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the int at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * ints in this buffer. + */ + public abstract int get (); + + /** + * Writes the int at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * ints in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract IntBuffer put (int b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract int get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract IntBuffer put (int index, int b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract IntBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new IntBuffer whose content is a shared + * subsequence of this buffer's content. + */ + public abstract IntBuffer slice (); + + /** + * Creates a new IntBuffer that shares this buffer's + * content. + */ + public abstract IntBuffer duplicate (); + + /** + * Creates a new read-only IntBuffer that shares this + * buffer's content. + */ + public abstract IntBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/IntBufferImpl.java b/libjava/classpath/java/nio/IntBufferImpl.java new file mode 100644 index 000000000..1b31b0795 --- /dev/null +++ b/libjava/classpath/java/nio/IntBufferImpl.java @@ -0,0 +1,169 @@ +/* IntBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class IntBufferImpl extends IntBuffer +{ + private final boolean readOnly; + + IntBufferImpl (int capacity) + { + this (new int [capacity], 0, capacity, capacity, 0, -1, false); + } + + IntBufferImpl (int[] buffer, int offset, int capacity, int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark, null, buffer, offset); + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public IntBuffer slice () + { + return new IntBufferImpl (backing_buffer, array_offset + position (), remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public IntBuffer duplicate () + { + return new IntBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, isReadOnly ()); + } + + public IntBuffer asReadOnlyBuffer () + { + return new IntBufferImpl (backing_buffer, array_offset, capacity (), limit (), position (), mark, true); + } + + public IntBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int p = position(); + int n = limit() - p; + if (n > 0) + { + System.arraycopy(backing_buffer, array_offset + p, + backing_buffer, array_offset, n); + } + position(n); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the int at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * ints in this buffer. + */ + public int get () + { + checkForUnderflow(); + + int result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes value to the next position + * in the buffer. + * + * @exception BufferOverflowException If there no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public IntBuffer put (int value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the int at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public int get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes value to position + * index in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public IntBuffer put (int index, int value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/IntViewBufferImpl.java b/libjava/classpath/java/nio/IntViewBufferImpl.java new file mode 100644 index 000000000..dd3d6941a --- /dev/null +++ b/libjava/classpath/java/nio/IntViewBufferImpl.java @@ -0,0 +1,171 @@ +/* IntViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class IntViewBufferImpl extends IntBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private final int offset; + private final ByteBuffer bb; + private final boolean readOnly; + private final ByteOrder endian; + + IntViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, bb.position()):null, null, 0); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + } + + public IntViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, offset):null, null, 0); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + } + + /** + * Reads the int at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * ints in this buffer. + */ + public int get () + { + int p = position(); + int result = ByteBufferHelper.getInt(bb, (p << 2) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the int at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public int get (int index) + { + return ByteBufferHelper.getInt(bb, (index << 2) + offset, endian); + } + + public IntBuffer put (int value) + { + int p = position(); + ByteBufferHelper.putInt(bb, (p << 2) + offset, value, endian); + position(p + 1); + return this; + } + + public IntBuffer put (int index, int value) + { + ByteBufferHelper.putInt(bb, (index << 2) + offset, value, endian); + return this; + } + + public IntBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 4 * position(), 4 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public IntBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new IntViewBufferImpl (bb, (position () << 2) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + IntBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new IntViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public IntBuffer duplicate () + { + return duplicate(readOnly); + } + + public IntBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/InvalidMarkException.java b/libjava/classpath/java/nio/InvalidMarkException.java new file mode 100644 index 000000000..e13d76f1f --- /dev/null +++ b/libjava/classpath/java/nio/InvalidMarkException.java @@ -0,0 +1,54 @@ +/* InvalidMarkException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @author Michael Koch + * @since 1.4 + */ +public class InvalidMarkException extends IllegalStateException +{ + private static final long serialVersionUID = 1698329710438510774L; + + /** + * Creates the exception + */ + public InvalidMarkException () + { + } +} diff --git a/libjava/classpath/java/nio/LongBuffer.java b/libjava/classpath/java/nio/LongBuffer.java new file mode 100644 index 000000000..b6e94e8a8 --- /dev/null +++ b/libjava/classpath/java/nio/LongBuffer.java @@ -0,0 +1,386 @@ +/* LongBuffer.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +// GCJ LOCAL: Change gnu.classpath.Pointer to RawData +import gnu.gcj.RawData; + +/** + * @since 1.4 + */ +public abstract class LongBuffer extends Buffer + implements Comparable +{ + final int array_offset; + final long[] backing_buffer; + + LongBuffer (int capacity, int limit, int position, int mark, + RawData address, long[] backing_buffer, int array_offset) + { + super (capacity, limit, position, mark, address); + this.backing_buffer = backing_buffer; + this.array_offset = array_offset; + } + + /** + * Allocates a new LongBuffer object with a given capacity. + */ + public static LongBuffer allocate (int capacity) + { + return new LongBufferImpl (capacity); + } + + /** + * Wraps a long array into a LongBuffer + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final LongBuffer wrap (long[] array, int offset, int length) + { + return new LongBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a long array into a LongBuffer + * object. + */ + public static final LongBuffer wrap (long[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers longs from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length longs remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first long + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * longs remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public LongBuffer get (long[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers longs from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * longs remaining in this buffer. + */ + public LongBuffer get (long[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the LongBUFFER src + * into the buffer. Before the transfer, it checks if there is fewer than + * src.remaining() space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining longs in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public LongBuffer put (LongBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining ()); + + if (src.remaining () > 0) + { + long[] toPut = new long [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the long array src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining longs in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public LongBuffer put (long[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the long array src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining longs in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final LongBuffer put (long[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * long array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the long array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final long[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with long arithmetic, + * where ** represents exponentiation, by this formula:
      + * s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1). + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + * + * @return the hash code (casted to int) + */ + public int hashCode () + { + long hashCode = get(position()) + 31; + long multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return ((int)hashCode); + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof LongBuffer) + { + return compareTo ((LongBuffer) obj) == 0; + } + + return false; + } + + /** + * Compares two LongBuffer objects. + * + * @exception ClassCastException If obj is not an object derived from + * LongBuffer. + */ + public int compareTo (LongBuffer other) + { + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + long a = get(pos_this++); + long b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the long at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * longs in this buffer. + */ + public abstract long get (); + + /** + * Writes the long at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * longs in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract LongBuffer put (long b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract long get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract LongBuffer put (int index, long b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract LongBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new LongBuffer whose content is a shared + * subsequence of this buffer's content. + */ + public abstract LongBuffer slice (); + + /** + * Creates a new LongBuffer that shares this buffer's + * content. + */ + public abstract LongBuffer duplicate (); + + /** + * Creates a new read-only LongBuffer that shares this + * buffer's content. + */ + public abstract LongBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/LongBufferImpl.java b/libjava/classpath/java/nio/LongBufferImpl.java new file mode 100644 index 000000000..85502c194 --- /dev/null +++ b/libjava/classpath/java/nio/LongBufferImpl.java @@ -0,0 +1,173 @@ +/* LongBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class LongBufferImpl extends LongBuffer +{ + private final boolean readOnly; + + LongBufferImpl (int capacity) + { + this (new long [capacity], 0, capacity, capacity, 0, -1, false); + } + + LongBufferImpl (long[] buffer, int offset, int capacity, int limit, + int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark, null, buffer, offset); + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public LongBuffer slice () + { + return new LongBufferImpl (backing_buffer, array_offset + position (), + remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public LongBuffer duplicate () + { + return new LongBufferImpl (backing_buffer, array_offset, capacity (), limit (), + position (), mark, isReadOnly ()); + } + + public LongBuffer asReadOnlyBuffer () + { + return new LongBufferImpl (backing_buffer, array_offset, capacity (), limit (), + position (), mark, true); + } + + public LongBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int p = position(); + int n = limit() - p; + if (n > 0) + { + System.arraycopy(backing_buffer, array_offset + p, + backing_buffer, array_offset, n); + } + position(n); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the long at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * longs in this buffer. + */ + public long get () + { + checkForUnderflow(); + + long result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes value to the next position + * in the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public LongBuffer put (long value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the long at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public long get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes value to position + * index in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public LongBuffer put (int index, long value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/LongViewBufferImpl.java b/libjava/classpath/java/nio/LongViewBufferImpl.java new file mode 100644 index 000000000..a0a7cd36a --- /dev/null +++ b/libjava/classpath/java/nio/LongViewBufferImpl.java @@ -0,0 +1,171 @@ +/* LongViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class LongViewBufferImpl extends LongBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private final int offset; + private final ByteBuffer bb; + private final boolean readOnly; + private final ByteOrder endian; + + LongViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, bb.position()):null, null, 0); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + } + + public LongViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, offset):null, null, 0); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + } + + /** + * Reads the long at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * longs in this buffer. + */ + public long get () + { + int p = position(); + long result = ByteBufferHelper.getLong(bb, (p << 3) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the long at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public long get (int index) + { + return ByteBufferHelper.getLong(bb, (index << 3) + offset, endian); + } + + public LongBuffer put (long value) + { + int p = position(); + ByteBufferHelper.putLong(bb, (p << 3) + offset, value, endian); + position(p + 1); + return this; + } + + public LongBuffer put (int index, long value) + { + ByteBufferHelper.putLong(bb, (index << 3) + offset, value, endian); + return this; + } + + public LongBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 8 * position(), 8 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public LongBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new LongViewBufferImpl (bb, (position () << 3) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + LongBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new LongViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public LongBuffer duplicate () + { + return duplicate(readOnly); + } + + public LongBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/MappedByteBuffer.java b/libjava/classpath/java/nio/MappedByteBuffer.java new file mode 100644 index 000000000..f71e63051 --- /dev/null +++ b/libjava/classpath/java/nio/MappedByteBuffer.java @@ -0,0 +1,97 @@ +/* MappedByteBuffer.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +// GCJ LOCAL: Use RawData instead of gnu.classpath.Pointer +import gnu.gcj.RawData; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public abstract class MappedByteBuffer extends ByteBuffer +{ + MappedByteBuffer (int capacity, int limit, int position, int mark, + RawData address) + { + super (capacity, limit, position, mark, address, null, 0); + } + + void forceImpl() + { + } + + public final MappedByteBuffer force () + { + forceImpl(); + return this; + } + + boolean isLoadedImpl() + { + load(); + return true; + } + + public final boolean isLoaded () + { + return isLoadedImpl(); + } + + void loadImpl() + { + } + + public final MappedByteBuffer load () + { + loadImpl(); + return this; + } + + void unmapImpl () + { + forceImpl(); + } + + protected void finalize() + throws Throwable + { + unmapImpl(); + } +} diff --git a/libjava/classpath/java/nio/MappedByteBufferImpl.java b/libjava/classpath/java/nio/MappedByteBufferImpl.java new file mode 100644 index 000000000..58ea635cd --- /dev/null +++ b/libjava/classpath/java/nio/MappedByteBufferImpl.java @@ -0,0 +1,359 @@ +/* MappedByteBufferImpl.java -- + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +import gnu.classpath.Pointer; + +import java.io.IOException; + +final class MappedByteBufferImpl extends MappedByteBuffer +{ + private final boolean readOnly; + + /** Posix uses this for the pointer returned by mmap; + * Win32 uses it for the pointer returned by MapViewOfFile. */ + public Pointer implPtr; + /** Posix uses this for the actual length passed to mmap; + * Win32 uses it for the pointer returned by CreateFileMapping. */ + public long implLen; + + public MappedByteBufferImpl(Pointer address, int size, boolean readOnly) + throws IOException + { + super(size, size, 0, -1, address); + this.readOnly = readOnly; + } + + public boolean isReadOnly() + { + return readOnly; + } + + public byte get() + { + checkForUnderflow(); + + int pos = position(); + byte result = VMDirectByteBuffer.get(address, pos); + position(pos + 1); + return result; + } + + public ByteBuffer put(byte value) + { + checkIfReadOnly(); + checkForOverflow(); + + int pos = position(); + VMDirectByteBuffer.put(address, pos, value); + position(pos + 1); + return this; + } + + public byte get(int index) + { + checkIndex(index); + + return VMDirectByteBuffer.get(address, index); + } + + public ByteBuffer get(byte[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + int index = position(); + VMDirectByteBuffer.get(address, index, dst, offset, length); + position(index+length); + + return this; + } + + public ByteBuffer put(int index, byte value) + { + checkIfReadOnly(); + checkIndex(index); + + VMDirectByteBuffer.put(address, index, value); + return this; + } + + public ByteBuffer compact() + { + checkIfReadOnly(); + mark = -1; + int pos = position(); + if (pos > 0) + { + int count = remaining(); + // Call shiftDown method optimized for direct buffers. + VMDirectByteBuffer.shiftDown(address, 0, pos, count); + position(count); + limit(capacity()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public boolean isDirect() + { + return true; + } + + public ByteBuffer slice() + { + int rem = remaining(); + if (isReadOnly()) + return new DirectByteBufferImpl.ReadOnly + (this, VMDirectByteBuffer.adjustAddress(address, position()), + rem, rem, 0); + else + return new DirectByteBufferImpl.ReadWrite + (this, VMDirectByteBuffer.adjustAddress(address, position()), + rem, rem, 0); + } + + private ByteBuffer duplicate(boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + DirectByteBufferImpl result; + if (readOnly) + result = new DirectByteBufferImpl.ReadOnly(this, address, capacity(), + limit(), pos); + else + result = new DirectByteBufferImpl.ReadWrite(this, address, capacity(), + limit(), pos); + + if (mark != pos) + { + result.position(mark); + result.mark(); + result.position(pos); + } + return result; + } + + public ByteBuffer duplicate() + { + return duplicate(isReadOnly()); + } + + public ByteBuffer asReadOnlyBuffer() + { + return duplicate(true); + } + + public CharBuffer asCharBuffer() + { + return new CharViewBufferImpl(this, remaining() >> 1); + } + + public ShortBuffer asShortBuffer() + { + return new ShortViewBufferImpl(this, remaining() >> 1); + } + + public IntBuffer asIntBuffer() + { + return new IntViewBufferImpl(this, remaining() >> 2); + } + + public LongBuffer asLongBuffer() + { + return new LongViewBufferImpl(this, remaining() >> 3); + } + + public FloatBuffer asFloatBuffer() + { + return new FloatViewBufferImpl(this, remaining() >> 2); + } + + public DoubleBuffer asDoubleBuffer() + { + return new DoubleViewBufferImpl(this, remaining() >> 3); + } + + public char getChar() + { + return ByteBufferHelper.getChar(this, order()); + } + + public ByteBuffer putChar(char value) + { + ByteBufferHelper.putChar(this, value, order()); + return this; + } + + public char getChar(int index) + { + return ByteBufferHelper.getChar(this, index, order()); + } + + public ByteBuffer putChar(int index, char value) + { + ByteBufferHelper.putChar(this, index, value, order()); + return this; + } + + public short getShort() + { + return ByteBufferHelper.getShort(this, order()); + } + + public ByteBuffer putShort(short value) + { + ByteBufferHelper.putShort(this, value, order()); + return this; + } + + public short getShort(int index) + { + return ByteBufferHelper.getShort(this, index, order()); + } + + public ByteBuffer putShort(int index, short value) + { + ByteBufferHelper.putShort(this, index, value, order()); + return this; + } + + public int getInt() + { + return ByteBufferHelper.getInt(this, order()); + } + + public ByteBuffer putInt(int value) + { + ByteBufferHelper.putInt(this, value, order()); + return this; + } + + public int getInt(int index) + { + return ByteBufferHelper.getInt(this, index, order()); + } + + public ByteBuffer putInt(int index, int value) + { + ByteBufferHelper.putInt(this, index, value, order()); + return this; + } + + public long getLong() + { + return ByteBufferHelper.getLong(this, order()); + } + + public ByteBuffer putLong(long value) + { + ByteBufferHelper.putLong(this, value, order()); + return this; + } + + public long getLong(int index) + { + return ByteBufferHelper.getLong(this, index, order()); + } + + public ByteBuffer putLong(int index, long value) + { + ByteBufferHelper.putLong(this, index, value, order()); + return this; + } + + public float getFloat() + { + return ByteBufferHelper.getFloat(this, order()); + } + + public ByteBuffer putFloat(float value) + { + ByteBufferHelper.putFloat(this, value, order()); + return this; + } + + public float getFloat(int index) + { + return ByteBufferHelper.getFloat(this, index, order()); + } + + public ByteBuffer putFloat(int index, float value) + { + ByteBufferHelper.putFloat(this, index, value, order()); + return this; + } + + public double getDouble() + { + return ByteBufferHelper.getDouble(this, order()); + } + + public ByteBuffer putDouble(double value) + { + ByteBufferHelper.putDouble(this, value, order()); + return this; + } + + public double getDouble(int index) + { + return ByteBufferHelper.getDouble(this, index, order()); + } + + public ByteBuffer putDouble(int index, double value) + { + ByteBufferHelper.putDouble(this, index, value, order()); + return this; + } + + // NOTE: In libgcj these methods are implemented in natFileChannelXxx.cc, + // because they're small, and to put them next to FileChannelImpl::mapImpl. + native void unmapImpl(); + native boolean isLoadedImpl(); + // FIXME: Try to load all pages into memory. + native void loadImpl(); + + native void forceImpl(); +} diff --git a/libjava/classpath/java/nio/ReadOnlyBufferException.java b/libjava/classpath/java/nio/ReadOnlyBufferException.java new file mode 100644 index 000000000..ea58c375b --- /dev/null +++ b/libjava/classpath/java/nio/ReadOnlyBufferException.java @@ -0,0 +1,54 @@ +/* ReadOnlyBufferException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio; + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ReadOnlyBufferException extends UnsupportedOperationException +{ + private static final long serialVersionUID = - 1210063976496234090L; + + /** + * Creates the exception + */ + public ReadOnlyBufferException () + { + } +} diff --git a/libjava/classpath/java/nio/ShortBuffer.java b/libjava/classpath/java/nio/ShortBuffer.java new file mode 100644 index 000000000..e7cb12167 --- /dev/null +++ b/libjava/classpath/java/nio/ShortBuffer.java @@ -0,0 +1,387 @@ +/* ShortBuffer.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +// GCJ LOCAL: Use RawData instead of gnu.classpath.Pointer +import gnu.gcj.RawData; + +/** + * @since 1.4 + */ +public abstract class ShortBuffer extends Buffer + implements Comparable +{ + final int array_offset; + final short[] backing_buffer; + + ShortBuffer (int capacity, int limit, int position, + int mark, RawData address, short[] backing_buffer, + int array_offset) + { + super (capacity, limit, position, mark, address); + this.backing_buffer = backing_buffer; + this.array_offset = array_offset; + } + + /** + * Allocates a new ShortBuffer object with a given capacity. + */ + public static ShortBuffer allocate (int capacity) + { + return new ShortBufferImpl (capacity); + } + + /** + * Wraps a short array into a ShortBuffer + * object. + * + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + */ + public static final ShortBuffer wrap (short[] array, int offset, int length) + { + return new ShortBufferImpl (array, 0, array.length, offset + length, offset, -1, false); + } + + /** + * Wraps a short array into a ShortBuffer + * object. + */ + public static final ShortBuffer wrap (short[] array) + { + return wrap (array, 0, array.length); + } + + /** + * This method transfers shorts from this buffer into the given + * destination array. Before the transfer, it checks if there are fewer than + * length shorts remaining in this buffer. + * + * @param dst The destination array + * @param offset The offset within the array of the first short + * to be written; must be non-negative and no larger than dst.length. + * @param length The maximum number of bytes to be written to the given array; + * must be non-negative and no larger than dst.length - offset. + * + * @exception BufferUnderflowException If there are fewer than length + * shorts remaining in this buffer. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold. + */ + public ShortBuffer get (short[] dst, int offset, int length) + { + checkArraySize(dst.length, offset, length); + checkForUnderflow(length); + + for (int i = offset; i < offset + length; i++) + { + dst [i] = get (); + } + + return this; + } + + /** + * This method transfers shorts from this buffer into the given + * destination array. + * + * @param dst The byte array to write into. + * + * @exception BufferUnderflowException If there are fewer than dst.length + * shorts remaining in this buffer. + */ + public ShortBuffer get (short[] dst) + { + return get (dst, 0, dst.length); + } + + /** + * Writes the content of the the ShortBUFFER src + * into the buffer. Before the transfer, it checks if there is fewer than + * src.remaining() space remaining in this buffer. + * + * @param src The source data. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining shorts in the source buffer. + * @exception IllegalArgumentException If the source buffer is this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ShortBuffer put (ShortBuffer src) + { + if (src == this) + throw new IllegalArgumentException (); + + checkForOverflow(src.remaining ()); + + if (src.remaining () > 0) + { + short[] toPut = new short [src.remaining ()]; + src.get (toPut); + put (toPut); + } + + return this; + } + + /** + * Writes the content of the the short array src + * into the buffer. Before the transfer, it checks if there is fewer than + * length space remaining in this buffer. + * + * @param src The array to copy into the buffer. + * @param offset The offset within the array of the first byte to be read; + * must be non-negative and no larger than src.length. + * @param length The number of bytes to be read from the given array; + * must be non-negative and no larger than src.length - offset. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining shorts in the source array. + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ShortBuffer put (short[] src, int offset, int length) + { + checkArraySize(src.length, offset, length); + checkForOverflow(length); + + for (int i = offset; i < offset + length; i++) + put (src [i]); + + return this; + } + + /** + * Writes the content of the the short array src + * into the buffer. + * + * @param src The array to copy into the buffer. + * + * @exception BufferOverflowException If there is insufficient space in this + * buffer for the remaining shorts in the source array. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public final ShortBuffer put (short[] src) + { + return put (src, 0, src.length); + } + + /** + * Tells whether ot not this buffer is backed by an accessible + * short array. + */ + public final boolean hasArray () + { + return (backing_buffer != null + && !isReadOnly ()); + } + + /** + * Returns the short array that backs this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final short[] array () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return backing_buffer; + } + + /** + * Returns the offset within this buffer's backing array of the first element. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + * @exception UnsupportedOperationException If this buffer is not backed + * by an accessible array. + */ + public final int arrayOffset () + { + if (backing_buffer == null) + throw new UnsupportedOperationException (); + + checkIfReadOnly(); + + return array_offset; + } + + /** + * Calculates a hash code for this buffer. + * + * This is done with int arithmetic, + * where ** represents exponentiation, by this formula:
      + * s[position()] + 31 + (s[position()+1] + 30)*31**1 + ... + + * (s[limit()-1]+30)*31**(limit()-1). + * Where s is the buffer data. Note that the hashcode is dependent + * on buffer content, and therefore is not useful if the buffer + * content may change. + * + * @return the hash code + */ + public int hashCode () + { + int hashCode = get(position()) + 31; + int multiplier = 1; + for (int i = position() + 1; i < limit(); ++i) + { + multiplier *= 31; + hashCode += (get(i) + 30)*multiplier; + } + return hashCode; + } + + /** + * Checks if this buffer is equal to obj. + */ + public boolean equals (Object obj) + { + if (obj instanceof ShortBuffer) + { + return compareTo ((ShortBuffer) obj) == 0; + } + + return false; + } + + /** + * Compares two ShortBuffer objects. + * + * @exception ClassCastException If obj is not an object derived from + * ShortBuffer. + */ + public int compareTo (ShortBuffer other) + { + int num = Math.min(remaining(), other.remaining()); + int pos_this = position(); + int pos_other = other.position(); + + for (int count = 0; count < num; count++) + { + short a = get(pos_this++); + short b = other.get(pos_other++); + + if (a == b) + continue; + + if (a < b) + return -1; + + return 1; + } + + return remaining() - other.remaining(); + } + + /** + * Returns the byte order of this buffer. + */ + public abstract ByteOrder order (); + + /** + * Reads the short at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * shorts in this buffer. + */ + public abstract short get (); + + /** + * Writes the short at this buffer's current position, + * and then increments the position. + * + * @exception BufferOverflowException If there no remaining + * shorts in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ShortBuffer put (short b); + + /** + * Absolute get method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public abstract short get (int index); + + /** + * Absolute put method. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ShortBuffer put (int index, short b); + + /** + * Compacts this buffer. + * + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public abstract ShortBuffer compact (); + + /** + * Tells wether or not this buffer is direct. + */ + public abstract boolean isDirect (); + + /** + * Creates a new ShortBuffer whose content is a shared + * subsequence of this buffer's content. + */ + public abstract ShortBuffer slice (); + + /** + * Creates a new ShortBuffer that shares this buffer's + * content. + */ + public abstract ShortBuffer duplicate (); + + /** + * Creates a new read-only ShortBuffer that shares this + * buffer's content. + */ + public abstract ShortBuffer asReadOnlyBuffer (); +} diff --git a/libjava/classpath/java/nio/ShortBufferImpl.java b/libjava/classpath/java/nio/ShortBufferImpl.java new file mode 100644 index 000000000..4d66ec907 --- /dev/null +++ b/libjava/classpath/java/nio/ShortBufferImpl.java @@ -0,0 +1,173 @@ +/* ShortBufferImpl.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +/** + * This is a Heap memory implementation + */ +final class ShortBufferImpl extends ShortBuffer +{ + private final boolean readOnly; + + ShortBufferImpl (int capacity) + { + this (new short [capacity], 0, capacity, capacity, 0, -1, false); + } + + ShortBufferImpl (short[] buffer, int offset, int capacity, + int limit, int position, int mark, boolean readOnly) + { + super (capacity, limit, position, mark, null, buffer, offset); + this.readOnly = readOnly; + } + + public boolean isReadOnly () + { + return readOnly; + } + + public ShortBuffer slice () + { + return new ShortBufferImpl (backing_buffer, array_offset + position (), + remaining (), remaining (), 0, -1, isReadOnly ()); + } + + public ShortBuffer duplicate () + { + return new ShortBufferImpl (backing_buffer, array_offset, capacity (), + limit (), position (), mark, isReadOnly ()); + } + + public ShortBuffer asReadOnlyBuffer () + { + return new ShortBufferImpl (backing_buffer, array_offset, capacity (), limit (), + position (), mark, true); + } + + public ShortBuffer compact () + { + checkIfReadOnly(); + mark = -1; + int p = position(); + int n = limit() - p; + if (n > 0) + { + System.arraycopy(backing_buffer, array_offset + p, + backing_buffer, array_offset, n); + } + position(n); + limit(capacity()); + return this; + } + + public boolean isDirect () + { + return false; + } + + /** + * Reads the short at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * shorts in this buffer. + */ + public short get () + { + checkForUnderflow(); + + short result = backing_buffer [position ()]; + position (position () + 1); + return result; + } + + /** + * Relative put method. Writes value to the next position + * in the buffer. + * + * @exception BufferOverflowException If there no remaining + * space in this buffer. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ShortBuffer put (short value) + { + checkIfReadOnly(); + checkForOverflow(); + + backing_buffer [position ()] = value; + position (position () + 1); + return this; + } + + /** + * Absolute get method. Reads the short at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public short get (int index) + { + checkIndex(index); + + return backing_buffer [index]; + } + + /** + * Absolute put method. Writes value to position + * index in the buffer. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + * @exception ReadOnlyBufferException If this buffer is read-only. + */ + public ShortBuffer put (int index, short value) + { + checkIfReadOnly(); + checkIndex(index); + + backing_buffer [index] = value; + return this; + } + + public ByteOrder order () + { + return ByteOrder.nativeOrder (); + } +} diff --git a/libjava/classpath/java/nio/ShortViewBufferImpl.java b/libjava/classpath/java/nio/ShortViewBufferImpl.java new file mode 100644 index 000000000..f7ef3e346 --- /dev/null +++ b/libjava/classpath/java/nio/ShortViewBufferImpl.java @@ -0,0 +1,173 @@ +/* ShortViewBufferImpl.java -- + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio; + +final class ShortViewBufferImpl extends ShortBuffer +{ + /** Position in bb (i.e. a byte offset) where this buffer starts. */ + private final int offset; + private final ByteBuffer bb; + private final boolean readOnly; + private final ByteOrder endian; + + ShortViewBufferImpl (ByteBuffer bb, int capacity) + { + super (capacity, capacity, 0, -1, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, bb.position()):null, + null, 0); + this.bb = bb; + this.offset = bb.position(); + this.readOnly = bb.isReadOnly(); + this.endian = bb.order(); + } + + public ShortViewBufferImpl (ByteBuffer bb, int offset, int capacity, + int limit, int position, int mark, + boolean readOnly, ByteOrder endian) + { + super (capacity, limit, position, mark, bb.isDirect() ? + VMDirectByteBuffer.adjustAddress(bb.address, offset):null, + null, 0); + this.bb = bb; + this.offset = offset; + this.readOnly = readOnly; + this.endian = endian; + } + + /** + * Reads the short at this buffer's current position, + * and then increments the position. + * + * @exception BufferUnderflowException If there are no remaining + * shorts in this buffer. + */ + public short get () + { + int p = position(); + short result = ByteBufferHelper.getShort(bb, (p << 1) + offset, endian); + position(p + 1); + return result; + } + + /** + * Absolute get method. Reads the short at position + * index. + * + * @exception IndexOutOfBoundsException If index is negative or not smaller + * than the buffer's limit. + */ + public short get (int index) + { + return ByteBufferHelper.getShort(bb, (index << 1) + offset, endian); + } + + public ShortBuffer put (short value) + { + int p = position(); + ByteBufferHelper.putShort(bb, (p << 1) + offset, value, endian); + position(p + 1); + return this; + } + + public ShortBuffer put (int index, short value) + { + ByteBufferHelper.putShort(bb, (index << 1) + offset, value, endian); + return this; + } + + public ShortBuffer compact () + { + if (position () > 0) + { + int count = limit () - position (); + bb.shiftDown(offset, offset + 2 * position(), 2 * count); + position (count); + limit (capacity ()); + } + else + { + position(limit()); + limit(capacity()); + } + return this; + } + + public ShortBuffer slice () + { + // Create a sliced copy of this object that shares its content. + return new ShortViewBufferImpl (bb, (position () << 1) + offset, + remaining(), remaining(), 0, -1, + readOnly, endian); + } + + ShortBuffer duplicate (boolean readOnly) + { + int pos = position(); + reset(); + int mark = position(); + position(pos); + return new ShortViewBufferImpl (bb, offset, capacity(), limit(), + pos, mark, readOnly, endian); + } + + public ShortBuffer duplicate () + { + return duplicate(readOnly); + } + + public ShortBuffer asReadOnlyBuffer () + { + return duplicate(true); + } + + public boolean isReadOnly () + { + return readOnly; + } + + public boolean isDirect () + { + return bb.isDirect (); + } + + public ByteOrder order () + { + return endian; + } +} diff --git a/libjava/classpath/java/nio/channels/AlreadyConnectedException.java b/libjava/classpath/java/nio/channels/AlreadyConnectedException.java new file mode 100644 index 000000000..979486f06 --- /dev/null +++ b/libjava/classpath/java/nio/channels/AlreadyConnectedException.java @@ -0,0 +1,50 @@ +/* AlreadyConnectedException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +public class AlreadyConnectedException extends IllegalStateException +{ + private static final long serialVersionUID = - 7331895245053773357L; + + /** + * Creates the exception + */ + public AlreadyConnectedException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/AsynchronousCloseException.java b/libjava/classpath/java/nio/channels/AsynchronousCloseException.java new file mode 100644 index 000000000..31e41528b --- /dev/null +++ b/libjava/classpath/java/nio/channels/AsynchronousCloseException.java @@ -0,0 +1,55 @@ +/* AsynchronousCloseException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class AsynchronousCloseException extends ClosedChannelException +{ + private static final long serialVersionUID = 6891178312432313966L; + + /** + * Creates the exception + */ + public AsynchronousCloseException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/ByteChannel.java b/libjava/classpath/java/nio/channels/ByteChannel.java new file mode 100644 index 000000000..d7d7a451b --- /dev/null +++ b/libjava/classpath/java/nio/channels/ByteChannel.java @@ -0,0 +1,43 @@ +/* ByteChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +public interface ByteChannel extends ReadableByteChannel, + WritableByteChannel +{ +} diff --git a/libjava/classpath/java/nio/channels/CancelledKeyException.java b/libjava/classpath/java/nio/channels/CancelledKeyException.java new file mode 100644 index 000000000..d94e65053 --- /dev/null +++ b/libjava/classpath/java/nio/channels/CancelledKeyException.java @@ -0,0 +1,55 @@ +/* CancelledKeyException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class CancelledKeyException extends IllegalStateException +{ + private static final long serialVersionUID = - 8438032138028814268L; + + /** + * Creates the exception + */ + public CancelledKeyException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/Channel.java b/libjava/classpath/java/nio/channels/Channel.java new file mode 100644 index 000000000..33fcf3174 --- /dev/null +++ b/libjava/classpath/java/nio/channels/Channel.java @@ -0,0 +1,60 @@ +/* Channel.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.channels; + +import java.io.IOException; +import java.io.Closeable; + +public interface Channel extends Closeable +{ + /** + * Tells whether this channel is open or not + * + * @return trueif channel is open, + * false otherwise + */ + boolean isOpen(); + + /** + * Closes this channel + * + * @exception IOException If an error occurs + */ + void close() throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/Channels.java b/libjava/classpath/java/nio/channels/Channels.java new file mode 100644 index 000000000..382a3d705 --- /dev/null +++ b/libjava/classpath/java/nio/channels/Channels.java @@ -0,0 +1,144 @@ +/* Channels.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.channels; + +import gnu.java.nio.ChannelReader; +import gnu.java.nio.ChannelWriter; +import gnu.java.nio.InputStreamChannel; +import gnu.java.nio.OutputStreamChannel; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.UnsupportedCharsetException; + + +/** + * @since 1.4 + */ +public final class Channels +{ + /** + * This class isn't intended to be instantiated. + */ + private Channels() + { + // Do nothing here. + } + + /** + * Constructs a stream that reads bytes from the given channel. + */ + public static InputStream newInputStream(ReadableByteChannel ch) + { + return VMChannels.newInputStream(ch); + } + + /** + * Constructs a stream that writes bytes to the given channel. + */ + public static OutputStream newOutputStream(WritableByteChannel ch) + { + return VMChannels.newOutputStream(ch); + } + + /** + * Constructs a channel that reads bytes from the given stream. + */ + public static ReadableByteChannel newChannel(InputStream in) + { + return new InputStreamChannel(in); + } + + /** + * Constructs a channel that writes bytes to the given stream. + */ + public static WritableByteChannel newChannel(OutputStream out) + { + return new OutputStreamChannel(out); + } + + /** + * Constructs a reader that decodes bytes from the given channel using the + * given decoder. + */ + public static Reader newReader(ReadableByteChannel ch, CharsetDecoder dec, + int minBufferCap) + { + return new ChannelReader(ch, dec, minBufferCap); + } + + /** + * Constructs a reader that decodes bytes from the given channel according to + * the named charset. + * + * @exception UnsupportedCharsetException If no support for the named charset + * is available in this instance of the Java virtual machine. + */ + public static Reader newReader(ReadableByteChannel ch, String csName) + { + return newReader(ch, Charset.forName(csName).newDecoder(), -1); + } + + /** + * Constructs a writer that encodes characters using the given encoder and + * writes the resulting bytes to the given channel. + */ + public static Writer newWriter(WritableByteChannel ch, CharsetEncoder enc, + int minBufferCap) + { + return new ChannelWriter(ch, enc, minBufferCap); + } + + /** + * Constructs a writer that encodes characters according to the named charset + * and writes the resulting bytes to the given channel. + * + * @exception UnsupportedCharsetException If no support for the named charset + * is available in this instance of the Java virtual machine. + */ + public static Writer newWriter(WritableByteChannel ch, String csName) + { + return newWriter(ch, Charset.forName(csName).newEncoder(), -1); + } +} diff --git a/libjava/classpath/java/nio/channels/ClosedByInterruptException.java b/libjava/classpath/java/nio/channels/ClosedByInterruptException.java new file mode 100644 index 000000000..ade7e2a27 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ClosedByInterruptException.java @@ -0,0 +1,55 @@ +/* ClosedByInterruptException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ClosedByInterruptException extends AsynchronousCloseException +{ + private static final long serialVersionUID = - 4488191543534286750L; + + /** + * Creates the exception + */ + public ClosedByInterruptException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/ClosedChannelException.java b/libjava/classpath/java/nio/channels/ClosedChannelException.java new file mode 100644 index 000000000..ea896a0fd --- /dev/null +++ b/libjava/classpath/java/nio/channels/ClosedChannelException.java @@ -0,0 +1,57 @@ +/* ClosedChannelException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ClosedChannelException extends IOException +{ + private static final long serialVersionUID = 882777185433553857L; + + /** + * Creates the exception + */ + public ClosedChannelException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/ClosedSelectorException.java b/libjava/classpath/java/nio/channels/ClosedSelectorException.java new file mode 100644 index 000000000..9dfbd7bf8 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ClosedSelectorException.java @@ -0,0 +1,55 @@ +/* ClosedSelectorException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ClosedSelectorException extends IllegalStateException +{ + private static final long serialVersionUID = 6466297122317847835L; + + /** + * Creates the exception + */ + public ClosedSelectorException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/ConnectionPendingException.java b/libjava/classpath/java/nio/channels/ConnectionPendingException.java new file mode 100644 index 000000000..2e54ed61a --- /dev/null +++ b/libjava/classpath/java/nio/channels/ConnectionPendingException.java @@ -0,0 +1,55 @@ +/* ConnectionPendingException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class ConnectionPendingException extends IllegalStateException +{ + private static final long serialVersionUID = 2008393366501760879L; + + /** + * Creates the exception + */ + public ConnectionPendingException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/DatagramChannel.java b/libjava/classpath/java/nio/channels/DatagramChannel.java new file mode 100644 index 000000000..b8815f47c --- /dev/null +++ b/libjava/classpath/java/nio/channels/DatagramChannel.java @@ -0,0 +1,208 @@ +/* DatagramChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; + + +/** + * @since 1.4 + */ +public abstract class DatagramChannel extends AbstractSelectableChannel + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel +{ + /** + * Initializes the channel. + */ + protected DatagramChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Opens a datagram channel. + * + * @exception IOException If an error occurs + */ + public static DatagramChannel open() throws IOException + { + return SelectorProvider.provider().openDatagramChannel(); + } + + /** + * Reads data from this channel. + */ + public final long read(ByteBuffer[] dsts) throws IOException + { + long b = 0; + + for (int i = 0; i < dsts.length; i++) + b += read(dsts[i]); + + return b; + } + + /** + * Writes data to this channel. + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public final long write(ByteBuffer[] srcs) throws IOException + { + long b = 0; + + for (int i = 0; i < srcs.length; i++) + b += write(srcs[i]); + + return b; + } + + /** + * Connects this channel's socket. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the connect operation is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the read operation is in progress, thereby closing the + * channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an error occurs. + * @exception SecurityException If a security manager has been installed and + * it does not permit datagrams to be sent to the given address. + */ + public abstract DatagramChannel connect(SocketAddress remote) + throws IOException; + + /** + * Disonnects this channel's socket. + * + * @exception IOException If an error occurs + */ + public abstract DatagramChannel disconnect() throws IOException; + + /** + * Tells whether or not this channel's socket is connected. + * + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public abstract boolean isConnected(); + + /** + * Reads data from this channel. + */ + public abstract int read(ByteBuffer dst) throws IOException; + + /** + * Reads data from this channel. + * + * @exception IOException If an error occurs. + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public abstract long read(ByteBuffer[] dsts, int offset, int length) + throws IOException; + + /** + * Receives a datagram via this channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the connect operation is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the read operation is in progress, thereby closing the + * channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager has been installed and + * it does not permit datagrams to be sent to the given address. + */ + public abstract SocketAddress receive(ByteBuffer dst) + throws IOException; + + /** + * Sends a datagram via this channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the connect operation is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the read operation is in progress, thereby closing the + * channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager has been installed and + * it does not permit datagrams to be sent to the given address. + */ + public abstract int send(ByteBuffer src, SocketAddress target) + throws IOException; + + /** + * Retrieves the channel's socket. + */ + public abstract DatagramSocket socket(); + + /** + * Writes data to this channel. + * + * @exception IOException If an error occurs. + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public abstract int write(ByteBuffer src) throws IOException; + + /** + * Writes data to this channel. + * + * @exception IOException If an error occurs. + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public abstract long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; + + /** + * Retrieves the valid operations for this channel. + * + * @exception NotYetConnectedException The channel's socket is not connected. + */ + public final int validOps() + { + return SelectionKey.OP_READ | SelectionKey.OP_WRITE; + } +} diff --git a/libjava/classpath/java/nio/channels/FileChannel.java b/libjava/classpath/java/nio/channels/FileChannel.java new file mode 100644 index 000000000..8c8029fe7 --- /dev/null +++ b/libjava/classpath/java/nio/channels/FileChannel.java @@ -0,0 +1,357 @@ +/* FileChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.spi.AbstractInterruptibleChannel; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class FileChannel extends AbstractInterruptibleChannel + implements ByteChannel, GatheringByteChannel, ScatteringByteChannel +{ + public static class MapMode + { + int m; + public static final MapMode READ_ONLY = new MapMode(0); + public static final MapMode READ_WRITE = new MapMode(1); + public static final MapMode PRIVATE = new MapMode(2); + + /** + * Initializes the MapMode. + */ + MapMode(int a) + { + m = a; + } + + /** + * Returns a string representation of the MapMode object. + */ + public String toString() + { + if (this == READ_ONLY) + return "READ_ONLY"; + else if (this == READ_WRITE) + return "READ_WRITE"; + + return "PRIVATE"; + } + } + + /** + * Initializes the channel. + */ + protected FileChannel() + { + } + + /** + * Maps the file into the memory. + * + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If mode is READ_ONLY but this channel was + * not opened for reading. + * @exception NonWritableChannelException If mode is READ_WRITE or PRIVATE but this + * channel was not opened for writing. + */ + public abstract MappedByteBuffer map(MapMode mode, long position, long size) + throws IOException; + + /** + * Return the size of the file thus far + * + * @exception ClosedChannelException If this channel is closed. + */ + public abstract long size() throws IOException; + + /** + * Writes data to the channel. + * + * @exception IOException If an I/O error occurs. + */ + public final long write(ByteBuffer[] srcs) throws IOException + { + return write(srcs, 0, srcs.length); + } + + /** + * Writes data to the channel. + * + * @exception IOException If an I/O error occurs. + */ + public abstract int write(ByteBuffer src) throws IOException; + + /** + * Writes data to the channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the transfer is in progress, thereby closing both + * channels and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If position is negative. + * @exception IOException If an I/O error occurs. + * @exception NonWritableChannelException If this channel was not opened for + * writing. + */ + public abstract int write(ByteBuffer srcs, long position) + throws IOException; + + /** + * Writes data to the channel. + * + * @exception IOException If an I/O error occurs. + */ + public abstract long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; + + /** + * Reads data from the channel. + * + * @exception IOException If an I/O error occurs. + */ + public abstract long read(ByteBuffer[] dsts, int offset, int length) + throws IOException; + + /** + * Reads data from the channel. + * + * @exception IOException If an I/O error occurs. + */ + public final long read(ByteBuffer[] dsts) throws IOException + { + return read(dsts, 0, dsts.length); + } + + /** + * Reads data from the channel. + * + * @exception IOException If an I/O error occurs. + */ + public abstract int read(ByteBuffer dst) throws IOException; + + /** + * Reads data from the channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the transfer is in progress, thereby closing both + * channels and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If position is negative. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If this channel was not opened for + * reading. + */ + public abstract int read(ByteBuffer dst, long position) + throws IOException; + + /** + * Closes the channel. + * + * This is called from @see close. + * + * @exception IOException If an I/O error occurs. + */ + protected abstract void implCloseChannel() throws IOException; + + /** + * msync with the disk + * + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an I/O error occurs. + */ + public abstract void force(boolean metaData) throws IOException; + + /** + * Creates a file lock for the whole associated file. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedChannelException If this channel is closed. + * @exception FileLockInterruptionException If the invoking thread is + * interrupted while blocked in this method. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If shared is true and this channel + * was not opened for reading. + * @exception NonWritableChannelException If shared is false and this channel + * was not opened for writing. + * @exception OverlappingFileLockException If a lock that overlaps the + * requested region is already held by this Java virtual machine, or if + * another thread is already blocked in this method and is attempting to lock + * an overlapping region. + */ + public final FileLock lock() throws IOException + { + return lock(0, Long.MAX_VALUE, false); + } + + /** + * Creates a file lock for a region of the associated file. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedChannelException If this channel is closed. + * @exception FileLockInterruptionException If the invoking thread is + * interrupted while blocked in this method. + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception OverlappingFileLockException If a lock that overlaps the + * requested region is already held by this Java virtual machine, or if + * another thread is already blocked in this method and is attempting to lock + * an overlapping region. + * @exception NonReadableChannelException If shared is true and this channel + * was not opened for reading. + * @exception NonWritableChannelException If shared is false and this channel + * was not opened for writing. + */ + public abstract FileLock lock(long position, long size, boolean shared) + throws IOException; + + /** + * Tries to aqquire alock on the whole associated file. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an I/O error occurs. + * @exception OverlappingFileLockException If a lock that overlaps the + * requested region is already held by this Java virtual machine, or if + * another thread is already blocked in this method and is attempting to lock + * an overlapping region. + */ + public final FileLock tryLock() throws IOException + { + return tryLock(0, Long.MAX_VALUE, false); + } + + /** + * Tries to aqquire a lock on a region of the associated file. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception OverlappingFileLockException If a lock that overlaps the + * requested region is already held by this Java virtual machine, or if + * another thread is already blocked in this method and is attempting to lock + * an overlapping region. + */ + public abstract FileLock tryLock(long position, long size, boolean shared) + throws IOException; + + /** + * Returns the current position on the file. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an I/O error occurs. + */ + public abstract long position() throws IOException; + + /** + * Sets the position of the channel on the assoziated file. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If newPosition is negative. + * @exception IOException If an I/O error occurs. + */ + public abstract FileChannel position(long newPosition) + throws IOException; + + /** + * Transfers bytes from this channel's file to the given writable byte + * channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the transfer is in progress, thereby closing both + * channels and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If this channel was not opened for + * reading. + * @exception NonWritableChannelException If the target channel was not + * opened for writing. + */ + public abstract long transferTo(long position, long count, + WritableByteChannel target) + throws IOException; + + /** + * Transfers bytes from the given readable channel into this channel. + * + * @exception AsynchronousCloseException If another thread closes this channel + * while the transfer is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the transfer is in progress, thereby closing both + * channels and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If the preconditions on the parameters + * do not hold. + * @exception IOException If an I/O error occurs. + * @exception NonReadableChannelException If the source channel was not + * opened for reading. + * @exception NonWritableChannelException If this channel was not opened for + * writing. + */ + public abstract long transferFrom(ReadableByteChannel src, long position, + long count) throws IOException; + + /** + * Truncates the channel's file at size. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If size is negative. + * @exception IOException If an I/O error occurs. + * @exception NonWritableChannelException If this channel was not opened for + * writing. + */ + public abstract FileChannel truncate(long size) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/FileLock.java b/libjava/classpath/java/nio/channels/FileLock.java new file mode 100644 index 000000000..78210b34d --- /dev/null +++ b/libjava/classpath/java/nio/channels/FileLock.java @@ -0,0 +1,151 @@ +/* FileLock.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; + +/** + * @since 1.4 + */ +public abstract class FileLock +{ + private final FileChannel channel; + private final long position; + private final long size; + private final boolean shared; + + /** + * Initializes the file lock. + * + * @exception IllegalArgumentException If the preconditions on the parameters do not hold + */ + protected FileLock(FileChannel channel, long position, long size, + boolean shared) + { + if (position < 0 || size < 0) + throw new IllegalArgumentException(); + + this.channel = channel; + this.position = position; + this.size = size; + this.shared = shared; + } + + /** + * Tells whether or not this lock is valid. + */ + public abstract boolean isValid(); + + /** + * Releases this lock. + * + * @exception IOException If an error occurs + * @exception ClosedChannelException If the locked channel is no longer open. + */ + public abstract void release() throws IOException; + + /** + * Returns the file channel upon whose file this lock is held. + */ + public final FileChannel channel() + { + return channel; + } + + /** + * Tells whether this lock is shared. + */ + public final boolean isShared() + { + return shared; + } + + /** + * Tells whether or not this lock overlaps the given lock range. + */ + public final boolean overlaps(long position, long size) + { + if (position > this.position + this.size) + return false; + + if (position + size < this.position) + return false; + + return true; + } + + /** + * Returns the position within the file of the first byte of the + * locked region. + */ + public final long position() + { + return position; + } + + /** + * Returns the size of the locked region in bytes. + */ + public final long size() + { + return size; + } + + /** + * Returns a string describing the range, type, and validity of this lock. + */ + public final String toString() + { + CPStringBuilder buf = new CPStringBuilder(getClass().getName()); + buf.append("["); + buf.append(position); + buf.append(":"); + buf.append(size); + if (shared) + buf.append(" shared"); + else + buf.append(" exclusive"); + if (isValid()) + buf.append(" valid]"); + else + buf.append(" invalid]"); + return buf.toString(); + } +} diff --git a/libjava/classpath/java/nio/channels/FileLockInterruptionException.java b/libjava/classpath/java/nio/channels/FileLockInterruptionException.java new file mode 100644 index 000000000..f1024331e --- /dev/null +++ b/libjava/classpath/java/nio/channels/FileLockInterruptionException.java @@ -0,0 +1,57 @@ +/* FileLockInterruptionException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class FileLockInterruptionException extends IOException +{ + private static final long serialVersionUID = 7104080643653532383L; + + /** + * Creates the exception + */ + public FileLockInterruptionException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/GatheringByteChannel.java b/libjava/classpath/java/nio/channels/GatheringByteChannel.java new file mode 100644 index 000000000..822ea232a --- /dev/null +++ b/libjava/classpath/java/nio/channels/GatheringByteChannel.java @@ -0,0 +1,79 @@ +/* GatheringByteChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +public interface GatheringByteChannel extends WritableByteChannel +{ + /** + * Writes a sequence of bytes to this channel from a subsequence of + * the given buffers + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception IOException If an error occurs + * @exception NonWritableChannelException If this channel was not opened for + * writing + */ + long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; + + /** + * Writes a sequence of bytes to this channel from the given buffers + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IOException If an error occurs + * @exception NonWritableChannelException If this channel was not opened for + * writing + */ + long write(ByteBuffer[] srcs) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/IllegalBlockingModeException.java b/libjava/classpath/java/nio/channels/IllegalBlockingModeException.java new file mode 100644 index 000000000..f34d76382 --- /dev/null +++ b/libjava/classpath/java/nio/channels/IllegalBlockingModeException.java @@ -0,0 +1,59 @@ +/* IllegalBlockingModeException.java -- + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + * + * Written using JDK 1.4.1 Online API from Sun + * Status: JDK 1.4 complete + */ +public class IllegalBlockingModeException extends IllegalStateException +{ + private static final long serialVersionUID = - 3335774961855590474L; + + /** + * Creates the exception + */ + public IllegalBlockingModeException() + { + super(); + } +} diff --git a/libjava/classpath/java/nio/channels/IllegalSelectorException.java b/libjava/classpath/java/nio/channels/IllegalSelectorException.java new file mode 100644 index 000000000..01b22529c --- /dev/null +++ b/libjava/classpath/java/nio/channels/IllegalSelectorException.java @@ -0,0 +1,55 @@ +/* IllegalSelectorException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class IllegalSelectorException extends IllegalArgumentException +{ + private static final long serialVersionUID = - 8406323347253320987L; + + /** + * Creates the exception + */ + public IllegalSelectorException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/InterruptibleChannel.java b/libjava/classpath/java/nio/channels/InterruptibleChannel.java new file mode 100644 index 000000000..54122cead --- /dev/null +++ b/libjava/classpath/java/nio/channels/InterruptibleChannel.java @@ -0,0 +1,51 @@ +/* InterruptibleChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; + + +public interface InterruptibleChannel extends Channel +{ + /** + * Closes this channel + * + * @exception IOException If an error occurs + */ + void close() throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/NoConnectionPendingException.java b/libjava/classpath/java/nio/channels/NoConnectionPendingException.java new file mode 100644 index 000000000..8dcbdf695 --- /dev/null +++ b/libjava/classpath/java/nio/channels/NoConnectionPendingException.java @@ -0,0 +1,55 @@ +/* NoConnectionPendingException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NoConnectionPendingException extends IllegalStateException +{ + private static final long serialVersionUID = - 8296561183633134743L; + + /** + * Creates the exception + */ + public NoConnectionPendingException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/NonReadableChannelException.java b/libjava/classpath/java/nio/channels/NonReadableChannelException.java new file mode 100644 index 000000000..bf4f4a433 --- /dev/null +++ b/libjava/classpath/java/nio/channels/NonReadableChannelException.java @@ -0,0 +1,55 @@ +/* NonReadableChannelException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NonReadableChannelException extends IllegalStateException +{ + private static final long serialVersionUID = - 3200915679294993514L; + + /** + * Creates the exception + */ + public NonReadableChannelException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/NonWritableChannelException.java b/libjava/classpath/java/nio/channels/NonWritableChannelException.java new file mode 100644 index 000000000..98c86eae2 --- /dev/null +++ b/libjava/classpath/java/nio/channels/NonWritableChannelException.java @@ -0,0 +1,55 @@ +/* NonWritableChannelException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NonWritableChannelException extends IllegalStateException +{ + private static final long serialVersionUID = - 7071230488279011621L; + + /** + * Creates the exception + */ + public NonWritableChannelException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/NotYetBoundException.java b/libjava/classpath/java/nio/channels/NotYetBoundException.java new file mode 100644 index 000000000..74bf1ed7f --- /dev/null +++ b/libjava/classpath/java/nio/channels/NotYetBoundException.java @@ -0,0 +1,55 @@ +/* NotYetBoundException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NotYetBoundException extends IllegalStateException +{ + private static final long serialVersionUID = 4640999303950202242L; + + /** + * Creates the exception + */ + public NotYetBoundException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/NotYetConnectedException.java b/libjava/classpath/java/nio/channels/NotYetConnectedException.java new file mode 100644 index 000000000..083ff6c6d --- /dev/null +++ b/libjava/classpath/java/nio/channels/NotYetConnectedException.java @@ -0,0 +1,55 @@ +/* NotYetConnectedException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class NotYetConnectedException extends IllegalStateException +{ + private static final long serialVersionUID = 4697316551909513464L; + + /** + * Creates the exception + */ + public NotYetConnectedException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/OverlappingFileLockException.java b/libjava/classpath/java/nio/channels/OverlappingFileLockException.java new file mode 100644 index 000000000..aa2cedd9a --- /dev/null +++ b/libjava/classpath/java/nio/channels/OverlappingFileLockException.java @@ -0,0 +1,55 @@ +/* OverlappingFileLockException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class OverlappingFileLockException extends IllegalStateException +{ + private static final long serialVersionUID = 2047812138163068433L; + + /** + * Creates the exception + */ + public OverlappingFileLockException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/Pipe.java b/libjava/classpath/java/nio/channels/Pipe.java new file mode 100644 index 000000000..c7b04c88c --- /dev/null +++ b/libjava/classpath/java/nio/channels/Pipe.java @@ -0,0 +1,121 @@ +/* Pipe.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class Pipe +{ + public abstract static class SinkChannel extends AbstractSelectableChannel + implements WritableByteChannel, GatheringByteChannel + { + /** + * Initializes the channel. + */ + protected SinkChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Returns an operation set that is valid on this channel. + * + * The only valid operation on this channel is @see SelectionKey.OP_WRITE. + */ + public final int validOps() + { + return SelectionKey.OP_WRITE; + } + } + + public abstract static class SourceChannel extends AbstractSelectableChannel + implements ReadableByteChannel, ScatteringByteChannel + { + /** + * Initializes the channel. + */ + protected SourceChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Returns an operation set that is valid on this channel. + * + * The only valid operation on this channel is @see SelectionKey.OP_READ. + */ + public final int validOps() + { + return SelectionKey.OP_READ; + } + } + + /** + * Initializes the pipe. + */ + protected Pipe() + { + } + + /** + * Opens a pipe. + * + * @exception IOException If an error occurs + */ + public static Pipe open() throws IOException + { + return SelectorProvider.provider().openPipe(); + } + + /** + * Returns a pipe's sink channel. + */ + public abstract Pipe.SinkChannel sink(); + + /** + * Returns a pipe's source channel + */ + public abstract Pipe.SourceChannel source(); +} diff --git a/libjava/classpath/java/nio/channels/ReadableByteChannel.java b/libjava/classpath/java/nio/channels/ReadableByteChannel.java new file mode 100644 index 000000000..889662de0 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ReadableByteChannel.java @@ -0,0 +1,64 @@ +/* ReadableByteChannel.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +public interface ReadableByteChannel extends Channel +{ + /** + * Reads a sequence of bytes from this channel into the given buffer + * + * @param dst the buffer to put the read data into + * + * @return the numer of bytes read + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the read operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the read operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IOException If an error occurs + * @exception NonReadableChannelException If this channel was not opened for + * reading + */ + int read(ByteBuffer dst) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/ScatteringByteChannel.java b/libjava/classpath/java/nio/channels/ScatteringByteChannel.java new file mode 100644 index 000000000..5437ea158 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ScatteringByteChannel.java @@ -0,0 +1,79 @@ +/* ScatteringByteChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +public interface ScatteringByteChannel extends ReadableByteChannel +{ + /** + * Reads a sequence of bytes from this channel into a subsequence of the + * given buffers + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IndexOutOfBoundsException If the preconditions on the offset + * and length parameters do not hold + * @exception IOException If an error occurs + * @exception NonReadableChannelException If this channel was not opened for + * reading + */ + long read(ByteBuffer[] srcs, int offset, int length) + throws IOException; + + /** + * Reads a sequence of bytes from this channel into the given buffers + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IOException If an error occurs + * @exception NonReadableChannelException If this channel was not opened for + * reading + */ + long read(ByteBuffer[] srcs) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/SelectableChannel.java b/libjava/classpath/java/nio/channels/SelectableChannel.java new file mode 100644 index 000000000..70fa785ce --- /dev/null +++ b/libjava/classpath/java/nio/channels/SelectableChannel.java @@ -0,0 +1,140 @@ +/* SelectableChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.channels.spi.AbstractInterruptibleChannel; +import java.nio.channels.spi.SelectorProvider; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class SelectableChannel extends AbstractInterruptibleChannel +{ + /** + * Initializes the channel. + */ + protected SelectableChannel() + { + } + + /** + * Returns the lock of this channel. + */ + public abstract Object blockingLock(); + + /** + * Adjusts this channel's blocking mode. + * + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalBlockingModeException If block is true and this channel + * is registered with one or more selectors. + * @exception IOException If an error occurs. + */ + public abstract SelectableChannel configureBlocking(boolean block) + throws IOException; + + /** + * Tells whether this channel is blocking or not. + */ + public abstract boolean isBlocking(); + + /** + * Tells whether or not this channel is currently registered with + * any selectors. + */ + public abstract boolean isRegistered(); + + /** + * Retrieves the key representing the channel's registration with + * the given selector. + */ + public abstract SelectionKey keyFor(Selector sel); + + /** + * Returns the provider that created this channel. + */ + public abstract SelectorProvider provider(); + + /** + * Registers this channel with the given selector, + * returning a selection key. + * + * @exception CancelledKeyException If this channel is currently registered + * with the given selector but the corresponding key has already been cancelled + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If a bit in ops does not correspond + * to an operation that is supported by this channel, that is, if + * set & ~validOps() != 0. + * @exception IllegalBlockingModeException If block is true and this channel + * is registered with one or more selectors. + * @exception IllegalSelectorException If this channel was not created by + * the same provider as the given selector. + */ + public final SelectionKey register(Selector sel, int ops) + throws ClosedChannelException + { + return register(sel, ops, null); + } + + /** + * Registers this channel with the given selector, + * returning a selection key. + * + * @exception CancelledKeyException If this channel is currently registered + * with the given selector but the corresponding key has already been + * cancelled. + * @exception ClosedChannelException If this channel is closed. + * @exception IllegalArgumentException If a bit in ops does not correspond + * to an operation that is supported by this channel, that is, if + * set & ~validOps() != 0. + * @exception IllegalBlockingModeException If block is true and this channel + * is registered with one or more selectors. + * @exception IllegalSelectorException If this channel was not created by + * the same provider as the given selector. + */ + public abstract SelectionKey register(Selector sel, int ops, Object att) + throws ClosedChannelException; + + /** + * Returns a set of valid operations on this channel. + */ + public abstract int validOps(); +} diff --git a/libjava/classpath/java/nio/channels/SelectionKey.java b/libjava/classpath/java/nio/channels/SelectionKey.java new file mode 100644 index 000000000..9723faf52 --- /dev/null +++ b/libjava/classpath/java/nio/channels/SelectionKey.java @@ -0,0 +1,164 @@ +/* SelectionKey.java -- + Copyright (C) 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class SelectionKey +{ + public static final int OP_ACCEPT = 16; + public static final int OP_CONNECT = 8; + public static final int OP_READ = 1; + public static final int OP_WRITE = 4; + Object attached; + + /** + * Initializes the selection key. + */ + protected SelectionKey() + { + } + + /** + * Attaches obj to the key and returns the old attached object. + */ + public final synchronized Object attach(Object obj) + { + Object old = attached; + attached = obj; + return old; + } + + /** + * Returns the object attached to the key. + */ + public final synchronized Object attachment() + { + return attached; + } + + /** + * Tests if the channel attached to this key is ready to accept + * a new socket connection. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public final boolean isAcceptable() + { + return (readyOps() & OP_ACCEPT) != 0; + } + + /** + * Tests whether this key's channel has either finished, + * or failed to finish, its socket-connection operation. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public final boolean isConnectable() + { + return (readyOps() & OP_CONNECT) != 0; + } + + /** + * Tests if the channel attached to the key is readable. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public final boolean isReadable() + { + return (readyOps() & OP_READ) != 0; + } + + /** + * Tests if the channel attached to the key is writable. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public final boolean isWritable() + { + return (readyOps() & OP_WRITE) != 0; + } + + /** + * Requests that the registration of this key's channel with + * its selector be cancelled. + */ + public abstract void cancel(); + + /** + * return the channel attached to the key. + */ + public abstract SelectableChannel channel(); + + /** + * Returns the key's interest set. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public abstract int interestOps(); + + /** + * Sets this key's interest set to the given value. + * + * @exception CancelledKeyException If this key has been cancelled + * @exception IllegalArgumentException If a bit in the set does not + * correspond to an operation that is supported by this key's channel, + * that is, if set & ~(channel().validOps()) != 0 + */ + public abstract SelectionKey interestOps(int ops); + + /** + * Tells whether or not this key is valid. + */ + public abstract boolean isValid(); + + /** + * Retrieves this key's ready-operation set. + * + * @exception CancelledKeyException If this key has been cancelled + */ + public abstract int readyOps(); + + /** + * Returns the selector for which this key was created. + */ + public abstract Selector selector(); +} diff --git a/libjava/classpath/java/nio/channels/Selector.java b/libjava/classpath/java/nio/channels/Selector.java new file mode 100644 index 000000000..1c09db702 --- /dev/null +++ b/libjava/classpath/java/nio/channels/Selector.java @@ -0,0 +1,134 @@ +/* Selector.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.channels.spi.SelectorProvider; +import java.util.Set; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class Selector +{ + /** + * Initializes the selector. + */ + protected Selector() + { + } + + /** + * Opens a selector. + * + * @exception IOException If an error occurs + */ + public static Selector open() throws IOException + { + return SelectorProvider.provider().openSelector(); + } + + /** + * Closes the selector. + * + * @exception IOException If an error occurs + */ + public abstract void close() throws IOException; + + /** + * Tells whether the selector is open or not. + */ + public abstract boolean isOpen(); + + /** + * Returns this selector's key set. + * + * @exception ClosedSelectorException If this selector is closed. + */ + public abstract Set keys(); + + /** + * Returns the SelectorProvider that created the selector. + */ + public abstract SelectorProvider provider(); + + /** + * Selects a set of keys whose corresponding channels are ready + * for I/O operations. + * + * @exception ClosedSelectorException If this selector is closed. + * @exception IOException If an error occurs + */ + public abstract int select() throws IOException; + + /** + * Selects a set of keys whose corresponding channels are ready + * for I/O operations. + * + * @param timeout The timeout to use. + * + * @exception ClosedSelectorException If this selector is closed. + * @exception IllegalArgumentException If the timeout value is negative. + * @exception IOException If an error occurs + */ + public abstract int select(long timeout) throws IOException; + + /** + * Returns this selector's selected-key set. + * + * @exception ClosedSelectorException If this selector is closed. + */ + public abstract Set selectedKeys(); + + /** + * Selects a set of keys whose corresponding channels are ready + * for I/O operations. + * + * @exception ClosedSelectorException If this selector is closed. + * @exception IOException If an error occurs + */ + public abstract int selectNow() throws IOException; + + /** + * Causes the first selection operation that has not yet returned to + * return immediately. + */ + public abstract Selector wakeup(); +} diff --git a/libjava/classpath/java/nio/channels/ServerSocketChannel.java b/libjava/classpath/java/nio/channels/ServerSocketChannel.java new file mode 100644 index 000000000..105d17f94 --- /dev/null +++ b/libjava/classpath/java/nio/channels/ServerSocketChannel.java @@ -0,0 +1,98 @@ +/* ServerSocketChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.net.ServerSocket; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class ServerSocketChannel extends AbstractSelectableChannel +{ + /** + * Initializes this channel. + */ + protected ServerSocketChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Accepts a connection made to this channel's socket. + * + * @exception IOException If an error occurs + * @exception AsynchronousCloseException If another thread closes this + * channel while the accept operation is in progress. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the accept operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If the channel is closed. + * @exception NotYetBoundException If the channel's socket is not yet bound. + * @exception SecurityException If a security manager has been installed and + * it does not permit access to the remote endpoint of the new connection. + */ + public abstract SocketChannel accept() throws IOException; + + /** + * Retrieves the channels socket. + */ + public abstract ServerSocket socket(); + + /** + * Opens a server socket channel. + * + * @exception IOException If an error occurs + */ + public static ServerSocketChannel open() throws IOException + { + return SelectorProvider.provider().openServerSocketChannel(); + } + + /** + * Retrieves the valid operations for this channel. + */ + public final int validOps() + { + return SelectionKey.OP_ACCEPT; + } +} diff --git a/libjava/classpath/java/nio/channels/SocketChannel.java b/libjava/classpath/java/nio/channels/SocketChannel.java new file mode 100644 index 000000000..324c9816b --- /dev/null +++ b/libjava/classpath/java/nio/channels/SocketChannel.java @@ -0,0 +1,248 @@ +/* SocketChannel.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.channels; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; + +/** + * @author Michael Koch (konqueror@gmx.de) + * @since 1.4 + */ +public abstract class SocketChannel extends AbstractSelectableChannel + implements ByteChannel, ScatteringByteChannel, GatheringByteChannel +{ + /** + * Initializes this socket channel. + */ + protected SocketChannel(SelectorProvider provider) + { + super(provider); + } + + /** + * Opens a socket channel. + * + * @return the new SocketChannel object + * + * @exception IOException If an error occurs + */ + public static SocketChannel open() throws IOException + { + return SelectorProvider.provider().openSocketChannel(); + } + + /** + * Opens a channel and connects it to a remote address. + * + * @return the new SocketChannel object + * + * @exception AsynchronousCloseException If this channel is already connected. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the connect operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager has been installed and + * it does not permit access to the given remote endpoint. + * @exception UnresolvedAddressException If the given remote address is not + * fully resolved. + * @exception UnsupportedAddressTypeException If the type of the given remote + * address is not supported. + */ + public static SocketChannel open(SocketAddress remote) + throws IOException + { + SocketChannel ch = open(); + ch.connect(remote); + return ch; + } + + /** + * Reads data from the channel. + * + * @return the number of bytes read, zero is valid too, -1 if end of stream + * is reached + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public final long read(ByteBuffer[] dsts) throws IOException + { + long b = 0; + + for (int i = 0; i < dsts.length; i++) + b += read(dsts[i]); + + return b; + } + + /** + * Writes data to the channel. + * + * @return the number of bytes written, zero is valid too + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public final long write(ByteBuffer[] dsts) throws IOException + { + long b = 0; + + for (int i = 0; i < dsts.length; i++) + b += write(dsts[i]); + + return b; + } + + /** + * Retrieves the valid operations for this channel. + * + * @return the valid operations + */ + public final int validOps() + { + return SelectionKey.OP_CONNECT | SelectionKey.OP_READ + | SelectionKey.OP_WRITE; + } + + /** + * Reads data from the channel. + * + * @return the number of bytes read, zero is valid too, -1 if end of stream + * is reached + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public abstract int read(ByteBuffer dst) throws IOException; + + /** + * Connects the channel's socket to the remote address. + * + * @return true if the channel got successfully connected, + * false if the channel is in non-blocking mode and connection + * operation is still in progress. + * + * @exception AlreadyConnectedException If this channel is already connected. + * @exception AsynchronousCloseException If this channel is already connected. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the connect operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception ConnectionPendingException If a non-blocking connection + * operation is already in progress on this channel. + * @exception IOException If an error occurs + * @exception SecurityException If a security manager has been installed and + * it does not permit access to the given remote endpoint. + * @exception UnresolvedAddressException If the given remote address is not + * fully resolved. + * @exception UnsupportedAddressTypeException If the type of the given remote + * address is not supported. + */ + public abstract boolean connect(SocketAddress remote) + throws IOException; + + /** + * Finishes the process of connecting a socket channel. + * + * @exception AsynchronousCloseException If this channel is already connected. + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the connect operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status. + * @exception ClosedChannelException If this channel is closed. + * @exception IOException If an error occurs + * @exception NoConnectionPendingException If this channel is not connected + * and a connection operation has not been initiated. + */ + public abstract boolean finishConnect() throws IOException; + + /** + * Tells whether or not the channel's socket is connected. + */ + public abstract boolean isConnected(); + + /** + * Tells whether or not a connection operation is in progress on this channel. + */ + public abstract boolean isConnectionPending(); + + /** + * Reads data from the channel. + * + * @return the number of bytes read, zero is valid too, -1 if end of stream + * is reached + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public abstract long read(ByteBuffer[] dsts, int offset, int length) + throws IOException; + + /** + * Retrieves the channel's socket. + * + * @return the socket + */ + public abstract Socket socket(); + + /** + * Writes data to the channel. + * + * @return the number of bytes written, zero is valid too + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public abstract int write(ByteBuffer src) throws IOException; + + /** + * Writes data to the channel. + * + * @return the number of bytes written, zero is valid too + * + * @exception IOException If an error occurs + * @exception NotYetConnectedException If this channel is not yet connected. + */ + public abstract long write(ByteBuffer[] srcs, int offset, int length) + throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/UnresolvedAddressException.java b/libjava/classpath/java/nio/channels/UnresolvedAddressException.java new file mode 100644 index 000000000..7a591709b --- /dev/null +++ b/libjava/classpath/java/nio/channels/UnresolvedAddressException.java @@ -0,0 +1,55 @@ +/* UnresolvedAddressException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class UnresolvedAddressException extends IllegalArgumentException +{ + private static final long serialVersionUID = 6136959093620794148L; + + /** + * Creates the exception + */ + public UnresolvedAddressException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/UnsupportedAddressTypeException.java b/libjava/classpath/java/nio/channels/UnsupportedAddressTypeException.java new file mode 100644 index 000000000..759d0964c --- /dev/null +++ b/libjava/classpath/java/nio/channels/UnsupportedAddressTypeException.java @@ -0,0 +1,55 @@ +/* UnsupportedAddressTypeException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public class UnsupportedAddressTypeException extends IllegalArgumentException +{ + private static final long serialVersionUID = - 2964323842829700493L; + + /** + * Creates the exception + */ + public UnsupportedAddressTypeException() + { + } +} diff --git a/libjava/classpath/java/nio/channels/WritableByteChannel.java b/libjava/classpath/java/nio/channels/WritableByteChannel.java new file mode 100644 index 000000000..3845723bc --- /dev/null +++ b/libjava/classpath/java/nio/channels/WritableByteChannel.java @@ -0,0 +1,60 @@ +/* WritableByteChannel.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels; + +import java.io.IOException; +import java.nio.ByteBuffer; + + +public interface WritableByteChannel extends Channel +{ + /** + * Writes a sequence of bytes to this channel from the given buffer + * + * @exception AsynchronousCloseException If another thread closes this + * channel while the write operation is in progress + * @exception ClosedByInterruptException If another thread interrupts the + * current thread while the write operation is in progress, thereby closing + * the channel and setting the current thread's interrupt status + * @exception ClosedChannelException If this channel is closed + * @exception IOException If an error occurs + * @exception NonWritableChannelException If this channel was not opened for + * writing + */ + int write(ByteBuffer src) throws IOException; +} diff --git a/libjava/classpath/java/nio/channels/package.html b/libjava/classpath/java/nio/channels/package.html new file mode 100644 index 000000000..4e2bbc9f6 --- /dev/null +++ b/libjava/classpath/java/nio/channels/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.nio.channels + + +

      Classes for creating, selecting and non-blocking reading and writing of +buffers from files and sockets.

      + + + diff --git a/libjava/classpath/java/nio/channels/spi/AbstractInterruptibleChannel.java b/libjava/classpath/java/nio/channels/spi/AbstractInterruptibleChannel.java new file mode 100644 index 000000000..b1ed7c7f2 --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/AbstractInterruptibleChannel.java @@ -0,0 +1,119 @@ +/* AbstractInterruptibleChannel.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels.spi; + +import java.io.IOException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.Channel; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.InterruptibleChannel; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class AbstractInterruptibleChannel + implements Channel, InterruptibleChannel +{ + private boolean closed; + + /** + * Initializes the channel. + */ + protected AbstractInterruptibleChannel() + { + } + + /** + * Marks the beginning of an I/O operation that might block indefinitely. + */ + protected final void begin() + { + } + + /** + * Closes the channel. + * + * @exception IOException If an error occurs + */ + public final void close() throws IOException + { + if (! closed) + { + closed = true; + implCloseChannel(); + } + } + + /** + * Marks the end of an I/O operation that might block indefinitely. + * + * @param completed true if the task completed successfully, + * false otherwise + * + * @exception AsynchronousCloseException If the channel was asynchronously + * closed. + * @exception ClosedByInterruptException If the thread blocked in the + * I/O operation was interrupted. + */ + protected final void end(boolean completed) + throws AsynchronousCloseException + { + // FIXME: check more here. + + if (closed) throw new AsynchronousCloseException(); + } + + /** + * Closes the channel. + * + * @exception IOException If an error occurs + */ + protected abstract void implCloseChannel() throws IOException; + + /** + * Tells whether or not this channel is open. + * + * @return true if the channel is open, false otherwise + */ + public final boolean isOpen() + { + return ! closed; + } +} diff --git a/libjava/classpath/java/nio/channels/spi/AbstractSelectableChannel.java b/libjava/classpath/java/nio/channels/spi/AbstractSelectableChannel.java new file mode 100644 index 000000000..97ed685b8 --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/AbstractSelectableChannel.java @@ -0,0 +1,271 @@ +/* AbstractSelectableChannel.java + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.channels.spi; + +import java.io.IOException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.IllegalBlockingModeException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; + +public abstract class AbstractSelectableChannel extends SelectableChannel +{ + private boolean blocking = true; + private Object LOCK = new Object(); + private SelectorProvider provider; + private LinkedList keys = new LinkedList(); + + /** + * Initializes the channel + * + * @param provider the provider that created this channel + */ + protected AbstractSelectableChannel(SelectorProvider provider) + { + this.provider = provider; + } + + /** + * Retrieves the object upon which the configureBlocking and register + * methods synchronize. + * + * @return the blocking lock + */ + public final Object blockingLock() + { + return LOCK; + } + + /** + * Adjusts this channel's blocking mode. + * + * @param blocking true if blocking should be enabled, false otherwise + * + * @return this channel + * + * @exception IOException If an error occurs + */ + public final SelectableChannel configureBlocking(boolean blocking) + throws IOException + { + synchronized (blockingLock()) + { + if (this.blocking != blocking) + { + implConfigureBlocking(blocking); + this.blocking = blocking; + } + } + + return this; + } + + /** + * Closes this channel. + * + * @exception IOException If an error occurs + */ + protected final void implCloseChannel() throws IOException + { + try + { + implCloseSelectableChannel(); + } + finally + { + for (Iterator it = keys.iterator(); it.hasNext(); ) + ((SelectionKey) it.next()).cancel(); + } + } + + /** + * Closes this selectable channel. + * + * @exception IOException If an error occurs + */ + protected abstract void implCloseSelectableChannel() + throws IOException; + + /** + * Adjusts this channel's blocking mode. + * + * @param blocking true if blocking should be enabled, false otherwise + * + * @exception IOException If an error occurs + */ + protected abstract void implConfigureBlocking(boolean blocking) + throws IOException; + + /** + * Tells whether or not every I/O operation on this channel will block + * until it completes. + * + * @return true of this channel is blocking, false otherwise + */ + public final boolean isBlocking() + { + return blocking; + } + + /** + * Tells whether or not this channel is currently registered with + * any selectors. + * + * @return true if this channel is registered, false otherwise + */ + public final boolean isRegistered() + { + return ! keys.isEmpty(); + } + + /** + * Retrieves the key representing the channel's registration with the + * given selector. + * + * @param selector the selector to get a selection key for + * + * @return the selection key this channel is registered with + */ + public final SelectionKey keyFor(Selector selector) + { + if (! isOpen()) + return null; + + try + { + synchronized (blockingLock()) + { + return locate(selector); + } + } + catch (Exception e) + { + return null; + } + } + + /** + * Returns the provider that created this channel. + * + * @return the selector provider that created this channel + */ + public final SelectorProvider provider() + { + return provider; + } + + private SelectionKey locate(Selector selector) + { + ListIterator it = keys.listIterator(); + + while (it.hasNext()) + { + SelectionKey key = (SelectionKey) it.next(); + + if (key.selector() == selector) + return key; + } + + return null; + } + + /** + * Registers this channel with the given selector, returning a selection key. + * + * @param selin the seletor to use + * @param ops the interested operations + * @param att an attachment for the returned selection key + * + * @return the registered selection key + * + * @exception ClosedChannelException If the channel is already closed. + * @exception IllegalBlockingModeException If the channel is configured in + * blocking mode. + */ + public final SelectionKey register(Selector selin, int ops, Object att) + throws ClosedChannelException + { + if (! isOpen()) + throw new ClosedChannelException(); + + if ((ops & ~validOps()) != 0) + throw new IllegalArgumentException(); + + SelectionKey key = null; + AbstractSelector selector = (AbstractSelector) selin; + + synchronized (blockingLock()) + { + if (blocking) + throw new IllegalBlockingModeException(); + + key = locate(selector); + + if (key != null && key.isValid()) + { + key.interestOps(ops); + key.attach(att); + } + else + { + key = selector.register(this, ops, att); + + if (key != null) + addSelectionKey(key); + } + } + + return key; + } + + void addSelectionKey(SelectionKey key) + { + keys.add(key); + } + + // This method gets called by AbstractSelector.deregister(). + void removeSelectionKey(SelectionKey key) + { + keys.remove(key); + } +} diff --git a/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java b/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java new file mode 100644 index 000000000..e0dc03f53 --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/AbstractSelectionKey.java @@ -0,0 +1,78 @@ +/* AbstractSelectionKey.java -- + Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels.spi; + +import java.nio.channels.SelectionKey; + + +/** + * @since 1.4 + */ +public abstract class AbstractSelectionKey extends SelectionKey +{ + private boolean cancelled; + + /** + * Initializes the key. + */ + protected AbstractSelectionKey() + { + } + + /** + * Cancels this key. + */ + public final synchronized void cancel() + { + if (isValid()) + { + ((AbstractSelector) selector()).cancelKey(this); + cancelled = true; + } + } + + /** + * Tells whether this key is valid or not. + * + * @return true if this key is valid, false otherwise + */ + public final synchronized boolean isValid() + { + return ! cancelled; + } +} diff --git a/libjava/classpath/java/nio/channels/spi/AbstractSelector.java b/libjava/classpath/java/nio/channels/spi/AbstractSelector.java new file mode 100644 index 000000000..b67d4aecc --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/AbstractSelector.java @@ -0,0 +1,167 @@ +/* AbstractSelector.java -- + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels.spi; + +import java.io.IOException; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.HashSet; +import java.util.Set; + + +public abstract class AbstractSelector extends Selector +{ + private boolean closed; + private SelectorProvider provider; + private HashSet cancelledKeys; + + /** + * Initializes the slector. + * + * @param provider the provider that created this selector + */ + protected AbstractSelector(SelectorProvider provider) + { + this.provider = provider; + this.cancelledKeys = new HashSet(); + } + + /** + * Closes the channel. + * + * @exception IOException If an error occurs + */ + public final synchronized void close() throws IOException + { + if (closed) + return; + + implCloseSelector(); + closed = true; + } + + /** + * Tells whether this channel is open or not. + * + * @return true if channel is open, false otherwise. + */ + public final boolean isOpen() + { + return ! closed; + } + + /** + * Marks the beginning of an I/O operation that might block indefinitely. + */ + protected final void begin() + { + } + + /** + * Marks the end of an I/O operation that might block indefinitely. + */ + protected final void end() + { + } + + /** + * Returns the provider for this selector object. + * + * @return the SelectorProvider object that created this seletor + */ + public final SelectorProvider provider() + { + return provider; + } + + /** + * Returns the cancelled keys set. + * + * @return the cancelled keys set + */ + protected final Set cancelledKeys() + { + if (! isOpen()) + throw new ClosedSelectorException(); + + return cancelledKeys; + } + + /** + * Cancels a selection key. + */ + + // This method is only called by AbstractSelectionKey.cancel(). + final void cancelKey(AbstractSelectionKey key) + { + synchronized (cancelledKeys) + { + cancelledKeys.add(key); + } + } + + /** + * Closes the channel. + * + * @exception IOException if an error occurs + */ + protected abstract void implCloseSelector() throws IOException; + + /** + * Registers a channel for the selection process. + * + * @param ch the channel register + * @param ops the interested operations + * @param att an attachement to the selection key + * + * @return the registered selection key + */ + protected abstract SelectionKey register(AbstractSelectableChannel ch, + int ops, Object att); + + /** + * Deregisters the given selection key. + * + * @param key the key to deregister + */ + protected final void deregister(AbstractSelectionKey key) + { + ((AbstractSelectableChannel) key.channel()).removeSelectionKey(key); + } +} diff --git a/libjava/classpath/java/nio/channels/spi/SelectorProvider.java b/libjava/classpath/java/nio/channels/spi/SelectorProvider.java new file mode 100644 index 000000000..821bc4361 --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/SelectorProvider.java @@ -0,0 +1,178 @@ +/* SelectorProvider.java + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.channels.spi; + +import gnu.java.nio.SelectorProviderImpl; + +import java.io.IOException; +import java.nio.channels.Channel; +import java.nio.channels.DatagramChannel; +import java.nio.channels.Pipe; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class SelectorProvider +{ + private static SelectorProvider systemDefaultProvider; + + /** + * Initializes the selector provider. + * + * @exception SecurityException If a security manager has been installed and + * it denies @see RuntimePermission ("selectorProvider"). + */ + protected SelectorProvider() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new RuntimePermission("selectorProvider")); + } + + /** + * Opens a datagram channel. + * + * @return a new datagram channel object + * + * @exception IOException if an error occurs + */ + public abstract DatagramChannel openDatagramChannel() + throws IOException; + + /** + * Opens a pipe. + * + * @return a new pipe object + * + * @exception IOException if an error occurs + */ + public abstract Pipe openPipe() throws IOException; + + /** + * Opens a selector. + * + * @return a new selector object + * + * @exception IOException if an error occurs + */ + public abstract AbstractSelector openSelector() throws IOException; + + /** + * Opens a server socket channel. + * + * @return a new server socket channel object + * + * @exception IOException if an error occurs + */ + public abstract ServerSocketChannel openServerSocketChannel() + throws IOException; + + /** + * Opens a socket channel. + * + * @return a new socket channel object + * + * @exception IOException if an error occurs + */ + public abstract SocketChannel openSocketChannel() throws IOException; + + /** + * Returns the inherited channel of the VM. + * + * @return the inherited channel of the VM + * + * @throws IOException If an I/O error occurs + * @throws SecurityException If an installed security manager denies access + * to RuntimePermission("inheritedChannel") + * + * @since 1.5 + */ + public Channel inheritedChannel() + throws IOException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { + sm.checkPermission(new RuntimePermission("inheritedChannel")); + } + // Implementation note: The default implementation can't do much more + // than return null. If a VM can provide something more useful it should + // install its own SelectorProvider (maybe a subclass of this one) to + // return something more useful. + return null; + } + + /** + * Returns the system-wide default selector provider for this invocation + * of the Java virtual machine. + * + * @return the default seletor provider + */ + public static synchronized SelectorProvider provider() + { + if (systemDefaultProvider == null) + { + String propertyValue = + System.getProperty("java.nio.channels.spi.SelectorProvider"); + + if (propertyValue == null || propertyValue.equals("")) + systemDefaultProvider = new SelectorProviderImpl(); + else + { + try + { + systemDefaultProvider = + (SelectorProvider) Class.forName(propertyValue) + .newInstance(); + } + catch (Exception e) + { + System.err.println("Could not instantiate class: " + + propertyValue); + systemDefaultProvider = new SelectorProviderImpl(); + } + } + } + + return systemDefaultProvider; + } +} diff --git a/libjava/classpath/java/nio/channels/spi/package.html b/libjava/classpath/java/nio/channels/spi/package.html new file mode 100644 index 000000000..133fb04f2 --- /dev/null +++ b/libjava/classpath/java/nio/channels/spi/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.nio.channels.spi + + +

      Utility classes and interfaces for implementing channels.

      + + + diff --git a/libjava/classpath/java/nio/charset/CharacterCodingException.java b/libjava/classpath/java/nio/charset/CharacterCodingException.java new file mode 100644 index 000000000..05f1e3aea --- /dev/null +++ b/libjava/classpath/java/nio/charset/CharacterCodingException.java @@ -0,0 +1,55 @@ +/* CharacterCodingException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +import java.io.IOException; + +/** + * @since 1.4 + */ +public class CharacterCodingException extends IOException +{ + private static final long serialVersionUID = 8421532232154627783L; + + /** + * Creates the exception + */ + public CharacterCodingException() + { + } +} diff --git a/libjava/classpath/java/nio/charset/Charset.java b/libjava/classpath/java/nio/charset/Charset.java new file mode 100644 index 000000000..1757b82ea --- /dev/null +++ b/libjava/classpath/java/nio/charset/Charset.java @@ -0,0 +1,402 @@ +/* Charset.java -- + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.nio.charset; + +import gnu.classpath.ServiceFactory; +import gnu.classpath.SystemProperties; +import gnu.java.nio.charset.Provider; +import gnu.java.nio.charset.iconv.IconvProvider; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.spi.CharsetProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * @author Jesse Rosenstock + * @since 1.4 + * @status updated to 1.5 + */ +public abstract class Charset implements Comparable +{ + private CharsetEncoder cachedEncoder; + private CharsetDecoder cachedDecoder; + + /** + * Extra Charset providers. + */ + private static CharsetProvider[] providers; + + private final String canonicalName; + private final String[] aliases; + + protected Charset (String canonicalName, String[] aliases) + { + checkName (canonicalName); + if (aliases != null) + { + int n = aliases.length; + for (int i = 0; i < n; ++i) + checkName (aliases[i]); + } + + cachedEncoder = null; + cachedDecoder = null; + this.canonicalName = canonicalName; + this.aliases = aliases; + } + + /** + * @throws IllegalCharsetNameException if the name is illegal + */ + private static void checkName (String name) + { + int n = name.length (); + + if (n == 0) + throw new IllegalCharsetNameException (name); + + char ch = name.charAt (0); + if (!(('A' <= ch && ch <= 'Z') + || ('a' <= ch && ch <= 'z') + || ('0' <= ch && ch <= '9'))) + throw new IllegalCharsetNameException (name); + + for (int i = 1; i < n; ++i) + { + ch = name.charAt (i); + if (!(('A' <= ch && ch <= 'Z') + || ('a' <= ch && ch <= 'z') + || ('0' <= ch && ch <= '9') + || ch == '-' || ch == '.' || ch == ':' || ch == '_')) + throw new IllegalCharsetNameException (name); + } + } + + /** + * Returns the system default charset. + * + * This may be set by the user or VM with the file.encoding + * property. + * + * @since 1.5 + */ + public static Charset defaultCharset() + { + String encoding; + + try + { + encoding = SystemProperties.getProperty("file.encoding"); + } + catch(SecurityException e) + { + // Use fallback. + encoding = "ISO-8859-1"; + } + catch(IllegalArgumentException e) + { + // Use fallback. + encoding = "ISO-8859-1"; + } + + try + { + return forName(encoding); + } + catch(UnsupportedCharsetException e) + { + // Ignore. + } + catch(IllegalCharsetNameException e) + { + // Ignore. + } + catch(IllegalArgumentException e) + { + // Ignore. + } + + throw new IllegalStateException("Can't get default charset!"); + } + + public static boolean isSupported (String charsetName) + { + return charsetForName (charsetName) != null; + } + + /** + * Returns the Charset instance for the charset of the given name. + * + * @param charsetName + * @return the Charset instance for the indicated charset + * @throws UnsupportedCharsetException if this VM does not support + * the charset of the given name. + * @throws IllegalCharsetNameException if the given charset name is + * legal. + * @throws IllegalArgumentException if charsetName is null. + */ + public static Charset forName (String charsetName) + { + // Throws IllegalArgumentException as the JDK does. + if(charsetName == null) + throw new IllegalArgumentException("Charset name must not be null."); + + Charset cs = charsetForName (charsetName); + if (cs == null) + throw new UnsupportedCharsetException (charsetName); + return cs; + } + + /** + * Retrieves a charset for the given charset name. + * + * @return A charset object for the charset with the specified name, or + * null if no such charset exists. + * + * @throws IllegalCharsetNameException if the name is illegal + */ + private static Charset charsetForName(String charsetName) + { + checkName (charsetName); + // Try the default provider first + // (so we don't need to load external providers unless really necessary) + // if it is an exotic charset try loading the external providers. + Charset cs = provider().charsetForName(charsetName); + if (cs == null) + { + CharsetProvider[] providers = providers2(); + for (int i = 0; i < providers.length; i++) + { + cs = providers[i].charsetForName(charsetName); + if (cs != null) + break; + } + } + return cs; + } + + public static SortedMap availableCharsets() + { + TreeMap charsets + = new TreeMap(String.CASE_INSENSITIVE_ORDER); + for (Iterator i = provider().charsets(); i.hasNext(); ) + { + Charset cs = i.next(); + charsets.put(cs.name(), cs); + } + + CharsetProvider[] providers = providers2(); + for (int j = 0; j < providers.length; j++) + { + for (Iterator i = providers[j].charsets(); i.hasNext(); ) + { + Charset cs = i.next(); + charsets.put(cs.name(), cs); + } + } + + return Collections.unmodifiableSortedMap(charsets); + } + + private static CharsetProvider provider() + { + String useIconv = SystemProperties.getProperty + ("gnu.classpath.nio.charset.provider.iconv"); + + if (useIconv != null) + return IconvProvider.provider(); + + return Provider.provider(); + } + + /** + * We need to support multiple providers, reading them from + * java.nio.charset.spi.CharsetProvider in the resource directory + * META-INF/services. This returns the "extra" charset providers. + */ + private static CharsetProvider[] providers2() + { + if (providers == null) + { + try + { + Iterator i = ServiceFactory.lookupProviders(CharsetProvider.class); + LinkedHashSet set = new LinkedHashSet(); + while (i.hasNext()) + set.add(i.next()); + + providers = new CharsetProvider[set.size()]; + set.toArray(providers); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + return providers; + } + + public final String name () + { + return canonicalName; + } + + public final Set aliases () + { + if (aliases == null) + return Collections.emptySet(); + + // should we cache the aliasSet instead? + int n = aliases.length; + HashSet aliasSet = new HashSet (n); + for (int i = 0; i < n; ++i) + aliasSet.add (aliases[i]); + return Collections.unmodifiableSet (aliasSet); + } + + public String displayName () + { + return canonicalName; + } + + public String displayName (Locale locale) + { + return canonicalName; + } + + public final boolean isRegistered () + { + return (!canonicalName.startsWith ("x-") + && !canonicalName.startsWith ("X-")); + } + + public abstract boolean contains (Charset cs); + + public abstract CharsetDecoder newDecoder (); + + public abstract CharsetEncoder newEncoder (); + + public boolean canEncode () + { + return true; + } + + // NB: This implementation serializes different threads calling + // Charset.encode(), a potential performance problem. It might + // be better to remove the cache, or use ThreadLocal to cache on + // a per-thread basis. + public final synchronized ByteBuffer encode (CharBuffer cb) + { + try + { + if (cachedEncoder == null) + { + cachedEncoder = newEncoder () + .onMalformedInput (CodingErrorAction.REPLACE) + .onUnmappableCharacter (CodingErrorAction.REPLACE); + } else + cachedEncoder.reset(); + return cachedEncoder.encode (cb); + } + catch (CharacterCodingException e) + { + throw new AssertionError (e); + } + } + + public final ByteBuffer encode (String str) + { + return encode (CharBuffer.wrap (str)); + } + + // NB: This implementation serializes different threads calling + // Charset.decode(), a potential performance problem. It might + // be better to remove the cache, or use ThreadLocal to cache on + // a per-thread basis. + public final synchronized CharBuffer decode (ByteBuffer bb) + { + try + { + if (cachedDecoder == null) + { + cachedDecoder = newDecoder () + .onMalformedInput (CodingErrorAction.REPLACE) + .onUnmappableCharacter (CodingErrorAction.REPLACE); + } else + cachedDecoder.reset(); + + return cachedDecoder.decode (bb); + } + catch (CharacterCodingException e) + { + throw new AssertionError (e); + } + } + + public final int compareTo (Charset other) + { + return canonicalName.compareToIgnoreCase (other.canonicalName); + } + + public final int hashCode () + { + return canonicalName.hashCode (); + } + + public final boolean equals (Object ob) + { + if (ob instanceof Charset) + return canonicalName.equalsIgnoreCase (((Charset) ob).canonicalName); + else + return false; + } + + public final String toString () + { + return canonicalName; + } +} diff --git a/libjava/classpath/java/nio/charset/CharsetDecoder.java b/libjava/classpath/java/nio/charset/CharsetDecoder.java new file mode 100644 index 000000000..cf43feafd --- /dev/null +++ b/libjava/classpath/java/nio/charset/CharsetDecoder.java @@ -0,0 +1,317 @@ +/* CharsetDecoder.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +/** + * @author Jesse Rosenstock + * @since 1.4 + */ +public abstract class CharsetDecoder +{ + private static final int STATE_RESET = 0; + private static final int STATE_CODING = 1; + private static final int STATE_END = 2; + private static final int STATE_FLUSHED = 3; + + private static final String DEFAULT_REPLACEMENT = "\uFFFD"; + + private final Charset charset; + private final float averageCharsPerByte; + private final float maxCharsPerByte; + private String replacement; + + private int state = STATE_RESET; + + private CodingErrorAction malformedInputAction + = CodingErrorAction.REPORT; + private CodingErrorAction unmappableCharacterAction + = CodingErrorAction.REPORT; + + private CharsetDecoder (Charset cs, float averageCharsPerByte, + float maxCharsPerByte, String replacement) + { + if (averageCharsPerByte <= 0.0f) + throw new IllegalArgumentException ("Non-positive averageCharsPerByte"); + if (maxCharsPerByte <= 0.0f) + throw new IllegalArgumentException ("Non-positive maxCharsPerByte"); + + this.charset = cs; + this.averageCharsPerByte + = averageCharsPerByte; + this.maxCharsPerByte + = maxCharsPerByte; + this.replacement = replacement; + implReplaceWith (replacement); + } + + protected CharsetDecoder (Charset cs, float averageCharsPerByte, + float maxCharsPerByte) + { + this (cs, averageCharsPerByte, maxCharsPerByte, DEFAULT_REPLACEMENT); + } + + public final float averageCharsPerByte () + { + return averageCharsPerByte; + } + + public final Charset charset () + { + return charset; + } + + public final CharBuffer decode (ByteBuffer in) + throws CharacterCodingException + { + // XXX: Sun's Javadoc seems to contradict itself saying an + // IllegalStateException is thrown "if a decoding operation is already + // in progress" and also that "it resets this Decoder". + // Should we check to see that the state is reset, or should we + // call reset()? + if (state != STATE_RESET) + throw new IllegalStateException (); + + // REVIEW: Using max instead of average may allocate a very large + // buffer. Maybe we should do something more efficient? + int remaining = in.remaining (); + int n = (int) (remaining * maxCharsPerByte ()); + CharBuffer out = CharBuffer.allocate (n); + + if (remaining == 0) + { + state = STATE_FLUSHED; + return out; + } + + CoderResult cr = decode (in, out, true); + if (cr.isError ()) + cr.throwException (); + + cr = flush (out); + if (cr.isError ()) + cr.throwException (); + + reset(); + out.flip (); + + // Unfortunately, resizing the actual charbuffer array is required. + char[] resized = new char[out.remaining()]; + out.get(resized); + return CharBuffer.wrap(resized); + } + + public final CoderResult decode (ByteBuffer in, CharBuffer out, + boolean endOfInput) + { + int newState = endOfInput ? STATE_END : STATE_CODING; + // XXX: Need to check for "previous step was an invocation [not] of + // this method with a value of true for the endOfInput parameter but + // a return value indicating an incomplete decoding operation" + // XXX: We will not check the previous return value, just + // that the previous call passed true for endOfInput + if (state != STATE_RESET && state != STATE_CODING + && !(endOfInput && state == STATE_END)) + throw new IllegalStateException (); + state = newState; + + for (;;) + { + CoderResult cr; + try + { + cr = decodeLoop (in, out); + } + catch (RuntimeException e) + { + throw new CoderMalfunctionError (e); + } + + if (cr.isOverflow ()) + return cr; + + if (cr.isUnderflow ()) + { + if (endOfInput && in.hasRemaining ()) + cr = CoderResult.malformedForLength (in.remaining ()); + else + return cr; + } + + CodingErrorAction action = cr.isMalformed () + ? malformedInputAction + : unmappableCharacterAction; + + if (action == CodingErrorAction.REPORT) + return cr; + + if (action == CodingErrorAction.REPLACE) + { + if (out.remaining () < replacement.length ()) + return CoderResult.OVERFLOW; + out.put (replacement); + } + + in.position (in.position () + cr.length ()); + } + } + + protected abstract CoderResult decodeLoop (ByteBuffer in, CharBuffer out); + + public Charset detectedCharset () + { + throw new UnsupportedOperationException (); + } + + public final CoderResult flush (CharBuffer out) + { + // It seems weird that you can flush after reset, but Sun's javadoc + // says an IllegalStateException is thrown "If the previous step of the + // current decoding operation was an invocation neither of the reset + // method nor ... of the three-argument decode method with a value of + // true for the endOfInput parameter." + // Further note that flush() only requires that there not be + // an IllegalStateException if the previous step was a call to + // decode with true as the last argument. It does not require + // that the call succeeded. decode() does require that it succeeded. + // XXX: test this to see if reality matches javadoc + if (state != STATE_RESET && state != STATE_END) + throw new IllegalStateException (); + + state = STATE_FLUSHED; + return implFlush (out); + } + + protected CoderResult implFlush (CharBuffer out) + { + return CoderResult.UNDERFLOW; + } + + public final CharsetDecoder onMalformedInput (CodingErrorAction newAction) + { + if (newAction == null) + throw new IllegalArgumentException ("Null action"); + + malformedInputAction = newAction; + implOnMalformedInput (newAction); + return this; + } + + protected void implOnMalformedInput (CodingErrorAction newAction) + { + // default implementation does nothing + } + + protected void implOnUnmappableCharacter (CodingErrorAction newAction) + { + // default implementation does nothing + } + + protected void implReplaceWith (String newReplacement) + { + // default implementation does nothing + } + + protected void implReset () + { + // default implementation does nothing + } + + public boolean isAutoDetecting () + { + return false; + } + + public boolean isCharsetDetected () + { + throw new UnsupportedOperationException (); + } + + public CodingErrorAction malformedInputAction () + { + return malformedInputAction; + } + + public final float maxCharsPerByte () + { + return maxCharsPerByte; + } + + public final CharsetDecoder onUnmappableCharacter + (CodingErrorAction newAction) + { + if (newAction == null) + throw new IllegalArgumentException ("Null action"); + + unmappableCharacterAction = newAction; + implOnUnmappableCharacter (newAction); + return this; + } + + public final String replacement () + { + return replacement; + } + + public final CharsetDecoder replaceWith (String newReplacement) + { + if (newReplacement == null) + throw new IllegalArgumentException ("Null replacement"); + if (newReplacement.length () == 0) + throw new IllegalArgumentException ("Empty replacement"); + // XXX: what about maxCharsPerByte? + + this.replacement = newReplacement; + implReplaceWith (newReplacement); + return this; + } + + public final CharsetDecoder reset () + { + state = STATE_RESET; + implReset (); + return this; + } + + public CodingErrorAction unmappableCharacterAction () + { + return unmappableCharacterAction; + } +} diff --git a/libjava/classpath/java/nio/charset/CharsetEncoder.java b/libjava/classpath/java/nio/charset/CharsetEncoder.java new file mode 100644 index 000000000..1f079a3cc --- /dev/null +++ b/libjava/classpath/java/nio/charset/CharsetEncoder.java @@ -0,0 +1,369 @@ +/* CharsetEncoder.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +/** + * @author Jesse Rosenstock + * @since 1.4 + */ +public abstract class CharsetEncoder +{ + private static final int STATE_RESET = 0; + private static final int STATE_CODING = 1; + private static final int STATE_END = 2; + private static final int STATE_FLUSHED = 3; + + private static final byte[] DEFAULT_REPLACEMENT = {(byte)'?'}; + + private final Charset charset; + private final float averageBytesPerChar; + private final float maxBytesPerChar; + private byte[] replacement; + + private int state = STATE_RESET; + + private CodingErrorAction malformedInputAction + = CodingErrorAction.REPORT; + private CodingErrorAction unmappableCharacterAction + = CodingErrorAction.REPORT; + + protected CharsetEncoder (Charset cs, float averageBytesPerChar, + float maxBytesPerChar) + { + this (cs, averageBytesPerChar, maxBytesPerChar, DEFAULT_REPLACEMENT); + } + + protected CharsetEncoder (Charset cs, float averageBytesPerChar, + float maxBytesPerChar, byte[] replacement) + { + if (averageBytesPerChar <= 0.0f) + throw new IllegalArgumentException ("Non-positive averageBytesPerChar"); + if (maxBytesPerChar <= 0.0f) + throw new IllegalArgumentException ("Non-positive maxBytesPerChar"); + + this.charset = cs; + this.averageBytesPerChar + = averageBytesPerChar; + this.maxBytesPerChar + = maxBytesPerChar; + this.replacement = replacement; + implReplaceWith (replacement); + } + + public final float averageBytesPerChar () + { + return averageBytesPerChar; + } + + public boolean canEncode (char c) + { + CharBuffer cb = CharBuffer.allocate (1).put (c); + cb.flip (); + return canEncode (cb); + } + + public boolean canEncode (CharSequence cs) + { + CharBuffer cb; + if (cs instanceof CharBuffer) + cb = ((CharBuffer) cs).duplicate (); + else + cb = CharBuffer.wrap (cs); + return canEncode (cb); + } + + private boolean canEncode (CharBuffer cb) + { + // It is an error if a coding operation is "in progress" + // I take that to mean the state is not reset or flushed. + // XXX: check "in progress" everywhere + if (state == STATE_FLUSHED) + reset (); + else if (state != STATE_RESET) + throw new IllegalStateException (); + + CodingErrorAction oldMalformedInputAction = malformedInputAction; + CodingErrorAction oldUnmappableCharacterAction + = unmappableCharacterAction; + + try + { + if (oldMalformedInputAction != CodingErrorAction.REPORT) + onMalformedInput (CodingErrorAction.REPORT); + if (oldUnmappableCharacterAction != CodingErrorAction.REPORT) + onUnmappableCharacter (CodingErrorAction.REPORT); + } + catch (Exception e) + { + return false; + } + finally + { + if (oldMalformedInputAction != CodingErrorAction.REPORT) + onMalformedInput (oldMalformedInputAction); + if (oldUnmappableCharacterAction != CodingErrorAction.REPORT) + onUnmappableCharacter (oldUnmappableCharacterAction); + } + + return true; + } + + public final Charset charset () + { + return charset; + } + + public final ByteBuffer encode (CharBuffer in) + throws CharacterCodingException + { + // XXX: Sun's Javadoc seems to contradict itself saying an + // IllegalStateException is thrown "if a decoding operation is already + // in progress" and also that "it resets this Encoder". + // Should we check to see that the state is reset, or should we + // call reset()? + if (state != STATE_RESET) + throw new IllegalStateException (); + + // REVIEW: Using max instead of average may allocate a very large + // buffer. Maybe we should do something more efficient? + int remaining = in.remaining (); + int n = (int) (remaining * maxBytesPerChar ()); + ByteBuffer out = ByteBuffer.allocate (n); + + if (remaining == 0) + { + state = STATE_FLUSHED; + return out; + } + + CoderResult cr = encode (in, out, true); + if (cr.isError ()) + cr.throwException (); + + cr = flush (out); + if (cr.isError ()) + cr.throwException (); + + out.flip (); + + // Unfortunately, resizing the actual bytebuffer array is required. + byte[] resized = new byte[out.remaining()]; + out.get(resized); + return ByteBuffer.wrap(resized); + } + + public final CoderResult encode (CharBuffer in, ByteBuffer out, + boolean endOfInput) + { + int newState = endOfInput ? STATE_END : STATE_CODING; + // XXX: Need to check for "previous step was an invocation [not] of + // this method with a value of true for the endOfInput parameter but + // a return value indicating an incomplete decoding operation" + // XXX: We will not check the previous return value, just + // that the previous call passed true for endOfInput + if (state != STATE_RESET && state != STATE_CODING + && !(endOfInput && state == STATE_END)) + throw new IllegalStateException (); + state = newState; + + for (;;) + { + CoderResult cr; + try + { + cr = encodeLoop (in, out); + } + catch (RuntimeException e) + { + throw new CoderMalfunctionError (e); + } + + if (cr.isOverflow ()) + return cr; + + if (cr.isUnderflow ()) + { + if (endOfInput && in.hasRemaining ()) + cr = CoderResult.malformedForLength (in.remaining ()); + else + return cr; + } + + CodingErrorAction action = cr.isMalformed () + ? malformedInputAction + : unmappableCharacterAction; + + if (action == CodingErrorAction.REPORT) + return cr; + + if (action == CodingErrorAction.REPLACE) + { + if (out.remaining () < replacement.length) + return CoderResult.OVERFLOW; + out.put (replacement); + } + + in.position (in.position () + cr.length ()); + } + } + + protected abstract CoderResult encodeLoop (CharBuffer in, ByteBuffer out); + + public final CoderResult flush (ByteBuffer out) + { + // It seems weird that you can flush after reset, but Sun's javadoc + // says an IllegalStateException is thrown "If the previous step of the + // current decoding operation was an invocation neither of the reset + // method nor ... of the three-argument encode method with a value of + // true for the endOfInput parameter." + // Further note that flush() only requires that there not be + // an IllegalStateException if the previous step was a call to + // encode with true as the last argument. It does not require + // that the call succeeded. encode() does require that it succeeded. + // XXX: test this to see if reality matches javadoc + if (state != STATE_RESET && state != STATE_END) + throw new IllegalStateException (); + + state = STATE_FLUSHED; + return implFlush (out); + } + + protected CoderResult implFlush (ByteBuffer out) + { + return CoderResult.UNDERFLOW; + } + + protected void implOnMalformedInput (CodingErrorAction newAction) + { + // default implementation does nothing + } + + protected void implOnUnmappableCharacter (CodingErrorAction newAction) + { + // default implementation does nothing + } + + protected void implReplaceWith (byte[] newReplacement) + { + // default implementation does nothing + } + + protected void implReset () + { + // default implementation does nothing + } + + public boolean isLegalReplacement (byte[] replacement) + { + // TODO: cache the decoder + // error actions will be REPORT after construction + CharsetDecoder decoder = charset.newDecoder (); + ByteBuffer bb = ByteBuffer.wrap (replacement); + CharBuffer cb + = CharBuffer.allocate ((int) (replacement.length + * decoder.maxCharsPerByte ())); + return !decoder.decode (bb, cb, true).isError (); + } + + public CodingErrorAction malformedInputAction () + { + return malformedInputAction; + } + + public final float maxBytesPerChar () + { + return maxBytesPerChar; + } + + public final CharsetEncoder onMalformedInput (CodingErrorAction newAction) + { + if (newAction == null) + throw new IllegalArgumentException ("Null action"); + + malformedInputAction = newAction; + implOnMalformedInput (newAction); + return this; + } + + public CodingErrorAction unmappableCharacterAction () + { + return unmappableCharacterAction; + } + + public final CharsetEncoder onUnmappableCharacter + (CodingErrorAction newAction) + { + if (newAction == null) + throw new IllegalArgumentException ("Null action"); + + unmappableCharacterAction = newAction; + implOnUnmappableCharacter (newAction); + return this; + } + + public final byte[] replacement () + { + return replacement; + } + + public final CharsetEncoder replaceWith (byte[] newReplacement) + { + if (newReplacement == null) + throw new IllegalArgumentException ("Null replacement"); + if (newReplacement.length == 0) + throw new IllegalArgumentException ("Empty replacement"); + // XXX: what about maxBytesPerChar? + + if (!isLegalReplacement (newReplacement)) + throw new IllegalArgumentException ("Illegal replacement"); + + this.replacement = newReplacement; + implReplaceWith (newReplacement); + return this; + } + + public final CharsetEncoder reset () + { + state = STATE_RESET; + implReset (); + return this; + } +} diff --git a/libjava/classpath/java/nio/charset/CoderMalfunctionError.java b/libjava/classpath/java/nio/charset/CoderMalfunctionError.java new file mode 100644 index 000000000..f7a32d207 --- /dev/null +++ b/libjava/classpath/java/nio/charset/CoderMalfunctionError.java @@ -0,0 +1,54 @@ +/* CoderMalfunctionError.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @since 1.4 + */ +public class CoderMalfunctionError extends Error +{ + private static final long serialVersionUID = - 1151412348057794301L; + + /** + * Creates the error + */ + public CoderMalfunctionError(Exception cause) + { + super (cause); + } +} diff --git a/libjava/classpath/java/nio/charset/CoderResult.java b/libjava/classpath/java/nio/charset/CoderResult.java new file mode 100644 index 000000000..b083d840e --- /dev/null +++ b/libjava/classpath/java/nio/charset/CoderResult.java @@ -0,0 +1,189 @@ +/* CoderResult.java -- + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +import java.lang.ref.WeakReference; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.util.HashMap; + +/** + * @author Jesse Rosenstock + * @since 1.4 + */ +public class CoderResult +{ + private static final int TYPE_MALFORMED = 0; + private static final int TYPE_OVERFLOW = 1; + private static final int TYPE_UNDERFLOW = 2; + private static final int TYPE_UNMAPPABLE = 3; + + public static final CoderResult OVERFLOW + = new CoderResult (TYPE_OVERFLOW, 0); + public static final CoderResult UNDERFLOW + = new CoderResult (TYPE_UNDERFLOW, 0); + + private static final String[] names + = { "MALFORMED", "OVERFLOW", "UNDERFLOW", "UNMAPPABLE" }; + + private static final Cache malformedCache + = new Cache () + { + protected CoderResult make (int length) + { + return new CoderResult (TYPE_MALFORMED, length); + } + }; + + private static final Cache unmappableCache + = new Cache () + { + protected CoderResult make (int length) + { + return new CoderResult (TYPE_UNMAPPABLE, length); + } + }; + + private final int type; + private final int length; + + // Package-private to avoid a trampoline constructor. + CoderResult (int type, int length) + { + this.type = type; + this.length = length; + } + + public boolean isError () + { + return length > 0; + } + + public boolean isMalformed () + { + return type == TYPE_MALFORMED; + } + + public boolean isOverflow () + { + return type == TYPE_OVERFLOW; + } + + public boolean isUnderflow () + { + return type == TYPE_UNDERFLOW; + } + + public boolean isUnmappable () + { + return type == TYPE_UNMAPPABLE; + } + + public int length () + { + if (length <= 0) + throw new UnsupportedOperationException (); + else + return length; + } + + public static CoderResult malformedForLength (int length) + { + return malformedCache.get (length); + } + + public void throwException () + throws CharacterCodingException + { + switch (type) + { + case TYPE_MALFORMED: + throw new MalformedInputException (length); + case TYPE_OVERFLOW: + throw new BufferOverflowException (); + case TYPE_UNDERFLOW: + throw new BufferUnderflowException (); + case TYPE_UNMAPPABLE: + throw new UnmappableCharacterException (length); + } + } + + public String toString () + { + String name = names[type]; + return (length > 0) ? name + '[' + length + ']' : name; + } + + public static CoderResult unmappableForLength (int length) + { + return unmappableCache.get (length); + } + + private abstract static class Cache + { + private final HashMap cache; + + // Package-private to avoid a trampoline constructor. + Cache () + { + cache = new HashMap (); + } + + // Package-private to avoid a trampoline. + synchronized CoderResult get (int length) + { + if (length <= 0) + throw new IllegalArgumentException ("Non-positive length"); + + Integer len = Integer.valueOf (length); + CoderResult cr = null; + Object o; + if ((o = cache.get (len)) != null) + cr = (CoderResult) ((WeakReference) o).get (); + if (cr == null) + { + cr = make (length); + cache.put (len, new WeakReference (cr)); + } + + return cr; + } + + protected abstract CoderResult make (int length); + } +} diff --git a/libjava/classpath/java/nio/charset/CodingErrorAction.java b/libjava/classpath/java/nio/charset/CodingErrorAction.java new file mode 100644 index 000000000..3b6403097 --- /dev/null +++ b/libjava/classpath/java/nio/charset/CodingErrorAction.java @@ -0,0 +1,66 @@ +/* CodingErrorAction.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +public class CodingErrorAction +{ + public static final CodingErrorAction IGNORE + = new CodingErrorAction("ignore"); + public static final CodingErrorAction REPLACE + = new CodingErrorAction("replace"); + public static final CodingErrorAction REPORT + = new CodingErrorAction("report"); + + private final String name; + + /** + * Private constructor only used to create the constant CodingErrorActions. + */ + private CodingErrorAction(String name) + { + this.name = name; + } + + /** + * Returns the name of the CodingErrorAction. + */ + public String toString () + { + return name; + } +} diff --git a/libjava/classpath/java/nio/charset/IllegalCharsetNameException.java b/libjava/classpath/java/nio/charset/IllegalCharsetNameException.java new file mode 100644 index 000000000..0e05d4dbf --- /dev/null +++ b/libjava/classpath/java/nio/charset/IllegalCharsetNameException.java @@ -0,0 +1,73 @@ +/* IllegalCharsetNameException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @author Michael Koch + * @since 1.4 + */ +public class IllegalCharsetNameException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.4+ + */ + private static final long serialVersionUID = 1457525358470002989L; + + private String charsetName; + + /** + * Creates the exception + * + * @param charsetName name of the illegal charset + */ + public IllegalCharsetNameException (String charsetName) + { + super (); + this.charsetName = charsetName; + } + + /** + * Retrieves the illegal charset name + * + * @return the illegal charset name + */ + public String getCharsetName () + { + return charsetName; + } +} diff --git a/libjava/classpath/java/nio/charset/MalformedInputException.java b/libjava/classpath/java/nio/charset/MalformedInputException.java new file mode 100644 index 000000000..3870b55be --- /dev/null +++ b/libjava/classpath/java/nio/charset/MalformedInputException.java @@ -0,0 +1,79 @@ +/* MalformedInputException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @since 1.4 + */ +public class MalformedInputException extends CharacterCodingException +{ + private static final long serialVersionUID = - 3438823399834806194L; + + private int inputLength; + + /** + * Creates the exception + * + * @param inputLength the position of malformed input in the input stream + */ + public MalformedInputException (int inputLength) + { + super (); + this.inputLength = inputLength; + } + + /** + * Retrieves the position of the malformed input in the input stream. + * + * @return the position + */ + public int getInputLength () + { + return inputLength; + } + + /** + * Returns the detail message string of this throwable + * + * @return the message + */ + public String getMessage () + { + return "Input length = " + inputLength; + } +} diff --git a/libjava/classpath/java/nio/charset/UnmappableCharacterException.java b/libjava/classpath/java/nio/charset/UnmappableCharacterException.java new file mode 100644 index 000000000..d9cdb49cf --- /dev/null +++ b/libjava/classpath/java/nio/charset/UnmappableCharacterException.java @@ -0,0 +1,73 @@ +/* UnmappableCharacterException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @since 1.4 + */ +public class UnmappableCharacterException extends CharacterCodingException +{ + private static final long serialVersionUID = - 7026962371537706123L; + + private int inputLength; + + /** + * Creates the exception + */ + public UnmappableCharacterException (int inputLength) + { + super (); + this.inputLength = inputLength; + } + + /** + * Retrieves the illegal charset name + */ + public int getInputLength () + { + return inputLength; + } + + /** + * Returns the detail message string of this throwable + */ + public String getMessage () + { + return "Input length = " + inputLength; + } +} diff --git a/libjava/classpath/java/nio/charset/UnsupportedCharsetException.java b/libjava/classpath/java/nio/charset/UnsupportedCharsetException.java new file mode 100644 index 000000000..c4f9d17fd --- /dev/null +++ b/libjava/classpath/java/nio/charset/UnsupportedCharsetException.java @@ -0,0 +1,69 @@ +/* UnsupportedCharsetException.java -- + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset; + +/** + * @author Michael Koch + * @since 1.4 + */ +public class UnsupportedCharsetException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.4+ + */ + private static final long serialVersionUID = 1490765524727386367L; + + String charsetName; + + /** + * Creates the exception + */ + public UnsupportedCharsetException (String charsetName) + { + super (); + this.charsetName = charsetName; + } + + /** + * Retrieves the illegal charset name + */ + public String getCharsetName () + { + return charsetName; + } +} diff --git a/libjava/classpath/java/nio/charset/package.html b/libjava/classpath/java/nio/charset/package.html new file mode 100644 index 000000000..acc7c0203 --- /dev/null +++ b/libjava/classpath/java/nio/charset/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.nio.charset + + +

      Classes to manipulate, encode and decode different character sets from +and to bytes.

      + + + diff --git a/libjava/classpath/java/nio/charset/spi/CharsetProvider.java b/libjava/classpath/java/nio/charset/spi/CharsetProvider.java new file mode 100644 index 000000000..03653f807 --- /dev/null +++ b/libjava/classpath/java/nio/charset/spi/CharsetProvider.java @@ -0,0 +1,95 @@ +/* CharsetProvider.java -- charset service provider interface + Copyright (C) 2002, 2005, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.nio.charset.spi; + +import java.nio.charset.Charset; +import java.util.Iterator; + + +/** + * This class allows an implementor to provide additional character sets. The + * subclass must have a nullary constructor, and be attached to charset + * implementation classes. These extensions are loaded via the context class + * loader. To provide the charset extension, all files named + * META-INF/services/java.nio.charset.spi.CharsetProvider are + * read from the classpath. Each one should be a UTF-8 encoded list of + * fully-qualified names of concrete subclasses of this class; whitespace is + * ignored, and '#' starts comments. Duplicates are ignored. The + * implementations must be accessible to the classloader that requests them. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Charset + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class CharsetProvider +{ + /** + * Initialize a new charset provider. This performs a security check on + * RuntimePermission("charsetProvider"). + * + * @throws SecurityException if building a new set is not allowed + */ + protected CharsetProvider() + { + // We only do the security check for custom providers, not for the + // built in ones. + SecurityManager s = System.getSecurityManager(); + if (s != null && + ! (this instanceof gnu.java.nio.charset.Provider + || this instanceof gnu.java.nio.charset.iconv.IconvProvider)) + s.checkPermission(new RuntimePermission("charsetProvider")); + } + + /** + * Returns an iterator over the charsets defined by this provider. + * + * @return the iterator + * @see Charset#availableCharsets() + */ + public abstract Iterator charsets(); + + /** + * Returns the named charset, by canonical name or alias. + * + * @param name the name of the character + * + * @return the charset, or null if not supported + */ + public abstract Charset charsetForName(String name); +} // class CharsetProvider diff --git a/libjava/classpath/java/nio/charset/spi/package.html b/libjava/classpath/java/nio/charset/spi/package.html new file mode 100644 index 000000000..dd47a250b --- /dev/null +++ b/libjava/classpath/java/nio/charset/spi/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.nio.charset.spi + + +

      Classes to provide new character sets.

      + + + diff --git a/libjava/classpath/java/nio/package.html b/libjava/classpath/java/nio/package.html new file mode 100644 index 000000000..274498be3 --- /dev/null +++ b/libjava/classpath/java/nio/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.nio + + +

      Abstract classes for manipulating buffers of different primitive types.

      + + + diff --git a/libjava/classpath/java/rmi/AccessException.java b/libjava/classpath/java/rmi/AccessException.java new file mode 100644 index 000000000..c14b87675 --- /dev/null +++ b/libjava/classpath/java/rmi/AccessException.java @@ -0,0 +1,77 @@ +/* AccessException.java -- thrown if the caller does not have access + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown to indicate that the caller does not have permission to access + * certain data, such as bind in an ActivationSystem. + * + * @author unknown + * @see Naming + * @see java.rmi.activation.ActivationSystem + * @since 1.1 + */ +public class AccessException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6314925228044966088l; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public AccessException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public AccessException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/AlreadyBoundException.java b/libjava/classpath/java/rmi/AlreadyBoundException.java new file mode 100644 index 000000000..c56a4ee85 --- /dev/null +++ b/libjava/classpath/java/rmi/AlreadyBoundException.java @@ -0,0 +1,74 @@ +/* AlreadyBoundException.java -- thrown if a binding is already bound + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown on an attempt to bind an object in the registry that is already + * bound. + * + * @author unknown + * @see java.rmi.Naming#bind(String, Remote) + * @see java.rmi.registry.Registry#bind(String, Remote) + * @since 1.1 + * @status updated to 1.4 + */ +public class AlreadyBoundException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 9218657361741657110L; + + /** + * Create an exception with no message. + */ + public AlreadyBoundException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public AlreadyBoundException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/ConnectException.java b/libjava/classpath/java/rmi/ConnectException.java new file mode 100644 index 000000000..dc3abd197 --- /dev/null +++ b/libjava/classpath/java/rmi/ConnectException.java @@ -0,0 +1,74 @@ +/* ConnectException.java -- thrown if a connection is refused + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if a connection is refused for a remote call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class ConnectException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4863550261346652506L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ConnectException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ConnectException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/ConnectIOException.java b/libjava/classpath/java/rmi/ConnectIOException.java new file mode 100644 index 000000000..dde753ad5 --- /dev/null +++ b/libjava/classpath/java/rmi/ConnectIOException.java @@ -0,0 +1,74 @@ +/* ConnectIOException.java -- thrown if an IO exception occurs during connect + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Wraps an I/O Exception thrown while connecting for a remote call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class ConnectIOException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8087809532704668744L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ConnectIOException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ConnectIOException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/MarshalException.java b/libjava/classpath/java/rmi/MarshalException.java new file mode 100644 index 000000000..e5c10a4bf --- /dev/null +++ b/libjava/classpath/java/rmi/MarshalException.java @@ -0,0 +1,76 @@ +/* MarshalException.java -- wraps error while marshalling parameters + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if an exception occurs while marshalling data to send in a remote + * call. The call may not be retransmitted, if the "at most once" semantics + * are to be preserved. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class MarshalException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6223554758134037936L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public MarshalException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public MarshalException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/MarshalledObject.java b/libjava/classpath/java/rmi/MarshalledObject.java new file mode 100644 index 000000000..a48e4c009 --- /dev/null +++ b/libjava/classpath/java/rmi/MarshalledObject.java @@ -0,0 +1,154 @@ +/* MarshalledObject.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi; + +import gnu.java.rmi.RMIMarshalledObjectInputStream; +import gnu.java.rmi.RMIMarshalledObjectOutputStream; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.Serializable; + +/** + * A MarshalledObject consists of a serialized object which is + * marshalled according to the RMI specification. + *

      + * An object passed to the constructor is serialized and tagged with the needed + * URL to retrieve its class definition for remote usage. If the object is a + * remote reference its stub is serialized instead. The instance of this + * marshalled object can be later retrieved by its get() method. + *

      + * + * @author unknown + */ +public final class MarshalledObject + implements Serializable +{ + // The following fields are from Java API Documentation "Serialized form" + private static final long serialVersionUID = 8988374069173025854L; + + byte[] objBytes; + byte[] locBytes; + int hash; + + /** + * Constructs a MarshalledObject from the given object. + * + * @param obj the object to marshal + * @throws IOException if an I/O error during serialization occurs. + */ + public MarshalledObject(T obj) throws IOException + { + ByteArrayOutputStream objStream = new ByteArrayOutputStream(); + RMIMarshalledObjectOutputStream stream = + new RMIMarshalledObjectOutputStream(objStream); + stream.writeObject(obj); + stream.flush(); + objBytes = objStream.toByteArray(); + locBytes = stream.getLocBytes(); + + // The following algorithm of calculating hashCode is similar to String + hash = 0; + for (int i = 0; i < objBytes.length; i++) + hash = hash * 31 + objBytes[i]; + + if (locBytes != null) + for (int i = 0; i < locBytes.length; i++) + hash = hash * 31 + locBytes[i]; + } + + /** + * Checks if the given object is equal to this marshalled object. + * + *

      Marshalled objects are considered equal if they contain the + * same serialized object. Codebase annotations where the class + * definition can be downloaded are ignored in the equals test.

      + * + * @param obj the object to compare. + * @return true if equal, false otherwise. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof MarshalledObject)) + return false; + + // hashCode even differs, don't do the time-consuming comparisons + if (obj.hashCode() != hash) + return false; + + MarshalledObject aobj = (MarshalledObject) obj; + if (objBytes == null || aobj.objBytes == null) + return objBytes == aobj.objBytes; + if (objBytes.length != aobj.objBytes.length) + return false; + for (int i = 0; i < objBytes.length; i++) + { + if (objBytes[i] != aobj.objBytes[i]) + return false; + } + // Ignore comparison of locBytes(annotation) + return true; + } + + /** + * Constructs and returns a copy of the internal serialized object. + * + * @return The deserialized object. + * + * @throws IOException if an I/O exception occurs during deserialization. + * @throws ClassNotFoundException if the class of the deserialized object + * cannot be found. + */ + public T get() throws IOException, ClassNotFoundException + { + if (objBytes == null) + return null; + + RMIMarshalledObjectInputStream stream = + new RMIMarshalledObjectInputStream(objBytes, locBytes); + return (T) stream.readObject(); + } + + public int hashCode() + { + return hash; + } + +} diff --git a/libjava/classpath/java/rmi/Naming.java b/libjava/classpath/java/rmi/Naming.java new file mode 100644 index 000000000..7f74add7a --- /dev/null +++ b/libjava/classpath/java/rmi/Naming.java @@ -0,0 +1,234 @@ +/* Naming.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +/** + *

      + * The Naming class handles interactions with RMI registries. + * Each method takes a URL in String form, which points to + * the RMI registry. The scheme of the URL is irrelevant. The relevant + * part is: + *

      + *

      + * //host:port/name + *

      + *

      + * which tells the method how to locate and access the registry. The host + * and port are both optional, and default to `localhost' and the standard + * RMI registry port (1099) respectively. The name is simply a string + * used to refer to a particular service hosted by the registry. The + * registry does not attempt to interpret this further. + *

      + *

      + * RMI services are registered using one of these names, and the same name + * is later used by the client to lookup the service and access its methods. + * Registries can be shared by multiple services, or a service can create + * its own registry using createRegistry(). + *

      + * + * @author Original author unknown. + * @author Ingo Proetel (proetel@aicas.com) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.1 + */ +public final class Naming +{ + /** + * This class isn't intended to be instantiated. + */ + private Naming() + { + } + + /** + * Looks for the remote object that is associated with the named service. + * Name and location is given in form of a URL without a scheme: + * + *
      +   * //host:port/service-name
      +   * 
      + * + * The port is optional. + * + * @param name the service name and location + * @return Remote-object that implements the named service + * @throws NotBoundException if no object implements the service + * @throws MalformedURLException + * @throws RemoteException + */ + public static Remote lookup(String name) throws NotBoundException, + MalformedURLException, RemoteException + { + URL u = parseURL(name); + String serviceName = getName(u); + return (getRegistry(u).lookup(serviceName)); + } + + /** + * Try to bind the given object to the given service name. + * + * @param name + * @param obj + * @throws AlreadyBoundException + * @throws MalformedURLException + * @throws RemoteException + */ + public static void bind(String name, Remote obj) + throws AlreadyBoundException, MalformedURLException, RemoteException + { + URL u = parseURL(name); + String serviceName = getName(u); + getRegistry(u).bind(serviceName, obj); + } + + /** + * Remove a binding for a given service name. + * + * @param name + * @throws RemoteException + * @throws NotBoundException + * @throws MalformedURLException + */ + public static void unbind(String name) throws RemoteException, + NotBoundException, MalformedURLException + { + URL u = parseURL(name); + String serviceName = getName(u); + getRegistry(u).unbind(serviceName); + } + + /** + * Forces the binding between the given Remote-object and the given service + * name, even if there was already an object bound to this name. + * + * @param name + * @param obj + * @throws RemoteException + * @throws MalformedURLException + */ + public static void rebind(String name, Remote obj) throws RemoteException, + MalformedURLException + { + URL u = parseURL(name); + String serviceName = getName(u); + getRegistry(u).rebind(serviceName, obj); + } + + /** + * Lists all services at the named registry. + * + * @param name url that specifies the registry + * @return list of services at the name registry + * @throws RemoteException + * @throws MalformedURLException + */ + public static String[] list(String name) throws RemoteException, + MalformedURLException + { + return (getRegistry(parseURL(name)).list()); + } + + private static Registry getRegistry(URL u) throws RemoteException + { + if (u.getPort() == - 1) + { + return (LocateRegistry.getRegistry(u.getHost())); + } + else + { + return (LocateRegistry.getRegistry(u.getHost(), u.getPort())); + } + } + + /** + * Parses the supplied URL and converts it to use the HTTP protocol. From an + * RMI perspective, the scheme is irrelevant and we want to be able to create + * a URL for which a handler is available. + * + * @param name the URL in String form. + * @throws MalformedURLException if the URL is invalid. + */ + private static URL parseURL(String name) throws MalformedURLException + { + try + { + URI uri = new URI(name); + String host = uri.getHost(); + int port = uri.getPort(); + String query = uri.getQuery(); + String path = uri.getPath(); + return new URL("http", (host == null ? "localhost" : host), + (port == - 1 ? 1099 : port), uri.getPath() + + (query == null ? "" : query)); + } + catch (URISyntaxException e) + { + throw new MalformedURLException("The URL syntax was invalid: " + + e.getMessage()); + } + } + + /** + * Checks that the URL contains a name, and removes any leading slashes. + * + * @param url the URL to check. + * @throws MalformedURLException if no name is specified. + */ + private static String getName(URL url) throws MalformedURLException + { + String filename = url.getFile(); + if (filename.length() == 0) + throw new MalformedURLException("No path specified: " + url); + // If the filename begins with a slash we must cut it for + // name resolution. + if (filename.charAt(0) == '/') + return filename.substring(1); + return filename; + } +} diff --git a/libjava/classpath/java/rmi/NoSuchObjectException.java b/libjava/classpath/java/rmi/NoSuchObjectException.java new file mode 100644 index 000000000..a2af433c9 --- /dev/null +++ b/libjava/classpath/java/rmi/NoSuchObjectException.java @@ -0,0 +1,69 @@ +/* NoSuchObjectException.java -- thrown if the remote object no longer exists + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown on an attempt to invoke a call on an object that no longer exists + * in the remote Virtual Machine. The call may be retransmitted and still + * obey the semantics of "at most once". + * + * @author unknown + * @see java.rmi.server.RemoteObject#toStub(Remote) + * @see java.rmi.server.UnicastRemoteObject#unexportObject(Remote, boolean) + * @see java.rmi.activation.Activatable#unexportObject(Remote, boolean) + * @since 1.1 + * @status updated to 1.4 + */ +public class NoSuchObjectException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6619395951570472985L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NoSuchObjectException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/NotBoundException.java b/libjava/classpath/java/rmi/NotBoundException.java new file mode 100644 index 000000000..fdd646857 --- /dev/null +++ b/libjava/classpath/java/rmi/NotBoundException.java @@ -0,0 +1,76 @@ +/* NotBoundException.java -- attempt to use a registry name with no binding + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown on an attempt to lookup or unbind a registry name that has no + * associated binding. + * + * @author unknown + * @see java.rmi.Naming#lookup(String) + * @see java.rmi.Naming#unbind(String) + * @see java.rmi.registry.Registry#lookup(String) + * @see java.rmi.registry.Registry#unbind(String) + * @since 1.1 + * @status updated to 1.4 + */ +public class NotBoundException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -1857741824849069317l; + + /** + * Create an exception with no message. + */ + public NotBoundException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public NotBoundException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/RMISecurityException.java b/libjava/classpath/java/rmi/RMISecurityException.java new file mode 100644 index 000000000..7a2d214a3 --- /dev/null +++ b/libjava/classpath/java/rmi/RMISecurityException.java @@ -0,0 +1,79 @@ +/* RMISecurityException.java -- deprecated version of SecurityException + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Never thrown, but originally intended to wrap a + * {@link java.lang.SecurityException} in the case of RMI. + * + * @author unknown + * @since 1.1 + * @deprecated use {@link java.lang.SecurityException} instead + * @status updated to 1.4 + */ +public class RMISecurityException extends SecurityException +{ + /** + * Compatible with JDK 1.1. + */ + private static final long serialVersionUID = -8433406075740433514L; + + /** + * Create an exception with a message. + * + * @param n the message + * @deprecated no longer needed + */ + public RMISecurityException(String n) + { + super(n); + } + + /** + * Create an exception with a message and a cause. + * + * @param n the message + * @param a the cause (ignored) + * @deprecated no longer needed + */ + public RMISecurityException(String n, String a) + { + super(n); + } +} diff --git a/libjava/classpath/java/rmi/RMISecurityManager.java b/libjava/classpath/java/rmi/RMISecurityManager.java new file mode 100644 index 000000000..3f7e1fbcf --- /dev/null +++ b/libjava/classpath/java/rmi/RMISecurityManager.java @@ -0,0 +1,48 @@ +/* RMISecurityManager.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * @since 1.1 + */ +public class RMISecurityManager extends SecurityManager +{ + public RMISecurityManager() + { + } +} diff --git a/libjava/classpath/java/rmi/Remote.java b/libjava/classpath/java/rmi/Remote.java new file mode 100644 index 000000000..8c2720e42 --- /dev/null +++ b/libjava/classpath/java/rmi/Remote.java @@ -0,0 +1,58 @@ +/* Remote.java + Copyright (c) 1996, 1997, 1998, 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Marker interface for interfaces which methods are invokable + * from outside of this virtual machine through remote method calls. + *

      + * Remote invokable methods of remote object implementations are specified + * as the methods defined in the implemented remote interfaces. Typically + * remote object implementations are subclasses of the convenience classes + * {@link java.rmi.server.UnicastRemoteObject} or + * {@link java.rmi.activation.Activatable} implementing one or more remote + * interfaces indicating their remotely accessible methods. The convenience + * classes provide implementations for correct remote object creation, + * hash, equals and toString methods. + *

      + * + * @author unknown + */ +public interface Remote { + // marker interface +} diff --git a/libjava/classpath/java/rmi/RemoteException.java b/libjava/classpath/java/rmi/RemoteException.java new file mode 100644 index 000000000..276aa3774 --- /dev/null +++ b/libjava/classpath/java/rmi/RemoteException.java @@ -0,0 +1,128 @@ +/* RemoteException.java -- common superclass for exceptions in java.rmi + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +import java.io.IOException; + +/** + * The superclass of exceptions related to RMI (remote method invocation). + * Classes that implement java.rmi.Remote should list this + * exception in their throws clause. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class RemoteException extends IOException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -5148567311918794206l; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Throwable detail; + + /** + * Create an exception with no message, and cause initialized to null. + */ + public RemoteException() + { + this(null, null); + } + + /** + * Create an exception with the given message, and cause initialized to null. + * + * @param s the message + */ + public RemoteException(String s) + { + this(s, null); + } + + /** + * Create an exception with the given message and cause. + * + * @param s the message + * @param e the cause + */ + public RemoteException(String s, Throwable e) + { + super(s); + initCause(e); + detail = e; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * super.getMessage() + (detail == null ? "" + * : "; nested exception is:\n\t" + detail). + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; nested exception is:\n\t" + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the detail field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if detail == this, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} diff --git a/libjava/classpath/java/rmi/ServerError.java b/libjava/classpath/java/rmi/ServerError.java new file mode 100644 index 000000000..b1a15b02e --- /dev/null +++ b/libjava/classpath/java/rmi/ServerError.java @@ -0,0 +1,64 @@ +/* ServerError.java -- wraps an error while creating the server + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Wraps any error thrown while processing the server of a remote call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class ServerError extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8455284893909696482L; + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ServerError(String s, Error e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/ServerException.java b/libjava/classpath/java/rmi/ServerException.java new file mode 100644 index 000000000..5170aa7b0 --- /dev/null +++ b/libjava/classpath/java/rmi/ServerException.java @@ -0,0 +1,74 @@ +/* ServerException.java -- wraps an exception while creating the server + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Wraps any exception thrown while processing the server of a remote call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class ServerException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4775845313121906682l; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ServerException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ServerException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/ServerRuntimeException.java b/libjava/classpath/java/rmi/ServerRuntimeException.java new file mode 100644 index 000000000..1f0813739 --- /dev/null +++ b/libjava/classpath/java/rmi/ServerRuntimeException.java @@ -0,0 +1,67 @@ +/* ServerRuntimeException.java -- wraps an exception while creating the server + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Wraps any runtime exception thrown while processing the server of a + * remote call. Note, this exception is no longer used. + * + * @author unknown + * @since 1.1 + * @deprecated no replacement + * @status updated to 1.4 + */ +public class ServerRuntimeException extends RemoteException +{ + /** + * Compatible with JDK 1.1. + */ + private static final long serialVersionUID = 7054464920481467219L; + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + * @deprecated no longer needed + */ + public ServerRuntimeException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/StubNotFoundException.java b/libjava/classpath/java/rmi/StubNotFoundException.java new file mode 100644 index 000000000..992df1217 --- /dev/null +++ b/libjava/classpath/java/rmi/StubNotFoundException.java @@ -0,0 +1,78 @@ +/* StubNotFoundException.java -- thrown if a valid stub is not found + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if a valid stub class is not found for an object when it is exported. + * + * @author unknown + * @see java.rmi.server.UnicastRemoteObject + * @see java.rmi.activation.Activatable + * @since 1.1 + * @status updated to 1.4 + */ +public class StubNotFoundException extends RemoteException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -7088199405468872373L; + + + /** + * Create an exception with a message. + * + * @param s the message + */ + public StubNotFoundException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public StubNotFoundException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/UnexpectedException.java b/libjava/classpath/java/rmi/UnexpectedException.java new file mode 100644 index 000000000..e9e0d6a9d --- /dev/null +++ b/libjava/classpath/java/rmi/UnexpectedException.java @@ -0,0 +1,75 @@ +/* UnexpectedException.java -- an unexpected checked exception was received + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if an unexpected checked exception was received in a remote + * procedure call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class UnexpectedException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 1800467484195073863L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnexpectedException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public UnexpectedException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/UnknownHostException.java b/libjava/classpath/java/rmi/UnknownHostException.java new file mode 100644 index 000000000..7e77dca31 --- /dev/null +++ b/libjava/classpath/java/rmi/UnknownHostException.java @@ -0,0 +1,75 @@ +/* UnknownHostException.java -- wraps java.net.UnknownHostException in RMI + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if a java.net.UnknownHostException occurs during a remote + * procedure call. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class UnknownHostException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8152710247442114228L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnknownHostException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public UnknownHostException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/UnmarshalException.java b/libjava/classpath/java/rmi/UnmarshalException.java new file mode 100644 index 000000000..6567062a5 --- /dev/null +++ b/libjava/classpath/java/rmi/UnmarshalException.java @@ -0,0 +1,88 @@ +/* UnmarshalException.java -- wraps error while unmarshalling parameters + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi; + +/** + * Thrown if an exception occurs while unmarshalling parameters or results + * of a remote method call. This includes:
        + *
      • if an exception occurs while unmarshalling the call header
      • + *
      • if the protocol for the return value is invalid
      • + *
      • if a java.io.IOException occurs unmarshalling parameters (on the + * server side) or the return value (on the client side).
      • + *
      • if a java.lang.ClassNotFoundException occurs during unmarshalling + * parameters or return values
      • + *
      • if no skeleton can be loaded on the server-side; note that skeletons + * are required in the 1.1 stub protocol, but not in the 1.2 stub + * protocol.
      • + *
      • if the method hash is invalid (i.e., missing method).
      • + *
      • if there is a failure to create a remote reference object for a remote + * object's stub when it is unmarshalled.
      • + *
      + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class UnmarshalException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 594380845140740218l; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnmarshalException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public UnmarshalException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/activation/Activatable.java b/libjava/classpath/java/rmi/activation/Activatable.java new file mode 100644 index 000000000..9ec7cad48 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/Activatable.java @@ -0,0 +1,531 @@ +/* Activatable.java -- A common ancestor for the activatable objects. + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import gnu.java.rmi.server.ActivatableServerRef; +import gnu.java.rmi.server.UnicastServer; +import gnu.java.rmi.server.UnicastServerRef; + +import java.lang.reflect.Field; +import java.rmi.MarshalledObject; +import java.rmi.NoSuchObjectException; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.server.ObjID; +import java.rmi.server.RMIClientSocketFactory; +import java.rmi.server.RMIServerSocketFactory; +import java.rmi.server.RemoteObject; +import java.rmi.server.RemoteServer; +import java.rmi.server.UnicastRemoteObject; + +/** + * A common ancestor for the implementations of the activatable objects. Such + * objects require persistent access over time and can be activated by the + * system. The derived classes also implements the needed interface of some + * remote object and usually have the two parameter constructor, the first + * parameter being the {@link ActivationID} and the second the + * {@link MarshalledObject}. Activatable is the main class that developers need + * to use to implement and manage activatable objects. It also contains methods + * for making activatable remote objects that are not derived from the + * Activatable class. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub) + */ +public abstract class Activatable + extends RemoteServer +{ + + /** + * Use SVUID for interoperability. + */ + static final long serialVersionUID = - 3120617863591563455L; + + /** + * The object activation id. + */ + final ActivationID id; + + /** + * This constructor is used to register export the object on the given port. A + * subclass of the Activatable class calls this constructor to register and + * export the object during initial construction. As a side-effect of + * activatable object construction, the remote object is both "registered" + * with the activation system and "exported" (on an anonymous port, if port is + * zero) to the RMI runtime so that it is available to accept incoming calls + * from clients. + * + * @param codebase the object code base url + * @param data the data, needed to activate the object. + * @param restart specifies reactivation mode after crash. If true, the object + * is activated when activator is restarted or the activation group + * is restarted. If false, the object is only activated on demand. + * This flag does has no effect during the normal operation (the + * object is normally activated on demand). + * @param port the port, on which the object will become available. The value + * 0 means anonymous port. + * @throws ActivationException if the activation failed + * @throws RemoteException if the remote call failed. + */ + protected Activatable(String codebase, MarshalledObject data, + boolean restart, int port) throws ActivationException, + RemoteException + { + ActivationDesc descriptor = new ActivationDesc(getClass().getName(), + codebase, data, restart); + id = obtainId(descriptor); + exportObject(this, id, port); + } + + /** + * This constructor is used to register export the object on the given port, + * additionally specifying the socket factories. A subclass of the Activatable + * class calls this constructor to register and export the object during + * initial construction. + * + * @param codebase the object code base url + * @param data the data, needed to activate the object. + * @param restart specifies reactivation mode after crash. If true, the object + * is activated when activator is restarted or the activation group + * is restarted. If false, the object is only activated on demand. + * This flag does has no effect during the normal operation (the + * object is normally activated on demand). + * @param port the port, on which the object will become available. The value + * 0 means anonymous port. + * @param csf the client socket factory + * @param ssf the server socket factory + * @throws ActivationException if the activation failed + * @throws RemoteException if the remote call failed. + */ + protected Activatable(String codebase, MarshalledObject data, + boolean restart, int port, RMIClientSocketFactory csf, + RMIServerSocketFactory ssf) throws ActivationException, + RemoteException + { + ActivationDesc descriptor = new ActivationDesc(getClass().getName(), + codebase, data, restart); + id = obtainId(descriptor); + exportObject(this, id, port); + } + + /** + * Creates the new instance of activatable with the given activation id and is + * listening at the given port. A subclass of the Activatable class calls this + * constructor when the object itself is activated via its special + * "activation" constructor with the two parameters ({@link ActivationID}, + * {@link MarshalledObject}). As a side effect, the object is exported and is + * available to accept incoming calls. + * + * @param anId the activation id + * @param port the port, on which the activatable will be listening + * @throws RemoteException if the activation failed. + */ + protected Activatable(ActivationID anId, int port) throws RemoteException + { + id = anId; + try + { + exportObject(this, anId, port); + } + catch (Exception e) + { + e.printStackTrace(); + RemoteException acex = + new RemoteException("cannot export Activatable", e); + throw acex; + } + } + + /** + * Creates the new instance of activatable with the given activation id and is + * listening at the given port, using the specified client and server sockets + * factories. A subclass of the Activatable class calls this + * constructor when the object itself is activated via its special + * "activation" constructor with the two parameters ({@link ActivationID}, + * {@link MarshalledObject}). As a side effect, the object is exported and is + * available to accept incoming calls. + * + * @param anId the activation id + * @param port the port, on which the activatable will be listening + * @param csf the client socket factory + * @param ssf the server socket factory + * + * @throws RemoteException if the remote call failed + */ + protected Activatable(ActivationID anId, int port, RMIClientSocketFactory csf, + RMIServerSocketFactory ssf) throws RemoteException + { + id = anId; + try + { + exportObject(this, anId, port, csf, ssf); + } + catch (Exception e) + { + RemoteException acex = new RemoteException(); + acex.initCause(e); + throw acex; + } + } + + /** + * Get the objects activation identifier. + * + * @return the object activation identifier + */ + protected ActivationID getID() + { + return id; + } + + /** + * Obtain the activation Id from the activation descriptor by registering + * within the current group. + */ + static ActivationID obtainId(ActivationDesc descriptor) + throws RemoteException, UnknownGroupException, ActivationException + { + ActivationGroupID id = descriptor.getGroupID(); + ActivationSystem system; + + if (id != null) + system = id.getSystem(); + else + system = ActivationGroup.currentGroupID().getSystem(); + return system.registerObject(descriptor); + } + + /** + * This method registers an activatable object. The object is expected to be + * on the anonymous port (null client and server socket factories). + * + * @param desc the object description. + * @return the remote stub for the activatable object (the first call on this + * stub will activate the object). + * @throws UnknownGroupException if the object group identifier is unknown + * @throws ActivationException if the activation system is not running + * @throws RemoteException if the remote call fails + */ + public static Remote register(ActivationDesc desc) + throws UnknownGroupException, ActivationException, RemoteException + { + ActivationID id = obtainId(desc); + try + { + return toStub( + id, + Thread.currentThread().getContextClassLoader().loadClass( + desc.getClassName())); + } + catch (ClassNotFoundException e) + { + throw new ActivationException("Class not found: "+desc.getClassName()); + } + } + + /** + * Inactivates and unexports the object. The subsequent calls will activate + * the object again. The object is not inactivated if it is currently + * executing calls. + * + * @param id the id of the object being inactivated + * @return true if the object has been inactivated, false if it has not been + * inactivated because of the running or pending calls. + * @throws UnknownObjectException if the object is unknown. + * @throws ActivationException if the object group is not active + * @throws RemoteException if the remote call fails + */ + public static boolean inactive(ActivationID id) + throws UnknownObjectException, ActivationException, RemoteException + { + if (id.group!=null) + id.group.inactiveObject(id); + return UnicastRemoteObject.unexportObject(id.activate(false), false); + } + + /** + * Unregister the object (the object will no longer be activable with that id) + * + * @param id the object id + * @throws UnknownObjectException if the id is unknown + * @throws ActivationException if the activation system is not running + * @throws RemoteException if the remote call fails. + */ + public static void unregister(ActivationID id) throws UnknownObjectException, + ActivationException, RemoteException + { + ActivationGroup.currentGroupId.getSystem().unregisterObject(id); + UnicastServer.unregisterActivatable(id); + } + + /** + * Register and export the object that activatable object that is not derived + * from the Activatable super class. It creates and registers the object + * activation descriptor. There is no need to call this method if the object + * extends Activable, as its work is done in the constructor + * {@link #Activatable(String, MarshalledObject, boolean, int)}. + * + * @param obj the object, that is exported, becoming available at the given + * port. + * @param location the object code location (codebase). + * @param data the data, needed to activate the object + * @param restart the restart mode + * @param port the port, where the object will be available + * + * @return the created object activation ID. + * + * @throws ActivationException if the activation group is not active + * @throws RemoteException if the registration or export fails + */ + public static ActivationID exportObject(Remote obj, String location, + MarshalledObject data, + boolean restart, int port) + throws ActivationException, RemoteException + { + ActivationDesc descriptor = new ActivationDesc(obj.getClass().getName(), + location, data, restart); + ActivationID id = obtainId(descriptor); + Remote stub = exportObject(obj, id, port); + return id; + } + + /** + * Register and export the object that activatable object that is not derived + * from the Activatable super class. It creates and registers the object + * activation descriptor. There is no need to call this method if the object + * extends Activable, as its work is done in the constructor + * {@link #Activatable(String, MarshalledObject, boolean, int, RMIClientSocketFactory, RMIServerSocketFactory)} + * + * @param obj the object, that is exported, becoming available at the given + * port. + * @param location the object code location (codebase). + * @param data the data, needed to activate the object + * @param restart the restart mode + * @param port the port, where the object will be available + * @param csf the client socket factory + * @param ssf the server socket factory + * + * @return the created object activation ID. + * + * @throws ActivationException if the activation group is not active + * @throws RemoteException if the registration or export fails + */ + public static ActivationID exportObject(Remote obj, String location, + MarshalledObject data, + boolean restart, int port, + RMIClientSocketFactory csf, + RMIServerSocketFactory ssf) + throws ActivationException, RemoteException + { + ActivationDesc descriptor = new ActivationDesc(obj.getClass().getName(), + location, data, restart); + ActivationID id = obtainId(descriptor); + Remote stub = exportObject(obj, id, port, csf, ssf); + return id; + + } + + /** + * During activation, this exportObject method should be invoked explicitly by + * the activatable object, that does is not derived from the Activatable + * class. There is no need to call this method if the object extends + * Activable, as its work is done in the constructor + * {@link #Activatable(ActivationID, int)} + * + * @param obj the object + * @param id the known activation id + * @param port the object port + * + * @return the remote stub of the activatable object + * + * @throws RemoteException if the object export fails + */ + public static Remote exportObject(Remote obj, ActivationID id, int port) + throws RemoteException + { + Remote stub = export(id, obj, port, null); + return stub; + } + + /** + * During activation, this exportObject method should be invoked explicitly by + * the activatable object, that does is not derived from the Activatable + * class. There is no need to call this method if the object extends + * Activable, as its work is done in the constructor + * {@link #Activatable(ActivationID, int)} + * + * @param obj the object + * @param id the known activation id + * @param port the object port + * @param csf the client socket factory + * @param ssf the server socket factory + * + * @return the remote stub of the activatable object + * + * @throws RemoteException if the object export fails + */ + public static Remote exportObject(Remote obj, ActivationID id, int port, + RMIClientSocketFactory csf, + RMIServerSocketFactory ssf) + throws RemoteException + { + Remote stub = export(id, obj, port, ssf); + return stub; + + } + + /** + * Make the remote object unavailable for incoming calls. This method also + * unregisters the object, so it cannot be activated again by incoming call + * (unless registered). + * + * @param obj the object to unexport + * @param force if true, cancel all pending or running calls to that object + * (if false, the object with such calls is not unexported and false + * is returned by this method). + * @return if the object was successfully unexported, false otherwise + * @throws NoSuchObjectException if such object is not known + */ + public static boolean unexportObject(Remote obj, boolean force) + throws NoSuchObjectException + { + Object aref = UnicastServer.getExportedRef(obj); + + // Unregister it also (otherwise will be activated during the subsequent + // call. + if (aref instanceof ActivatableServerRef) + { + ActivatableServerRef aar = (ActivatableServerRef) aref; + UnicastServer.unregisterActivatable(aar.actId); + } + return UnicastRemoteObject.unexportObject(obj, force); + } + + static Remote exportObject(Remote obj, int port, + RMIServerSocketFactory serverSocketFactory) + throws RemoteException + { + UnicastServerRef sref = null; + if (obj instanceof RemoteObject) + sref = (UnicastServerRef) ((RemoteObject) obj).getRef(); + + if (sref == null) + sref = new UnicastServerRef(new ObjID(), port, serverSocketFactory); + + Remote stub = sref.exportObject(obj); + // addStub(obj, stub); + // TODO Need to change the place of the stub repository + return stub; + } + + /** + * Create and export the new remote object, making it available at the given + * port, using sockets, produced by the specified factories. + * + * @param port the port, on that the object should become available. Zero + * means anonymous port. + * @param serverSocketFactory the server socket factory + */ + private static Remote export(ActivationID id, Remote obj, int port, + RMIServerSocketFactory serverSocketFactory) + throws RemoteException + { + ActivatableServerRef sref = null; + sref = new ActivatableServerRef(makeId(id), id, port, serverSocketFactory); + return sref.exportObject(obj); + } + + /** + * Make the object ID from the activation ID. The same activation ID always + * produces the identical object id. + * + * @param aid the activation id + * + * @return the object id + */ + private static ObjID makeId(ActivationID aid) + { + ObjID id = new ObjID(0); + + // The fields of both ObjID and ActivationID must be package private, + // so we need to use the reflection to access them anyway. + // Probably other implementations use some very different approach. + + try + { + Field idUid = ObjID.class.getDeclaredField("space"); + Field aidUid = ActivationID.class.getDeclaredField("uid"); + + aidUid.setAccessible(true); + idUid.setAccessible(true); + + idUid.set(id, aidUid.get(aid)); + } + catch (Exception e) + { + InternalError ierr = new InternalError("Unable to set UID field"); + ierr.initCause(e); + throw ierr; + } + + return id; + } + + /** + * Connect the object to the UnicastServer (export), but not activate it. + * The object will be activated on the first call. + */ + static Remote toStub(ActivationID anId, Class stubFor) + { + try + { + ActivatableServerRef asr = + new ActivatableServerRef(makeId(anId), anId, 0, null); + UnicastServer.exportActivatableObject(asr); + return asr.exportClass(stubFor); + } + catch (RemoteException e) + { + InternalError ierr = new InternalError( + "Failed to obtain activatable stub"); + ierr.initCause(e); + throw ierr; + } + } +} diff --git a/libjava/classpath/java/rmi/activation/ActivateFailedException.java b/libjava/classpath/java/rmi/activation/ActivateFailedException.java new file mode 100644 index 000000000..1c2e10ee3 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivateFailedException.java @@ -0,0 +1,76 @@ +/* ActivateFailedException.java -- thrown when activation fails + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +import java.rmi.RemoteException; + +/** + * Thrown when activation fails on a remote call to an activatable object. + * + * @author unknown + * @since 1.2 + * @status updated to 1.4 + */ +public class ActivateFailedException extends RemoteException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 4863550261346652506L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ActivateFailedException(String s) + { + super(s); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param ex the cause + */ + public ActivateFailedException(String s, Exception ex) + { + super(s, ex); + } +} diff --git a/libjava/classpath/java/rmi/activation/ActivationDesc.java b/libjava/classpath/java/rmi/activation/ActivationDesc.java new file mode 100644 index 000000000..9970cd63b --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationDesc.java @@ -0,0 +1,254 @@ +/* ActivationDesc.java -- record with info to activate an object + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +import java.io.Serializable; +import java.rmi.MarshalledObject; + +/** + * Contains the information, necessary to activate the object. This information + * includes: + *
        + *
      • the object class name
      • + *
      • the object group identifier
      • + *
      • the code location (codebase URL) that can be used to load the class + * remotely
      • + *
      • the object restart mode
      • + *
      • the object specific intialization information
      • + *
      + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub) + */ +public final class ActivationDesc + implements Serializable +{ + /** + * Use SVUID for interoperability. + */ + static final long serialVersionUID = 7455834104417690957L; + + /** + * The group id. + */ + private ActivationGroupID groupid; + + /** + * The class name. + */ + private String classname; + + /** + * The code location URL. + */ + private String location; + + /** + * The object specific intitalization data. + */ + private MarshalledObject data; + + /** + * The start mode. + */ + private boolean restart; + + /** + * Create the new activation description, assuming the object group is the + * {@link ActivationGroup#currentGroupID()}. + * + * @param className the object fully qualified class name + * @param location the code base URL + * @param data the object initialization data, contained in a marshalled form + */ + public ActivationDesc(String className, String location, MarshalledObject data) + throws ActivationException + { + this(ActivationGroup.currentGroupID(), className, location, data, false); + } + + /** + * Create the new activation description, assuming the object group is the + * {@link ActivationGroup#currentGroupID()}. + * + * @param className the object fully qualified class name + * @param location the code base URL + * @param data the object initialization data, contained in a marshalled form + * @param restart specifies reactivation mode after crash. If true, the object + * is activated when activator is restarted or the activation group + * is restarted. If false, the object is only activated on demand. + * This flag does has no effect during the normal operation (the + * object is normally activated on demand). + */ + public ActivationDesc(String className, String location, + MarshalledObject data, boolean restart) + throws ActivationException + { + this(ActivationGroup.currentGroupID(), className, location, data, restart); + } + + /** + * Create the new activation description. Under crash, the object will only + * be reactivated on demand. + * + * @param groupID the object group id. + * @param className the object fully qualified class name + * @param location the code base URL + * @param data the object initialization data, contained in a marshalled form + */ + public ActivationDesc(ActivationGroupID groupID, String className, + String location, MarshalledObject data) + { + this(groupID, className, location, data, false); + } + + /** + * Create the new activation description, providing full information. + * + * @param groupID the object group id. + * @param className the object fully qualified class name + * @param location the code base URL + * @param data the object initialization data, contained in a marshalled form + * @param restart specifies reactivation mode after crash. If true, the object + * is activated when activator is restarted or the activation group + * is restarted. If false, the object is only activated on demand. + * This flag does has no effect during the normal operation (the + * object is normally activated on demand). + */ + public ActivationDesc(ActivationGroupID groupID, String className, + String location, MarshalledObject data, boolean restart) + { + this.groupid = groupID; + this.classname = className; + this.location = location; + this.data = data; + this.restart = restart; + } + + public ActivationGroupID getGroupID() + { + return groupid; + } + + /** + * Get the class name of the object being activated + * + * @return the fully qualified class name of the object being activated + */ + public String getClassName() + { + return classname; + } + + /** + * Get the code location URL ("codebase") of the object being activated. + * + * @return the codebase of the object being activated. + */ + public String getLocation() + { + return location; + } + + public MarshalledObject getData() + { + return data; + } + + /** + * Get the object reactivation strategy after crash. + * + * @return true ir the object is activated when activator is restarted or the + * activation group is restarted. False if the object is only + * activated on demand. This flag does has no effect during the normal + * operation (the object is normally activated on demand). + */ + public boolean getRestartMode() + { + return restart; + } + + /** + * Compare this object with another activation description for equality. + * + * @return true if all fields have the equal values, false otherwise. + */ + public boolean equals(Object obj) + { + if (obj instanceof ActivationDesc) + { + ActivationDesc that = (ActivationDesc) obj; + return eq(groupid, that.groupid) && + eq(classname, that.classname) && + eq(location, that.location) && + eq(data, that.data) + && restart == that.restart; + } + else + return false; + } + + /** + * Get the hash code of this object (overridden to make the returned value + * consistent with .equals(..). + */ + public int hashCode() + { + return hash(groupid) ^ hash(classname) ^ + hash(location) ^ hash(data); + } + + /** + * Get the hashcode of x or 0 if x == null. + */ + static final int hash(Object x) + { + return x == null ? 0 : x.hashCode(); + } + + /** + * Compare by .equals if both a and b are not null, compare directly if at + * least one of them is null. + */ + static final boolean eq(Object a, Object b) + { + if (a == null || b == null) + return a == b; + else + return a.equals(b); + } +} diff --git a/libjava/classpath/java/rmi/activation/ActivationException.java b/libjava/classpath/java/rmi/activation/ActivationException.java new file mode 100644 index 000000000..418f43857 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationException.java @@ -0,0 +1,122 @@ +/* ActivationException.java -- general Activation exception + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +/** + * General exception class for java.rmi.activation. + * + * @author unknown + * @since 1.2 + * @status updated to 1.4 + */ +public class ActivationException extends Exception +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -4320118837291406071L; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Throwable detail; + + /** + * Create an exception with no message, and cause initialized to null. + */ + public ActivationException() + { + this(null, null); + } + + /** + * Create an exception with the given message, and cause initialized to null. + * + * @param s the message + */ + public ActivationException(String s) + { + this(s, null); + } + + /** + * Create an exception with the given message and cause. + * + * @param s the message + * @param ex the cause + */ + public ActivationException(String s, Throwable ex) + { + super(s, ex); + detail = ex; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * super.getMessage() + (detail == null ? "" + * : "; nested exception is:\n\t" + detail). + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; nested exception is:\n\t" + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the detail field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if detail == this, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} diff --git a/libjava/classpath/java/rmi/activation/ActivationGroup.java b/libjava/classpath/java/rmi/activation/ActivationGroup.java new file mode 100644 index 000000000..230c71455 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationGroup.java @@ -0,0 +1,340 @@ +/* ActivationGroup.java -- the RMI activation group. + Copyright (c) 1996, 1997, 1998, 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import gnu.java.rmi.activation.DefaultActivationGroup; +import gnu.java.rmi.activation.DefaultActivationSystem; + +import java.lang.reflect.Constructor; +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; + +/** + * The entity that receives the request to activate object and activates it. + * Frequently there is one activation group per virtual machine. + * + * @author Audrius Meskauskas (audriusa@Bioinformatics.org) (from stub) + */ +public abstract class ActivationGroup + extends UnicastRemoteObject + implements ActivationInstantiator +{ + + /** + * Use the SVUID for interoperability. + */ + static final long serialVersionUID = - 7696947875314805420L; + + /** + * The Id of the current group on this VM (null if none). + */ + static ActivationGroupID currentGroupId = null; + + /** + * The groups identifier. + */ + final ActivationGroupID groupId; + + /** + * The groups activation monitor. + */ + ActivationMonitor monitor; + + /** + * The groups incarnation number. + */ + long incarnation; + + /** + * The groups activation system. + */ + static ActivationSystem system; + + /** + * Used during the group creation (required constructor). + */ + static final Class[] cConstructorTypes = new Class[] + { + ActivationGroupID.class, + MarshalledObject.class + }; + + /** + * Create the new activation group with the given group id. + * + * @param aGroupId the group Id. + * + * @throws RemoteException if the group export fails. + */ + protected ActivationGroup(ActivationGroupID aGroupId) throws RemoteException + { + groupId = aGroupId; + } + + /** + * The method is called when the object is exported. The group must notify + * the activation monitor, if this was not already done before. + * + * @param id the object activation id + * @param obj the remote object implementation + * + * @throws ActivationException if the group is inactive + * @throws UnknownObjectException if such object is not known + * @throws RemoteException if the call to monitor fails + */ + public abstract void activeObject(ActivationID id, Remote obj) + throws ActivationException, UnknownObjectException, RemoteException; + + /** + * Notifies the monitor about the object being inactivated. + * + * @param id the object being inactivated. + * @return true always (must be overridden to return other values). + * @throws ActivationException never + * @throws UnknownObjectException if the object is not known + * @throws RemoteException if the remote call to monitor fails + */ + public boolean inactiveObject(ActivationID id) throws ActivationException, + UnknownObjectException, RemoteException + { + if (monitor != null) + monitor.inactiveObject(id); + return true; + } + + /** + * Create the new instance of the activation group, using the class name and + * location information, stored in the passed descriptor. The method expects + * the group class to have the two parameter constructor, the first parameter + * being the {@link ActivationGroupID} and the second the + * {@link MarshalledObject}. The group must be first be registered with the + * ActivationSystem. Once a group is created, the currentGroupID method + * returns the identifier for this group until the group becomes inactive. + * + * @param id the activation group id + * @param desc the group descriptor, providing the information, necessary to + * create the group + * @param incarnation the incarnation number + * @return the created group instance + * @throws ActivationException if the activation fails due any reason + */ + public static ActivationGroup createGroup(ActivationGroupID id, + ActivationGroupDesc desc, + long incarnation) + throws ActivationException + { + // If the activation system is not yet set, set it to the system. + // passed in the group id. + if (system == null) + system = id.system; + + ActivationGroup group = null; + + // TODO at the moment all groups are created on the current jre and the + // group class must be reachable via thread context class loader. + Class groupClass; + + if (desc.className != null) + { + try + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + groupClass = loader.loadClass(desc.className); + } + catch (ClassNotFoundException e) + { + ActivationException acex = new ActivationException( + "Cannot load " + desc.className); + acex.detail = e; + throw acex; + } + } + else + groupClass = DefaultActivationGroup.class; + + try + { + Constructor constructor = groupClass.getConstructor(cConstructorTypes); + group = (ActivationGroup) constructor.newInstance( + new Object[] { id, desc.data }); + } + catch (Exception e) + { + ActivationException acex = new ActivationException( + "Cannot instantiate " + desc.className); + acex.detail = e; + throw acex; + } + + currentGroupId = id; + try + { + group.monitor = getSystem().activeGroup(id, group, incarnation); + return group; + } + catch (RemoteException e) + { + ActivationException acex = new ActivationException("createGroup"); + acex.detail = e; + throw acex; + } + } + + /** + * Get the id of current activation group. + * + * @return the id of the current activation group or null if none exists. + */ + public static ActivationGroupID currentGroupID() + { + try + { + if (currentGroupId==null) + { + // This will also assing the currentGroupId to the current + // (default) group of the default system. + setSystem(DefaultActivationSystem.get()); + } + } + catch (ActivationException e) + { + InternalError ierr = new InternalError("Unable to activate AS"); + ierr.initCause(e); + throw ierr; + } + + return currentGroupId; + } + + /** + * Set the activation system for this virtual machine. The system can only + * be set if no group is active. + * + * @param aSystem the system to set + * + * @throws ActivationException if some group is active now. + */ + public static void setSystem(ActivationSystem aSystem) + throws ActivationException + { + if (currentGroupId!=null) + throw new ActivationException("Group active"); + else + { + try + { + // Register the default transient activation system and group. + system = aSystem; + ActivationGroupDesc def = new ActivationGroupDesc( + DefaultActivationGroup.class.getName(), + "", + null, + null, + null); + currentGroupId = system.registerGroup(def); + } + catch (Exception ex) + { + InternalError ierr = new InternalError("Unable to start default AG"); + ierr.initCause(ex); + throw ierr; + } + } + } + + /** + * Get the current activation system. If the system is not set via + * {@link #setSystem} method, the default system for this virtual machine is + * returned. The default system is first searched by name + * "java.rmi.activation.ActivationSystem" on the activation registry port. The + * default value of the activation registry port is + * {@link ActivationSystem#SYSTEM_PORT}, but it can be changed by putting the + * system property java.rmi.activation.port. Both activation system and + * activation registry are provided by the RMI daemon tool, RMID, if it is + * running on the local host. If the RMID is not running, the internal + * transient activation system will be created and returned. This internal + * system is highly limited in in capabilities and is not intended to be used + * anywhere apart automated testing. + * + * @return the activation system for this virtual machine + * @throws ActivationException + */ + public static ActivationSystem getSystem() throws ActivationException + { + if (system == null) + system = DefaultActivationSystem.get(); + return system; + } + + /** + * Makes the call back to the groups {@link ActivationMonitor}. + * + * @param id the id obj the object being activated + * @param mObject the marshalled object, contains the activated remote object + * stub. + * @throws ActivationException on activation error + * @throws UnknownObjectException if such object is not registered + * @throws RemoteException on remote call (to monitor) error + */ + protected void activeObject(ActivationID id, + MarshalledObject mObject) + throws ActivationException, UnknownObjectException, RemoteException + { + if (monitor!=null) + monitor.activeObject(id, mObject); + + id.group = this; + } + + /** + * Makes the call back to the groups {@link ActivationMonitor} and sets + * the current group to null. + */ + protected void inactiveGroup() throws UnknownGroupException, RemoteException + { + if (monitor!=null) + monitor.inactiveGroup(groupId, incarnation); + + if (currentGroupId!=null && currentGroupId.equals(groupId)) + currentGroupId = null; + } + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationGroupDesc.java b/libjava/classpath/java/rmi/activation/ActivationGroupDesc.java new file mode 100644 index 000000000..a0c88ec0a --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationGroupDesc.java @@ -0,0 +1,433 @@ +/* ActivationGroupDesc.java -- the RMI activation group descriptor + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import gnu.java.rmi.activation.DefaultActivationGroup; + +import java.io.Serializable; +import java.rmi.MarshalledObject; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Properties; +import java.util.TreeSet; +import java.util.zip.Adler32; + +/** + * Contains information, necessary to create of recreate the activation objects. + * The group descriptor contains: + *
        + *
      • The name of the group's class. This class is derived from the + * {@link ActivationGroup}.
      • + *
      • The group class code location.
      • + *
      • The marshalled object that contains the group specific initialization + * information
      • + *
      + * The groups are created by the {@link ActivationGroup#createGroup} method that + * expectes the group class to have the two parameter constructor, the first + * parameter being the {@link ActivationGroupID} and the second the + * {@link MarshalledObject}. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub) + */ +public final class ActivationGroupDesc + implements Serializable +{ + /** + * Contains the startup options for the {@link ActivationGroup} + * implementations. Allows to override system properties and specify other + * options for the implementation groups. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub) + */ + public static class CommandEnvironment + implements Serializable + { + + /** + * Use the SVUID for interoperability. + */ + static final long serialVersionUID = 6165754737887770191L; + + /** + * The zero size string array used as argv value when null is passed. + */ + private static final String[] NO_ARGS = new String[0]; + + /** + * The path to the java executable (or null for using default jre). + */ + final String command; + + /** + * The extra parameters (may be empty array but never null). + */ + final String[] options; + + /** + * Create the new command environment. + * + * @param commandPatch the full path (and name) to the java executable of + * null for using the default executable. + * @param args extra options that will be used when creating the activation + * group. Null has the same effect as the empty list. + */ + public CommandEnvironment(String commandPatch, String[] args) + { + command = commandPatch; + if (args != null) + options = args; + else + options = NO_ARGS; + } + + /** + * Get the path to the java executable. + * + * @return the path to the java executable or null for using the default + * jre. + */ + public String getCommandPath() + { + return command; + } + + /** + * Get the additional command options. + * + * @return the command options array, may be empty string + */ + public String[] getCommandOptions() + { + return options; + } + + /** + * Compare for content equality. + */ + public boolean equals(Object obj) + { + if (obj instanceof CommandEnvironment) + { + CommandEnvironment that = (CommandEnvironment) obj; + + if (command == null || that.command == null) + { + // Use direct comparison if null is involved. + if (command != that.command) + return false; + } + else + { + // Use .equals if null is not involved. + if (! this.command.equals(that.command)) + return false; + } + + return Arrays.equals(options, that.options); + } + else + return false; + } + + /** + * Get the hash code. + */ + public int hashCode() + { + int h = command == null ? 0 : command.hashCode(); + for (int i = 0; i < options.length; i++) + h ^= options[i].hashCode(); + + return h; + } + } + + /** + * Use the SVUID for interoperability. + */ + static final long serialVersionUID = - 4936225423168276595L; + + /** + * The group class name or null for the default group class implementation. + */ + final String className; + + /** + * The group class download location URL (codebase), ignored by the + * default implementation. + */ + final String location; + + /** + * The group initialization data. + */ + final MarshalledObject data; + + /** + * The path to the group jre and the parameters of this jre, may be + * null for the default jre. + */ + final ActivationGroupDesc.CommandEnvironment env; + + /** + * The properties that override the system properties. + */ + final Properties props; + + /** + * The cached hash code. + */ + transient long hash; + + /** + * Create the new activation group descriptor that will use the default + * activation group implementation with the given properties and + * environment. + * + * @param aProperties the properties that override the system properties + * @param environment the command line (and parameters), indicating, where to + * find the jre executable and with that parameters to call it. May + * be null if the default executable should be used. In this case, + * the activation group with the null name (the system default group) + * will be created. + */ + public ActivationGroupDesc(Properties aProperties, + ActivationGroupDesc.CommandEnvironment environment) + { + this(DefaultActivationGroup.class.getName(), null, null, aProperties, + environment); + } + + /** + * Create the new activation group descriptor. + * + * @param aClassName the name of the group implementation class. The null + * value indicates the default implementation. + * @param aLocation the location, from where the group implementation class + * should be loaded (ignored for the system default implementation). + * @param aData the group intialization data + * @param aProperties the properties that will override the system properties + * of the new group. These properties will be translated into -D + * options. + * @param environment the record, containing path to the jre executable and + * start options for the jre or null for using the default jre and + * options. + */ + public ActivationGroupDesc(String aClassName, String aLocation, + MarshalledObject aData, Properties aProperties, + ActivationGroupDesc.CommandEnvironment environment) + { + className = aClassName; + location = aLocation; + data = aData; + props = aProperties; + env = environment; + } + + /** + * Get the activation group class name. + * + * @return the activation group class name (null for default implementation) + */ + public String getClassName() + { + return className; + } + + /** + * Get the location, from where the group class will be loaded + * + * @return the location, from where the implementation should be loaded (null + * for the default implementation) + */ + public String getLocation() + { + return location; + } + + /** + * Get the group intialization data. + * + * @return the group intialization data in the marshalled form. + */ + public MarshalledObject getData() + { + return data; + } + + /** + * Get the overridded system properties. + * + * @return the overridden group system properties. + */ + public Properties getPropertyOverrides() + { + return props; + } + + /** + * Get the group command environment, containing path to the jre executable + * and startup options. + * + * @return the command environment or null if the default environment should + * be used. + */ + public ActivationGroupDesc.CommandEnvironment getCommandEnvironment() + { + return env; + } + + /** + * Compare for the content equality. + */ + public boolean equals(Object obj) + { + if (obj instanceof ActivationGroupDesc) + { + ActivationGroupDesc that = (ActivationGroupDesc) obj; + + // Ensure the hashcodes are computed. + if (hash == 0) + hashCode(); + if (that.hash == 0) + that.hashCode(); + + // We compare the hash fields as they are type long rather than int. + if (hash != that.hash) + return false; + + if (! eq(className, that.className)) + return false; + if (! eq(data, that.data)) + return false; + if (! eq(env, that.env)) + return false; + if (! eq(location, that.location)) + return false; + + // Compare the properties. + if (eq(props, that.props)) + return true; + + if (props.size() != that.props.size()) + return false; + + Enumeration en = props.propertyNames(); + Object key, value; + + while (en.hasMoreElements()) + { + key = en.nextElement(); + if (! that.props.containsKey(key)) + return false; + if (! eq(props.get(key), that.props.get(key))) + return false; + } + return true; + } + else + return false; + } + + /** + * Compare for direct equality if one or both parameters are null, otherwise + * call .equals. + */ + static boolean eq(Object a, Object b) + { + if (a == null || b == null) + return a == b; + else + return a.equals(b); + } + + /** + * Return the hashcode. + */ + public int hashCode() + { + if (hash==0) + { + // Using Adler32 - the hashcode is cached, will be computed only + // once and due need to scan properties is the expensive operation + // anyway. Reliability is more important. + Adler32 adler = new Adler32(); + if (className!=null) + adler.update(className.getBytes()); + if (data!=null) + adler.update(data.hashCode()); + if (env!=null) + adler.update(env.hashCode()); + if (location!=null) + adler.update(location.getBytes()); + if (props!=null) + { + Enumeration en = props.propertyNames(); + + // Using the intermediate sorted set to ensure that the + // properties are sorted. + TreeSet pr = new TreeSet(); + + Object key; + Object value; + while (en.hasMoreElements()) + { + key = en.nextElement(); + if (key!=null) + pr.add(key); + } + + Iterator it = pr.iterator(); + while (it.hasNext()) + { + key = it.next(); + value = props.get(key); + adler.update(key.hashCode()); + if (value!=null) + adler.update(value.hashCode()); + } + } + hash = adler.getValue(); + } + return (int) hash; + } + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationGroupID.java b/libjava/classpath/java/rmi/activation/ActivationGroupID.java new file mode 100644 index 000000000..77fa4fba1 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationGroupID.java @@ -0,0 +1,122 @@ +/* ActivationGroupID.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.io.Serializable; +import java.rmi.server.UID; + +/** + * This identifier identifies the activation group inside the scope of its + * activation system. It also contains (and can provide) the reference to the + * groups activation system. + * + * @see ActivationSystem#registerGroup(ActivationGroupDesc) + */ +public class ActivationGroupID + implements Serializable +{ + /** + * Use SVUID for interoperability. + */ + static final long serialVersionUID = - 1648432278909740833L; + + /** + * The associated activation system. + */ + final ActivationSystem system; + + /** + * The object identifier, making the ID unique. + */ + final UID uid; + + /** + * Create the new activation group id in the scope of the given activation + * system + * + * @param aSystem the activation system + */ + public ActivationGroupID(ActivationSystem aSystem) + { + system = aSystem; + uid = new UID(); + } + + /** + * Get the associated activation system + * + * @return the associated activation system + */ + public ActivationSystem getSystem() + { + return system; + } + + /** + * Get the hash code of the associated activation system. + */ + public int hashCode() + { + return uid.hashCode(); + } + + /** + * Copmare for equality, returns true if the passed object is also the + * activation group id and its activation system is the same. + */ + public boolean equals(Object obj) + { + if (obj instanceof ActivationGroupID) + { + ActivationGroupID that = (ActivationGroupID) obj; + return uid.equals(that.uid); + } + else + return false; + } + + /** + * Get the string representation + */ + public String toString() + { + return uid.toString(); + } + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationGroup_Stub.java b/libjava/classpath/java/rmi/activation/ActivationGroup_Stub.java new file mode 100644 index 000000000..fb55e5fd9 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationGroup_Stub.java @@ -0,0 +1,110 @@ +/* ActivationGroup_Stub.java -- Stub class for ActivationGroup impls. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.lang.reflect.Method; +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.UnexpectedException; +import java.rmi.server.RemoteRef; +import java.rmi.server.RemoteStub; + +/** + * A stub class for {@link ActivationGroup} implementations. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public final class ActivationGroup_Stub extends RemoteStub + implements ActivationInstantiator, Remote +{ + private static final long serialVersionUID = 2L; + + /** + * Creates a new instance of ActivationGroup_Stub. + * + * @param ref the remote reference + */ + public ActivationGroup_Stub(RemoteRef ref) + { + super(ref); + } + + /** + * Stub method for ActivationGroup.newInstance(). + * + * @param id the activation ID + * @param desc the activation description + * + * @return the return value of the invocation + * + * @throws RemoteException if the invocation throws a RemoteException + * @throws ActivationException if the invocation throws an + * ActivationException + */ + public MarshalledObject newInstance(ActivationID id, ActivationDesc desc) + throws RemoteException, ActivationException + { + try + { + Method method = ActivationGroup_Stub.class.getDeclaredMethod + ("newInstance", new Class[]{ ActivationID.class, + ActivationDesc.class }); + return (MarshalledObject) ref.invoke(this, method, + new Object[]{id, desc}, + -5274445189091581345L); + } + catch (RuntimeException ex) + { + throw ex; + } + catch (RemoteException ex) + { + throw ex; + } + catch (ActivationException ex) + { + throw ex; + } + catch (Exception ex) + { + throw new UnexpectedException("Unexpected exception", ex); + } + } +} diff --git a/libjava/classpath/java/rmi/activation/ActivationID.java b/libjava/classpath/java/rmi/activation/ActivationID.java new file mode 100644 index 000000000..f89a0edf8 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationID.java @@ -0,0 +1,199 @@ +/* ActivationID.java -- the object activation identifier + Copyright (c) 1996, 1997, 1998, 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.server.UID; + +/** + * Denotes the object that can be activated over time. The instance of the + * ActivationID for the given object can be obtained in the following ways: + *
        + *
      • via {@link Activatable#register(ActivationDesc)}
      • + *
      • via Activatable constructor
      • + *
      • via Activatable.exportObject + *
      • + *
      + * An instance of the ActivationID has the {@link UID} as its component and + * hence is globally unique. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub) + */ +public class ActivationID + implements Serializable +{ + /** + * Use SVUID for interoperability. + */ + static final long serialVersionUID = - 4608673054848209235L; + + /** + * The activator. + */ + transient Activator activator; + + /** + * The UID, making this instance unique. + */ + transient UID uid; + + /** + * The activation group that has activated the object with this + * activation id. The field is filled in inside the group and is used + * to notify the group about the request to inactivated the object. + */ + transient ActivationGroup group; + + /** + * Create a new instance with the given activator. + * + * @param an_activator tha activator that should activate the object. + */ + public ActivationID(Activator an_activator) + { + activator = an_activator; + uid = new UID(); + } + + /** + * Activate the object. + * + * @param force if true, always contact the group. Otherwise, the cached value + * may be returned. + * @return the activated object + * @throws UnknownObjectException if the object is unknown + * @throws ActivationException if the activation has failed + * @throws RemoteException if the remote call has failed + */ + public Remote activate(boolean force) throws ActivationException, + UnknownObjectException, RemoteException + { + try + { + return (Remote) activator.activate(this, force).get(); + } + catch (IOException e) + { + ActivationException acex = new ActivationException("id "+uid, e); + throw acex; + } + catch (ClassNotFoundException e) + { + ActivationException acex = new ActivationException("id "+uid, e); + throw acex; + } + } + + /** + * Returns the hash code of the activator. + */ + public int hashCode() + { + return uid == null ? 0 : uid.hashCode(); + } + + /** + * Compares the activators for equality. + */ + public boolean equals(Object obj) + { + if (obj instanceof ActivationID) + { + ActivationID that = (ActivationID) obj; + return eq(uid, that.uid); + } + else + return false; + } + + /** + * Read the object from the input stream. + * + * @param in the stream to read from + * + * @throws IOException if thrown by the stream + * @throws ClassNotFoundException + */ + private void readObject(ObjectInputStream in) throws IOException, + ClassNotFoundException + { + uid = (UID) in.readObject(); + activator = (Activator) in.readObject(); + } + + /** + * Write the object to the output stream. + * + * @param out the stream to write int + * @throws IOException if thrown by the stream + * @throws ClassNotFoundException + */ + private void writeObject(ObjectOutputStream out) throws IOException, + ClassNotFoundException + { + out.writeObject(uid); + out.writeObject(activator); + } + + /** + * Compare by .equals if both a and b are not null, compare directly if at + * least one of them is null. + */ + static final boolean eq(Object a, Object b) + { + if (a == null || b == null) + return a == b; + else + return a.equals(b); + } + + /** + * Return the content based string representation. + */ + public String toString() + { + return uid.toString(); + } + +} diff --git a/libjava/classpath/java/rmi/activation/ActivationInstantiator.java b/libjava/classpath/java/rmi/activation/ActivationInstantiator.java new file mode 100644 index 000000000..e4ea54151 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationInstantiator.java @@ -0,0 +1,73 @@ +/* ActivationInstantiator.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * The implementation of this interface creates (instantiates) the new remote + * objects in response to the activation request. The instantiator is returned + * by the {@link ActivationGroup} that calls + * {@link ActivationSystem#activeGroup(ActivationGroupID, ActivationInstantiator, long)}. + */ +public interface ActivationInstantiator + extends Remote +{ + /** + * Creates and instantiate a new remote object. This method performs the + * following tasks: + *
        + *
      • Finds and loads (if not already loaded) the class of the object being + * instantiated
      • + *
      • Creates an instance of the object using its special two parameter + * activation constructor, the first parameter being the {@link ActivationID} + * and the second the {@link MarshalledObject}.
      • + * + * @param id the id of the object being instantiated + * @param desc the activation descriptor being instantiated + * @return the MarshalledObject, containing the stub to the newly created + * object. + * @throws ActivationException if the activation fails + * @throws RemoteException if the remote call fails + */ + MarshalledObject newInstance (ActivationID id, ActivationDesc desc) + throws ActivationException, RemoteException; +} diff --git a/libjava/classpath/java/rmi/activation/ActivationMonitor.java b/libjava/classpath/java/rmi/activation/ActivationMonitor.java new file mode 100644 index 000000000..7c4c17144 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationMonitor.java @@ -0,0 +1,87 @@ +/* ActivationMonitor.java -- the RMI activation/inactivation event listener + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * The activation and inactivation event listener. The group obtains this + * listener via {@link ActivationSystem#activeGroup} and must notify it + * when the group objects are activated or inactivated and also when the + * whole group becomes inactive. + * @author root. + */ +public interface ActivationMonitor extends Remote +{ + /** + * Informs that the object is now active. + * + * @param id the activation id of the object that is now active + * @throws UnknownObjectException is such object is not known in this group + * @throws RemoteException if remote call fails + */ + void activeObject (ActivationID id, MarshalledObject obj) + throws UnknownObjectException, RemoteException; + + /** + * Informs that the object is not inactive. + * + * @param id the activation id of the object that is now inactive + * @throws UnknownObjectException is such object is not known in this group + * @throws RemoteException if remote call fails + */ + void inactiveObject (ActivationID id) + throws UnknownObjectException, RemoteException; + + /** + * Informs that the whole group is now inactive because all group objects are + * inactive. The group will be recreated upon the later request to activate + * any object, belonging to the group. + * + * @param groupId the group id + * @param incarnation the group incarnation number + * @throws UnknownGroupException if the group id is not known + * @throws RemoteException if the remote call fails + */ + void inactiveGroup (ActivationGroupID groupId, long incarnation) + throws UnknownGroupException, RemoteException; +} diff --git a/libjava/classpath/java/rmi/activation/ActivationSystem.java b/libjava/classpath/java/rmi/activation/ActivationSystem.java new file mode 100644 index 000000000..090ce4815 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/ActivationSystem.java @@ -0,0 +1,206 @@ +/* ActivationSystem.java -- registers groups and objects to be activated. + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + *

        + * The ActivationSystem registers groups and activatable objects to be activated + * within those groups. The ActivationSystem cooperates with both the Activator, + * which activates objects registered via the ActivationSystem, and the + * ActivationMonitor, which obtains information about active and inactive + * objects and inactive groups. + *

        + *

        + * The activation system if frequently a remote object. As a security mean, all + * methods in this interface throw {@link java.rmi.AccessException} if called + * from the client that is not reside on the same host as the activation system. + *

        + * @see ActivationGroup#getSystem() + */ +public interface ActivationSystem + extends Remote +{ + /** + * The port, used by the activation system. The value is equal to 1098 by + * default, but it can be changed by putting the system property + * . + */ + int SYSTEM_PORT = 1098; + + /** + * Registers the activation descriptor and creates (and returns) its + * activation identifier. The map entry (identifier to descriptor) is stored + * in the stable map and used when the {@link Activator} receives the request + * to activate the object. + * + * @param desc the activation descriptor to register. + * @return the created activation identifier that is mapped to the passed + * descriptor. + * @throws ActivationException if the registration fails (database update + * problems, etc). + * @throws UnknownGroupException the if group, specified in decriptor, is + * unknown. + * @throws RemoteException if the remote call fails. + */ + ActivationID registerObject(ActivationDesc desc) throws ActivationException, + UnknownGroupException, RemoteException; + + /** + * Removes the stored identifier-description map entry. The object will no + * longer be activable using the passed activation id + * + * @param id the activation id to remove + * @throws ActivationException if the entry removing operation failed + * (database update problems, etc) + * @throws UnknownObjectException if the passed id is not known to the system + * @throws RemoteException if the remote call fails + */ + void unregisterObject(ActivationID id) throws ActivationException, + UnknownObjectException, RemoteException; + + /** + * Register the new activation group. For instance, it can be one activation + * group per virtual machine. + * + * @param groupDesc the activation group descriptor. + * @return the created activation group ID for the activation group + * @throws ActivationException if the group registration fails + * @throws RemoteException if the remote call fails + */ + ActivationGroupID registerGroup(ActivationGroupDesc groupDesc) + throws ActivationException, RemoteException; + + /** + * This method is called from the {@link ActivationGroup} to inform the + * ActivatinSystem that the group is now active and there is the + * {@link ActivationInstantiator} for that group. This call is made internally + * from the {@link ActivationGroup#createGroup}. + * + * @param id the group id + * @param group the group activation instantiator + * @param incarnation the groups incarnatin number. + * @return the activation monitor that should be informed about the group + * state changes + * @throws UnknownGroupException if this group has not been registered + * @throws ActivationException if this group is already active + * @throws RemoteException if the remote call fails + */ + ActivationMonitor activeGroup(ActivationGroupID id, + ActivationInstantiator group, long incarnation) + throws UnknownGroupException, ActivationException, RemoteException; + + /** + * Removes the activation group with the given identifier. The group calls + * back, informing the activator about the shutdown. + * + * @param id the group activation id. + * @throws ActivationException if the database update fails + * @throws UnknownGroupException if such group is not registered + * @throws RemoteException if the remote call fails + */ + void unregisterGroup(ActivationGroupID id) throws ActivationException, + UnknownGroupException, RemoteException; + + /** + * Shutdown the activation system and all associated activation groups + * + * @throws RemoteException if the remote call fails + */ + void shutdown() throws RemoteException; + + /** + * Replace the activation descriptor for the object with the given activation + * id. + * + * @param id the activation id + * @param desc the new activation descriptor + * @return the previous activation descriptor for that object. + * @throws ActivationException if the database update fails + * @throws UnknownObjectException if the object with such id is not known + * @throws UnknownGroupException if the activation group (in desc) is not + * known. + * @throws RemoteException if the remote call fails + */ + ActivationDesc setActivationDesc(ActivationID id, ActivationDesc desc) + throws ActivationException, UnknownObjectException, + UnknownGroupException, RemoteException; + + /** + * Replaces the group descriptor for the group with the given group activation + * id. + * + * @param groupId the group id + * @param groupDesc the new group descriptor + * @return the previous group descriptor + * @throws ActivationException if the database update fails + * @throws UnknownGroupException if such group is not known + * @throws RemoteException if the remote call fails + */ + ActivationGroupDesc setActivationGroupDesc(ActivationGroupID groupId, + ActivationGroupDesc groupDesc) + throws ActivationException, UnknownGroupException, RemoteException; + + /** + * Get the activation descriptor for the object with the given activation id. + * + * @param id the object activation id + * @return the activation descriptor for that object + * @throws ActivationException if the database access fails + * @throws UnknownObjectException if this object is not known + * @throws RemoteException if the remote call fails + */ + ActivationDesc getActivationDesc(ActivationID id) throws ActivationException, + UnknownObjectException, RemoteException; + + /** + * Get the group descriptor for the group with the given id. + * + * @param groupId the group id + * @return the group descriptor + * @throws ActivationException if the database access fails + * @throws UnknownGroupException if the group with such id is not known + * @throws RemoteException if the remote call fails + */ + ActivationGroupDesc getActivationGroupDesc(ActivationGroupID groupId) + throws ActivationException, UnknownGroupException, RemoteException; +} diff --git a/libjava/classpath/java/rmi/activation/Activator.java b/libjava/classpath/java/rmi/activation/Activator.java new file mode 100644 index 000000000..fc66b2b63 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/Activator.java @@ -0,0 +1,72 @@ +/* Activator.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.activation; + +import java.rmi.MarshalledObject; +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * Activates remote object, providing the live reference to the activable remote + * object. Usually there is only one activator per host. + * + * @see ActivationSystem + * @see ActivationMonitor + */ +public interface Activator + extends Remote +{ + /** + * Activate the object, associated with the given activation identifier. The + * activator looks for the {@link ActivationDesc}riptor for the passed + * identifier, determines the object activation group and initiates object + * recreation either via {@link ActivationInstantiator} or via + * {@link Class#newInstance()}. + * + * @param id the identifier of the object to activate. + * @param force if true, the activator always contacts the group to obtain the + * reference. If false, it may return the cached value. + * @return the activated remote object (its stub). + * @throws UnknownObjectException if the object with this id is unknown + * @throws ActivationException if the activation has failed due other reason + * @throws RemoteException if the remote call has failed. + */ + MarshalledObject activate (ActivationID id, boolean force) + throws ActivationException, UnknownObjectException, RemoteException; +} diff --git a/libjava/classpath/java/rmi/activation/UnknownGroupException.java b/libjava/classpath/java/rmi/activation/UnknownGroupException.java new file mode 100644 index 000000000..3c67aec0c --- /dev/null +++ b/libjava/classpath/java/rmi/activation/UnknownGroupException.java @@ -0,0 +1,69 @@ +/* UnknownGroupException.java -- thrown on an invalid ActivationGroupID + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +/** + * Thrown when an ActivationGroupID parameter is invalid or + * unknown. + * + * @author unknown + * @see Activatable + * @see ActivationGroup + * @see ActivationID + * @see ActivationMonitor + * @see ActivationSystem + * @since 1.2 + * @status updated to 1.4 + */ +public class UnknownGroupException extends ActivationException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7056094974750002460L; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public UnknownGroupException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/activation/UnknownObjectException.java b/libjava/classpath/java/rmi/activation/UnknownObjectException.java new file mode 100644 index 000000000..8dbeb0e60 --- /dev/null +++ b/libjava/classpath/java/rmi/activation/UnknownObjectException.java @@ -0,0 +1,69 @@ +/* UnknownObjectException.java -- thrown on an invalid ActivationID + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.activation; + +/** + * Thrown when an ActivationID parameter is invalid or unknown. + * + * @author unknown + * @see Activatable + * @see ActivationGroup + * @see ActivationID + * @see ActivationMonitor + * @see ActivationSystem + * @see Activator + * @since 1.2 + * @status updated to 1.4 + */ +public class UnknownObjectException extends ActivationException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 3425547551622251430L; + + /** + * Create an exception with an error message. + * + * @param s the message + */ + public UnknownObjectException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/activation/package.html b/libjava/classpath/java/rmi/activation/package.html new file mode 100644 index 000000000..9df518fed --- /dev/null +++ b/libjava/classpath/java/rmi/activation/package.html @@ -0,0 +1,73 @@ + + + + +GNU Classpath - java.rmi.activation + + +In the previous Classpath releases, an instance of a UnicastRemoteObject +could be accessed from a server that: +
          +
        • has created an instance of that object
        • +
        • has been running all the time
        • +
        +

        The the activation system allows to activate and execute the object +implementation on demand rather than running all time. If the activation +system is persistent, the server can be terminated and then restarted. +The clients, still holding remote references to the server side +activatable objects, will activate those objects again. The server side +objects will be reinstantiated (activated) during the first call of any +remote method of such object. +

        +The RMI client code for activatable objects is no different than the code for +accessing non-activatable remote objects. Activation is a server-side feature. +

        +In order for an object to be activated, the "activatable" object class +(independently if it extends the {@link Activatable} class or not) defines a +special public constructor that takes two arguments, its activation identifier +({@link ActivationID}) and its activation data ({@link java.rmi.MarshalledObject}), +supplied in the activation descriptor used during registration. When an +activation group activates a remote object, it constructs the object via +this special constructor. The remote object implementation may use the +activation data to initialize itself in a needed manner. The remote object may +also retain its activation identifier, so that it can inform the activation +group when it becomes inactive (via a call to the Activatable.inactive method). +

        +@author Audrius Meskauskas (audriusa@bioinformatics.org) (from empty) + + diff --git a/libjava/classpath/java/rmi/dgc/DGC.java b/libjava/classpath/java/rmi/dgc/DGC.java new file mode 100644 index 000000000..5ec874a5e --- /dev/null +++ b/libjava/classpath/java/rmi/dgc/DGC.java @@ -0,0 +1,80 @@ +/* DGC.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.dgc; + +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.server.ObjID; + +/** + * The DGC implementation is used for the server side during the distributed + * garbage collection. This interface contains the two methods: dirty and clean. + * A dirty call is made when a remote reference is unmarshaled in a client. A + * corresponding clean call is made by client it no longer uses that remote + * reference. A reference to a remote object is also automatically released + * after so called lease period that starts after the dirty call is received. It + * is the client's responsibility to renew the leases, by making additional + * dirty calls before such leases expire. + */ +public interface DGC + extends Remote +{ + /** + * Mark the given objects referecnes as used on the client side. + * + * @param ids the ids of the used objects. + * @param sequenceNum the number of the call (used to detect and discard late + * calls). + * @param lease the requested lease + * @return the granted lease + */ + Lease dirty(ObjID[] ids, long sequenceNum, Lease lease) + throws RemoteException; + + /** + * Mark the given objects as no longer used on the client side. + * + * @param ids the ids of the objects that are no longer used. + * @param sequenceNum the number of the call (used to detect and discard late + * @param vmid the VMID of the client. + * @param strong make the "strong" clean call ("strong" calls are scheduled + * after the failed dirty calls). + */ + void clean(ObjID[] ids, long sequenceNum, VMID vmid, boolean strong) + throws RemoteException; +} diff --git a/libjava/classpath/java/rmi/dgc/Lease.java b/libjava/classpath/java/rmi/dgc/Lease.java new file mode 100644 index 000000000..58b82cf58 --- /dev/null +++ b/libjava/classpath/java/rmi/dgc/Lease.java @@ -0,0 +1,100 @@ +/* Lease.java + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.dgc; + +import java.io.Serializable; + +/** + * A lease object is used to request and grant leases for the remote objects. It + * contains the lease duration and the unique VM indentifier. + */ +public final class Lease + implements Serializable +{ + + static final long serialVersionUID = - 5713411624328831948L; + + private VMID vmid; + + private long value; + + /** + * Create the new lease with the given id and duration + * + * @param id the lease id + * @param duration the lease duration + */ + public Lease(VMID id, long duration) + { + vmid = id; + value = duration; + } + + /** + * Get the lease id. + * + * @return the lease id + */ + public VMID getVMID() + { + return (vmid); + } + + /** + * Get the lease duration + * + * @return the lease duration + */ + public long getValue() + { + return (value); + } + + /** + * Get the string representation of this lease + * + * @return the string represenation (lease id, followed by the lease + * duration). + */ + public String toString() + { + return ("[" + vmid.toString() + ", " + Long.toString(value) + "]"); + } + +} diff --git a/libjava/classpath/java/rmi/dgc/VMID.java b/libjava/classpath/java/rmi/dgc/VMID.java new file mode 100644 index 000000000..925c1bfb1 --- /dev/null +++ b/libjava/classpath/java/rmi/dgc/VMID.java @@ -0,0 +1,191 @@ +/* VMID.java -- The object ID, unique between all virtual machines. + Copyright (c) 1996, 1997, 1998, 1999, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.dgc; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Serializable; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.rmi.server.UID; +import java.util.Arrays; + +/** + * An identifier that is unique accross the all virtual machines. This class is + * used by distributed garbage collector to identify the virtual machine of + * the client, but may also be used in various other cases, when such identifier + * is required. This class separately stores and transfers the host IP + * address, but will try to do its best also for the case if it failed to + * determine it. The alternative algorithms are used in {@link UID} that is + * part of this class. The VMID's, created on the same host, but in the two + * separately (parallely) running virtual machines are different. + */ +public final class VMID implements Serializable +{ + /** + * Use SVUID for interoperability. + */ + static final long serialVersionUID = -538642295484486218L; + + /** + * If true, the IP of this host can ve reliably determined. + */ + static boolean areWeUnique; + + /** + * The IP address of the local host. + */ + static byte[] localAddr; + + /** + * The IP address of the local host. + */ + private byte[] addr; + + /** + * The cached hash code. + */ + transient int hash; + + /** + * The UID of this VMID. + */ + private UID uid; + + static + { + // This "local host" value usually indicates that the local + // IP address cannot be reliably determined. + byte[] localHost = new byte[] { 127, 0, 0, 1 }; + + try + { + localAddr = InetAddress.getLocalHost().getAddress(); + areWeUnique = !Arrays.equals(localHost, localAddr); + } + catch (UnknownHostException uhex) + { + localAddr = localHost; + areWeUnique = false; + } + } + + /** + * Create the new VMID. All VMID's are unique accross tha all virtual + * machines. + */ + public VMID() + { + addr = localAddr; + uid = new UID(); + } + + /** + * Return true if it is possible to get the accurate address of this host. + * If false is returned, the created VMID's are less reliable, but the + * starting time and possibly the memory allocation are also taken into + * consideration in the incorporated UID. Hence the VMID's, created on the + * different virtual machines, still should be different. + * + * @deprecated VMID's are more or less always reliable. + * + * @return false if the local host ip address is 127.0.0.1 or unknown, + * true otherwise. + */ + public static boolean isUnique () + { + return areWeUnique; + } + + /** + * Get the hash code of this VMID. + */ + public int hashCode () + { + if (hash==0) + { + for (int i = 0; i < localAddr.length; i++) + hash += addr[i]; + hash = hash ^ uid.hashCode(); + } + return hash; + } + + /** + * Returns true if the passed parameter is also VMID and it is equal to this + * VMID. The VMID should only be equal to itself (also if the passed value is + * another instance, cloned by serialization). + */ + public boolean equals(Object obj) + { + if (obj instanceof VMID) + { + VMID other = (VMID) obj; + + // The UID's are compared faster than arrays. + return uid.equals(other.uid) && Arrays.equals(addr, other.addr); + } + else + return false; + + } + + /** + * Get the string representation of this VMID. + */ + public String toString () + { + CPStringBuilder buf = new CPStringBuilder ("[VMID: "); + + for (int i = 0; i < addr.length; i++) + { + if (i > 0) + { + buf.append ("."); + } + + buf.append (Integer.toString (addr [i])); + } + + buf.append (" "); + buf.append (uid.toString ()); + buf.append ("]"); + + return buf.toString(); + } +} diff --git a/libjava/classpath/java/rmi/dgc/package.html b/libjava/classpath/java/rmi/dgc/package.html new file mode 100644 index 000000000..7f0a2081c --- /dev/null +++ b/libjava/classpath/java/rmi/dgc/package.html @@ -0,0 +1,52 @@ + + + + +GNU Classpath - java.rmi.dgc + + +The Distributed Garbage Collector (DGC). The DGC is reponsible for keeping all +locally created and exported remote objects as long as they are referenced +by some remote clients. The client must periodically notify the server that +it still wants to keep the remote object on the server side. The client +notifies the server by calling methods, defined in the DGC interface. +Other classes in this package define the parameters that are used in this +interface. The DGC object of some host can be found and accessed by its +object identifier (ObjID.DGC_ID), without involving the name service. + + diff --git a/libjava/classpath/java/rmi/package.html b/libjava/classpath/java/rmi/package.html new file mode 100644 index 000000000..1d36fe80a --- /dev/null +++ b/libjava/classpath/java/rmi/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.rmi + + +

        Provides basic Remote Method Invocation (RMI) interfaces, classes and exceptions.

        + + + diff --git a/libjava/classpath/java/rmi/registry/LocateRegistry.java b/libjava/classpath/java/rmi/registry/LocateRegistry.java new file mode 100644 index 000000000..d0918e1b9 --- /dev/null +++ b/libjava/classpath/java/rmi/registry/LocateRegistry.java @@ -0,0 +1,87 @@ +/* LocateRegistry.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.registry; + +import gnu.java.rmi.registry.RegistryImpl; +import gnu.java.rmi.registry.RegistryImpl_Stub; +import gnu.java.rmi.server.UnicastRef; + +import java.rmi.RemoteException; +import java.rmi.server.ObjID; +import java.rmi.server.RMIClientSocketFactory; +import java.rmi.server.RMIServerSocketFactory; +import java.rmi.server.RMISocketFactory; +import java.rmi.server.RemoteRef; + +public final class LocateRegistry { + /** + * This class isn't intended to be instantiated. + */ + private LocateRegistry() {} + +public static Registry getRegistry() throws RemoteException { + return (getRegistry("localhost", Registry.REGISTRY_PORT)); +} + +public static Registry getRegistry(int port) throws RemoteException { + return (getRegistry("localhost", port)); +} + +public static Registry getRegistry(String host) throws RemoteException { + return (getRegistry(host, Registry.REGISTRY_PORT)); +} + +public static Registry getRegistry(String host, int port) throws RemoteException { + return (getRegistry(host, port, RMISocketFactory.getSocketFactory())); +} + +public static Registry getRegistry(String host, int port, RMIClientSocketFactory csf) throws RemoteException { + RemoteRef ref = new UnicastRef(new ObjID(ObjID.REGISTRY_ID), host, port, csf); + return (new RegistryImpl_Stub(ref)); +} + +public static Registry createRegistry(int port) throws RemoteException { + return (createRegistry(port, RMISocketFactory.getSocketFactory(), RMISocketFactory.getSocketFactory())); +} + +public static Registry createRegistry(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException { + return (new RegistryImpl(port, csf, ssf)); +} + +} diff --git a/libjava/classpath/java/rmi/registry/Registry.java b/libjava/classpath/java/rmi/registry/Registry.java new file mode 100644 index 000000000..dabe6549c --- /dev/null +++ b/libjava/classpath/java/rmi/registry/Registry.java @@ -0,0 +1,87 @@ +/* Registry.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.registry; + +import java.rmi.AccessException; +import java.rmi.AlreadyBoundException; +import java.rmi.NotBoundException; +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface Registry extends Remote +{ + int REGISTRY_PORT = 1099; + + /** + * Find and return the reference to the object that was previously bound + * to the registry by this name. For remote objects, this method returns + * the stub instances, containing the code for remote invocations. + * + * Since jdk 1.5 this method does not longer require the stub class + * (nameImpl_Stub) to be present. If such class is not found, the stub is + * replaced by the dynamically constructed proxy class. No attempt to find + * and load the stubs is made if the system property + * java.rmi.server.ignoreStubClasses is set to true (set to reduce the + * starting time if the stubs are surely not present and exclusively 1.2 + * RMI is used). + * + * @param name the name of the object + * + * @return the reference to that object on that it is possible to invoke + * the (usually remote) object methods. + * + * @throws RemoteException + * @throws NotBoundException + * @throws AccessException + */ + Remote lookup(String name) + throws RemoteException, NotBoundException, AccessException; + + void bind(String name, Remote obj) + throws RemoteException, AlreadyBoundException, AccessException; + + void unbind(String name) + throws RemoteException, NotBoundException, AccessException; + + void rebind(String name, Remote obj) + throws RemoteException, AccessException; + + String[] list() + throws RemoteException, AccessException; +} diff --git a/libjava/classpath/java/rmi/registry/RegistryHandler.java b/libjava/classpath/java/rmi/registry/RegistryHandler.java new file mode 100644 index 000000000..19e8caa70 --- /dev/null +++ b/libjava/classpath/java/rmi/registry/RegistryHandler.java @@ -0,0 +1,58 @@ +/* RegistryHandler.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.registry; + +import java.rmi.RemoteException; +import java.rmi.UnknownHostException; + +/** + * @deprecated + */ +public interface RegistryHandler +{ + /** + * @deprecated + */ + Registry registryStub (String host, int port) + throws RemoteException, UnknownHostException; + + /** + * @deprecated + */ + Registry registryImpl (int port) throws RemoteException; +} diff --git a/libjava/classpath/java/rmi/registry/package.html b/libjava/classpath/java/rmi/registry/package.html new file mode 100644 index 000000000..4e1667270 --- /dev/null +++ b/libjava/classpath/java/rmi/registry/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.rmi.registry + + +

        + + + diff --git a/libjava/classpath/java/rmi/server/ExportException.java b/libjava/classpath/java/rmi/server/ExportException.java new file mode 100644 index 000000000..b2d5bfca5 --- /dev/null +++ b/libjava/classpath/java/rmi/server/ExportException.java @@ -0,0 +1,78 @@ +/* ExportException.java -- an export attempt failed + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.rmi.RemoteException; + +/** + * Thrown if an attempt to export a remote object fails. + * + * @author unknown + * @see UnicastRemoteObject + * @see Activatable + * @since 1.1 + * @status updated to 1.4 + */ +public class ExportException extends RemoteException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -9155485338494060170L; + + /** + * Create an exception with the specified message. + * + * @param s the message + */ + public ExportException(String s) + { + super(s); + } + + /** + * Create an exception with the specified message and cause. + * + * @param s the message + * @param e the cause + */ + public ExportException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/server/LoaderHandler.java b/libjava/classpath/java/rmi/server/LoaderHandler.java new file mode 100644 index 000000000..de9d6532f --- /dev/null +++ b/libjava/classpath/java/rmi/server/LoaderHandler.java @@ -0,0 +1,71 @@ +/* LoaderHandler.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * @deprecated + * @since 1.1 + */ +public interface LoaderHandler +{ + /** + * For binary compatibility with the JDK, the string "sun.rmi.server". + * Not actually used for anything. + */ + String packagePrefix = "sun.rmi.server"; + + /** + * @deprecated + */ + Class loadClass(String name) + throws MalformedURLException, ClassNotFoundException; + + /** + * @deprecated + */ + Class loadClass(URL codebase, String name) + throws MalformedURLException, ClassNotFoundException; + + /** + * @deprecated + */ + Object getSecurityContext(ClassLoader loader); +} diff --git a/libjava/classpath/java/rmi/server/LogStream.java b/libjava/classpath/java/rmi/server/LogStream.java new file mode 100644 index 000000000..a74ffb439 --- /dev/null +++ b/libjava/classpath/java/rmi/server/LogStream.java @@ -0,0 +1,146 @@ +/* LogStream.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * @deprecated + */ +public class LogStream extends PrintStream +{ + public static final int SILENT = 0; + public static final int BRIEF = 10; + public static final int VERBOSE = 20; + + private static PrintStream defStream; + + private LogStream (OutputStream s) + { + super (s); + } + + /** + * @deprecated + */ + public static LogStream log (String name) + { + throw new Error ("Not implemented"); + } + + /** + * @deprecated + */ + public static PrintStream getDefaultStream () + { + return defStream; + } + + /** + * @deprecated + */ + public static void setDefaultStream (PrintStream s) + { + defStream = s; + } + + /** + * @deprecated + */ + public OutputStream getOutputStream () + { + return out; + } + + /** + * @deprecated + */ + public void setOutputStream (OutputStream s) + { + out = s; + } + + /** + * @deprecated + */ + public void write (int buffer) + { + super.write (buffer); + } + + /** + * @deprecated + */ + public void write (byte[] buffer, int offset, int len) + { + super.write (buffer, offset, len); + } + + /** + * @deprecated + */ + public String toString () + { + throw new Error ("Not implemented"); + } + + /** + * @deprecated + */ + public static int parseLevel (String s) + { + if (s.equalsIgnoreCase ("silent")) + { + return SILENT; + } + + if (s.equalsIgnoreCase ("brief")) + { + return BRIEF; + } + + if (s.equalsIgnoreCase ("verbose")) + { + return VERBOSE; + } + + return SILENT; + } +} diff --git a/libjava/classpath/java/rmi/server/ObjID.java b/libjava/classpath/java/rmi/server/ObjID.java new file mode 100644 index 000000000..2bdd44ae0 --- /dev/null +++ b/libjava/classpath/java/rmi/server/ObjID.java @@ -0,0 +1,197 @@ +/* ObjID.java -- Unique object id with respect to the given host. + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.Serializable; + +/** + * Represents the object identifier, unique for the host that generated it. + * The ObjID contains inside the integer object identifier that, if needed, + * may indicated that this is a reference to one of the well known objects + * on that host (registry, activator or dgc) and the {@link UID} that + * ensures uniqueness. + */ +public final class ObjID + implements Serializable +{ + + /** + * Use serial version uid for interoperability. + */ + static final long serialVersionUID = - 6386392263968365220L; + + /** + * The object counter, which value is assigned when creating the ordinary + * objects without the known object id. The counter is incremented each time + * the new ObjID is constructed. + */ + private static long next = 0x8000000000000000L; + + /** + * The object to put the lock on when incrementing {@link #next} + */ + private static final Object lock = ObjID.class; + + /** + * Defines the ID of the naming service. + */ + public static final int REGISTRY_ID = 0; + + /** + * Defines the ID of the activator. + */ + public static final int ACTIVATOR_ID = 1; + + /** + * Defines the ID of the distributed garbage collector. + */ + public static final int DGC_ID = 2; + + /** + * The object Id (either well-known value or the value of the incrementing + * object counter. + */ + long objNum; + + /** + * The object unique identifier, generated individually for each object. + */ + UID space; + + /** + * Create the new object id, unique for this host. + */ + public ObjID() + { + synchronized (lock) + { + objNum = next++; + } + space = new UID(); + } + + /** + * Create the new object id defining the well known remotely accessible + * object, present in this host. The well - known objects are: + *
          + *
        • {@link #REGISTRY_ID} - RMI naming service.
        • + *
        • {@link #ACTIVATOR_ID} - activator
        • + *
        • {@link #DGC_ID} - distributed garbage collector (grants lease + * durations to keep the object before it is garbage collected.
        • + *
        + * + * @param id the well known object id, one of the above. + */ + public ObjID(int id) + { + objNum = (long) id; + space = new UID((short) 0); + } + + /** + * Write object id as long, then the object {@link UID}. + */ + public void write(ObjectOutput out) throws IOException + { + DataOutput dout = (DataOutput) out; + dout.writeLong(objNum); + space.write(dout); + } + + /** + * Read object id (as long), then the object {@link UID}. + */ + public static ObjID read(ObjectInput in) throws IOException + { + DataInput din = (DataInput) in; + ObjID id = new ObjID(); + id.objNum = din.readLong(); + id.space = UID.read(din); + return (id); + } + + /** + * Get the hashcode. + */ + public int hashCode() + { + return space == null ? (int) objNum : space.hashCode() ^ (int) objNum; + } + + /** + * Compare for equality. + */ + public boolean equals(Object obj) + { + if (obj instanceof ObjID) + { + ObjID that = (ObjID) obj; + return that.objNum == objNum && eq(that.space, space); + } + else + return false; + } + + /** + * Compare by .equals if both a and b are not null, compare directly if at + * least one of them is null. + */ + static final boolean eq(Object a, Object b) + { + if (a == null || b == null) + return a == b; + else + return a.equals(b); + } + + /** + * Get the string representation. + */ + public String toString() + { + return (objNum + ":" + space); + } + +} diff --git a/libjava/classpath/java/rmi/server/Operation.java b/libjava/classpath/java/rmi/server/Operation.java new file mode 100644 index 000000000..de30cd08a --- /dev/null +++ b/libjava/classpath/java/rmi/server/Operation.java @@ -0,0 +1,78 @@ +/* Operation.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * This class was used with jdk 1.1 stubs and skeletons. It is no longer + * needed since jdk 1.2 and higher. + * + * @deprecated + */ +public class Operation +{ + private String operation; + + /** + * Create operation with the given name. + * @deprecated + */ + public Operation (String op) + { + operation = op; + } + + /** + * Get the name of the operation. + * + * @deprecated + */ + public String getOperation () + { + return operation; + } + + /** + * Return the name of the operation. + * + * @deprecated + */ + public String toString () + { + return operation; + } +} diff --git a/libjava/classpath/java/rmi/server/RMIClassLoader.java b/libjava/classpath/java/rmi/server/RMIClassLoader.java new file mode 100644 index 000000000..f43a68c5b --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIClassLoader.java @@ -0,0 +1,204 @@ +/* RMIClassLoader.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2003, 2004 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import gnu.classpath.ServiceFactory; +import gnu.classpath.SystemProperties; +import gnu.java.rmi.server.RMIClassLoaderImpl; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Iterator; + +/** + * This class provides a set of public static utility methods for supporting + * network-based class loading in RMI. These methods are called by RMI's + * internal marshal streams to implement the dynamic class loading of types for + * RMI parameters and return values. + * @since 1.1 + */ +public class RMIClassLoader +{ + /** + * This class isn't intended to be instantiated. + */ + private RMIClassLoader() {} + + /** + * @deprecated + */ + public static Class loadClass(String name) + throws MalformedURLException, ClassNotFoundException + { + return loadClass("", name); + } + + public static Class loadClass(String codebase, String name) + throws MalformedURLException, ClassNotFoundException + { + RMIClassLoaderSpi spi = getProviderInstance(); + if (spi == null) + spi = getDefaultProviderInstance(); + return spi.loadClass(codebase, name, null); + } + + public static Class loadClass(String codebase, String name, + ClassLoader defaultLoader) + throws MalformedURLException, ClassNotFoundException + { + RMIClassLoaderSpi spi = getProviderInstance(); + if (spi == null) + spi = getDefaultProviderInstance(); + return spi.loadClass(codebase, name, defaultLoader); + } + + public static Class loadProxyClass (String codeBase, String[] interfaces, + ClassLoader defaultLoader) + throws MalformedURLException, ClassNotFoundException + { + RMIClassLoaderSpi spi = getProviderInstance(); + if (spi == null) + spi = getDefaultProviderInstance(); + return spi.loadProxyClass(codeBase, interfaces, defaultLoader); + } + + /** + * Loads a class from codeBase. + * + * This method delegates to + * {@link RMIClassLoaderSpi#loadClass(String, String, ClassLoader)} and + * passes codeBase.toString() as first argument, + * name as second argument and null as third + * argument. + * + * @param codeBase the code base from which to load the class + * @param name the name of the class + * + * @return the loaded class + * + * @throws MalformedURLException if the URL is not well formed + * @throws ClassNotFoundException if the requested class cannot be found + */ + public static Class loadClass(URL codeBase, String name) + throws MalformedURLException, ClassNotFoundException + { + RMIClassLoaderSpi spi = getProviderInstance(); + if (spi == null) + spi = getDefaultProviderInstance(); + return spi.loadClass(codeBase.toString(), name, null); + } + + /** + * Gets a classloader for the given codebase and with the current + * context classloader as parent. + * + * @param codebase + * + * @return a classloader for the given codebase + * + * @throws MalformedURLException if the codebase contains a malformed URL + */ + public static ClassLoader getClassLoader(String codebase) + throws MalformedURLException + { + RMIClassLoaderSpi spi = getProviderInstance(); + if (spi == null) + spi = getDefaultProviderInstance(); + return spi.getClassLoader(codebase); + } + + /** + * Returns a string representation of the network location where a remote + * endpoint can get the class-definition of the given class. + * + * @param cl + * + * @return a space seperated list of URLs where the class-definition + * of cl may be found + */ + public static String getClassAnnotation(Class cl) + { + RMIClassLoaderSpi spi = getProviderInstance(); + if (spi == null) + spi = getDefaultProviderInstance(); + return spi.getClassAnnotation(cl); + } + + /** + * @deprecated + */ + public static Object getSecurityContext (ClassLoader loader) + { + throw new Error ("Not implemented"); + } + + /** + * Returns the default service provider for RMIClassLoader. + * + * @return the default provider for RMIClassLoader + */ + public static RMIClassLoaderSpi getDefaultProviderInstance() + { + return RMIClassLoaderImpl.getInstance(); + } + + /** + * Chooses, instantiates and returns a service provider. + * + * @return a service provider + */ + private static RMIClassLoaderSpi getProviderInstance() + { + // If the user asked for the default, return it. We do a special + // check here because our standard service lookup function does not + // handle this -- nor should it. + String prop = SystemProperties.getProperty("java.rmi.server.RMIClassLoaderSpi"); + if ("default".equals(prop)) + return null; + Iterator it = ServiceFactory.lookupProviders(RMIClassLoaderSpi.class, + null); + if (it == null || ! it.hasNext()) + return null; + // FIXME: the spec says we ought to throw an Error of some kind if + // the specified provider is not suitable for some reason. However + // our service factory simply logs the problem and moves on to the next + // provider in this situation. + return (RMIClassLoaderSpi) it.next(); + } +} diff --git a/libjava/classpath/java/rmi/server/RMIClassLoaderSpi.java b/libjava/classpath/java/rmi/server/RMIClassLoaderSpi.java new file mode 100644 index 000000000..ec2c204f1 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIClassLoaderSpi.java @@ -0,0 +1,64 @@ +/* RMIClassLoaderSpi.java -- + Copyright (c) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.net.MalformedURLException; + +/** + * @author Michael Koch + * @since 1.4 + */ +public abstract class RMIClassLoaderSpi +{ + public RMIClassLoaderSpi() + { + } + + public abstract Class loadClass (String codeBase, String name, + ClassLoader defaultLoader) + throws MalformedURLException, ClassNotFoundException; + + public abstract Class loadProxyClass (String codeBase, String[] interfaces, + ClassLoader defaultLoader) + throws MalformedURLException, ClassNotFoundException; + + public abstract ClassLoader getClassLoader (String codebase) + throws MalformedURLException; + + public abstract String getClassAnnotation (Class cl); +} diff --git a/libjava/classpath/java/rmi/server/RMIClientSocketFactory.java b/libjava/classpath/java/rmi/server/RMIClientSocketFactory.java new file mode 100644 index 000000000..ee829fb29 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIClientSocketFactory.java @@ -0,0 +1,47 @@ +/* RMIClientSocketFactory.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.IOException; +import java.net.Socket; + +public interface RMIClientSocketFactory +{ + Socket createSocket (String host, int port) throws IOException; +} diff --git a/libjava/classpath/java/rmi/server/RMIFailureHandler.java b/libjava/classpath/java/rmi/server/RMIFailureHandler.java new file mode 100644 index 000000000..68a5706ed --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIFailureHandler.java @@ -0,0 +1,46 @@ +/* RMIFailureHandler.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +public interface RMIFailureHandler +{ + /** + * @exception IOException If an error occurs + */ + boolean failure (Exception ex); +} diff --git a/libjava/classpath/java/rmi/server/RMIServerSocketFactory.java b/libjava/classpath/java/rmi/server/RMIServerSocketFactory.java new file mode 100644 index 000000000..7af8ef069 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMIServerSocketFactory.java @@ -0,0 +1,47 @@ +/* RMIServerSocketFactory.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.IOException; +import java.net.ServerSocket; + +public interface RMIServerSocketFactory +{ + ServerSocket createServerSocket(int port) throws IOException; +} diff --git a/libjava/classpath/java/rmi/server/RMISocketFactory.java b/libjava/classpath/java/rmi/server/RMISocketFactory.java new file mode 100644 index 000000000..af5a12072 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RMISocketFactory.java @@ -0,0 +1,108 @@ +/* RMISocketFactory.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import gnu.java.rmi.server.RMIDefaultSocketFactory; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +public abstract class RMISocketFactory + implements RMIClientSocketFactory, RMIServerSocketFactory +{ + private static RMISocketFactory defaultFactory; + private static RMISocketFactory currentFactory; + private static RMIFailureHandler currentHandler; + + static + { + defaultFactory = new RMIDefaultSocketFactory(); + currentFactory = defaultFactory; + } + + public RMISocketFactory () + { + } + + /** + * @exception IOException If an error occurs + */ + public abstract Socket createSocket (String host, int port) + throws IOException; + + /** + * @exception IOException If an error occurs + */ + public abstract ServerSocket createServerSocket (int port) + throws IOException; + + /** + * @exception IOException If an error occurs + * @exception SecurityException FIXME + */ + public static void setSocketFactory (RMISocketFactory fac) + throws IOException + { + currentFactory = fac; + } + + public static RMISocketFactory getSocketFactory () + { + return currentFactory; + } + + public static RMISocketFactory getDefaultSocketFactory () + { + return defaultFactory; + } + + /** + * @exception SecurityException FIXME + */ + public static void setFailureHandler (RMIFailureHandler fh) + { + currentHandler = fh; + } + + public static RMIFailureHandler getFailureHandler () + { + return currentHandler; + } +} diff --git a/libjava/classpath/java/rmi/server/RemoteCall.java b/libjava/classpath/java/rmi/server/RemoteCall.java new file mode 100644 index 000000000..cd4b004d8 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteCall.java @@ -0,0 +1,86 @@ +/* RemoteCall.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.StreamCorruptedException; + +/** + * @deprecated + */ +public interface RemoteCall +{ + /** + * @deprecated + */ + ObjectOutput getOutputStream () throws IOException; + + /** + * @deprecated + */ + void releaseOutputStream () throws IOException; + + /** + * @deprecated + */ + ObjectInput getInputStream () throws IOException; + + /** + * @deprecated + */ + void releaseInputStream () throws IOException; + + /** + * @deprecated + */ + ObjectOutput getResultStream (boolean success) + throws IOException, StreamCorruptedException; + + /** + * @deprecated + */ + void executeCall () throws Exception; + + /** + * @deprecated + */ + void done () throws IOException; +} diff --git a/libjava/classpath/java/rmi/server/RemoteObject.java b/libjava/classpath/java/rmi/server/RemoteObject.java new file mode 100644 index 000000000..220c1c95b --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteObject.java @@ -0,0 +1,202 @@ +/* RemoteObject.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.rmi.NoSuchObjectException; +import java.rmi.Remote; +import java.rmi.UnmarshalException; +import java.util.WeakHashMap; + +public abstract class RemoteObject + implements Remote, Serializable { + +private static final long serialVersionUID = -3215090123894869218l; + +protected transient RemoteRef ref; + +private static final WeakHashMap stubs = new WeakHashMap(); + +protected RemoteObject() { + this(null); +} + +protected RemoteObject(RemoteRef newref) { + ref = newref; +} + +public RemoteRef getRef() { + return (ref); +} + +synchronized static void addStub(Remote obj, Remote stub) +{ + stubs.put(obj, stub); +} + +synchronized static void deleteStub(Remote obj) +{ + stubs.remove(obj); +} + + public static Remote toStub(Remote obj) throws NoSuchObjectException + { + Remote stub = (Remote)stubs.get(obj); + + if (stub == null) + throw new NoSuchObjectException(obj.getClass().getName()); + + return stub; + } + +public int hashCode() { + if (ref == null) { + return (0); + } + else { + return (ref.hashCode()); + } +} + +public boolean equals(Object obj) { + // We only compare references. + return (this == obj); +} + +/** + * Get the string representation of this remote object. + */ + public String toString() + { + if (ref == null) + return getClass ().toString (); + return (ref.toString ()); + } + + /** + * Read the remote object from the input stream. Expects the class name + * without package first. Then the method creates and assigns the {@link #ref} + * an instance of this class and calls its .readExternal method. The standard + * packageless class names are UnicastRef, UnicastRef2, UnicastServerRef, + * UnicastServerRef2, ActivatableRef or ActivatableServerRef. + * + * @param in the stream to read from + * @throws IOException if the IO exception occurs + * @throws ClassNotFoundException if the class with the given name is not + * present in the package gnu.java.rmi.server (for the case of the + * GNU Classpath. + */ + private void readObject(ObjectInputStream in) throws IOException, + ClassNotFoundException + { + String cname = in.readUTF(); + if (! cname.equals("")) + { + if (cname.equals("UnicastRef2")) + { + // hack for interoperating with JDK + cname = "UnicastRef"; + in.read(); // some unknown UnicastRef2 field + } + + // It would be nice to use RemoteRef.packagePrefix here, but for binary + // compatibility with the JDK that has to contain "sun.rmi.server"... + cname = "gnu.java.rmi.server." + cname; + try + { + Class cls = Class.forName(cname); + ref = (RemoteRef) cls.newInstance(); + } + catch (InstantiationException e1) + { + throw new UnmarshalException("failed to create ref", e1); + } + catch (IllegalAccessException e2) + { + throw new UnmarshalException("failed to create ref", e2); + } + ref.readExternal(in); + } + else + { + ref = (RemoteRef) in.readObject(); + } + } + + /** + * Write the remote object to the output stream. This method first calls + * {@link RemoteRef#getRefClass(ObjectOutput)} on the {@link #ref} to get the + * class name without package, writes this name and then calls the + * ref.writeObject to write the data. The standard packageless class names are + * UnicastRef, UnicastRef2, UnicastServerRef, UnicastServerRef2, + * ActivatableRef or ActivatableServerRef. The empty string with the + * subsequently following serialized ref instance be written if the + * ref.getRefClass returns null. + * + * @param out the stream to write to + * @throws IOException if one occurs during writing + * @throws ClassNotFoundException never in this implementation (specified as + * part of the API standard) + * @throws UnmarshalException if the remote reference of this remote object is + * null. + */ + private void writeObject(ObjectOutputStream out) throws IOException, + ClassNotFoundException + { + if (ref == null) + { + throw new UnmarshalException("no ref to serialize"); + } + String cname = ref.getRefClass(out); + if (cname != null && cname.length() > 0) + { + out.writeUTF(cname); + ref.writeExternal(out); + } + else + { + out.writeUTF(""); + out.writeObject(ref); + } + } + +} diff --git a/libjava/classpath/java/rmi/server/RemoteObjectInvocationHandler.java b/libjava/classpath/java/rmi/server/RemoteObjectInvocationHandler.java new file mode 100644 index 000000000..0cf4bca98 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteObjectInvocationHandler.java @@ -0,0 +1,229 @@ +/* RemoteObjectInvocationHandler.java -- RMI stub replacement. + Copyright (C) 2005 Free Software Foundation, Inc. + + This file is part of GNU Classpath. + + GNU Classpath is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Classpath is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Classpath; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ + + +package java.rmi.server; + +import gnu.java.rmi.server.RMIHashes; + +import java.io.Serializable; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.UnexpectedException; +import java.rmi.registry.Registry; +import java.rmi.server.RemoteObject; +import java.rmi.server.RemoteRef; +import java.rmi.server.UnicastRemoteObject; +import java.util.Hashtable; + +/** + * Together with dynamic proxy instance, this class replaces the generated RMI + * stub (*_Stub) classes that (following 1.5 specification) should be no longer + * required. It is unusual to use the instances of this class directly in the + * user program. Such instances are automatically created and returned by + * {@link Registry} or {@link UnicastRemoteObject} methods if the remote + * reference is known but the corresponding stub class is not accessible. + * + * @see Registry#lookup + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public class RemoteObjectInvocationHandler extends RemoteObject implements + InvocationHandler, Remote, Serializable +{ + /** + * Use the jdk 1.5 SUID for interoperability. + */ + static final long serialVersionUID = 2L; + + /** + * The RMI method hash codes, computed once as described in the section 8.3 + * of the Java Remote Method Invocation (RMI) Specification. + */ + static Hashtable methodHashCodes = new Hashtable(); + + /** + * The empty class array to define parameters of .hashCode and .toString. + */ + static final Class[] noArgsC = new Class[0]; + + /** + * The class array to define parameters of .equals + */ + static final Class[] anObjectC = new Class[] { Object.class }; + + /** + * The empty object array to replace null when no args are passed. + */ + static final Object[] noArgs = new Object[0]; + + /** + * Construct the remote invocation handler that forwards calls to the given + * remote object. + * + * @param reference the reference to the remote object where the method + * calls should be forwarded. + */ + public RemoteObjectInvocationHandler(RemoteRef reference) + { + super(reference); + } + + /** + * Invoke the remote method. When the known method is invoked on a created RMI + * stub proxy class, the call is delivered to this method and then transferred + * to the {@link RemoteRef#invoke(Remote, Method, Object[], long)} of the + * remote reference that was passed in constructor. The methods are handled as + * following: + *
          + *
        • The toString() method is delegated to the passed proxy instance.
        • + *
        • The .equals method only returns true if the passed object is an + * instance of proxy class and its invocation handler is equal to this + * invocation handles.
        • + *
        • The .hashCode returns the hashCode of this invocation handler (if the.
        • + *
        • All other methods are converted to remote calls and forwarded to the + * remote reference.
        • + *
        + * + * @param proxyInstance + * the instance of the proxy stub + * @param method + * the method being invoked + * @param parameters + * the method parameters + * @return the method return value, returned by RemoteRef.invoke + * @throws IllegalAccessException + * if the passed proxy instance does not implement Remote interface. + * @throws UnexpectedException + * if remote call throws some exception, not listed in the + * throws clause of the method being called. + * @throws Throwable + * that is thrown by remote call, if that exception is listend in + * the throws clause of the method being called. + */ + public Object invoke(Object proxyInstance, Method method, Object[] parameters) + throws Throwable + { + if (!(proxyInstance instanceof Remote)) + { + String name = proxyInstance == null ? "null" + : proxyInstance.getClass().getName(); + throw new IllegalAccessException(name + " does not implement " + + Remote.class.getName()); + } + + if (parameters == null) + parameters = noArgs; + + String name = method.getName(); + switch (name.charAt(0)) + { + case 'e': + if (parameters.length == 1 && name.equals("equals") + && method.getParameterTypes()[0].equals(Object.class)) + { + if (parameters[0] instanceof Proxy) + { + Object handler = Proxy.getInvocationHandler(parameters[0]); + if (handler == null) + return Boolean.FALSE; + else + return handler.equals(this) ? Boolean.TRUE : Boolean.FALSE; + } + else + return Boolean.FALSE; + } + break; + case 'h': + if (parameters.length == 0 && name.equals("hashCode")) + { + int hashC = Proxy.getInvocationHandler(proxyInstance).hashCode(); + return new Integer(hashC); + } + break; + case 't': + if (parameters.length == 0 && name.equals("toString")) + return "Proxy stub:"+ref.remoteToString(); + break; + default: + break; + } + + Long hash = (Long) methodHashCodes.get(method); + if (hash == null) + { + hash = new Long(RMIHashes.getMethodHash(method)); + methodHashCodes.put(method, hash); + } + + try + { + return getRef().invoke((Remote) proxyInstance, method, parameters, + hash.longValue()); + } + catch (RuntimeException exception) + { + // RuntimeException is always supported. + throw exception; + } + catch (RemoteException exception) + { + // All remote methods can throw RemoteException. + throw exception; + } + catch (Error exception) + { + throw exception; + } + catch (Exception exception) + { + Class[] exceptions = method.getExceptionTypes(); + Class exceptionClass = exception.getClass(); + + for (int i = 0; i < exceptions.length; i++) + { + if (exceptions[i].equals(exceptionClass)) + throw exception; + } + throw new UnexpectedException(method.getName(), exception); + } + } + +} diff --git a/libjava/classpath/java/rmi/server/RemoteRef.java b/libjava/classpath/java/rmi/server/RemoteRef.java new file mode 100644 index 000000000..e0488fb4c --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteRef.java @@ -0,0 +1,137 @@ +/* RemoteRef.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.Externalizable; +import java.io.ObjectOutput; +import java.lang.reflect.Method; +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * Represents a handler to the remote object. Each instance of the + * {@link RemoteStub} contains such handler and uses it to invoke remote + * methods via {@link #invoke(Remote, Method, Object[], long)}. + */ +public interface RemoteRef extends Externalizable +{ + /** + * Indicates compatibility with JDK 1.1.* + */ + long serialVersionUID = 3632638527362204081L; + + /** + * For binary compatibility with the JDK, the string "sun.rmi.server". + * Not actually used for anything. + */ + String packagePrefix = "sun.rmi.server"; + + /** + * @deprecated use {@link #invoke(Remote, Method, Object[], long)} instead. + */ + void invoke (RemoteCall call) throws Exception; + + /** + * Invoke a method. This method either returns the result of remote invocation + * or throws RemoteException if the remote call failed. Other exceptions may + * be thrown if some problem has occured in the application level. + * + * @param obj the object, containing the remote reference (for instance, + * remote stub, generated by rmic). + * @param method the method to invoke + * @param params the method parameters + * @param methodHash a persistent hash code that can be used to represent a + * method + * @return the result of the remote invocation + * @throws RemoteException if the remote call has failed + * @throws Exception if one is raised at the application level + */ + Object invoke (Remote obj, Method method, Object[] params, long methodHash) + throws Exception; + + /** + * @deprecated use {@link #invoke(Remote, Method, Object[], long)} instead. + */ + RemoteCall newCall (RemoteObject obj, Operation[] op, int opnum, long hash) + throws RemoteException; + + /** + * @deprecated use {@link #invoke(Remote, Method, Object[], long)} instead. + */ + void done (RemoteCall call) throws RemoteException; + + /** + * Compare two remote objects for equality. The references are equal if + * they point to the same remote object. + * + * @param ref the reference to compare. + * + * @return true if this and passed references both point to the same remote + * object, false otherwise. + */ + boolean remoteEquals (RemoteRef ref); + + /** + * Get the hashcode for a remote object. Two remote object stubs, referring + * to the same remote object, have the same hash code. + * + * @return the hashcode of the remote object + */ + int remoteHashCode(); + + + /** + * Returns the class name of the reference type that must be written to the + * given stream. When writing, this returned name is passed first, and + * the reference.writeExternal(out) writes the reference specific data. + * + * @param out the stream, where the data must be written + * + * @return the class name. + */ + String getRefClass (ObjectOutput out); + + /** + * Get the string representation of this remote reference. + * + * @return the string representation. + */ + String remoteToString(); +} diff --git a/libjava/classpath/java/rmi/server/RemoteServer.java b/libjava/classpath/java/rmi/server/RemoteServer.java new file mode 100644 index 000000000..158c46439 --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteServer.java @@ -0,0 +1,115 @@ +/* RemoteServer.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import gnu.java.rmi.server.RMIIncomingThread; + +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * A common superclass for the server implementations. + */ +public abstract class RemoteServer + extends RemoteObject +{ + private static final long serialVersionUID = - 4100238210092549637L; + + /** + * Does nothing, delegates to super(). + */ + protected RemoteServer() + { + super(); + } + + /** + * Does nothing, delegates to super(ref). + */ + protected RemoteServer(RemoteRef ref) + { + super(ref); + } + + /** + * Get the host of the calling client. The current thread must be an instance + * of the {@link RMIIncomingThread}. + * + * @return the client host address + * + * @throws ServerNotActiveException if the current thread is not an instance + * of the RMIIncomingThread. + */ + public static String getClientHost() throws ServerNotActiveException + { + Thread currThread = Thread.currentThread(); + if (currThread instanceof RMIIncomingThread) + { + RMIIncomingThread incomingThread = (RMIIncomingThread) currThread; + return incomingThread.getClientHost(); + } + else + { + throw new ServerNotActiveException( + "Unknown client host - current thread not instance of 'RMIIncomingThread'"); + } + } + + /** + * Set the stream for logging RMI calls. + * + * @param out the stream to set or null to turn the logging off. + */ + public static void setLog(OutputStream out) + { + throw new Error("Not implemented"); + } + + /** + * Get the stream for logging RMI calls. + * + * @return the associated stream. + */ + public static PrintStream getLog() + { + throw new Error("Not implemented"); + } + +} diff --git a/libjava/classpath/java/rmi/server/RemoteStub.java b/libjava/classpath/java/rmi/server/RemoteStub.java new file mode 100644 index 000000000..9fd5846ee --- /dev/null +++ b/libjava/classpath/java/rmi/server/RemoteStub.java @@ -0,0 +1,80 @@ +/* RemoteStub.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * This is a base class for the automatically generated RMI stubs. + */ +public abstract class RemoteStub extends RemoteObject +{ + /** + * Use serialVersionUID for interoperability. + */ + static final long serialVersionUID = -1585587260594494182l; + + /** + * Constructs the remote stub with no reference set. + */ + protected RemoteStub () + { + super (); + } + + /** + * Constructs the remote stub that uses given remote reference for the + * method invocations. + * + * @param ref the remote reference for the method invocation. + */ + protected RemoteStub (RemoteRef ref) + { + super (ref); + } + + /** + * Sets the given remote reference for the given stub. This method is + * deprecated. Pass the stub remote reference to the RemoteStub + * constructor instead. + * + * @deprecated + */ + protected static void setRef (RemoteStub stub, RemoteRef ref) + { + stub.ref = ref; + } +} // class RemoteSub diff --git a/libjava/classpath/java/rmi/server/ServerCloneException.java b/libjava/classpath/java/rmi/server/ServerCloneException.java new file mode 100644 index 000000000..bda41b3ce --- /dev/null +++ b/libjava/classpath/java/rmi/server/ServerCloneException.java @@ -0,0 +1,117 @@ +/* ServerCloneException.java -- a UnicastRemoteObject could not be cloned + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * Thrown if a remote exception occurs during the cloning process of a + * UnicastRemoteObject. + * + * @author unknown + * @see UnicastRemoteObject#clone() + * @since 1.1 + * @status updated to 1.4 + */ +public class ServerCloneException extends CloneNotSupportedException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6617456357664815945L; + + /** + * The cause of this exception. This pre-dates the exception chaining + * of Throwable; and although you can change this field, you are wiser + * to leave it alone. + * + * @serial the exception cause + */ + public Exception detail; + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ServerCloneException(String s) + { + this(s, null); + } + + /** + * Create an exception with a message and a cause. + * + * @param s the message + * @param e the cause + */ + public ServerCloneException(String s, Exception e) + { + super(s); + initCause(e); + detail = e; + } + + /** + * This method returns a message indicating what went wrong, in this + * format: + * super.getMessage() + (detail == null ? "" + * : "; nested exception is:\n\t" + detail). + * + * @return the chained message + */ + public String getMessage() + { + if (detail == this || detail == null) + return super.getMessage(); + return super.getMessage() + "; nested exception is:\n\t" + detail; + } + + /** + * Returns the cause of this exception. Note that this may not be the + * original cause, thanks to the detail field being public + * and non-final (yuck). However, to avoid violating the contract of + * Throwable.getCause(), this returns null if detail == this, + * as no exception can be its own cause. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return detail == this ? null : detail; + } +} diff --git a/libjava/classpath/java/rmi/server/ServerNotActiveException.java b/libjava/classpath/java/rmi/server/ServerNotActiveException.java new file mode 100644 index 000000000..0581b63bc --- /dev/null +++ b/libjava/classpath/java/rmi/server/ServerNotActiveException.java @@ -0,0 +1,72 @@ +/* ServerNotActiveException.java -- the method is not servicing a remote call + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * Thrown during RemoteServer.getClientHost if the host is + * not servicing a remote method call. + * + * @author unknown + * @see RemoteServer#getClientHost() + * @since 1.1 + * @status updated to 1.4 + */ +public class ServerNotActiveException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4687940720827538231L; + + /** + * Create an exception with no message. + */ + public ServerNotActiveException() + { + } + + /** + * Create an exception with a message. + * + * @param s the message + */ + public ServerNotActiveException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/server/ServerRef.java b/libjava/classpath/java/rmi/server/ServerRef.java new file mode 100644 index 000000000..5d34ef2e2 --- /dev/null +++ b/libjava/classpath/java/rmi/server/ServerRef.java @@ -0,0 +1,51 @@ +/* ServerRef.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface ServerRef extends RemoteRef +{ + long serialVersionUID = -4557750989390278438L; + + RemoteStub exportObject(Remote obj, Object data) throws RemoteException; + + String getClientHost() throws ServerNotActiveException; +} diff --git a/libjava/classpath/java/rmi/server/Skeleton.java b/libjava/classpath/java/rmi/server/Skeleton.java new file mode 100644 index 000000000..94d57986b --- /dev/null +++ b/libjava/classpath/java/rmi/server/Skeleton.java @@ -0,0 +1,57 @@ +/* Skeleton.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.rmi.Remote; + +/** + * @deprecated + */ +public interface Skeleton +{ + /** + * @deprecated + */ + void dispatch (Remote obj, RemoteCall theCall, int opnum, long hash) + throws Exception; + + /** + * @deprecated + */ + Operation[] getOperations(); +} diff --git a/libjava/classpath/java/rmi/server/SkeletonMismatchException.java b/libjava/classpath/java/rmi/server/SkeletonMismatchException.java new file mode 100644 index 000000000..9c0206ab3 --- /dev/null +++ b/libjava/classpath/java/rmi/server/SkeletonMismatchException.java @@ -0,0 +1,68 @@ +/* SkeletonMismatchException.java -- thrown when stub class versions mismatch + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +import java.rmi.RemoteException; + +/** + * Thrown if a call is received that does not match a Skeleton. Note that + * Skeletons are no longer required. + * + * @author unknown + * @since 1.1 + * @deprecated no replacement. Skeletons are no longer required. + * @status updated to 1.4 + */ +public class SkeletonMismatchException extends RemoteException +{ + /** + * Compatible with JDK 1.1. + */ + private static final long serialVersionUID = -7780460454818859281l; + + /** + * Create an exception with the specified message. + * + * @param s the message + * @deprecated no longer needed + */ + public SkeletonMismatchException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/rmi/server/SkeletonNotFoundException.java b/libjava/classpath/java/rmi/server/SkeletonNotFoundException.java new file mode 100644 index 000000000..596aae154 --- /dev/null +++ b/libjava/classpath/java/rmi/server/SkeletonNotFoundException.java @@ -0,0 +1,79 @@ +/* SkeletonNotFoundException.java -- thrown if a Skeleton is not found + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.rmi.RemoteException; + +/** + * Thrown if a Skeleton corresponding to the remote object is not found. + * Note that Skeletons are no longer required. + * + * @author unknown + * @since 1.1 + * @deprecated no replacement. Skeletons are no longer required. + * @status updated to 1.4 + */ +public class SkeletonNotFoundException extends RemoteException +{ + /** + * Compatible with JDK 1.1. + */ + private static final long serialVersionUID = -7860299673822761231L; + + /** + * Create an exception with the specified message. + * + * @param s the message + */ + public SkeletonNotFoundException(String s) + { + super(s); + } + + /** + * Create an exception with the specified message and cause. + * + * @param s the message + * @param e the cause + */ + public SkeletonNotFoundException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/server/SocketSecurityException.java b/libjava/classpath/java/rmi/server/SocketSecurityException.java new file mode 100644 index 000000000..aaf7698f4 --- /dev/null +++ b/libjava/classpath/java/rmi/server/SocketSecurityException.java @@ -0,0 +1,75 @@ +/* SocketSecurityException.java -- the socket could not be created + Copyright (c) 1996, 1997, 1998, 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +/** + * Thrown during remote object export if the code does not have permission + * to create a java.net.ServerSocket on the specified port. + * + * @author unknown + * @since 1.1 + * @status updated to 1.4 + */ +public class SocketSecurityException extends ExportException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7622072999407781979L; + + /** + * Create an exception with the specified message. + * + * @param s the message + */ + public SocketSecurityException(String s) + { + super(s); + } + + /** + * Create an exception with the specified message and cause. + * + * @param s the message + * @param e the cause + */ + public SocketSecurityException(String s, Exception e) + { + super(s, e); + } +} diff --git a/libjava/classpath/java/rmi/server/UID.java b/libjava/classpath/java/rmi/server/UID.java new file mode 100644 index 000000000..464d3d860 --- /dev/null +++ b/libjava/classpath/java/rmi/server/UID.java @@ -0,0 +1,225 @@ +/* UID.java -- The unique object Id + Copyright (c) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.Serializable; +import java.net.InetAddress; + +/** + * Represents the unique identifier over time for the host which has generated + * it. It contains time (when created), counter (the number of the UID + * creation order) and virtual machine id components. The UID can also be + * constructed specifying a "well known" identifier in the for of short: + * this identifier defines the UID uniqueness alone. + * + * @author Audrius Meskauskas (audriusa@bioinformatics.org) + */ +public final class UID + implements Serializable +{ + /** + * Use the serial version uid for interoperability. + */ + private static final long serialVersionUID = 1086053664494604050L; + + /** + * The UID counter (the ordinary number in the sequence of number of UID's, + * created during the recent millisecond). In the next millisecond, it + * starts from the minimal value again. In the unlikely case of creating + * more than 65536 uids per millisecond the process pauses till the next + * ms. + */ + private static short uidCounter = Short.MIN_VALUE; + + /** + * The time, when the last UID has been created. + */ + private static long last; + + /** + * This constant tries to be the unique identifier of the virtual machine. + */ + private static final int machineId = getMachineId(); + + /** + * The UID number in the UID creation sequence. + */ + private short count; + + /** + * Always gets the uniqueNr value. + */ + private int unique; + + /** + * The time stamp, when the UID was created. + */ + private long time; + + /** + * Create the new UID that would have the described features of the + * uniqueness. + */ + public UID() + { + synchronized (UID.class) + { + time = System.currentTimeMillis(); + unique = machineId; + if (time > last) + { + last = time; + count = uidCounter = Short.MIN_VALUE; + } + else + { + if (uidCounter == Short.MAX_VALUE) + { + // Make a 2 ms pause if the counter has reached the maximal + // value. This should seldom happen. + try + { + Thread.sleep(2); + } + catch (InterruptedException e) + { + } + uidCounter = Short.MIN_VALUE; + time = last = System.currentTimeMillis(); + } + count = ++uidCounter; + } + } + } + + /** + * Create the new UID with the well known id (number). All UIDs, creates + * with the this constructor having the same parameter are equal to each + * other (regardless to the host and time where they were created. + * + * @param wellKnownId the well known UID. + */ + public UID(short wellKnownId) + { + unique = wellKnownId; + } + + /** + * Get the hashCode of this UID. + */ + public int hashCode() + { + return (int) (unique ^ time ^ count); + } + + /** + * Compare this UID with another UID for equality (not equal to other types of + * objects). + */ + public boolean equals(Object other) + { + if (other instanceof UID) + { + UID ui = (UID) other; + return unique == ui.unique && time == ui.time && count == ui.count; + } + else + return false; + } + + public static UID read(DataInput in) throws IOException + { + UID uid = new UID(); + uid.unique = in.readInt(); + uid.time = in.readLong(); + uid.count = in.readShort(); + return (uid); + } + + public void write(DataOutput out) throws IOException + { + out.writeInt(unique); + out.writeLong(time); + out.writeShort(count); + } + + /** + * Do our best to get the Id of this virtual machine. + */ + static int getMachineId() + { + int hostIpHash; + + try + { + // Try to get the host IP. + String host = InetAddress.getLocalHost().toString(); + // This hash is content - based, not the address based. + hostIpHash = host.hashCode(); + } + catch (Exception e) + { + // Failed due some reason. + hostIpHash = 0; + } + + // Should be the unque address if hashcodes are addresses. + // Additionally, add the time when the RMI system was probably started + // (this class was first instantiated). + return new Object().hashCode() ^ (int) System.currentTimeMillis() + ^ hostIpHash; + } + + /** + * Get the string representation of this UID. + * + * @return a string, uniquely identifying this id. + */ + public String toString() + { + int max = Character.MAX_RADIX; + // Translate into object count, counting from 0. + long lc = (count - Short.MIN_VALUE) & 0xFFFF; + return Long.toString(unique, max) + ":" + Long.toString(time, max) + "." + + Long.toString(lc, max); + } +} diff --git a/libjava/classpath/java/rmi/server/UnicastRemoteObject.java b/libjava/classpath/java/rmi/server/UnicastRemoteObject.java new file mode 100644 index 000000000..b6a972126 --- /dev/null +++ b/libjava/classpath/java/rmi/server/UnicastRemoteObject.java @@ -0,0 +1,251 @@ +/* UnicastRemoteObject.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2003, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.rmi.server; + +import gnu.java.rmi.server.UnicastServerRef; + +import java.rmi.NoSuchObjectException; +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * This class obtains stub that communicates with the remote object. + */ +public class UnicastRemoteObject extends RemoteServer +{ + /** + * Use SVUID for interoperability. + */ + private static final long serialVersionUID = 4974527148936298033L; + + //The following serialized fields are from Java API Documentation + // "Serialized form" + + /** + * The port, on that the created remote object becomes available, + * zero meaning the anonymous port. + */ + private int port; + + /** + * The client socket factory for producing client sockets, used by this + * object. + */ + private RMIClientSocketFactory csf; + + /** + * The server socket factory for producing server sockets, used by this + * object. + */ + private RMIServerSocketFactory ssf; + + /** + * Create and export new remote object without specifying the port value. + * + * @throws RemoteException if the attempt to export the object failed. + */ + protected UnicastRemoteObject() + throws RemoteException + { + this(0); + } + + /** + * Create and export the new remote object, making it available at the + * given port, local host. + * + * @param port the port, on that the object should become available. + * Zero means anonymous port. + * + * @throws RemoteException if the attempt to export the object failed. + */ + protected UnicastRemoteObject(int port) + throws RemoteException + { + this(port, RMISocketFactory.getSocketFactory(), + RMISocketFactory.getSocketFactory()); + } + + /** + * Create and export the new remote object, making it available at the + * given port, using sockets, produced by the specified factories. + * + * @param port the port, on that the object should become available. + * Zero means anonymous port. + * + * @param clientSocketFactory the client socket factory + * @param serverSocketFactory the server socket factory + * + * @throws RemoteException if the attempt to export the object failed. + */ + protected UnicastRemoteObject(int port, + RMIClientSocketFactory clientSocketFactory, + RMIServerSocketFactory serverSocketFactory) + throws RemoteException + { + this.port = port; + //Is RMIXXXSocketFactory serializable + //this.csf = csf; + //this.ssf = ssf; + this.ref = new UnicastServerRef(new ObjID(), port, serverSocketFactory); + exportObject(this, port); + } + + protected UnicastRemoteObject(RemoteRef ref) + throws RemoteException + { + super((UnicastServerRef) ref); + exportObject(this, 0); + } + + public Object clone() + throws CloneNotSupportedException + { + throw new Error("Not implemented"); + } + + /** + * Export object, making it available for the remote calls at the + * anonymous port. + * + * This method returns the instance of the abstract class, not an interface. + * Hence it will not work with the proxy stubs that are supported since + * jdk 1.5 (such stubs cannot be derived from the RemoteStub). Only use + * this method if you are sure that the stub class will be accessible. + * + * @param obj the object being exported. + * + * @return the remote object stub + * + * @throws RemoteException if the attempt to export the object failed. + */ + public static RemoteStub exportObject(Remote obj) + throws RemoteException + { + return (RemoteStub) exportObject(obj, 0); + } + + /** + * Export object, making it available for the remote calls at the + * specified port. + * + * Since jdk 1.5 this method does not longer require the stub class to be + * present. If such class is not found, the stub is replaced by the + * dynamically constructed proxy class. No attempt to find and load the stubs + * is made if the system property java.rmi.server.ignoreStubClasses + * is set to true (set to reduce the starting time if the stubs are + * surely not present and exclusively 1.2 RMI is used). + * + * @param obj the object being exported. + * @param port the remote object port + * + * @return the remote object stub + * + * @throws RemoteException if the attempt to export the object failed. + */ + public static Remote exportObject(Remote obj, int port) + throws RemoteException + { + return exportObject(obj, port, null); + } + + /** + * Create and export the new remote object, making it available at the + * given port, using sockets, produced by the specified factories. + * + * Since jdk 1.5 this method does not longer require the stub class to be + * present. If such class is not found, the stub is replaced by the + * dynamically constructed proxy class. No attempt to find and load the stubs + * is made if the system property java.rmi.server.ignoreStubClasses + * is set to true (set to reduce the starting time if the stubs are + * surely not present and exclusively 1.2 RMI is used). + * + * @param port the port, on that the object should become available. + * Zero means anonymous port. + * + * @param serverSocketFactory the server socket factory + */ + static Remote exportObject(Remote obj, int port, + RMIServerSocketFactory serverSocketFactory) + throws RemoteException + { + UnicastServerRef sref = null; + if (obj instanceof RemoteObject) + sref = (UnicastServerRef) ((RemoteObject) obj).getRef(); + + if (sref == null) + sref = new UnicastServerRef(new ObjID(), port, serverSocketFactory); + + Remote stub = sref.exportObject(obj); + addStub(obj, stub); + return stub; + } + + /** + * FIXME + */ + public static Remote exportObject(Remote obj, int port, + RMIClientSocketFactory csf, + RMIServerSocketFactory ssf) + throws RemoteException + { + return (exportObject(obj, port, ssf)); + } + + public static boolean unexportObject(Remote obj, boolean force) + throws NoSuchObjectException + { + if (obj instanceof RemoteObject) + { + deleteStub(obj); + UnicastServerRef sref = + (UnicastServerRef) ((RemoteObject) obj).getRef(); + return sref.unexportObject(obj, force); + } + // FIXME + /* else + { + ; + } + */ + return true; + } + +} diff --git a/libjava/classpath/java/rmi/server/Unreferenced.java b/libjava/classpath/java/rmi/server/Unreferenced.java new file mode 100644 index 000000000..6d4df48bc --- /dev/null +++ b/libjava/classpath/java/rmi/server/Unreferenced.java @@ -0,0 +1,43 @@ +/* Unreferenced.java -- + Copyright (c) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.rmi.server; + +public interface Unreferenced +{ + void unreferenced(); +} diff --git a/libjava/classpath/java/rmi/server/package.html b/libjava/classpath/java/rmi/server/package.html new file mode 100644 index 000000000..54055a721 --- /dev/null +++ b/libjava/classpath/java/rmi/server/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.rmi.server + + +

        + + + diff --git a/libjava/classpath/java/security/AccessControlContext.java b/libjava/classpath/java/security/AccessControlContext.java new file mode 100644 index 000000000..fd964751c --- /dev/null +++ b/libjava/classpath/java/security/AccessControlContext.java @@ -0,0 +1,218 @@ +/* AccessControlContext.java --- Access Control Context Class + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.util.HashSet; + +/** + * AccessControlContext makes system resource access decsion + * based on permission rights. + * + * It is used for a specific context and has only one method + * checkPermission. It is similar to AccessController except + * that it makes decsions based on the current context instead + * of the the current thread. + * + * It is created by call AccessController.getContext method. + * + * @author Mark Benvenuto + * @since 1.2 + */ +public final class AccessControlContext +{ + private final ProtectionDomain[] protectionDomains; + private final DomainCombiner combiner; + + /** + * Construct a new AccessControlContext with the specified + * ProtectionDomains. context must not be + * null and duplicates will be removed. + * + * @param context The ProtectionDomains to use + */ + public AccessControlContext(ProtectionDomain[] context) + { + HashSet domains = new HashSet (context.length); + for (int i = 0; i < context.length; i++) + domains.add (context[i]); + protectionDomains = (ProtectionDomain[]) + domains.toArray (new ProtectionDomain[domains.size()]); + combiner = null; + } + + /** + * Construct a new AccessControlContext with the specified + * {@link ProtectionDomain}s and {@link DomainCombiner}. + * + *

        Code calling this constructor must have a {@link + * SecurityPermission} of createAccessControlContext.

        + * + * @throws SecurityException If the caller does not have permission + * to create an access control context. + * @since 1.3 + */ + public AccessControlContext(AccessControlContext acc, + DomainCombiner combiner) + { + AccessControlContext acc2 = null; + SecurityManager sm = System.getSecurityManager (); + if (sm != null) + { + Permission perm = + new SecurityPermission ("createAccessControlContext"); + + // The default SecurityManager.checkPermission(perm) just calls + // AccessController.checkPermission(perm) which in turn just + // calls AccessController.getContext().checkPermission(perm). + // This means AccessController.getContext() is called twice, + // once for the security check and once by us. It's a very + // expensive call (on gcj at least) so if we're using the + // default security manager we avoid this duplication. + if (sm.getClass() == SecurityManager.class) + { + acc2 = AccessController.getContext (); + acc2.checkPermission (perm); + } + else + sm.checkPermission (perm); + } + if (acc2 == null) + acc2 = AccessController.getContext (); + protectionDomains = combiner.combine (acc2.protectionDomains, + acc.protectionDomains); + this.combiner = combiner; + } + + AccessControlContext (ProtectionDomain[] domains, AccessControlContext acc, + DomainCombiner combiner) + { + protectionDomains = combiner.combine (domains, acc.protectionDomains); + this.combiner = combiner; + } + + /** + * Returns the Domain Combiner associated with the AccessControlContext + * + * @return the DomainCombiner + */ + public DomainCombiner getDomainCombiner() + { + return combiner; + } + + /** + * Determines whether or not the specific permission is granted + * depending on the context it is within. + * + * @param perm a permission to check + * + * @throws AccessControlException if the permssion is not permitted + */ + public void checkPermission(Permission perm) throws AccessControlException + { + if (protectionDomains.length == 0) + throw new AccessControlException ("permission " + + perm + + " not granted: no protection domains"); + + for (int i = 0; i < protectionDomains.length; i++) + { + final ProtectionDomain domain = protectionDomains[i]; + if (!domain.implies(perm)) + throw new AccessControlException ("permission " + + perm + + " not granted: " + + domain + + " does not imply it."); + } + } + + /** + * Checks if two AccessControlContexts are equal. + * + * It first checks if obj is an AccessControlContext class, and + * then checks if each ProtectionDomain matches. + * + * @param obj The object to compare this class to + * + * @return true if equal, false otherwise + */ + public boolean equals(Object obj) + { + if (obj instanceof AccessControlContext) + { + AccessControlContext acc = (AccessControlContext) obj; + + if (acc.protectionDomains.length != protectionDomains.length) + return false; + + int i, j; + for (i = 0; i < protectionDomains.length; i++) + { + for (j = 0; j < acc.protectionDomains.length; j++) + { + if (acc.protectionDomains[j].equals (protectionDomains[i])) + break; + } + if (j == acc.protectionDomains.length) + return false; + } + return true; + } + return false; + } + + /** + * Computes a hash code of this class + * + * @return a hash code representing this class + */ + public int hashCode() + { + int h = 0; + for (int i = 0; i < protectionDomains.length; i++) + h ^= protectionDomains[i].hashCode(); + + return h; + } + + ProtectionDomain[] getProtectionDomains () + { + return protectionDomains; + } +} diff --git a/libjava/classpath/java/security/AccessControlException.java b/libjava/classpath/java/security/AccessControlException.java new file mode 100644 index 000000000..27aee7c86 --- /dev/null +++ b/libjava/classpath/java/security/AccessControlException.java @@ -0,0 +1,97 @@ +/* AccessControlException.java -- Permission is denied + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when the AccessController denies + * an attempt to perform an operation. This often keeps track of the + * permission that was not granted. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see AccessController + * @status updated to 1.4 + */ +public class AccessControlException extends SecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5138225684096988535L; + + /** + * The Permission associated with this exception. + * + * @serial the permission + */ + private final Permission perm; + + /** + * Create a new instance with a descriptive error message, and a null + * Permission object. + * + * @param msg the descriptive error message + */ + public AccessControlException(String msg) + { + this(msg, null); + } + + /** + * Create a new instance with a descriptive error message and an associated + * Permission object. + * + * @param msg the descriptive error message + * @param perm the permission that caused this + */ + public AccessControlException(String msg, Permission perm) + { + super(msg); + this.perm = perm; + } + + /** + * This method returns the Permission object that caused + * this exception to be thrown. + * + * @return the denied permission, or null + */ + public Permission getPermission() + { + return perm; + } +} diff --git a/libjava/classpath/java/security/AccessController.java b/libjava/classpath/java/security/AccessController.java new file mode 100644 index 000000000..ec5b14c9e --- /dev/null +++ b/libjava/classpath/java/security/AccessController.java @@ -0,0 +1,229 @@ +/* AccessController.java --- Access control context and permission checker + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Access control context and permission checker. + * Can check permissions in the access control context of the current thread + * through the checkPermission() method. + * Manipulates the access control context for code that needs to be executed + * the protection domain of the calling class (by explicitly ignoring the + * context of the calling code) in the doPrivileged() methods. + * And provides a getContext() method which gives the access + * control context of the current thread that can be used for checking + * permissions at a later time and/or in another thread. + * + * @author Mark Wielaard (mark@klomp.org) + * @since 1.2 + */ +public final class AccessController +{ + /** + * This class only has static methods so there is no public contructor. + */ + private AccessController() + { + } + + /** + * Checks wether the access control context of the current thread allows + * the given Permission. Throws an AccessControlException + * when the permission is not allowed in the current context. Otherwise + * returns silently without throwing an exception. + * + * @param perm the permission to be checked. + * @exception AccessControlException thrown if the current context does not + * allow the given permission. + */ + public static void checkPermission(Permission perm) + throws AccessControlException + { + getContext().checkPermission(perm); + } + + /** + * Calls the run() method of the given action with as + * (initial) access control context only the protection domain of the + * calling class. Calls to checkPermission() in the + * run() method ignore all earlier protection domains of + * classes in the call chain. Note that the protection domains of classes + * called by the code in the run() method are not ignored. + * + * @param action the PrivilegedAction whose run() + * should be be called. + * @return the result of the action.run() method. + */ + public static T doPrivileged(PrivilegedAction action) + { + VMAccessController.pushContext(null); + try + { + return action.run(); + } + finally + { + VMAccessController.popContext(); + } + } + + /** + * Calls the run() method of the given action with as + * (initial) access control context the given context combined with the + * protection domain of the calling class. Calls to + * checkPermission() in the run() method ignore + * all earlier protection domains of classes in the call chain, but add + * checks for the protection domains given in the supplied context. + * + * @param action the PrivilegedAction whose run() + * should be be called. + * @param context the AccessControlContext whose protection + * domains should be added to the protection domain of the calling class. + * @return the result of the action.run() method. + */ + public static T doPrivileged(PrivilegedAction action, + AccessControlContext context) + { + VMAccessController.pushContext(context); + try + { + return action.run(); + } + finally + { + VMAccessController.popContext(); + } + } + + /** + * Calls the run() method of the given action with as + * (initial) access control context only the protection domain of the + * calling class. Calls to checkPermission() in the + * run() method ignore all earlier protection domains of + * classes in the call chain. Note that the protection domains of classes + * called by the code in the run() method are not ignored. + * If the run() method throws an exception then this method + * will wrap that exception in an PrivilegedActionException. + * + * @param action the PrivilegedExceptionAction whose + * run() should be be called. + * @return the result of the action.run() method. + * @exception PrivilegedActionException wrapped around any checked exception + * that is thrown in the run() method. + */ + public static T doPrivileged(PrivilegedExceptionAction action) + throws PrivilegedActionException + { + VMAccessController.pushContext(null); + try + { + return action.run(); + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new PrivilegedActionException(e); + } + finally + { + VMAccessController.popContext(); + } + } + + /** + * Calls the run() method of the given action with as + * (initial) access control context the given context combined with the + * protection domain of the calling class. Calls to + * checkPermission() in the run() method ignore + * all earlier protection domains of classes in the call chain, but add + * checks for the protection domains given in the supplied context. + * If the run() method throws an exception then this method + * will wrap that exception in an PrivilegedActionException. + * + * @param action the PrivilegedExceptionAction whose + * run() should be be called. + * @param context the AccessControlContext whose protection + * domains should be added to the protection domain of the calling class. + * @return the result of the action.run() method. + * @exception PrivilegedActionException wrapped around any checked exception + * that is thrown in the run() method. + */ + public static T doPrivileged(PrivilegedExceptionAction action, + AccessControlContext context) + throws PrivilegedActionException + { + VMAccessController.pushContext(context); + try + { + return action.run(); + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new PrivilegedActionException(e); + } + finally + { + VMAccessController.popContext(); + } + } + + /** + * Returns the complete access control context of the current thread. + * The returned object encompasses all {@link ProtectionDomain} objects + * for all classes in the current call stack, or the set of protection + * domains until the last call to {@link + * #doPrivileged(java.security.PrivilegedAction)}. + * + *

        Additionally, if a call was made to {@link + * #doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext)} + * that supplied an {@link AccessControlContext}, then that context + * will be intersected with the calculated one. + * + * @return The context. + */ + public static AccessControlContext getContext() + { + return VMAccessController.getContext(); + } +} diff --git a/libjava/classpath/java/security/AlgorithmParameterGenerator.java b/libjava/classpath/java/security/AlgorithmParameterGenerator.java new file mode 100644 index 000000000..a92552b9e --- /dev/null +++ b/libjava/classpath/java/security/AlgorithmParameterGenerator.java @@ -0,0 +1,277 @@ +/* AlgorithmParameterGenerator.java --- Algorithm Parameter Generator + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Engine; + +import java.lang.reflect.InvocationTargetException; +import java.security.spec.AlgorithmParameterSpec; + +/** + * AlgorithmParameterGenerator is used to generate algorithm + * parameters for specified algorithms. + * + *

        In case the client does not explicitly initialize the + * AlgorithmParameterGenerator (via a call to an + * init() method), each provider must supply (and document) a + * default initialization. For example, the GNU provider uses a default + * modulus prime size of 1024 bits for the generation of DSA + * parameters. + * + * @author Mark Benvenuto + * @since 1.2 + * @see AlgorithmParameters + * @see AlgorithmParameterSpec + */ +public class AlgorithmParameterGenerator +{ + /** Service name for algorithm parameter generators. */ + private static final String ALGORITHM_PARAMETER_GENERATOR = + "AlgorithmParameterGenerator"; + + private AlgorithmParameterGeneratorSpi paramGenSpi; + private Provider provider; + private String algorithm; + + /** + * Constructs a new instance of AlgorithmParameterGenerator. + * + * @param paramGenSpi + * the generator to use. + * @param provider + * the provider to use. + * @param algorithm + * the algorithm to use. + */ + protected AlgorithmParameterGenerator(AlgorithmParameterGeneratorSpi + paramGenSpi, Provider provider, + String algorithm) + { + this.paramGenSpi = paramGenSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + /** @return the name of the algorithm. */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Returns a new AlgorithmParameterGenerator instance which + * generates algorithm parameters for the specified algorithm. + * + * @param algorithm the name of algorithm to use. + * @return the new instance. + * @throws NoSuchAlgorithmException if algorithm is not + * implemented by any provider. + * @throws IllegalArgumentException if algorithm is + * null or is an empty string. + */ + public static AlgorithmParameterGenerator 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns a new AlgorithmParameterGenerator 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. + * @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 IllegalArgumentException if either algorithm or + * provider is null or empty. + */ + public static AlgorithmParameterGenerator getInstance(String algorithm, + String provider) + 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); + } + + /** + * Returns a new AlgorithmParameterGenerator instance which + * generates algorithm parameters for the specified algorithm. + * + * @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 IllegalArgumentException if either algorithm or + * provider is null, or if + * algorithm is an empty string. + * @since 1.4 + * @see Provider + */ + public static AlgorithmParameterGenerator getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder() + .append("AlgorithmParameterGenerator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; + try + { + Object spi = Engine.getInstance(ALGORITHM_PARAMETER_GENERATOR, + algorithm, + provider); + return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) spi, + provider, + algorithm); + } + catch (InvocationTargetException x) + { + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + } + catch (ClassCastException x) + { + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; + } + + /** @return the {@link Provider} of this generator. */ + public final Provider getProvider() + { + return provider; + } + + /** + * Initializes this instance with the specified size. Since no source of + * randomness is supplied, a default one will be used. + * + * @param size + * size (in bits) to use. + */ + public final void init(int size) + { + init(size, new SecureRandom()); + } + + /** + * Initializes this instance with the specified key-size and source of + * randomness. + * + * @param size + * the size (in bits) to use. + * @param random + * the {@link SecureRandom} to use. + */ + public final void init(int size, SecureRandom random) + { + paramGenSpi.engineInit(size, random); + } + + /** + * Initializes this instance with the specified {@link AlgorithmParameterSpec}. + * Since no source of randomness is supplied, a default one will be used. + * + * @param genParamSpec + * the {@link AlgorithmParameterSpec} to use. + * @throws InvalidAlgorithmParameterException + * if genParamSpec is invalid. + */ + public final void init(AlgorithmParameterSpec genParamSpec) + throws InvalidAlgorithmParameterException + { + init(genParamSpec, new SecureRandom()); + } + + /** + * Initializes this instance with the specified {@link AlgorithmParameterSpec} + * and source of randomness. + * + * @param genParamSpec + * the {@link AlgorithmParameterSpec} to use. + * @param random + * the {@link SecureRandom} to use. + * @throws InvalidAlgorithmParameterException + * if genParamSpec is invalid. + */ + public final void init(AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + paramGenSpi.engineInit(genParamSpec, random); + } + + /** @return a new instance of {@link AlgorithmParameters}. */ + public final AlgorithmParameters generateParameters() + { + return paramGenSpi.engineGenerateParameters(); + } +} diff --git a/libjava/classpath/java/security/AlgorithmParameterGeneratorSpi.java b/libjava/classpath/java/security/AlgorithmParameterGeneratorSpi.java new file mode 100644 index 000000000..15f39f646 --- /dev/null +++ b/libjava/classpath/java/security/AlgorithmParameterGeneratorSpi.java @@ -0,0 +1,94 @@ +/* AlgorithmParameterGeneratorSpi.java --- Algorithm Parameter Generator SPI + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; +import java.security.spec.AlgorithmParameterSpec; + +/** + AlgorithmParameterGeneratorSpi is the Service Provider + Interface for the AlgorithmParameterGenerator class. + This class is used to generate the algorithm parameters + for a specific algorithm. + + @since JDK 1.2 + @author Mark Benvenuto + */ +public abstract class AlgorithmParameterGeneratorSpi +{ + + /** + Constructs a new AlgorithmParameterGeneratorSpi + */ + public AlgorithmParameterGeneratorSpi() + { + } + + /** + Initializes the parameter generator with the specified size + and SecureRandom + + @param size the size( in number of bits) + @param random the SecureRandom class to use for randomness + */ + protected abstract void engineInit(int size, SecureRandom random); + + /** + Initializes the parameter generator with the specified + AlgorithmParameterSpec and SecureRandom classes. + + If genParamSpec is an invalid AlgorithmParameterSpec for this + AlgorithmParameterGeneratorSpi then it throws + InvalidAlgorithmParameterException + + @param genParamSpec the AlgorithmParameterSpec class to use + @param random the SecureRandom class to use for randomness + + @throws InvalidAlgorithmParameterException genParamSpec is invalid + */ + protected abstract void engineInit(AlgorithmParameterSpec genParamSpec, + SecureRandom random) throws + InvalidAlgorithmParameterException; + + + /** + Generate a new set of AlgorithmParameters. + + @returns a new set of algorithm parameters + */ + protected abstract AlgorithmParameters engineGenerateParameters(); + +} diff --git a/libjava/classpath/java/security/AlgorithmParameters.java b/libjava/classpath/java/security/AlgorithmParameters.java new file mode 100644 index 000000000..ba805143e --- /dev/null +++ b/libjava/classpath/java/security/AlgorithmParameters.java @@ -0,0 +1,317 @@ +/* AlgorithmParameters.java --- Algorithm Parameters Implementation Class + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Engine; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +/** + * AlgorithmParameters is an Algorithm Parameters class which + * provides an interface through which the user can manage the parameters of an + * Algorithm. + * + * @author Mark Benvenuto + * @since 1.2 + * @see AlgorithmParameterSpec + * @see java.security.spec.DSAParameterSpec + * @see KeyPairGenerator + */ +public class AlgorithmParameters +{ + /** Service name for algorithm parameters. */ + private static final String ALGORITHM_PARAMETERS = "AlgorithmParameters"; + + private AlgorithmParametersSpi paramSpi; + private Provider provider; + private String algorithm; + + /** + * Constructs a new instance of AlgorithmParameters. + * + * @param paramSpi + * the engine to use. + * @param provider + * the provider to use. + * @param algorithm + * the algorithm to use. + */ + protected AlgorithmParameters(AlgorithmParametersSpi paramSpi, + Provider provider, String algorithm) + { + this.paramSpi = paramSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + /** @return A string with the name of the algorithm used. */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Returns a new instance of AlgorithmParameters representing + * the specified algorithm parameters. + *

        + * The returned AlgorithmParameters must still be initialized + * with an init() method. + * + * @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 IllegalArgumentException if algorithm is + * null or is an empty string. + */ + public static AlgorithmParameters 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns a new instance of AlgorithmParameters representing + * the specified algorithm parameters from a named provider. + *

        + * The returned AlgorithmParameters must still be intialized + * with an init() method. + *

        + * + * @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 either algorithm or + * provider is null or empty. + */ + public static AlgorithmParameters getInstance(String algorithm, + String provider) + 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); + } + + /** + * Returns a new instance of AlgorithmParameters representing + * the specified algorithm parameters from the specified {@link Provider}. + *

        + * The returned AlgorithmParameters must still be intialized + * with an init() method. + * + * @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 either algorithm or + * provider is null, or if + * algorithm is an empty string. + * @since 1.4 + */ + public static AlgorithmParameters getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("AlgorithmParameters for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; + try + { + Object spi = Engine.getInstance(ALGORITHM_PARAMETERS, algorithm, provider); + return new AlgorithmParameters((AlgorithmParametersSpi) spi, + provider, + algorithm); + } + catch (InvocationTargetException x) + { + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + } + catch (ClassCastException x) + { + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; + } + + /** @return the provider of this parameter object. */ + public final Provider getProvider() + { + return provider; + } + + /** + * Initializes the engine with the specified {@link AlgorithmParameterSpec}. + * + * @param paramSpec + * A {@link AlgorithmParameterSpec} to use. + * @throws InvalidParameterSpecException + * if paramSpec is invalid. + */ + public final void init(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + paramSpi.engineInit(paramSpec); + } + + /** + * Initializes the engine with the specified parameters stored in the byte + * array and decodes them according to the ASN.1 specification. If the ASN.1 + * specification exists then it succeeds otherwise an {@link IOException} is + * thrown. + * + * @param params + * the parameters to use. + * @throws IOException + * if a decoding error occurs. + */ + public final void init(byte[]params) throws IOException + { + paramSpi.engineInit(params); + } + + /** + * Initializes the engine with the specified parameters stored in the byte + * array and decodes them according to the specified decoding specification. + * If format is null, then this method decodes the + * byte array using the ASN.1 specification if it exists, otherwise it throws + * an {@link IOException}. + * + * @param params + * the parameters to use. + * @param format + * the name of decoding format to use. + * @throws IOException + * if a decoding error occurs. + */ + public final void init(byte[]params, String format) throws IOException + { + paramSpi.engineInit(params, format); + } + + /** + * Returns a new instance of AlgorithmParameters as a + * designated parameter specification {@link Class}. + * + * @param paramSpec + * the {@link Class} to use. + * @return the parameter specification. + * @throws InvalidParameterSpecException + * if paramSpec is invalid. + */ + public final + T getParameterSpec(Class paramSpec) + throws InvalidParameterSpecException + { + return paramSpi.engineGetParameterSpec(paramSpec); + } + + /** + * Returns the parameters in the default encoding format. The primary encoding + * format is ASN.1 if it exists for the specified type. + * + * @return byte array representing the parameters. + */ + public final byte[] getEncoded() throws IOException + { + return paramSpi.engineGetEncoded(); + } + + /** + * Returns the parameters in the specified encoding format. If + * format is null then the ASN.1 encoding + * format is used if it exists for the specified type. + * + * @param format + * the name of the encoding format to use. + * @return the parameters encoded using the specified encoding scheme. + * @throws IOException + * if an encoding exception occurs, or if this parameter object has + * not been initialized. + */ + public final byte[] getEncoded(String format) throws IOException + { + return paramSpi.engineGetEncoded(format); + } + + /** + * Returns a string representation of the encoded form. + * + * @return a string representation of the encoded form. + */ + public final String toString() + { + return paramSpi.engineToString(); + } +} diff --git a/libjava/classpath/java/security/AlgorithmParametersSpi.java b/libjava/classpath/java/security/AlgorithmParametersSpi.java new file mode 100644 index 000000000..15cc1c657 --- /dev/null +++ b/libjava/classpath/java/security/AlgorithmParametersSpi.java @@ -0,0 +1,149 @@ +/* AlgorithmParametersSpi.java --- Algorithm Parameters SPI + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +/** + * AlgorithmParametersSpi is the Service Provider Interface + * for the Algorithm Parameters class. This class is used + * to manage the algorithm parameters. + * + * @since 1.2 + * @author Mark Benvenuto + */ +public abstract class AlgorithmParametersSpi +{ + /** + * Creates a new instance of AlgorithmParametersSpi + */ + public AlgorithmParametersSpi() + { + } + + /** + * Initializes the engine with the specified + * AlgorithmParameterSpec class. + * + * @param paramSpec A AlgorithmParameterSpec to initialize with + * + * @throws InvalidParameterSpecException For an inapporiate + * ParameterSpec class + */ + protected abstract void engineInit(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException; + + /** + * Initializes the engine with the specified + * parameters stored in the byte array and decodes them + * according to the ASN.1 specification. If the ASN.1 + * specification exists then it succeeds or else it throws + * IOException. + * + * @param params Parameters to initialize with + * + * @throws IOException Decoding Error + */ + protected abstract void engineInit(byte[]params) throws IOException; + + /** + * Initializes the engine with the specified + * parameters stored in the byte array and decodes them + * according to the specified decoding specification. + * If format is null, then it is decoded using the ASN.1 + * specification if it exists or else it throws + * IOException. + * + * @param params Parameters to initialize with + * @param format Name of decoding format to use + * + * @throws IOException Decoding Error + */ + protected abstract void engineInit(byte[]params, String format) + throws IOException; + + + /** + * Returns a specification of this AlgorithmParameters object. + * paramSpec identifies the class to return the AlgortihmParameters + * in. + * + * @param paramSpec Class to return AlgorithmParameters in + * + * @return the parameter specification + * + * @throws InvalidParameterSpecException if the paramSpec is an + * invalid parameter class + */ + protected abstract + T engineGetParameterSpec(Class paramSpec) + throws InvalidParameterSpecException; + + + /** + * Returns the parameters in the default encoding format. + * The primary encoding format is ASN.1 format if it exists + * for the specified type. + * + * @return byte array representing the parameters + */ + protected abstract byte[] engineGetEncoded() throws IOException; + + + /** + * Returns the parameters in the specified encoding format. + * If format is null then the + * primary encoding format is used, the ASN.1 format, + * if it exists for the specified type. + * + * @return byte array representing the parameters + */ + protected abstract byte[] engineGetEncoded(String format) + throws IOException; + + /** + * Returns a string describing the parameters in the + * AlgorithmParametersSpi class. + * + * @return A string representing the format of the parameters. + */ + protected abstract String engineToString(); +} diff --git a/libjava/classpath/java/security/AllPermission.java b/libjava/classpath/java/security/AllPermission.java new file mode 100644 index 000000000..6adcd8c9c --- /dev/null +++ b/libjava/classpath/java/security/AllPermission.java @@ -0,0 +1,198 @@ +/* AllPermission.java -- Permission to do anything + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.util.EmptyEnumeration; + +import java.util.Collections; +import java.util.Enumeration; + +/** + * This class is a permission that implies all other permissions. Granting + * this permission effectively grants all others. Extreme caution should + * be exercised in granting this permission. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see AccessController + * @see Permissions + * @see SecurityManager + * @since 1.1 + * @status updated to 1.4 + */ +public final class AllPermission extends Permission +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -2916474571451318075L; + + /** + * Create a new AllPermission object. + */ + public AllPermission() + { + super("*"); + } + + /** + * Create a new AllPermission object. The parameters are ignored, as all + * permission implies ALL PERMISSION. + * + * @param name ignored + * @param actions ignored + */ + public AllPermission(String name, String actions) + { + super("*"); + } + + /** + * This method always returns true to indicate that this + * permission always implies that any other permission is also granted. + * + * @param perm ignored + * @return true, the permission is implied + */ + public boolean implies(Permission perm) + { + return true; + } + + /** + * Checks an object for equality. All AllPermissions are equal. + * + * @param obj the Object to test for equality + */ + public boolean equals(Object obj) + { + return obj instanceof AllPermission; + } + + /** + * This method returns a hash code for this object. This returns 1. + * + * @return a hash value for this object + */ + public int hashCode() + { + return 1; + } + + /** + * This method returns the list of actions associated with this object. + * This will always be the empty string ("") for this class. + * + * @return the action list + */ + public String getActions() + { + return ""; + } + + /** + * Returns a PermissionCollection which can hold AllPermission. + * + * @return a permission collection + */ + public PermissionCollection newPermissionCollection() + { + return new AllPermissionCollection(); + } + + /** + * Implements AllPermission.newPermissionCollection, and obeys serialization + * of JDK. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class AllPermissionCollection extends PermissionCollection + { + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4023755556366636806L; + + /** + * Whether an AllPermission has been added to the collection. + * + * @serial if all permission is in the collection yet + */ + private boolean all_allowed; + + /** + * Add an AllPermission. + * + * @param perm the permission to add + * @throws IllegalArgumentException if perm is not an AllPermission + * @throws SecurityException if the collection is read-only + */ + public void add(Permission perm) + { + if (isReadOnly()) + throw new SecurityException(); + if (! (perm instanceof AllPermission)) + throw new IllegalArgumentException(); + all_allowed = true; + } + + /** + * Returns true if this collection implies a permission. + * + * @param perm the permission to check + * @return true if this collection contains an AllPermission + */ + public boolean implies(Permission perm) + { + return all_allowed; + } + + /** + * Returns an enumeration of the elements in the collection. + * + * @return the elements in the collection + */ + public Enumeration elements() + { + return all_allowed + ? Collections.enumeration(Collections.singleton(new AllPermission())) + : EmptyEnumeration.getInstance(); + } + } // class AllPermissionCollection +} // class AllPermission diff --git a/libjava/classpath/java/security/BasicPermission.java b/libjava/classpath/java/security/BasicPermission.java new file mode 100644 index 000000000..6296cffea --- /dev/null +++ b/libjava/classpath/java/security/BasicPermission.java @@ -0,0 +1,308 @@ +/* BasicPermission.java -- implements a simple named permission + Copyright (C) 1998, 1999, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * This class implements a simple model for named permissions without an + * associated action list. That is, either the named permission is granted + * or it is not. + * + *

        It also supports trailing wildcards to allow the easy granting of + * permissions in a hierarchical fashion. (For example, the name "org.gnu.*" + * might grant all permissions under the "org.gnu" permissions hierarchy). + * The only valid wildcard character is a '*' which matches anything. It + * must be the rightmost element in the permission name and must follow a + * '.' or else the Permission name must consist of only a '*'. Any other + * occurrence of a '*' is not valid. + * + *

        This class ignores the action list. Subclasses can choose to implement + * actions on top of this class if desired. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Permission + * @see Permissions + * @see PermissionCollection + * @see RuntimePermission + * @see SecurityPermission + * @see PropertyPermission + * @see AWTPermission + * @see NetPermission + * @see SecurityManager + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class BasicPermission extends Permission + implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 6279438298436773498L; + + /** + * Create a new instance with the specified permission name. If the + * name is empty an exception is thrown. + * + * @param name the name of this permission + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name is invalid + */ + public BasicPermission(String name) + { + super(name); + + // This routine used to check for illegal wildcards, but no such + // requirement exists in the specification and Sun's runtime + // doesn't appear to do it. + + if (name.equals("")) + throw new IllegalArgumentException("Empty name"); + } + + /** + * Create a new instance with the specified permission name. If the name + * is empty, or contains an illegal wildcard character, an exception is + * thrown. The actions parameter is ignored. + * + * @param name the name of this permission + * @param actions ignored + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name is invalid + */ + public BasicPermission(String name, String actions) + { + this(name); + } + + /** + * This method tests to see if the specified permission is implied by this + * permission. This will be true if the following conditions are met:

          + *
        • The specified object is an instance of the same class as this + * object.
        • + *
        • The name of the specified permission is implied by this permission's + * name based on wildcard matching. For example, "a.*" implies "a.b".
        • + *
        + * + * @param perm the Permission object to test against + * @return true if the specified permission is implied + */ + public boolean implies(Permission perm) + { + if (! getClass().isInstance(perm)) + return false; + + String otherName = perm.getName(); + String name = getName(); + + if (name.equals(otherName)) + return true; + + int last = name.length() - 1; + return name.charAt(last) == '*' + && otherName.startsWith(name.substring(0, last)); + } + + /** + * This method tests to see if this object is equal to the specified + * Object. This will be true if and only if the specified + * object meets the following conditions:
          + *
        • It is an instance of the same class as this.
        • + *
        • It has the same name as this permission.
        • + *
        + * + * @param obj the Object to test for equality + * @return true if obj is semantically equal to this + */ + public boolean equals(Object obj) + { + return getClass().isInstance(obj) + && getName().equals(((BasicPermission) obj).getName()); + } + + /** + * This method returns a hash code for this permission object. The hash + * code returned is the value returned by calling the hashCode + * method on the String that is the name of this permission. + * + * @return a hash value for this object + */ + public int hashCode() + { + return getName().hashCode(); + } + + /** + * This method returns a list of the actions associated with this + * permission. This method always returns the empty string ("") since + * this class ignores actions. + * + * @return the action list + */ + public String getActions() + { + return ""; + } + + /** + * This method returns an instance of PermissionCollection + * suitable for storing BasicPermission objects. The + * collection returned can only store objects of the same type as this. + * Subclasses which use actions must override this method; but a class with + * no actions will work fine with this. + * + * @return a new empty PermissionCollection object + */ + public PermissionCollection newPermissionCollection() + { + return new BasicPermissionCollection(getClass()); + } + + /** + * Implements AllPermission.newPermissionCollection, and obeys serialization + * of JDK. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class BasicPermissionCollection extends PermissionCollection + { + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 739301742472979399L; + + /** + * The permissions in the collection. + * + * @serial a hash mapping name to permissions, all of type permClass + */ + private final Hashtable permissions = new Hashtable(); + + /** + * If "*" is in the collection. + * + * @serial true if a permission named "*" is in the collection + */ + private boolean all_allowed; + + /** + * The runtime class which all entries in the table must belong to. + * + * @serial the limiting subclass of this collection + */ + private final Class permClass; + + /** + * Construct a collection over the given runtime class. + * + * @param c the class + */ + BasicPermissionCollection(Class c) + { + permClass = c; + } + + /** + * Add a Permission. It must be of the same type as the permission which + * created this collection. + * + * @param perm the permission to add + * @throws IllegalArgumentException if perm is not the correct type + * @throws SecurityException if the collection is read-only + */ + public void add(Permission perm) + { + if (isReadOnly()) + throw new SecurityException("readonly"); + if (! permClass.isInstance(perm)) + throw new IllegalArgumentException("Expecting instance of " + permClass); + BasicPermission bp = (BasicPermission) perm; + String name = bp.getName(); + if (name.equals("*")) + all_allowed = true; + permissions.put(name, bp); + } + + /** + * Returns true if this collection implies the given permission. + * + * @param permission the permission to check + * @return true if it is implied by this + */ + public boolean implies(Permission permission) + { + if (! permClass.isInstance(permission)) + return false; + if (all_allowed) + return true; + BasicPermission toImply = (BasicPermission) permission; + String name = toImply.getName(); + if (name.equals("*")) + return false; + int prefixLength = name.length(); + if (name.endsWith("*")) + prefixLength -= 2; + + while (true) + { + if (permissions.get(name) != null) + return true; + prefixLength = name.lastIndexOf('.', prefixLength); + if (prefixLength < 0) + return false; + name = name.substring(0, prefixLength + 1) + '*'; + } + } + + /** + * Enumerate over the collection. + * + * @return an enumeration of the collection contents + */ + public Enumeration elements() + { + return permissions.elements(); + } + } // class BasicPermissionCollection +} // class BasicPermission diff --git a/libjava/classpath/java/security/Certificate.java b/libjava/classpath/java/security/Certificate.java new file mode 100644 index 000000000..5cdba6e10 --- /dev/null +++ b/libjava/classpath/java/security/Certificate.java @@ -0,0 +1,125 @@ +/* Certificate.java -- deprecated interface for modeling digital certificates + Copyright (C) 1998, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This interface models a digital certificate which verifies the + * authenticity of a party. This class simply allows certificate + * information to be queried, it does not guarantee that the certificate + * is valid. + * + *

        This class is deprecated in favor of the new java.security.cert package. + * It exists for backward compatibility only. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @deprecated use {@link java.security.cert} instead + * @status updated to 1.4 + */ +public interface Certificate +{ + /** + * This method returns the Principal that is guaranteeing + * this certificate. + * + * @return the Principal guaranteeing the certificate + */ + Principal getGuarantor(); + + /** + * This method returns the Principal being guaranteed by + * this certificate. + * + * @return the Principal guaranteed by this certificate + */ + Principal getPrincipal(); + + /** + * This method returns the public key for the Principal that + * is being guaranteed. + * + * @return the PublicKey of the Principal being guaranteed + */ + PublicKey getPublicKey(); + + /** + * This method writes the certificate to an OutputStream in + * a format that can be understood by the decode method. + * + * @param out the OutputStream to write to + * @throws KeyException if there is a problem with the certificate + * @throws IOException if an error occurs writing to the stream + * @see #decode(InputStream) + * @see #getFormat() + */ + void encode(OutputStream out) throws KeyException, IOException; + + /** + * This method reads an encoded certificate from an InputStream. + * + * @param in the InputStream to read from + * @throws KeyException if there is a problem with the certificate data + * @throws IOException if an error occurs reading from the stream + * @see #encode(OutputStream) + * @see #getFormat() + */ + void decode(InputStream in) throws KeyException, IOException; + + /** + * This method returns the encoding format of the certificate (e.g., "PGP", + * "X.509"). This format is used by the encode and + * decode methods. + * + * @return the encoding format being used + */ + String getFormat(); + + /** + * This method returns a String representation of the contents + * of this certificate. + * + * @param detail true to provided more detailed information + * @return the string representation + */ + String toString(boolean detail); +} // interface Certificate diff --git a/libjava/classpath/java/security/CodeSource.java b/libjava/classpath/java/security/CodeSource.java new file mode 100644 index 000000000..dd353eda0 --- /dev/null +++ b/libjava/classpath/java/security/CodeSource.java @@ -0,0 +1,356 @@ +/* CodeSource.java -- Code location and certifcates + Copyright (C) 1998, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.net.SocketPermission; +import java.net.URL; +// Note that this overrides Certificate in this package. +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; + +/** + * This class represents a location from which code is loaded (as + * represented by a URL), and the list of certificates that are used to + * check the signatures of signed code loaded from this source. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + * @status updated to 1.4 + */ +public class CodeSource implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4977541819976013951L; + + /** + * This is the URL that represents the code base from which code will + * be loaded. + * + * @serial the code location + */ + private final URL location; + + /** The set of certificates for this code base. */ + private transient HashSet certs; + + /** + * This creates a new instance of CodeSource that loads code + * from the specified URL location and which uses the specified certificates + * for verifying signatures. + * + * @param location the location from which code will be loaded + * @param certs the list of certificates + */ + public CodeSource(URL location, Certificate[] certs) + { + this.location = location; + if (certs != null) + this.certs = new HashSet(Arrays.asList(certs)); + } + + /** + * This method returns a hash value for this object. + * + * @return a hash value for this object + */ + public int hashCode() + { + return (location == null ? 0 : location.hashCode()) + ^ (certs == null ? 0 : certs.hashCode()); + } + + /** + * This method tests the specified Object for equality with + * this object. This will be true if and only if the locations are equal + * and the certificate sets are identical (ignoring order). + * + * @param obj the Object to test against + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + if (! (obj instanceof CodeSource)) + return false; + CodeSource cs = (CodeSource) obj; + return (certs == null ? cs.certs == null : certs.equals(cs.certs)) + && (location == null ? cs.location == null + : location.equals(cs.location)); + } + + /** + * This method returns the URL specifying the location from which code + * will be loaded under this CodeSource. + * + * @return the code location for this CodeSource + */ + public final URL getLocation() + { + return location; + } + + /** + * This method returns the list of digital certificates that can be used + * to verify the signatures of code loaded under this + * CodeSource. + * + * @return the certifcate list for this CodeSource + */ + public final Certificate[] getCertificates() + { + if (certs == null) + return null; + Certificate[] c = new Certificate[certs.size()]; + certs.toArray(c); + return c; + } + + /** + * This method tests to see if a specified CodeSource is + * implied by this object. Effectively, to meet this test, the specified + * object must have all the certifcates this object has (but may have more), + * and must have a location that is a subset of this object's. In order + * for this object to imply the specified object, the following must be + * true: + * + *

          + *
        1. codesource must not be null.
        2. + *
        3. If codesource has a certificate list, all of it's + * certificates must be present in the certificate list of this + * code source.
        4. + *
        5. If this object does not have a null location, then + * the following addtional tests must be passed. + * + *
            + *
          1. codesource must not have a null + * location.
          2. + *
          3. codesource's location must be equal to this object's + * location, or + *
              + *
            • codesource's location protocol, port, and ref (aka, + * anchor) must equal this objects
            • + *
            • codesource's location host must imply this object's + * location host, as determined by contructing + * SocketPermission objects from each with no + * action list and using that classes's implies + * method
            • + *
            • If this object's location file ends with a '/', then the + * specified object's location file must start with this + * object's location file. Otherwise, the specified object's + * location file must start with this object's location file + * with the '/' character appended to it.
            • + *
          4. + *
        6. + *
        + * + *

        For example, each of these locations imply the location + * "http://java.sun.com/classes/foo.jar":

        + * + *
        +   * http:
        +   * http://*.sun.com/classes/*
        +   * http://java.sun.com/classes/-
        +   * http://java.sun.com/classes/foo.jar
        +   * 
        + * + *

        Note that the code source with null location and null certificates implies + * all other code sources.

        + * + * @param cs the CodeSource to test against this object + * @return true if this specified CodeSource is implied + */ + public boolean implies(CodeSource cs) + { + if (cs == null) + return false; + // First check the certificate list. + if (certs != null && (cs.certs == null || ! certs.containsAll(cs.certs))) + return false; + // Next check the location. + if (location == null) + return true; + if (cs.location == null + || ! location.getProtocol().equals(cs.location.getProtocol()) + || (location.getPort() != -1 + && location.getPort() != cs.location.getPort()) + || (location.getRef() != null + && ! location.getRef().equals(cs.location.getRef()))) + return false; + if (location.getHost() != null) + { + String their_host = cs.location.getHost(); + if (their_host == null) + return false; + SocketPermission our_sockperm = + new SocketPermission(location.getHost(), "accept"); + SocketPermission their_sockperm = + new SocketPermission(their_host, "accept"); + if (! our_sockperm.implies(their_sockperm)) + return false; + } + String our_file = location.getFile(); + if (our_file != null) + { + if (! our_file.endsWith("/")) + our_file += "/"; + String their_file = cs.location.getFile(); + if (their_file == null + || ! their_file.startsWith(our_file)) + return false; + } + return true; + } + + /** + * This method returns a String that represents this object. + * The result is in the format "(" + getLocation() followed + * by a space separated list of certificates (or "<no certificates>"), + * followed by ")". + * + * @return a String for this object + */ + public String toString() + { + CPStringBuilder sb = new CPStringBuilder("(").append(location); + if (certs == null || certs.isEmpty()) + sb.append(" "); + else + { + Iterator iter = certs.iterator(); + for (int i = certs.size(); --i >= 0; ) + sb.append(' ').append(iter.next()); + } + return sb.append(")").toString(); + } + + /** + * Reads this object from a serialization stream. + * + * @param s the input stream + * @throws IOException if reading fails + * @throws ClassNotFoundException if deserialization fails + * @serialData this reads the location, then expects an int indicating the + * number of certificates. Each certificate is a String type + * followed by an int encoding length, then a byte[] encoding + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + int count = s.readInt(); + certs = new HashSet(); + while (--count >= 0) + { + String type = (String) s.readObject(); + int bytes = s.readInt(); + byte[] encoded = new byte[bytes]; + for (int i = 0; i < bytes; i++) + encoded[i] = s.readByte(); + ByteArrayInputStream stream = new ByteArrayInputStream(encoded); + try + { + CertificateFactory factory = CertificateFactory.getInstance(type); + certs.add(factory.generateCertificate(stream)); + } + catch (CertificateException e) + { + // XXX Should we ignore this certificate? + } + } + } + + /** + * Writes this object to a serialization stream. + * + * @param s the output stream + * @throws IOException if writing fails + * @serialData this writes the location, then writes an int indicating the + * number of certificates. Each certificate is a String type + * followed by an int encoding length, then a byte[] encoding + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + if (certs == null) + s.writeInt(0); + else + { + int count = certs.size(); + s.writeInt(count); + Iterator iter = certs.iterator(); + while (--count >= 0) + { + Certificate c = (Certificate) iter.next(); + s.writeObject(c.getType()); + byte[] encoded; + try + { + encoded = c.getEncoded(); + } + catch (CertificateEncodingException e) + { + // XXX Should we ignore this certificate? + encoded = null; + } + if (encoded == null) + s.writeInt(0); + else + { + s.writeInt(encoded.length); + for (int i = 0; i < encoded.length; i++) + s.writeByte(encoded[i]); + } + } + } + } +} // class CodeSource diff --git a/libjava/classpath/java/security/DigestException.java b/libjava/classpath/java/security/DigestException.java new file mode 100644 index 000000000..b4df0c1d5 --- /dev/null +++ b/libjava/classpath/java/security/DigestException.java @@ -0,0 +1,92 @@ +/* DigestException.java -- A generic message digest exception + Copyright (C) 1998, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception indicates that a generic message digest exception has + * occurred. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class DigestException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5821450303093652515L; + + /** + * Create a new instance with no descriptive message. + */ + public DigestException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive message + */ + public DigestException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public DigestException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public DigestException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/DigestInputStream.java b/libjava/classpath/java/security/DigestInputStream.java new file mode 100644 index 000000000..c0a74f3ab --- /dev/null +++ b/libjava/classpath/java/security/DigestInputStream.java @@ -0,0 +1,167 @@ +/* DigestInputStream.java --- An Input stream tied to a message digest + Copyright (C) 1999, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * DigestInputStream is a class that ties an InputStream with a + * MessageDigest. The Message Digest is used by the class to + * update it self as bytes are read from the InputStream. + * + * The updating to the digest depends on the on flag which is set + * to true by default to tell the class to update the data + * in the message digest. + * + * @version 0.0 + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public class DigestInputStream extends FilterInputStream +{ + /** + * The message digest for the DigestInputStream + */ + protected MessageDigest digest; + + //Manages the on flag + private boolean state = true; + + /** + * Constructs a new DigestInputStream. + * It associates a MessageDigest with the stream to + * compute the stream as data is written. + * + * @param stream An InputStream to associate this stream with + * @param digest A MessageDigest to hash the stream with + */ + public DigestInputStream(InputStream stream, MessageDigest digest) + { + super(stream); + //this.in = stream; + this.digest = digest; + } + + /** + * Returns the MessageDigest associated with this DigestInputStream + * + * @return The MessageDigest used to hash this stream + */ + public MessageDigest getMessageDigest() + { + return digest; + } + + /** + * Sets the current MessageDigest to current parameter + * + * @param digest A MessageDigest to associate with this stream + */ + public void setMessageDigest(MessageDigest digest) + { + this.digest = digest; + } + + /** + * Reads a byte from the input stream and updates the digest. + * This method reads the underlying input stream and if the + * on flag is true then updates the message digest. + * + * @return Returns a byte from the input stream, -1 is returned to indicate that + * the end of stream was reached before this read call + * + * @throws IOException if an IO error occurs in the underlying input stream, + * this error is thrown + */ + public int read() throws IOException + { + int temp = in.read(); + + if (state == true && temp != -1) + digest.update((byte) temp); + + return temp; + } + + /** + * Reads bytes from the input stream and updates the digest. + * This method reads the underlying input stream and if the + * on flag is true then updates the message digest. + * + * @param b a byte array to store the data from the input stream + * @param off an offset to start at in the array + * @param len length of data to read + * @return Returns count of bytes read, -1 is returned to indicate that + * the end of stream was reached before this read call + * + * @throws IOException if an IO error occurs in the underlying input stream, + * this error is thrown + */ + public int read(byte[]b, int off, int len) throws IOException + { + int temp = in.read(b, off, len); + + if (state == true && temp != -1) + digest.update(b, off, temp); + + return temp; + } + + /** + * Sets the flag specifing if this DigestInputStream updates the + * digest in the write() methods. The default is on; + * + * @param on True means it digests stream, false means it does not + */ + public void on(boolean on) + { + state = on; + } + + /** + * Converts the input stream and underlying message digest to a string. + * + * @return A string representing the input stream and message digest. + */ + public String toString() + { + return "[Digest Input Stream] " + digest.toString(); + } +} diff --git a/libjava/classpath/java/security/DigestOutputStream.java b/libjava/classpath/java/security/DigestOutputStream.java new file mode 100644 index 000000000..748f83d79 --- /dev/null +++ b/libjava/classpath/java/security/DigestOutputStream.java @@ -0,0 +1,158 @@ +/* DigestOutputStream.java --- An output stream tied to a message digest + Copyright (C) 1999, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * DigestOutputStream is a class that ties an OutputStream with a + * MessageDigest. The Message Digest is used by the class to update it + * self as bytes are written to the OutputStream. + * + * The updating to the digest depends on the on flag which is set to + * true by default that tells the class to update the data in the + * message digest. + * + * @version 0.0 + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public class DigestOutputStream extends FilterOutputStream +{ + /** + * The message digest for the DigestOutputStream + */ + protected MessageDigest digest; + + //Manages the on flag + private boolean state = true; + + /** + * Constructs a new DigestOutputStream. It associates a + * MessageDigest with the stream to compute the stream as data is + * written. + * + * @param stream An OutputStream to associate this stream with + * @param digest A MessageDigest to hash the stream with + */ + public DigestOutputStream(OutputStream stream, MessageDigest digest) + { + super(stream); + this.digest = digest; + } + + /** + * Returns the MessageDigest associated with this DigestOutputStream + * + * @return The MessageDigest used to hash this stream + */ + public MessageDigest getMessageDigest() + { + return digest; + } + + /** + * Sets the current MessageDigest to current parameter + * + * @param digest A MessageDigest to associate with this stream + */ + public void setMessageDigest(MessageDigest digest) + { + this.digest = digest; + } + + + /** + * Updates the hash if the on flag is true and then writes a byte to + * the underlying output stream. + * + * @param b A byte to write to the output stream + * + * @exception IOException if the underlying output stream + * cannot write the byte, this is thrown. + */ + public void write(int b) throws IOException + { + if (state) + digest.update((byte) b); + + out.write(b); + } + + /** + * Updates the hash if the on flag is true and then writes the bytes + * to the underlying output stream. + * + * @param b Bytes to write to the output stream + * @param off Offset to start to start at in array + * @param len Length of data to write + * + * @exception IOException if the underlying output stream + * cannot write the bytes, this is thrown. + */ + public void write(byte[]b, int off, int len) throws IOException + { + if (state) + digest.update(b, off, len); + + out.write(b, off, len); + } + + /** + * Sets the flag specifying if this DigestOutputStream updates the + * digest in the write() methods. The default is on; + * + * @param on True means it digests stream, false means it does not + */ + public void on(boolean on) + { + state = on; + } + + /** + * Converts the output stream and underlying message digest to a string. + * + * @return A string representing the output stream and message digest. + */ + public String toString() + { + return "[Digest Output Stream] " + digest.toString(); + } +} diff --git a/libjava/classpath/java/security/DomainCombiner.java b/libjava/classpath/java/security/DomainCombiner.java new file mode 100644 index 000000000..9ec680c63 --- /dev/null +++ b/libjava/classpath/java/security/DomainCombiner.java @@ -0,0 +1,67 @@ +/* DomainCombiner.java -- Combines ProtectionDomains + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * A public interface used to combine two ProtectionDomains in a new + * ProtectionDomain and update the current Protection Domains + * associated with the current AccessControlContext. + * + * It can add, subtract, or update ProtectionDomains or possibly + * remove duplicates or any possible complex action but just not add + * ones that do not already exist in either array. + * + * @author Mark Benvenuto + * @see AccessControlContext + * @see AccessController + * @since 1.3 + * @status updated to 1.4 + */ +public interface DomainCombiner +{ + /** + * Combines the current ProtectionDomains of the Thread with new + * ProtectionDomains. + * + * @param currentDomains - the ProtectionDomains for the current thread. + * @param assignedDomains - ProtectionsDomains to add + * @return a new array of all the ProtectionDomains + */ + ProtectionDomain[] combine(ProtectionDomain[] currentDomains, + ProtectionDomain[] assignedDomains); +} // interface DomainCombiner diff --git a/libjava/classpath/java/security/DummyKeyPairGenerator.java b/libjava/classpath/java/security/DummyKeyPairGenerator.java new file mode 100644 index 000000000..da8c362eb --- /dev/null +++ b/libjava/classpath/java/security/DummyKeyPairGenerator.java @@ -0,0 +1,75 @@ +/* DummyKeyPairGenerator.java - Wrapper for KeyPairGeneratorSpi + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.security.spec.AlgorithmParameterSpec; + +final class DummyKeyPairGenerator extends KeyPairGenerator +{ + private KeyPairGeneratorSpi kpgSpi = null; + + public DummyKeyPairGenerator(KeyPairGeneratorSpi kpgSpi, String algorithm) + { + super(algorithm); + this.kpgSpi = kpgSpi; + } + + public Object clone() throws CloneNotSupportedException + { + KeyPairGenerator result = new DummyKeyPairGenerator + ((KeyPairGeneratorSpi) kpgSpi.clone(), this.getAlgorithm()); + result.provider = this.getProvider(); + return result; + } + + public void initialize(int keysize, SecureRandom random) + { + kpgSpi.initialize(keysize, random); + } + + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException + { + kpgSpi.initialize(params, random); + } + + public KeyPair generateKeyPair() + { + return kpgSpi.generateKeyPair(); + } +} diff --git a/libjava/classpath/java/security/DummyMessageDigest.java b/libjava/classpath/java/security/DummyMessageDigest.java new file mode 100644 index 000000000..6cecdcf68 --- /dev/null +++ b/libjava/classpath/java/security/DummyMessageDigest.java @@ -0,0 +1,90 @@ +/* DummyMessageDigest.java - Wrapper for MessageDigestSpi + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +final class DummyMessageDigest extends MessageDigest +{ + private MessageDigestSpi mdSpi = null; + + public DummyMessageDigest(MessageDigestSpi mdSpi, String algorithm) + { + super(algorithm); + this.mdSpi = mdSpi; + } + + public Object clone() throws CloneNotSupportedException + { + MessageDigest result = new DummyMessageDigest + ((MessageDigestSpi) mdSpi.clone(), this.getAlgorithm()); + result.provider = this.getProvider(); + return result; + } + + // java.security.MessageDigestSpi abstract methods implementation --------- + + public byte[] engineDigest() + { + return mdSpi.engineDigest(); + } + + public int engineDigest(byte[] buf, int offset, int len) + throws DigestException + { + return mdSpi.engineDigest(buf, offset, len); + } + + public int engineGetDigestLength() + { + return mdSpi.engineGetDigestLength(); + } + + public void engineReset() + { + mdSpi.engineReset(); + } + + public void engineUpdate(byte input) + { + mdSpi.engineUpdate(input); + } + + public void engineUpdate(byte[] input, int offset, int len) + { + mdSpi.engineUpdate(input, offset, len); + } +} diff --git a/libjava/classpath/java/security/DummySignature.java b/libjava/classpath/java/security/DummySignature.java new file mode 100644 index 000000000..b74885c99 --- /dev/null +++ b/libjava/classpath/java/security/DummySignature.java @@ -0,0 +1,102 @@ +/* DummySignature.java - Signature wrapper for SignatureSpi. + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +final class DummySignature extends Signature +{ + private SignatureSpi sigSpi = null; + + public DummySignature(SignatureSpi sigSpi, String algorithm) + { + super(algorithm); + this.sigSpi = sigSpi; + } + + public Object clone() throws CloneNotSupportedException + { + Signature result = new DummySignature + ((SignatureSpi) sigSpi.clone(), this.getAlgorithm()); + result.provider = this.getProvider(); + return result; + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + sigSpi.engineInitVerify(publicKey); + } + + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException + { + sigSpi.engineInitSign(privateKey); + } + + protected void engineUpdate(byte b) throws SignatureException + { + sigSpi.engineUpdate(b); + } + + protected void engineUpdate(byte[]b, int off, int len) + throws SignatureException + { + sigSpi.engineUpdate(b, off, len); + } + + protected byte[] engineSign() throws SignatureException + { + return sigSpi.engineSign(); + } + + protected boolean engineVerify(byte[]sigBytes) throws SignatureException + { + return sigSpi.engineVerify(sigBytes); + } + + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException + { + sigSpi.engineSetParameter(param, value); + } + + protected Object engineGetParameter(String param) + throws InvalidParameterException + { + return sigSpi.engineGetParameter(param); + } +} diff --git a/libjava/classpath/java/security/GeneralSecurityException.java b/libjava/classpath/java/security/GeneralSecurityException.java new file mode 100644 index 000000000..b2594c5fb --- /dev/null +++ b/libjava/classpath/java/security/GeneralSecurityException.java @@ -0,0 +1,97 @@ +/* GeneralSecurityException.java -- Common superclass of security exceptions + Copyright (C) 1998, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This class is the common superclass of all security exceptions. All + * exceptions in java.security extend this class with the exception (no + * pun intended) of AccessControlException and + * CertificateException (which extend + * SecurityException), ProviderException + * (RuntimeException), and InvalidParamterException + * (IllegalArgumentException). + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class GeneralSecurityException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 894798122053539237L; + + /** + * Create a new instance with no descriptive error message. + */ + public GeneralSecurityException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public GeneralSecurityException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public GeneralSecurityException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public GeneralSecurityException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/Guard.java b/libjava/classpath/java/security/Guard.java new file mode 100644 index 000000000..4f22360a4 --- /dev/null +++ b/libjava/classpath/java/security/Guard.java @@ -0,0 +1,60 @@ +/* Guard.java -- Check access to a guarded object + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This interface specifies a mechanism for querying whether or not + * access is allowed to a guarded object. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see GuardedObject + * @since 1.1 + * @status updated to 1.4 + */ +public interface Guard +{ + /** + * This method tests whether or not access is allowed to the specified + * guarded object. Access is allowed if this method returns silently. If + * access is denied, an exception is generated. + * + * @param obj the Object to test + * @throws SecurityException if access to the object is denied + */ + void checkGuard(Object obj); +} // interface Guard diff --git a/libjava/classpath/java/security/GuardedObject.java b/libjava/classpath/java/security/GuardedObject.java new file mode 100644 index 000000000..5ca08835d --- /dev/null +++ b/libjava/classpath/java/security/GuardedObject.java @@ -0,0 +1,121 @@ +/* GuardedObject.java -- An object protected by a Guard + Copyright (C) 1998, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class is an object that is guarded by a Guard object. + * The object that is being guarded is retrieved by a call to the only + * method in this class - getObject. That method returns the + * guarded Object after first checking with the + * Guard. If the Guard disallows access, an + * exception will be thrown. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.4 + */ +public class GuardedObject implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5240450096227834308L; + + /** + * This is the Guard that is protecting the object. + * + * @serial the guard + */ + private final Guard guard; + + /** + * This is the object that is being guarded. + * + * @serial the protected object + */ + private final Object object; + + /** + * This method initializes a new instance of GuardedObject + * that protects the specified Object using the specified + * Guard. A null guard means there are no restrictions on + * accessing the object. + * + * @param object the Object to guard + * @param guard the Guard that is protecting the object + */ + public GuardedObject(Object object, Guard guard) + { + this.object = object; + this.guard = guard; + } + + /** + * This method first call the checkGuard method on the + * Guard object protecting the guarded object. If the + * Guard disallows access, an exception is thrown, otherwise + * the Object is returned. + * + * @return The object being guarded + * @throws SecurityException if access is denied + */ + public Object getObject() + { + if (guard != null) + guard.checkGuard(object); + return object; + } + + /** + * Ensures that serialization is legal, by checking the guard. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + if (guard != null) + guard.checkGuard(object); + s.defaultWriteObject(); + } +} // class GuardedObject diff --git a/libjava/classpath/java/security/Identity.java b/libjava/classpath/java/security/Identity.java new file mode 100644 index 000000000..83ec4c8e1 --- /dev/null +++ b/libjava/classpath/java/security/Identity.java @@ -0,0 +1,346 @@ +/* Identity.java --- Identity Class + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.Serializable; +import java.util.Vector; + +/** + * The Identity class is used to represent people and companies + * that can be authenticated using public key encryption. The identities can + * also be abstract objects such as smart cards. + * + *

        Identity objects store a name and public key for each + * identity. The names cannot be changed and the identities can be scoped. Each + * identity (name and public key) within a scope are unique to that scope.

        + * + *

        Each identity has a set of ceritificates which all specify the same + * public key, but not necessarily the same name.

        + * + *

        The Identity class can be subclassed to allow additional + * information to be attached to it.

        + * + * @author Mark Benvenuto + * @see IdentityScope + * @see Signer + * @see Principal + * @deprecated Replaced by java.security.KeyStore, the + * java.security.cert package, and + * java.security.Principal. + */ +public abstract class Identity implements Principal, Serializable +{ + private static final long serialVersionUID = 3609922007826600659L; + + private String name; + private IdentityScope scope; + private PublicKey publicKey; + private String info; + private Vector certificates; + + /** Constructor for serialization only. */ + protected Identity() + { + } + + /** + * Constructs a new instance of Identity with the specified + * name and scope. + * + * @param name + * the name to use. + * @param scope + * the scope to use. + * @throws KeyManagementException + * if the identity is already present. + */ + public Identity(String name, IdentityScope scope) + throws KeyManagementException + { + this.name = name; + this.scope = scope; + } + + /** + * Constructs a new instance of Identity with the specified + * name and no scope. + * + * @param name + * the name to use. + */ + public Identity(String name) + { + this.name = name; + this.scope = null; + } + + /** @return the name of this identity. */ + public final String getName() + { + return name; + } + + /** @return the scope of this identity. */ + public final IdentityScope getScope() + { + return scope; + } + + /** + * @return the public key of this identity. + * @see #setPublicKey(java.security.PublicKey) + */ + public PublicKey getPublicKey() + { + return publicKey; + } + + /** + * Sets the public key for this identity. The old key and all certificates + * are removed. + * + * @param key + * the public key to use. + * @throws KeyManagementException + * if this public key is used by another identity in the current + * scope. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public void setPublicKey(PublicKey key) throws KeyManagementException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setIdentityPublicKey"); + + this.publicKey = key; + } + + /** + * Sets the general information string. + * + * @param info + * the general information string. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public void setInfo(String info) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setIdentityInfo"); + + this.info = info; + } + + /** + * @return the general information string of this identity. + * @see #setInfo(String) + */ + public String getInfo() + { + return info; + } + + /** + * Adds a certificate to the list of ceritificates for this identity. The + * public key in this certificate must match the existing public key if it + * exists. + * + * @param certificate + * the certificate to add. + * @throws KeyManagementException + * if the certificate is invalid, or the public key conflicts. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public void addCertificate(Certificate certificate) + throws KeyManagementException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("addIdentityCertificate"); + + // Check public key of this certificate against the first one in the vector + if (certificates.size() > 0) + { + if (((Certificate) certificates.firstElement()).getPublicKey() != publicKey) + throw new KeyManagementException("Public key does not match"); + } + certificates.addElement(certificate); + } + + /** + * Removes a certificate from the list of ceritificates for this identity. + * + * @param certificate + * the certificate to remove. + * @throws KeyManagementException + * if the certificate is invalid. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public void removeCertificate(Certificate certificate) + throws KeyManagementException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("removeIdentityCertificate"); + + if (certificates.contains(certificate) == false) + throw new KeyManagementException("Certificate not found"); + + certificates.removeElement(certificate); + } + + /** @return an array of {@link Certificate}s for this identity. */ + public Certificate[] certificates() + { + Certificate[] certs = new Certificate[certificates.size()]; + int max = certificates.size(); + for (int i = 0; i < max; i++) + certs[i] = (Certificate) certificates.elementAt(i); + + return certs; + } + + /** + * Checks for equality between this Identity and a specified object. It first + * checks if they are the same object, then if the name and scope match and + * returns true if successful. If these tests fail, the + * {@link #identityEquals(Identity)} method is called. + * + * @return true if they are equal, false + * otherwise. + */ + public final boolean equals(Object identity) + { + if (identity instanceof Identity) + { + if (identity == this) + return true; + + if ((((Identity) identity).getName().equals(this.name)) && + (((Identity) identity).getScope().equals(this.scope))) + return true; + + return identityEquals((Identity) identity); + } + return false; + } + + /** + * Checks for equality between this Identity and a specified object. A + * subclass should override this method. The default behavior is to return + * true if the public key and names match. + * + * @return true if they are equal, false + * otherwise. + */ + protected boolean identityEquals(Identity identity) + { + return ((identity.getName().equals(this.name)) && + (identity.getPublicKey().equals(this.publicKey))); + } + + /** + * Returns a string representation of this Identity. + * + * @return a string representation of this Identity. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public String toString() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("printIdentity"); + + /* TODO: Insert proper format here */ + return (name + ":@" + scope + " Public Key: " + publicKey); + } + + /** + * Returns a detailed string representation of this Identity. + * + * @param detailed + * indicates whether or detailed information is desired. + * @return a string representation of this Identity. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public String toString(boolean detailed) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("printIdentity"); + + if (detailed) + { + /* TODO: Insert proper detailed format here */ + return (name + ":@" + scope + " Public Key: " + publicKey); + } + else + { + /* TODO: Insert proper format here */ + return (name + ":@" + scope + " Public Key: " + publicKey); + } + } + + /** @return a hashcode of this identity. */ + public int hashCode() + { + int ret = name.hashCode(); + if (publicKey != null) + ret |= publicKey.hashCode(); + if (scope != null) + ret |= scope.hashCode(); + if (info != null) + ret |= info.hashCode(); + if (certificates != null) + ret |= certificates.hashCode(); + + return ret; + } +} diff --git a/libjava/classpath/java/security/IdentityScope.java b/libjava/classpath/java/security/IdentityScope.java new file mode 100644 index 000000000..4391fbd49 --- /dev/null +++ b/libjava/classpath/java/security/IdentityScope.java @@ -0,0 +1,216 @@ +/* IdentityScope.java --- IdentityScope Class + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.util.Enumeration; + +/** + * IdentityScope represents a scope of an identity. + * IdentityScope is also an {@link Identity} and can have a name + * and scope along with the other qualitites identities possess. + * + *

        An IdentityScope contains other {@link Identity} objects. + * All {@link Identity} objects are manipulated in the scope the same way. The + * scope is supposed to apply different scope to different type of + * Identities.

        + * + *

        No identity within the same scope can have the same public key.

        + * + * @author Mark Benvenuto + * @see Identity + * @see Signer + * @see Principal + * @see Key + * @deprecated Use java.security.KeyStore, the java.security.cert package, and + * java.security.Principal. + */ +public abstract class IdentityScope extends Identity +{ + private static final long serialVersionUID = -2337346281189773310L; + private static IdentityScope systemScope; + + /** Constructor for serialization purposes. */ + protected IdentityScope() + { + super(); + } + + /** + * Constructs a new instance of IdentityScope with the + * specified name and no scope. + * + * @param name + * the name to use. + */ + public IdentityScope(String name) + { + super(name); + } + + /** + * Constructs a new instance of IdentityScope with the + * specified name and {@link IdentityScope}. + * + * @param name + * the name to use. + * @param scope + * the scope to use. + * @throws KeyManagementException + * if the identity scope is already present. + */ + public IdentityScope(String name, IdentityScope scope) + throws KeyManagementException + { + super(name, scope); + } + + /** + * Returns the system's Scope. + * + * @return the system's Scope. + */ + public static IdentityScope getSystemScope() + { + if (systemScope == null) + { + //Load it + //systemScope; + } + return systemScope; + } + + /** + * Sets the scope of the system. + * + * @param scope + * the new system scope. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + protected static void setSystemScope(IdentityScope scope) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setSystemScope"); + + systemScope = scope; + } + + /** + * Returns the number of entries within this IdentityScope. + * + * @return the number of entries within this IdentityScope. + */ + public abstract int size(); + + /** + * Returns the specified {@link Identity}, by name, within this scope. + * + * @param name + * name of {@link Identity} to get. + * @return an {@link Identity} representing the name or null if + * it cannot be found. + */ + public abstract Identity getIdentity(String name); + + /** + * Returns the specified {@link Identity}, by {@link Principal}, within this + * scope. + * + * @param principal + * the {@link Principal} to use. + * @return an identity representing the {@link Principal} or null + * if it cannot be found. + */ + public Identity getIdentity(Principal principal) + { + return getIdentity(principal.getName()); + } + + /** + * Returns the specified {@link Identity}, by public key, within this scope. + * + * @param key + * the {@link PublicKey} to use. + * @return an identity representing the public key or null if + * it cannot be found. + */ + public abstract Identity getIdentity(PublicKey key); + + /** + * Adds an identity to his scope. + * + * @param identity + * the {@link Identity} to add. + * @throws KeyManagementException + * if it is an invalid identity, an identity with the same key + * exists, or if another error occurs. + */ + public abstract void addIdentity(Identity identity) + throws KeyManagementException; + + /** + * Removes an identity in this scope. + * + * @param identity + * the {@link Identity} to remove. + * @throws KeyManagementException + * if it is a missing identity, or if another error occurs. + */ + public abstract void removeIdentity(Identity identity) + throws KeyManagementException; + + /** + * Returns an {@link Enumeration} of identities in this scope. + * + * @return an {@link Enumeration} of the identities in this scope. + */ + public abstract Enumeration identities(); + + /** + * Returns a string representing this instance. It includes the name, the + * scope name, and number of identities. + * + * @return a string representation of this instance. + */ + public String toString() + { + return (super.getName() + " " + super.getScope().getName() + " " + size()); + } +} diff --git a/libjava/classpath/java/security/IntersectingDomainCombiner.java b/libjava/classpath/java/security/IntersectingDomainCombiner.java new file mode 100644 index 000000000..2bfcfb442 --- /dev/null +++ b/libjava/classpath/java/security/IntersectingDomainCombiner.java @@ -0,0 +1,82 @@ +/* IntersectingDomainCombiner.java -- + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.util.HashSet; + +/** + * A trivial implementation of {@link DomainCombiner} that produces the + * intersection of the supplied {@link ProtectionDomain} objects. + */ +final class IntersectingDomainCombiner implements DomainCombiner +{ + + // Contstant. + // ------------------------------------------------------------------------- + + static final IntersectingDomainCombiner SINGLETON = new IntersectingDomainCombiner(); + + // Constructor. + // ------------------------------------------------------------------------- + + private IntersectingDomainCombiner() + { + } + + // Methods. + // ------------------------------------------------------------------------- + + public ProtectionDomain[] combine (ProtectionDomain[] currentDomains, + ProtectionDomain[] assignedDomains) + { + HashSet newDomains = new HashSet (); + for (int i = 0; i < currentDomains.length; i++) + { + if (currentDomains[i] == null) + continue; + for (int j = 0; j < assignedDomains.length; j++) + { + if (currentDomains[i].equals (assignedDomains[j])) + newDomains.add (currentDomains[i]); + } + } + return (ProtectionDomain[]) + newDomains.toArray(new ProtectionDomain[newDomains.size()]); + } +} diff --git a/libjava/classpath/java/security/InvalidAlgorithmParameterException.java b/libjava/classpath/java/security/InvalidAlgorithmParameterException.java new file mode 100644 index 000000000..aa77937fb --- /dev/null +++ b/libjava/classpath/java/security/InvalidAlgorithmParameterException.java @@ -0,0 +1,95 @@ +/* InvalidAlgorithmParameterException.java -- an invalid parameter to a + security algorithm + Copyright (C) 2000, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Thrown for an invalid security algorithm parameter. + * + * @author Warren Levy (warrenl@cygnus.com) + * @since 1.2 + * @status updated to 1.4 + */ +public class InvalidAlgorithmParameterException + extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 2864672297499471472L; + + /** + * Construct an exception with no message. + */ + public InvalidAlgorithmParameterException() + { + super(); + } + + /** + * Construct an exception with a message. + * + * @param msg the message + */ + public InvalidAlgorithmParameterException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public InvalidAlgorithmParameterException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public InvalidAlgorithmParameterException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/InvalidKeyException.java b/libjava/classpath/java/security/InvalidKeyException.java new file mode 100644 index 000000000..39aa3df43 --- /dev/null +++ b/libjava/classpath/java/security/InvalidKeyException.java @@ -0,0 +1,91 @@ +/* InvalidKeyException -- thrown for an invalid key + Copyright (C) 2000, 2002, 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Thrown for an invalid key. + * + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class InvalidKeyException extends KeyException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5698479920593359816L; + + /** + * Construct an exception with no message. + */ + public InvalidKeyException() + { + } + + /** + * Construct an exception with a message. + * + * @param msg the message + */ + public InvalidKeyException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public InvalidKeyException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public InvalidKeyException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/InvalidParameterException.java b/libjava/classpath/java/security/InvalidParameterException.java new file mode 100644 index 000000000..c5218a049 --- /dev/null +++ b/libjava/classpath/java/security/InvalidParameterException.java @@ -0,0 +1,70 @@ +/* InvalidParameterException.java -- an invalid parameter in the JCA/JCE engine + Copyright (C) 2000, 2002 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Thrown when an invalid parameter is passed to a method of the JCA/JCE + * engine classes. + * + * @author Warren Levy (warrenl@cygnus.com) + * @status updated to 1.4 + */ +public class InvalidParameterException extends IllegalArgumentException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -857968536935667808L; + + /** + * Construct an exception with no message. + */ + public InvalidParameterException() + { + } + + /** + * Construct an exception with a message. + * + * @param msg the message + */ + public InvalidParameterException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/Key.java b/libjava/classpath/java/security/Key.java new file mode 100644 index 000000000..23652b6e7 --- /dev/null +++ b/libjava/classpath/java/security/Key.java @@ -0,0 +1,94 @@ +/* Key.java -- A abstract representation of a digital key + Copyright (C) 1998, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.Serializable; + +/** + * This interfaces models the base characteristics that all keys must + * have. These are: a key algorithm, an encoded form, and a format used + * to encode the key. Specific key types inherit from this interface. + * Note that since this interface extends Serializable, all + * keys may be serialized. Keys are generally obtained through key generators, + * including {@link KeyFactory}. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see PublicKey + * @see PrivateKey + * @see KeyPair + * @see KeyPairGenerator + * @see KeyFactory + * @see KeySpec + * @see Identity + * @see Signer + * @since 1.1 + * @status updated to 1.4 + */ +public interface Key extends Serializable +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 6603384152749567654L; + + /** + * This method returns the name of the algorithm for this key. This is a + * String such as "RSA". + * + * @return the name of the algorithm in use + */ + String getAlgorithm(); + + /** + * This method returns the name of the encoding format for this key. This + * is the name of the ASN.1 data format used for this key, such as + * "X.509" or "PKCS#8". This method returns null if this key + * does not have an encoding format. + * + * @return the name of the encoding format for this key, or null + */ + String getFormat(); + + /** + * This method returns the encoded form of the key. If this key does not + * support encoding, this method returns null. + * + * @return the encoded form of the key, or null + */ + byte[] getEncoded(); +} // interface Key diff --git a/libjava/classpath/java/security/KeyException.java b/libjava/classpath/java/security/KeyException.java new file mode 100644 index 000000000..66f1feb64 --- /dev/null +++ b/libjava/classpath/java/security/KeyException.java @@ -0,0 +1,94 @@ +/* KeyException.java -- Thrown when there is a problem with a key + Copyright (C) 1998, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when there is a problem with a key. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Key + * @status updated to 1.4 + */ +public class KeyException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7483676942812432108L; + + /** + * This method initializes a new instance of KeyException + * with no descriptive message. + */ + public KeyException() + { + } + + /** + * This method initializes a new instance of KeyException + * with a descriptive message. + * + * @param msg the descriptive message + */ + public KeyException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public KeyException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public KeyException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/KeyFactory.java b/libjava/classpath/java/security/KeyFactory.java new file mode 100644 index 000000000..6f47de044 --- /dev/null +++ b/libjava/classpath/java/security/KeyFactory.java @@ -0,0 +1,280 @@ +/* KeyFactory.java --- Key Factory Class + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Engine; + +import java.lang.reflect.InvocationTargetException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +/** + * Key factories are used to convert keys (opaque cryptographic keys of type + * {@link Key}) into key specifications (transparent representations of the + * underlying key material). + * + *

        Key factories are bi-directional. They allow a key class to be converted + * into a key specification (key material) and back again. For example DSA + * public keys can be specified as DSAPublicKeySpec or + * X509EncodedKeySpec. A key factory translates these key + * specifications.

        + * + * @since 1.2 + * @see Key + * @see KeySpec + * @see java.security.spec.DSAPublicKeySpec + * @see java.security.spec.X509EncodedKeySpec + @author Mark Benvenuto + */ +public class KeyFactory +{ + /** The service name for key factories. */ + private static final String KEY_FACTORY = "KeyFactory"; + + private KeyFactorySpi keyFacSpi; + private Provider provider; + private String algorithm; + + /** + * Constructs a new instance of KeyFactory with the specified + * parameters. + * + * @param keyFacSpi + * the key factory to use. + * @param provider + * the provider to use. + * @param algorithm + * the name of the key algorithm to use. + */ + protected KeyFactory(KeyFactorySpi keyFacSpi, Provider provider, + String algorithm) + { + this.keyFacSpi = keyFacSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + /** + * Returns a new instance of KeyFactory representing the + * specified key factory. + * + * @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 IllegalArgumentException if algorithm is + * null or is an empty string. + */ + public static KeyFactory 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns a new instance of KeyFactory 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. + * @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 IllegalArgumentException if either algorithm or + * provider is null or empty. + */ + public static KeyFactory getInstance(String algorithm, String provider) + 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); + } + + /** + * Returns a new instance of KeyFactory 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. + * @return a new instance repesenting the desired algorithm. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by + * {@link Provider}. + * @throws IllegalArgumentException if either algorithm or + * provider is null, or if + * algorithm is an empty string. + * @since 1.4 + * @see Provider + */ + public static KeyFactory getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("KeyFactory for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; + try + { + Object spi = Engine.getInstance(KEY_FACTORY, algorithm, provider); + return new KeyFactory((KeyFactorySpi) spi, provider, algorithm); + } + catch (InvocationTargetException x) + { + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + } + catch (ClassCastException x) + { + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; + } + + /** + * Returns the {@link Provider} of this instance. + * + * @return the {@link Provider} of this instance. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Returns the name of the algorithm used. + * + * @return the name of the algorithm used. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Generates a public key from the provided key specification. + * + * @param keySpec + * the key specification. + * @return the public key. + * @throws InvalidKeySpecException + * if the key specification is invalid. + */ + public final PublicKey generatePublic(KeySpec keySpec) + throws InvalidKeySpecException + { + return keyFacSpi.engineGeneratePublic(keySpec); + } + + /** + * Generates a private key from the provided key specification. + * + * @param keySpec + * the key specification. + * @return the private key. + * @throws InvalidKeySpecException + * if the key specification is invalid. + */ + public final PrivateKey generatePrivate(KeySpec keySpec) + throws InvalidKeySpecException + { + return keyFacSpi.engineGeneratePrivate(keySpec); + } + + /** + * Returns a key specification for the given key. keySpec + * identifies the specification class to return the key material in. + * + * @param key + * the key to use. + * @param keySpec + * the specification class to use. + * @return the key specification in an instance of the requested specification + * class. + * @throws InvalidKeySpecException + * the requested key specification is inappropriate for this key or + * the key is unrecognized. + */ + public final T getKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + return keyFacSpi.engineGetKeySpec(key, keySpec); + } + + /** + * Translates the key from an unknown or untrusted provider into a key from + * this key factory. + * + * @param key + * the key to translate from. + * @return the translated key. + * @throws InvalidKeyException + * if the key cannot be processed by this key factory. + */ + public final Key translateKey(Key key) throws InvalidKeyException + { + return keyFacSpi.engineTranslateKey(key); + } +} diff --git a/libjava/classpath/java/security/KeyFactorySpi.java b/libjava/classpath/java/security/KeyFactorySpi.java new file mode 100644 index 000000000..b8424638f --- /dev/null +++ b/libjava/classpath/java/security/KeyFactorySpi.java @@ -0,0 +1,134 @@ +/* KeyFactorySpi.java --- Key Factory Service Provider Interface + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +/** + * KeyFactorySpi is the Service Provider Interface (SPI) for the + * KeyFactory class. This is the interface for providers to + * supply to implement a key factory for an algorithm. + * + * Key factories are used to convert keys (opaque cryptographic + * keys of type Key) into key specifications (transparent + * representations of the underlying key material). + * + * Key factories are bi-directional. They allow a key class + * to be converted into a key specification (key material) and + * back again. + * + * For example DSA public keys can be specified as + * DSAPublicKeySpec or X509EncodedKeySpec. The key factory + * translate these key specifications. + * + * @since JDK 1.2 + * @author Mark Benvenuto + */ +public abstract class KeyFactorySpi +{ + /** + * Constucts a new KeyFactorySpi. + */ + public KeyFactorySpi() + { + } + + /** + * Generates a public key from the provided key specification. + * + * @param keySpec key specification + * + * @return the public key + * + * @throws InvalidKeySpecException invalid key specification for + * this key factory to produce a public key + */ + protected abstract PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException; + + + /** + * Generates a private key from the provided key specification. + * + * @param keySpec key specification + * + * @return the private key + * + * @throws InvalidKeySpecException invalid key specification for + * this key factory to produce a private key + */ + protected abstract PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException; + + /** + * Returns a key specification for the given key. keySpec + * identifies the specification class to return the key + * material in. + * + * @param key the key + * @param keySpec the specification class to return the + * key material in. + * + * @return the key specification in an instance of the requested + * specification class + * + * @throws InvalidKeySpecException the requested key specification + * is inappropriate for this key or the key is + * unrecognized. + */ + protected abstract T engineGetKeySpec(Key key, + Class keySpec) + throws InvalidKeySpecException; + + + /** + * Translates the key from an unknown or untrusted provider + * into a key for this key factory. + * + * @param key key from an unknown or untrusted provider + * + * @return the translated key + * + * @throws InvalidKeyException if the key cannot be + * processed by this key factory + */ + protected abstract Key engineTranslateKey(Key key) + throws InvalidKeyException; +} diff --git a/libjava/classpath/java/security/KeyManagementException.java b/libjava/classpath/java/security/KeyManagementException.java new file mode 100644 index 000000000..f39fe312e --- /dev/null +++ b/libjava/classpath/java/security/KeyManagementException.java @@ -0,0 +1,93 @@ +/* KeyManagementException.java -- an exception in key management + Copyright (C) 1998, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown whenever a problem related to the management of + * security keys is encountered. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Key + * @status updated to 1.4 + */ +public class KeyManagementException extends KeyException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 947674216157062695L; + + /** + * Create a new instance with no descriptive error message. + */ + public KeyManagementException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public KeyManagementException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public KeyManagementException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public KeyManagementException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/KeyPair.java b/libjava/classpath/java/security/KeyPair.java new file mode 100644 index 000000000..bf1a40a23 --- /dev/null +++ b/libjava/classpath/java/security/KeyPair.java @@ -0,0 +1,87 @@ +/* KeyPair.java --- Key Pair Class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; +import java.io.Serializable; + +/** + KeyPair serves as a simple container for public and private keys. + If properly initialized, this class should be treated like the + private key since it contains it and take approriate security + measures. + + @author Mark Benvenuto + */ +public final class KeyPair implements Serializable +{ + private static final long serialVersionUID = -7565189502268009837L; + + private PublicKey publicKey; + private PrivateKey privateKey; + + /** + Initializes the KeyPair with a pubilc and private key. + + @param publicKey Public Key to store + @param privateKey Private Key to store + */ + public KeyPair(PublicKey publicKey, PrivateKey privateKey) + { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + /** + Returns the public key stored in the KeyPair + + @return The public key + */ + public PublicKey getPublic() + { + return publicKey; + } + + /** + Returns the private key stored in the KeyPair + + @return The private key + */ + public PrivateKey getPrivate() + { + return privateKey; + } +} diff --git a/libjava/classpath/java/security/KeyPairGenerator.java b/libjava/classpath/java/security/KeyPairGenerator.java new file mode 100644 index 000000000..5e6bb1a3c --- /dev/null +++ b/libjava/classpath/java/security/KeyPairGenerator.java @@ -0,0 +1,313 @@ +/* KeyPairGenerator.java --- Key Pair Generator Class + Copyright (C) 1999, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Engine; + +import java.lang.reflect.InvocationTargetException; +import java.security.spec.AlgorithmParameterSpec; + +/** + * KeyPairGenerator is a class used to generate key-pairs for a + * security algorithm. + * + *

        The KeyPairGenerator is created with the + * getInstance() Factory methods. It is used to generate a pair of + * public and private keys for a specific algorithm and associate this key-pair + * with the algorithm parameters it was initialized with.

        + * + * @see KeyPair + * @see AlgorithmParameterSpec + * @author Mark Benvenuto + * @author Casey Marshall + */ +public abstract class KeyPairGenerator extends KeyPairGeneratorSpi +{ + /** The service name for key pair generators. */ + private static final String KEY_PAIR_GENERATOR = "KeyPairGenerator"; + + Provider provider; + private String algorithm; + + /** + * Constructs a new instance of KeyPairGenerator. + * + * @param algorithm + * the algorithm to use. + */ + protected KeyPairGenerator(String algorithm) + { + this.algorithm = algorithm; + this.provider = null; + } + + /** + * Returns the name of the algorithm used. + * + * @return the name of the algorithm used. + */ + public String getAlgorithm() + { + return algorithm; + } + + /** + * Returns a new instance of KeyPairGenerator which generates + * key-pairs for the specified algorithm. + * + * @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 IllegalArgumentException if algorithm is + * null or is an empty string. + */ + public static KeyPairGenerator 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns a new instance of KeyPairGenerator 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. + * @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 IllegalArgumentException if either algorithm or + * provider is null or empty. + */ + public static KeyPairGenerator getInstance(String algorithm, String provider) + 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); + } + + /** + * Returns a new instance of KeyPairGenerator which generates + * key-pairs for the specified algorithm from a designated {@link Provider}. + * + * @param algorithm + * the name of the algorithm to use. + * @param provider + * the {@link Provider} to use. + * @return a new insatnce repesenting the desired algorithm. + * @throws NoSuchAlgorithmException + * if the algorithm is not implemented by the {@link Provider}. + * @throws IllegalArgumentException if either algorithm or + * provider is null, or if + * algorithm is an empty string. + * @since 1.4 + * @see Provider + */ + public static KeyPairGenerator getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("KeyPairGenerator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; + try + { + o = Engine.getInstance(KEY_PAIR_GENERATOR, algorithm, provider); + } + catch (InvocationTargetException x) + { + 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; + if (o instanceof KeyPairGenerator) + { + result = (KeyPairGenerator) o; + result.algorithm = algorithm; + } + 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; + } + + /** + * Returns the {@link Provider} of this instance. + * + * @return the {@link Provider} of this instance. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Initializes this instance for the specified key size. Since no source of + * randomness is specified, a default one will be used. + * + * @param keysize + * the size of keys to use. + */ + public void initialize(int keysize) + { + initialize(keysize, new SecureRandom()); + } + + /** + * Initializes this instance for the specified key size and + * {@link SecureRandom}. + * + * @param keysize + * the size of keys to use. + * @param random + * the {@link SecureRandom} to use. + * @since 1.2 + */ + public void initialize(int keysize, SecureRandom random) + { + } + + /** + * Initializes this instance with the specified + * {@link AlgorithmParameterSpec}. Since no source of randomness is specified, + * a default one will be used. + * + * @param params + * the {@link AlgorithmParameterSpec} to use. + * @throws InvalidAlgorithmParameterException + * if the designated specifications are invalid. + * @since 1.2 + */ + public void initialize(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { + initialize(params, new SecureRandom()); + } + + /** + * Initializes this instance with the specified {@link AlgorithmParameterSpec} + * and {@link SecureRandom}. + * + * @param params + * the {@link AlgorithmParameterSpec} to use. + * @param random + * the {@link SecureRandom} to use. + * @throws InvalidAlgorithmParameterException + * if the designated specifications are invalid. + * @since 1.2 + */ + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException + { + super.initialize(params, random); + } + + /** + * Generates a new "DSA" {@link KeyPair} from the "GNU" security provider. + * + *

        This method generates a unique key-pair each time it is called.

        + * + * @return a new unique {@link KeyPair}. + * @see #generateKeyPair() + * @since 1.2 + */ + public final KeyPair genKeyPair() + { + try + { + return getInstance("DSA", "GNU").generateKeyPair(); + } + catch (Exception e) + { + System.err.println("genKeyPair failed: " + e); + e.printStackTrace(); + return null; + } + } + + /** + * Generates a new "DSA" {@link KeyPair} from the "GNU" security provider. + * + *

        This method generates a unique key pair each time it is called.

        + * + * @return a new unique {@link KeyPair}. + * @see #genKeyPair() + */ + public KeyPair generateKeyPair() + { + return genKeyPair(); + } +} diff --git a/libjava/classpath/java/security/KeyPairGeneratorSpi.java b/libjava/classpath/java/security/KeyPairGeneratorSpi.java new file mode 100644 index 000000000..1eaad9832 --- /dev/null +++ b/libjava/classpath/java/security/KeyPairGeneratorSpi.java @@ -0,0 +1,102 @@ +/* KeyPairGeneratorSpi.java --- Key Pair Generator SPI Class + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; +import java.security.spec.AlgorithmParameterSpec; + +/** + KeyPairGeneratorSpi is the interface used to generate key pairs + for security algorithms. + + @author Mark Benvenuto + */ +public abstract class KeyPairGeneratorSpi +{ + /** + Constructs a new KeyPairGeneratorSpi + */ + public KeyPairGeneratorSpi() + { + } + + /** + Initialize the KeyPairGeneratorSpi with the specified + key size and source of randomness + + @param keysize size of the key to generate + @param random A SecureRandom source of randomness + */ + public abstract void initialize(int keysize, SecureRandom random); + + /** + Initialize the KeyPairGeneratorSpi with the specified + AlgorithmParameterSpec and source of randomness + + This is a concrete method. It may be overridden by the provider + and if the AlgorithmParameterSpec class is invalid + throw InvalidAlgorithmParameterException. By default this + method just throws UnsupportedOperationException. + + @param params A AlgorithmParameterSpec to intialize with + @param random A SecureRandom source of randomness + + @throws InvalidAlgorithmParameterException + */ + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException + { + throw new java.lang.UnsupportedOperationException(); + } + + /** + Generates a KeyPair according the rules for the algorithm. + Unless intialized, algorithm defaults will be used. It + creates a unique key pair each time. + + @return a key pair + */ + public abstract KeyPair generateKeyPair(); + + /** + * We override clone here to make it accessible for use by + * DummyKeyPairGenerator. + */ + protected Object clone() throws CloneNotSupportedException + { + return super.clone(); + } +} diff --git a/libjava/classpath/java/security/KeyStore.java b/libjava/classpath/java/security/KeyStore.java new file mode 100644 index 000000000..b7a0e2ab1 --- /dev/null +++ b/libjava/classpath/java/security/KeyStore.java @@ -0,0 +1,503 @@ +/* KeyStore.java --- Key Store Class + Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +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; + +/** + * Keystore represents an in-memory collection of keys and + * certificates. There are two types of entries: + * + *
        + *
        Key Entry
        + * + *

        This type of keystore entry store sensitive crytographic key + * information in a protected format.Typically this is a secret + * key or a private key with a certificate chain.

        + * + *
        Trusted Ceritificate Entry
        + * + *

        This type of keystore entry contains a single public key + * certificate belonging to annother entity. It is called trusted + * because the keystore owner trusts that the certificates + * belongs to the subject (owner) of the certificate.

        + *
        + * + *

        Entries in a key store are referred to by their "alias": a simple + * unique string. + * + *

        The structure and persistentence of the key store is not + * specified. Any method could be used to protect sensitive + * (private or secret) keys. Smart cards or integrated + * cryptographic engines could be used or the keystore could + * be simply stored in a file.

        + * + * @see java.security.cert.Certificate + * @see Key + */ +public class KeyStore +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for key stores. */ + private static final String KEY_STORE = "KeyStore"; + + private KeyStoreSpi keyStoreSpi; + private Provider provider; + private String type; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + Creates an instance of KeyStore + + @param keyStoreSpi A KeyStore engine to use + @param provider A provider to use + @param type The type of KeyStore + */ + protected KeyStore(KeyStoreSpi keyStoreSpi, Provider provider, String type) + { + this.keyStoreSpi = keyStoreSpi; + this.provider = provider; + this.type = type; + } + + /** + * Returns an instance of a KeyStore representing the specified + * type, from the first provider that implements it. + * + * @param type the type of keystore to create. + * @return a KeyStore 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 type is + * null 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new KeyStoreException(type); + } + + /** + * Returns an instance of a KeyStore 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 KeyStore 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 type or + * provider is null or empty. + */ + public static KeyStore getInstance(String type, String provider) + throws KeyStoreException, 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(type, p); + } + + /** + * Returns an instance of a KeyStore representing the specified + * type, from the specified provider. + * + * @param type the type of keystore to create. + * @param provider the provider to use. + * @return a KeyStore repesenting the desired type. + * @throws KeyStoreException if the designated type is not implemented by the + * given provider. + * @throws IllegalArgumentException if either type or + * provider is null, or if + * type is an empty string. + * @since 1.4 + */ + public static KeyStore getInstance(String type, Provider provider) + throws KeyStoreException + { + Throwable cause; + try + { + Object spi = Engine.getInstance(KEY_STORE, type, provider); + return new KeyStore((KeyStoreSpi) spi, provider, type); + } + catch (NoSuchAlgorithmException x) + { + cause = x; + } + catch (InvocationTargetException x) + { + cause = x.getCause() != null ? x.getCause() : x; + } + catch (ClassCastException x) + { + cause = x; + } + KeyStoreException x = new KeyStoreException(type); + x.initCause(cause); + throw x; + } + + /** + * Returns the default KeyStore type. This method looks up the + * type in <JAVA_HOME>/lib/security/java.security with the + * property "keystore.type" or if that fails then "gkr" . + */ + public static final String getDefaultType() + { + // Security reads every property in java.security so it + // will return this property if it exists. + String tmp = Security.getProperty("keystore.type"); + + if (tmp == null) + tmp = "gkr"; + + return tmp; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + Gets the provider that the class is from. + + @return the provider of this class + */ + public final Provider getProvider() + { + return provider; + } + + /** + Returns the type of the KeyStore supported + + @return A string with the type of KeyStore + */ + public final String getType() + { + return type; + } + + /** + Returns the key associated with given alias using the + supplied password. + + @param alias an alias for the key to get + @param password password to access key with + + @return the requested key, or null otherwise + + @throws NoSuchAlgorithmException if there is no algorithm + for recovering the key + @throws UnrecoverableKeyException key cannot be reocovered + (wrong password). + */ + public final Key getKey(String alias, char[]password) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException + { + return keyStoreSpi.engineGetKey(alias, password); + } + + /** + Gets a Certificate chain for the specified alias. + + @param alias the alias name + + @return a chain of Certificates ( ordered from the user's + certificate to the Certificate Authority's ) or + null if the alias does not exist or there is no + certificate chain for the alias ( the alias refers + to a trusted certificate entry or there is no entry). + */ + public final java.security.cert. + Certificate[] getCertificateChain(String alias) throws KeyStoreException + { + return keyStoreSpi.engineGetCertificateChain(alias); + } + + /** + Gets a Certificate for the specified alias. + + If there is a trusted certificate entry then that is returned. + it there is a key entry with a certificate chain then the + first certificate is return or else null. + + @param alias the alias name + + @return a Certificate or null if the alias does not exist + or there is no certificate for the alias + */ + public final java.security.cert.Certificate getCertificate(String alias) + throws KeyStoreException + { + return keyStoreSpi.engineGetCertificate(alias); + } + + /** + Gets entry creation date for the specified alias. + + @param alias the alias name + + @returns the entry creation date or null + */ + public final Date getCreationDate(String alias) throws KeyStoreException + { + return keyStoreSpi.engineGetCreationDate(alias); + } + + /** + Assign the key to the alias in the keystore, protecting it + with the given password. It will overwrite an existing + entry and if the key is a PrivateKey, also add the + certificate chain representing the corresponding public key. + + @param alias the alias name + @param key the key to add + @password the password to protect with + @param chain the certificate chain for the corresponding + public key + + @throws KeyStoreException if it fails + */ + public final void setKeyEntry(String alias, Key key, char[]password, + java.security.cert. + Certificate[]chain) throws KeyStoreException + { + keyStoreSpi.engineSetKeyEntry(alias, key, password, chain); + } + + /** + Assign the key to the alias in the keystore. It will overwrite + an existing entry and if the key is a PrivateKey, also + add the certificate chain representing the corresponding + public key. + + @param alias the alias name + @param key the key to add + @param chain the certificate chain for the corresponding + public key + + @throws KeyStoreException if it fails + */ + public final void setKeyEntry(String alias, byte[]key, + java.security.cert. + Certificate[]chain) throws KeyStoreException + { + keyStoreSpi.engineSetKeyEntry(alias, key, chain); + } + + /** + Assign the certificate to the alias in the keystore. It + will overwrite an existing entry. + + @param alias the alias name + @param cert the certificate to add + + @throws KeyStoreException if it fails + */ + public final void setCertificateEntry(String alias, + java.security.cert. + Certificate cert) throws + KeyStoreException + { + keyStoreSpi.engineSetCertificateEntry(alias, cert); + } + + /** + Deletes the entry for the specified entry. + + @param alias the alias name + + @throws KeyStoreException if it fails + */ + public final void deleteEntry(String alias) throws KeyStoreException + { + keyStoreSpi.engineDeleteEntry(alias); + } + + /** + Generates a list of all the aliases in the keystore. + + @return an Enumeration of the aliases + */ + public final Enumeration aliases() throws KeyStoreException + { + return keyStoreSpi.engineAliases(); + } + + /** + Determines if the keystore contains the specified alias. + + @param alias the alias name + + @return true if it contains the alias, false otherwise + */ + public final boolean containsAlias(String alias) throws KeyStoreException + { + return keyStoreSpi.engineContainsAlias(alias); + } + + /** + Returns the number of entries in the keystore. + + @returns the number of keystore entries. + */ + public final int size() throws KeyStoreException + { + return keyStoreSpi.engineSize(); + } + + /** + Determines if the keystore contains a key entry for + the specified alias. + + @param alias the alias name + + @return true if it is a key entry, false otherwise + */ + public final boolean isKeyEntry(String alias) throws KeyStoreException + { + return keyStoreSpi.engineIsKeyEntry(alias); + } + + + /** + Determines if the keystore contains a certificate entry for + the specified alias. + + @param alias the alias name + + @return true if it is a certificate entry, false otherwise + */ + public final boolean isCertificateEntry(String alias) + throws KeyStoreException + { + return keyStoreSpi.engineIsCertificateEntry(alias); + } + + /** + Determines if the keystore contains the specified certificate + entry and returns the alias. + + It checks every entry and for a key entry checks only the + first certificate in the chain. + + @param cert Certificate to look for + + @return alias of first matching certificate, null if it + does not exist. + */ + public final String getCertificateAlias(java.security.cert.Certificate cert) + throws KeyStoreException + { + return keyStoreSpi.engineGetCertificateAlias(cert); + } + + /** + Stores the keystore in the specified output stream and it + uses the specified key it keep it secure. + + @param stream the output stream to save the keystore to + @param password the password to protect the keystore integrity with + + @throws IOException if an I/O error occurs. + @throws NoSuchAlgorithmException the data integrity algorithm + used cannot be found. + @throws CertificateException if any certificates could not be + stored in the output stream. + */ + public final void store(OutputStream stream, char[]password) + throws KeyStoreException, IOException, NoSuchAlgorithmException, + CertificateException + { + keyStoreSpi.engineStore(stream, password); + } + + /** + Loads the keystore from the specified input stream and it + uses the specified password to check for integrity if supplied. + + @param stream the input stream to load the keystore from + @param password the password to check the keystore integrity with + + @throws IOException if an I/O error occurs. + @throws NoSuchAlgorithmException the data integrity algorithm + used cannot be found. + @throws CertificateException if any certificates could not be + stored in the output stream. + */ + public final void load(InputStream stream, char[]password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + keyStoreSpi.engineLoad(stream, password); + } + +} diff --git a/libjava/classpath/java/security/KeyStoreException.java b/libjava/classpath/java/security/KeyStoreException.java new file mode 100644 index 000000000..62f906e6e --- /dev/null +++ b/libjava/classpath/java/security/KeyStoreException.java @@ -0,0 +1,92 @@ +/* KeyStoreException.java -- Indicates a problem with the key store + Copyright (C) 1998, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Indicates a problem with the key store. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + * @status updated to 1.4 + */ +public class KeyStoreException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -1119353179322377262L; + + /** + * Create a new instance detailed error message. + */ + public KeyStoreException() + { + } + + /** + * Create a new instance with a detailed error message. + * + * @param msg the descriptive error message + */ + public KeyStoreException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public KeyStoreException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public KeyStoreException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/KeyStoreSpi.java b/libjava/classpath/java/security/KeyStoreSpi.java new file mode 100644 index 000000000..b44bd84a8 --- /dev/null +++ b/libjava/classpath/java/security/KeyStoreSpi.java @@ -0,0 +1,275 @@ +/* KeyStoreSpi.java --- Key Store Service Provider Interface + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Date; +import java.util.Enumeration; + +/** + * KeyStoreSpi is the Service Provider Interface (SPI) for the + * KeyStore class. This is the interface for providers to + * supply to implement a keystore for a particular keystore + * type. + * + * @since 1.2 + * @author Mark Benvenuto + */ +public abstract class KeyStoreSpi +{ + /** + * Constructs a new KeyStoreSpi + */ + public KeyStoreSpi() + { + } + + /** + * Returns the key associated with given alias using the + * supplied password. + * + * @param alias an alias for the key to get + * @param password password to access key with + * + * @return the requested key, or null otherwise + * + * @throws NoSuchAlgorithmException if there is no algorithm + * for recovering the key + * @throws UnrecoverableKeyException key cannot be reocovered + * (wrong password). + */ + public abstract Key engineGetKey(String alias, char[]password) + throws NoSuchAlgorithmException, UnrecoverableKeyException; + + /** + * Gets a Certificate chain for the specified alias. + * + * @param alias the alias name + * + * @return a chain of Certificates ( ordered from the user's + * certificate to the Certificate Authority's ) or + * null if the alias does not exist or there is no + * certificate chain for the alias ( the alias refers + * to a trusted certificate entry or there is no entry). + */ + public abstract java.security.cert. + Certificate[] engineGetCertificateChain(String alias); + + + /** + * Gets a Certificate for the specified alias. + * + * If there is a trusted certificate entry then that is returned. + * it there is a key entry with a certificate chain then the + * first certificate is return or else null. + * + * @param alias the alias name + * + * @return a Certificate or null if the alias does not exist + * or there is no certificate for the alias + */ + public abstract java.security.cert. + Certificate engineGetCertificate(String alias); + + /** + * Gets entry creation date for the specified alias. + * + * @param alias the alias name + * + * @returns the entry creation date or null + */ + public abstract Date engineGetCreationDate(String alias); + + /** + * Assign the key to the alias in the keystore, protecting it + * with the given password. It will overwrite an existing + * entry and if the key is a PrivateKey, also add the + * certificate chain representing the corresponding public key. + * + * @param alias the alias name + * @param key the key to add + * @password the password to protect with + * @param chain the certificate chain for the corresponding + * public key + * + * @throws KeyStoreException if it fails + */ + public abstract void engineSetKeyEntry(String alias, Key key, + char[]password, + java.security.cert. + Certificate[]chain) throws + KeyStoreException; + + /** + * Assign the key to the alias in the keystore. It will overwrite + * an existing entry and if the key is a PrivateKey, also + * add the certificate chain representing the corresponding + * public key. + * + * @param alias the alias name + * @param key the key to add + * @param chain the certificate chain for the corresponding + * public key + * + * @throws KeyStoreException if it fails + */ + public abstract void engineSetKeyEntry(String alias, byte[]key, + java.security.cert. + Certificate[]chain) throws + KeyStoreException; + + + /** + * Assign the certificate to the alias in the keystore. It + * will overwrite an existing entry. + * + * @param alias the alias name + * @param cert the certificate to add + * + * @throws KeyStoreException if it fails + */ + public abstract void engineSetCertificateEntry(String alias, + java.security.cert. + Certificate cert) throws + KeyStoreException; + + /** + * Deletes the entry for the specified entry. + * + * @param alias the alias name + * + * @throws KeyStoreException if it fails + */ + public abstract void engineDeleteEntry(String alias) + throws KeyStoreException; + + /** + * Generates a list of all the aliases in the keystore. + * + * @return an Enumeration of the aliases + */ + public abstract Enumeration engineAliases(); + + /** + * Determines if the keystore contains the specified alias. + * + * @param alias the alias name + * + * @return true if it contains the alias, false otherwise + */ + public abstract boolean engineContainsAlias(String alias); + + /** + * Returns the number of entries in the keystore. + * + * @returns the number of keystore entries. + */ + public abstract int engineSize(); + + /** + * Determines if the keystore contains a key entry for + * the specified alias. + * + * @param alias the alias name + * + * @return true if it is a key entry, false otherwise + */ + public abstract boolean engineIsKeyEntry(String alias); + + /** + * Determines if the keystore contains a certificate entry for + * the specified alias. + * + * @param alias the alias name + * + * @return true if it is a certificate entry, false otherwise + */ + public abstract boolean engineIsCertificateEntry(String alias); + + /** + * Determines if the keystore contains the specified certificate + * entry and returns the alias. + * + * It checks every entry and for a key entry checks only the + * first certificate in the chain. + * + * @param cert Certificate to look for + * + * @return alias of first matching certificate, null if it + * does not exist. + */ + public abstract String engineGetCertificateAlias(java.security.cert. + Certificate cert); + + /** + * Stores the keystore in the specified output stream and it + * uses the specified key it keep it secure. + * + * @param stream the output stream to save the keystore to + * @param password the password to protect the keystore integrity with + * + * @throws IOException if an I/O error occurs. + * @throws NoSuchAlgorithmException the data integrity algorithm + * used cannot be found. + * @throws CertificateException if any certificates could not be + * stored in the output stream. + */ + public abstract void engineStore(OutputStream stream, char[]password) + throws IOException, NoSuchAlgorithmException, CertificateException; + + + /** + * Loads the keystore from the specified input stream and it + * uses the specified password to check for integrity if supplied. + * + * @param stream the input stream to load the keystore from + * @param password the password to check the keystore integrity with + * + * @throws IOException if an I/O error occurs. + * @throws NoSuchAlgorithmException the data integrity algorithm + * used cannot be found. + * @throws CertificateException if any certificates could not be + * stored in the output stream. + */ + public abstract void engineLoad(InputStream stream, char[]password) + throws IOException, NoSuchAlgorithmException, CertificateException; +} diff --git a/libjava/classpath/java/security/MessageDigest.java b/libjava/classpath/java/security/MessageDigest.java new file mode 100644 index 000000000..a4eeab447 --- /dev/null +++ b/libjava/classpath/java/security/MessageDigest.java @@ -0,0 +1,382 @@ +/* MessageDigest.java --- The message digest interface. + Copyright (C) 1999, 2002, 2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +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. + * + * @see MessageDigestSpi + * @since JDK 1.1 + */ +public abstract class MessageDigest extends MessageDigestSpi +{ + /** The service name for message digests. */ + private static final String MESSAGE_DIGEST = "MessageDigest"; + + private String algorithm; + Provider provider; + private byte[] lastDigest; + + /** + * Constructs a new instance of MessageDigest representing the + * specified algorithm. + * + * @param algorithm + * the name of the digest algorithm to use. + */ + protected MessageDigest(String algorithm) + { + this.algorithm = algorithm; + provider = null; + } + + /** + * Returns a new instance of MessageDigest representing the + * specified algorithm. + * + * @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 IllegalArgumentException if algorithm is + * null or is an empty string. + */ + public static MessageDigest 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns a new instance of MessageDigest 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. + * @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 IllegalArgumentException if either algorithm or + * provider is null or empty. + */ + public static MessageDigest getInstance(String algorithm, String provider) + 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); + } + + /** + * Returns a new instance of MessageDigest 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. + * @return a new instance representing the desired algorithm. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by + * {@link Provider}. + * @throws IllegalArgumentException if either algorithm or + * provider is null, or if + * algorithm is an empty string. + * @since 1.4 + * @see Provider + */ + public static MessageDigest getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("MessageDigest for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; + try + { + o = Engine.getInstance(MESSAGE_DIGEST, algorithm, provider); + } + catch (InvocationTargetException x) + { + 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); + else if (o instanceof MessageDigest) + { + result = (MessageDigest) o; + result.algorithm = algorithm; + } + else + { + sb.append("is of an unexpected Type: ").append(o.getClass().getName()); + throw new NoSuchAlgorithmException(sb.toString()); + } + result.provider = provider; + return result; + } + + /** + * Returns the {@link Provider} of this instance. + * + * @return the {@link Provider} of this instance. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Updates the digest with the byte. + * + * @param input byte to update the digest with. + */ + public void update(byte input) + { + engineUpdate(input); + } + + /** + * Updates the digest with the bytes from the array starting from the + * specified offset and using the specified length of bytes. + * + * @param input + * bytes to update the digest with. + * @param offset + * the offset to start at. + * @param len + * length of the data to update with. + */ + public void update(byte[] input, int offset, int len) + { + engineUpdate(input, offset, len); + } + + /** + * Updates the digest with the bytes of an array. + * + * @param input bytes to update the digest with. + */ + public void update(byte[] input) + { + engineUpdate(input, 0, input.length); + } + + /** + * Updates the digest with the remaining bytes of a buffer. + * + * @param input The input byte buffer. + * @since 1.5 + */ + public final void update (ByteBuffer input) + { + engineUpdate (input); + } + + /** + * Computes the final digest of the stored data. + * + * @return a byte array representing the message digest. + */ + public byte[] digest() + { + return lastDigest = engineDigest(); + } + + /** + * Computes the final digest of the stored bytes and returns the result. + * + * @param buf + * an array of bytes to store the result in. + * @param offset + * an offset to start storing the result at. + * @param len + * the length of the buffer. + * @return Returns the length of the buffer. + */ + public int digest(byte[] buf, int offset, int len) throws DigestException + { + return engineDigest(buf, offset, len); + } + + /** + * Computes a final update using the input array of bytes, then computes a + * final digest and returns it. It calls {@link #update(byte[])} and then + * {@link #digest(byte[])}. + * + * @param input + * an array of bytes to perform final update with. + * @return a byte array representing the message digest. + */ + public byte[] digest(byte[] input) + { + update(input); + return digest(); + } + + /** + * Returns a string representation of this instance. + * + * @return a string representation of this instance. + */ + public String toString() + { + return (getClass()).getName() + " Message Digest <" + digestToString() + ">"; + } + + /** + * Does a simple byte comparison of the two digests. + * + * @param digesta + * first digest to compare. + * @param digestb + * second digest to compare. + * @return true if both are equal, false + * otherwise. + */ + public static boolean isEqual(byte[] digesta, byte[] digestb) + { + if (digesta.length != digestb.length) + return false; + + for (int i = digesta.length - 1; i >= 0; --i) + if (digesta[i] != digestb[i]) + return false; + + return true; + } + + /** Resets this instance. */ + public void reset() + { + engineReset(); + } + + /** + * Returns the name of message digest algorithm. + * + * @return the name of message digest algorithm. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Returns the length of the message digest. The default is zero which means + * that the concrete implementation does not implement this method. + * + * @return length of the message digest. + * @since 1.2 + */ + public final int getDigestLength() + { + return engineGetDigestLength(); + } + + /** + * Returns a clone of this instance if cloning is supported. If it does not + * then a {@link CloneNotSupportedException} is thrown. Cloning depends on + * whether the subclass {@link MessageDigestSpi} implements {@link Cloneable} + * which contains the actual implementation of the appropriate algorithm. + * + * @return a clone of this instance. + * @throws CloneNotSupportedException + * the implementation does not support cloning. + */ + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } + + private String digestToString() + { + byte[] digest = lastDigest; + + if (digest == null) + return "incomplete"; + + CPStringBuilder buf = new CPStringBuilder(); + int len = digest.length; + for (int i = 0; i < len; ++i) + { + byte b = digest[i]; + byte high = (byte) ((b & 0xff) >>> 4); + byte low = (byte) (b & 0xf); + + buf.append(high > 9 ? ('a' - 10) + high : '0' + high); + buf.append(low > 9 ? ('a' - 10) + low : '0' + low); + } + + return buf.toString(); + } +} diff --git a/libjava/classpath/java/security/MessageDigestSpi.java b/libjava/classpath/java/security/MessageDigestSpi.java new file mode 100644 index 000000000..63cc96047 --- /dev/null +++ b/libjava/classpath/java/security/MessageDigestSpi.java @@ -0,0 +1,174 @@ +/* MessageDigestSpi.java --- The message digest service provider interface. + Copyright (C) 1999, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.nio.ByteBuffer; + +/** + This is the Service Provider Interface (SPI) for MessageDigest + class in java.security. It provides the back end functionality + for the MessageDigest class so that it can compute message + hashes. The default hashes are SHA-1 and MD5. A message hash + takes data of arbitrary length and produces a unique number + representing it. + + Cryptography service providers who want to implement their + own message digest hashes need only to subclass this class. + + The implementation of a Cloneable interface is left to up to + the programmer of a subclass. + + @version 0.0 + + @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public abstract class MessageDigestSpi +{ + /** + Default constructor of the MessageDigestSpi class + */ + public MessageDigestSpi() + { + } + + /** + Returns the length of the digest. It may be overridden by the + provider to return the length of the digest. Default is to + return 0. It is concrete for backwards compatibility with JDK1.1 + message digest classes. + + @return Length of Digest in Bytes + + @since 1.2 + */ + protected int engineGetDigestLength() + { + return 0; + } + + /** + Updates the digest with the specified byte. + + @param input the byte to update digest with + */ + protected abstract void engineUpdate(byte input); + + + /** + Updates the digest with the specified bytes starting with the + offset and proceeding for the specified length. + + @param input the byte array to update digest with + @param offset the offset of the byte to start with + @param len the number of the bytes to update with + */ + protected abstract void engineUpdate(byte[]input, int offset, int len); + + /** + * Updates this digest with the remaining bytes of a byte buffer. + * + * @param input The input buffer. + * @since 1.5 + */ + protected void engineUpdate (ByteBuffer input) + { + byte[] buf = new byte[1024]; + while (input.hasRemaining()) + { + int n = Math.min(input.remaining(), buf.length); + input.get (buf, 0, n); + engineUpdate (buf, 0, n); + } + } + + /** + Computes the final digest of the stored bytes and returns + them. It performs any necessary padding. The message digest + should reset sensitive data after performing the digest. + + @return An array of bytes containing the digest + */ + protected abstract byte[] engineDigest(); + + /** + Computes the final digest of the stored bytes and returns + them. It performs any necessary padding. The message digest + should reset sensitive data after performing the digest. This + method is left concrete for backwards compatibility with JDK1.1 + message digest classes. + + @param buf An array of bytes to store the digest + @param offset An offset to start storing the digest at + @param len The length of the buffer + @return Returns the length of the buffer + + @since 1.2 + */ + protected int engineDigest(byte[]buf, int offset, int len) + throws DigestException + { + if (engineGetDigestLength() > len) + throw new DigestException("Buffer is too small."); + + byte[] tmp = engineDigest(); + if (tmp.length > len) + throw new DigestException("Buffer is too small"); + + System.arraycopy(tmp, 0, buf, offset, tmp.length); + return tmp.length; + } + + /** + Resets the digest engine. Reinitializes internal variables + and clears sensitive data. + */ + protected abstract void engineReset(); + + /** + Returns a clone of this class. + + If cloning is not supported, then by default the class throws a + CloneNotSupportedException. The MessageDigestSpi provider + implementation has to overload this class in order to be + cloneable. + */ + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } +} diff --git a/libjava/classpath/java/security/NoSuchAlgorithmException.java b/libjava/classpath/java/security/NoSuchAlgorithmException.java new file mode 100644 index 000000000..518f2f726 --- /dev/null +++ b/libjava/classpath/java/security/NoSuchAlgorithmException.java @@ -0,0 +1,92 @@ +/* NoSuchAlgorithmException.java -- an algorithm was not available + Copyright (C) 1998, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when the requested security algorithm is + * not available + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class NoSuchAlgorithmException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7443947487218346562L; + + /** + * Create a new instance with no descriptive error message. + */ + public NoSuchAlgorithmException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public NoSuchAlgorithmException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public NoSuchAlgorithmException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public NoSuchAlgorithmException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/NoSuchProviderException.java b/libjava/classpath/java/security/NoSuchProviderException.java new file mode 100644 index 000000000..bd26df5ef --- /dev/null +++ b/libjava/classpath/java/security/NoSuchProviderException.java @@ -0,0 +1,70 @@ +/* NoSuchProviderException.java -- thrown when a provider is not found + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when the requested security provider is + * not available. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class NoSuchProviderException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 8488111756688534474L; + + /** + * Create a new instance with no descriptive error message. + */ + public NoSuchProviderException() + { + } + + /** + * Create a new instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public NoSuchProviderException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/Permission.java b/libjava/classpath/java/security/Permission.java new file mode 100644 index 000000000..cf6399b16 --- /dev/null +++ b/libjava/classpath/java/security/Permission.java @@ -0,0 +1,202 @@ +/* Permission.java -- The superclass for all permission objects + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Serializable; + +/** + * This class is the abstract superclass of all classes that implement + * the concept of a permission. A permission consists of a permission name + * and optionally a list of actions that relate to the permission. The + * actual meaning of the name of the permission is defined only in the + * context of a subclass. It may name a resource to which access permissions + * are granted (for example, the name of a file) or it might represent + * something else entirely. Similarly, the action list only has meaning + * within the context of a subclass. Some permission names may have no + * actions associated with them. That is, you either have the permission + * or you don't. + * + *

        The most important method in this class is implies. This + * checks whether if one has this permission, then the specified + * permission is also implied. As a conceptual example, consider the + * permissions "Read All Files" and "Read File foo". The permission + * "Read All Files" implies that the caller has permission to read the + * file foo. + * + *

        Permission's must be immutable - do not change their + * state after creation. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Permissions + * @see PermissionCollection + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class Permission implements Guard, Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5636570222231596674L; + + /** + * This is the name assigned to this permission object. + * + * @serial the name of the permission + */ + private String name; + + /** + * Create an instance with the specified name. + * + * @param name the permission name + */ + public Permission(String name) + { + this.name = name; + } + + /** + * This method implements the Guard interface for this class. + * It calls the checkPermission method in + * SecurityManager with this Permission as its + * argument. This method returns silently if the security check succeeds + * or throws an exception if it fails. + * + * @param obj the Object being guarded - ignored by this class + * @throws SecurityException if the security check fails + * @see GuardedObject + * @see SecurityManager#checkPermission(Permission) + */ + public void checkGuard(Object obj) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(this); + } + + /** + * This method tests whether this Permission implies that the + * specified Permission is also granted. + * + * @param perm the Permission to test against + * @return true if perm is implied by this + */ + public abstract boolean implies(Permission perm); + + /** + * Check to see if this object equals obj. Use implies, rather + * than equals, when making access control decisions. + * + * @param obj the object to compare to + */ + public abstract boolean equals(Object obj); + + /** + * This method returns a hash code for this Permission. It + * must satisfy the contract of Object.hashCode: it must be + * the same for all objects that equals considers to be the same. + * + * @return a hash value + */ + public abstract int hashCode(); + + /** + * Get the name of this Permission. + * + * @return the name + */ + public final String getName() + { + return name; + } + + /** + * This method returns the list of actions for this Permission + * as a String. The string should be in canonical order, for + * example, both new FilePermission(f, "write,read") and + * new FilePermission(f, "read,write") have the action list + * "read,write". + * + * @return the action list for this Permission + */ + public abstract String getActions(); + + /** + * This method returns an empty PermissionCollection object + * that can store permissions of this type, or null if no + * such collection is defined. Subclasses must override this to provide + * an appropriate collection when one is needed to accurately calculate + * implies. + * + * @return a new PermissionCollection + */ + public PermissionCollection newPermissionCollection() + { + return null; + } + + /** + * This method returns a String representation of this + * Permission object. This is in the format: + * '(' + getClass().getName() + ' ' + getName() + ' ' + getActions + * + ')'. + * + * @return this object as a String + */ + public String toString() + { + CPStringBuilder string = new CPStringBuilder(); + + string = string.append('('); + string = string.append(getClass().getName()); + string = string.append(' '); + string = string.append(getName()); + + if (!(getActions().equals(""))) + { + string = string.append(' '); + string = string.append(getActions()); + } + + string = string.append(')'); + return string.toString(); + } +} // class Permission diff --git a/libjava/classpath/java/security/PermissionCollection.java b/libjava/classpath/java/security/PermissionCollection.java new file mode 100644 index 000000000..ef87cc7e1 --- /dev/null +++ b/libjava/classpath/java/security/PermissionCollection.java @@ -0,0 +1,169 @@ +/* PermissionCollection.java -- A collection of permission objects + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Serializable; +import java.util.Enumeration; + +/** + * This class models a group of Java permissions. It has convenient + * methods for determining whether or not a given permission is implied + * by any of the permissions in this collection. + * + *

        Some care must be taken in storing permissions. First, a collection of + * the appropriate type must be created. This is done by calling the + * newPermissionCollection method on an object of the + * permission class you wish to add to the collection. If this method + * returns null, any type of PermissionCollection + * can be used to store permissions of that type. However, if a + * PermissionCollection collection object is returned, that + * type must be used. + * + *

        A PermissionCollection returned by the + * newPermissionCollection method in a subclass of + * Permission is a homogeneous collection. It only will + * hold permissions of one specified type - instances of the class that + * created it. Not all PermissionCollection subclasses + * have to hold permissions of only one type however. For example, + * the Permissions class holds permissions of many types. + * + *

        Since the newPermissionCollection in Permission + * itself returns null, by default a permission can be stored + * in any type of collection unless it overrides that method to create its + * own collection type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Permission + * @see Permissions + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class PermissionCollection implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -6727011328946861783L; + + /** + * Indicates whether or not this collection is read only. + * + * @serial if the collection is read-only + */ + private boolean readOnly; + + /** + * Create a new collection. + */ + public PermissionCollection() + { + } + + /** + * This method adds a new Permission object to the collection. + * + * @param perm the Permission to add + * + * @throws SecurityException if the collection is marked read only + * @throws IllegalArgumentException if perm is of the wrong type + */ + public abstract void add(Permission perm); + + /** + * This method tests whether the specified Permission object is + * implied by this collection of Permission objects. + * + * @param perm the Permission object to test + * @return true if the collection implies perm + */ + public abstract boolean implies(Permission perm); + + /** + * This method returns an Enumeration of all the objects in + * this collection. + * + * @return an Enumeration of this collection's objects + */ + public abstract Enumeration elements(); + + /** + * This method sets this PermissionCollection object to be + * read only. No further permissions can be added to it after calling this + * method. + */ + public void setReadOnly() + { + readOnly = true; + } + + /** + * This method tests whether or not this PermissionCollection + * object is read only. + * + * @return true if this collection is read only + */ + public boolean isReadOnly() + { + return readOnly; + } + + /** + * This method returns a String representation of this + * collection. It is formed by: + *

        +   * super.toString()" (\n"
        +   *   // enumerate all permissions, one per line
        +   * ")\n"
        +   * 
        + * + * @return a String representing this object + */ + public String toString() + { + CPStringBuilder sb = new CPStringBuilder(super.toString()); + + sb.append(" (\n"); + Enumeration e = elements(); + while (e.hasMoreElements()) + sb.append(' ').append(e.nextElement()).append('\n'); + return sb.append(")\n").toString(); + } +} // class PermissionCollection diff --git a/libjava/classpath/java/security/Permissions.java b/libjava/classpath/java/security/Permissions.java new file mode 100644 index 000000000..d814064e0 --- /dev/null +++ b/libjava/classpath/java/security/Permissions.java @@ -0,0 +1,254 @@ +/* Permissions.java -- a collection of permission collections + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import java.io.Serializable; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.NoSuchElementException; + +/** + * This class is a heterogeneous collection of permissions. It is + * organized as a collection of PermissionCollection's stored + * in a hashtable. Each individual PermissionCollection + * contains permissions of a single type. If a specific type of + * Permission does not provide a collection type to use + * via its newPermissionCollection method, then a default + * collection type which stores its permissions in a hash table will be + * used. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.1 + */ +public final class Permissions extends PermissionCollection + implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4858622370623524688L; + + /** + * Holds instances of AllPermission. + * + * @serial the permission collection for AllPermission + */ + private PermissionCollection allPermission; + + // Package-private to avoid a trampoline. + /** + * This is the Hashtable that contains our collections. + * + * @serial maps Class to PermissionCollection + */ + final Hashtable perms = new Hashtable(); + + /** + * This method initializes a new instance of Permissions. + */ + public Permissions() + { + } + + /** + * This method adds a new Permission to this collection. It + * will be stored in a PermissionCollection of the appropriate + * type, as determined by calling newPermissionCollection on + * the specified permission (if an appropriate collection does not already + * exist). If this object does not specify a particular type of collection, + * a default collection, which stores in permissions in a hash table, will + * be used. + * + * @param perm the Permission to add + * @throws SecurityException if this collection is marked as read only + */ + public void add(Permission perm) + { + if (isReadOnly()) + throw new SecurityException("PermissionCollection is read only"); + if (perm instanceof AllPermission) + { + if (allPermission == null) + { + allPermission = perm.newPermissionCollection(); + allPermission.add(perm); + perms.put(perm.getClass(), allPermission); + } + } + else + { + PermissionCollection pc + = (PermissionCollection) perms.get(perm.getClass()); + if (pc == null) + { + pc = perm.newPermissionCollection(); + if (pc == null) + pc = new PermissionsHash(); + perms.put(perm.getClass(), pc); + } + pc.add(perm); + } + } + + /** + * This method tests whether or not the specified Permission + * is implied by this PermissionCollection. + * + * @param perm the Permission to test + * @return true if the specified permission is implied by this + */ + public boolean implies(Permission perm) + { + if (allPermission != null) + return true; + PermissionCollection pc + = (PermissionCollection) perms.get(perm.getClass()); + return pc == null ? false : pc.implies(perm); + } + + /** + * This method returns an Enumeration which contains a + * list of all Permission objects contained in this + * collection. + * + * @return an Enumeration of this collection's elements + */ + public Enumeration elements() + { + return new Enumeration() + { + Enumeration main_enum = perms.elements(); + Enumeration sub_enum; + + public boolean hasMoreElements() + { + if (sub_enum == null) + { + if (main_enum == null) + return false; + if (! main_enum.hasMoreElements()) + { + main_enum = null; + return false; + } + PermissionCollection pc = + (PermissionCollection) main_enum.nextElement(); + sub_enum = pc.elements(); + } + if (! sub_enum.hasMoreElements()) + { + sub_enum = null; + return hasMoreElements(); + } + return true; + } + + public Object nextElement() + { + if (! hasMoreElements()) + throw new NoSuchElementException(); + return sub_enum.nextElement(); + } + }; + } + + /** + * Implements the permission collection for all permissions without one of + * their own, and obeys serialization of JDK. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class PermissionsHash extends PermissionCollection + { + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -8491988220802933440L; + + /** + * Hashtable where we store permissions. + * + * @serial the stored permissions, both as key and value + */ + private final Hashtable perms = new Hashtable(); + + /** + * Add a permission. We don't need to check for read-only, as this + * collection is never exposed outside of Permissions, which has already + * done that check. + * + * @param perm the permission to add + */ + public void add(Permission perm) + { + perms.put(perm, perm); + } + + /** + * Returns true if perm is in the collection. + * + * @param perm the permission to check + * @return true if it is implied + */ + // FIXME: Should this method be synchronized? + public boolean implies(Permission perm) + { + Enumeration elements = elements(); + + while (elements.hasMoreElements()) + { + Permission p = (Permission)elements.nextElement(); + if (p.implies(perm)) + return true; + } + return false; + } + + /** + * Return the elements. + * + * @return the elements + */ + public Enumeration elements() + { + return perms.elements(); + } + } // class PermissionsHash +} // class Permissions diff --git a/libjava/classpath/java/security/Policy.java b/libjava/classpath/java/security/Policy.java new file mode 100644 index 000000000..118626ea1 --- /dev/null +++ b/libjava/classpath/java/security/Policy.java @@ -0,0 +1,297 @@ +/* Policy.java --- Policy Manager Class + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Policy is an abstract class for managing the system security + * policy for the Java application environment. It specifies which permissions + * are available for code from various sources. The security policy is + * represented through a subclass of Policy. + * + *

        Only one Policy is in effect at any time. A + * {@link ProtectionDomain} initializes itself with information from this class + * on the set of permssions to grant.

        + * + *

        The location for the actual Policy could be anywhere in any + * form because it depends on the Policy implementation. The default system is + * in a flat ASCII file or it could be in a database.

        + * + *

        The current installed Policy can be accessed with + * {@link #getPolicy()} and changed with {@link #setPolicy(Policy)} if the code + * has the correct permissions.

        + * + *

        The {@link #refresh()} method causes the Policy instance to + * refresh/reload its configuration. The method used to refresh depends on the + * Policy implementation.

        + * + *

        When a protection domain initializes its permissions, it uses code like + * the following:

        + * + * + * policy = Policy.getPolicy(); + * PermissionCollection perms = policy.getPermissions(myCodeSource); + * + * + *

        The protection domain passes the Policy handler a + * {@link CodeSource} instance which contains the codebase URL and a public key. + * The Policy implementation then returns the proper set of + * permissions for that {@link CodeSource}.

        + * + *

        The default Policy implementation can be changed by setting + * the "policy.provider" security provider in the "java.security" file to the + * correct Policy implementation class.

        + * + * @author Mark Benvenuto + * @see CodeSource + * @see PermissionCollection + * @see SecureClassLoader + * @since 1.2 + */ +public abstract class Policy +{ + private static Policy currentPolicy; + + /** Map of ProtectionDomains to PermissionCollections for this instance. */ + private Map pd2pc = null; + + /** Constructs a new Policy object. */ + public Policy() + { + } + + /** + * Returns the currently installed Policy handler. The value + * should not be cached as it can be changed any time by + * {@link #setPolicy(Policy)}. + * + * @return the current Policy. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public static Policy getPolicy() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SecurityPermission("getPolicy")); + + return getCurrentPolicy(); + } + + /** + * Sets the Policy handler to a new value. + * + * @param policy + * the new Policy to use. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public static void setPolicy(Policy policy) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SecurityPermission("setPolicy")); + + setup(policy); + currentPolicy = policy; + } + + private static void setup(final Policy policy) + { + if (policy.pd2pc == null) + policy.pd2pc = Collections.synchronizedMap(new LinkedHashMap()); + + ProtectionDomain pd = policy.getClass().getProtectionDomain(); + if (pd.getCodeSource() != null) + { + PermissionCollection pc = null; + if (currentPolicy != null) + pc = currentPolicy.getPermissions(pd); + + if (pc == null) // assume it has all + { + pc = new Permissions(); + pc.add(new AllPermission()); + } + + policy.pd2pc.put(pd, pc); // add the mapping pd -> pc + } + } + + /** + * Ensures/forces loading of the configured policy provider, while bypassing + * the {@link SecurityManager} checks for "getPolicy" security + * permission. Needed by {@link ProtectionDomain}. + */ + static Policy getCurrentPolicy() + { + // FIXME: The class name of the Policy provider should really be sourced + // from the "java.security" configuration file. For now, just hard-code + // a stub implementation. + if (currentPolicy == null) + { + String pp = System.getProperty ("policy.provider"); + if (pp != null) + try + { + currentPolicy = (Policy) Class.forName(pp).newInstance(); + } + catch (Exception e) + { + // Ignored. + } + + if (currentPolicy == null) + currentPolicy = new gnu.java.security.provider.DefaultPolicy(); + } + return currentPolicy; + } + + /** + * Tests if currentPolicy is not null, + * thus allowing clients to not force loading of any policy + * provider; needed by {@link ProtectionDomain}. + */ + static boolean isLoaded() + { + return currentPolicy != null; + } + + /** + * Returns the set of Permissions allowed for a given {@link CodeSource}. + * + * @param codesource + * the {@link CodeSource} for which, the caller needs to find the + * set of granted permissions. + * @return a set of permissions for {@link CodeSource} specified by the + * current Policy. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public abstract PermissionCollection getPermissions(CodeSource codesource); + + /** + * Returns the set of Permissions allowed for a given {@link ProtectionDomain}. + * + * @param domain + * the {@link ProtectionDomain} for which, the caller needs to find + * the set of granted permissions. + * @return a set of permissions for {@link ProtectionDomain} specified by the + * current Policy.. + * @since 1.4 + * @see ProtectionDomain + * @see SecureClassLoader + */ + public PermissionCollection getPermissions(ProtectionDomain domain) + { + if (domain == null) + return new Permissions(); + + if (pd2pc == null) + setup(this); + + PermissionCollection result = (PermissionCollection) pd2pc.get(domain); + if (result != null) + { + Permissions realResult = new Permissions(); + for (Enumeration e = result.elements(); e.hasMoreElements(); ) + realResult.add((Permission) e.nextElement()); + + return realResult; + } + + result = getPermissions(domain.getCodeSource()); + if (result == null) + result = new Permissions(); + + PermissionCollection pc = domain.getPermissions(); + if (pc != null) + for (Enumeration e = pc.elements(); e.hasMoreElements(); ) + result.add((Permission) e.nextElement()); + + return result; + } + + /** + * Checks if the designated {@link Permission} is granted to a designated + * {@link ProtectionDomain}. + * + * @param domain + * the {@link ProtectionDomain} to test. + * @param permission + * the {@link Permission} to check. + * @return true if permission is implied by a + * permission granted to this {@link ProtectionDomain}. Returns + * false otherwise. + * @since 1.4 + * @see ProtectionDomain + */ + public boolean implies(ProtectionDomain domain, Permission permission) + { + if (pd2pc == null) + setup(this); + + PermissionCollection pc = (PermissionCollection) pd2pc.get(domain); + if (pc != null) + return pc.implies(permission); + + boolean result = false; + pc = getPermissions(domain); + if (pc != null) + { + result = pc.implies(permission); + pd2pc.put(domain, pc); + } + + return result; + } + + /** + * Causes this Policy instance to refresh / reload its + * configuration. The method used to refresh depends on the concrete + * implementation. + */ + public abstract void refresh(); +} diff --git a/libjava/classpath/java/security/Principal.java b/libjava/classpath/java/security/Principal.java new file mode 100644 index 000000000..6d9de6ccd --- /dev/null +++ b/libjava/classpath/java/security/Principal.java @@ -0,0 +1,85 @@ +/* Principal.java -- A security entity + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +/** + * This interface models an entity (such as a user or a certificate authority) + * for the purposes of applying the Java security model. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see X509Certificate + * @since 1.1 + * @status updated to 1.4 + */ +public interface Principal +{ + /** + * This method tests another Principal object for equality + * with this one. + * + * @param obj the Object to test for equality + * @return true if the specified Principal is equal + */ + boolean equals(Object obj); + + /** + * This method returns a String representation of this + * Principal. + * + * @return this Principal represented as a String + */ + String toString(); + + /** + * This method returns a hash code value for this Principal. + * Remember the contract of hashCode - two objects which compare as + * equals() must have the same hashCode(). + * + * @return a hash value + */ + int hashCode(); + + /** + * This method returns a String that names this + * Principal. + * + * @return the name of this Principal + */ + String getName(); +} // interface Principal diff --git a/libjava/classpath/java/security/PrivateKey.java b/libjava/classpath/java/security/PrivateKey.java new file mode 100644 index 000000000..70607c134 --- /dev/null +++ b/libjava/classpath/java/security/PrivateKey.java @@ -0,0 +1,62 @@ +/* PrivateKey.java -- tagging interface for all private keys + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +/** + * This interface specified no methods. In simply provides a common + * super-interface for all algorithm specific private key values. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Key + * @see PublicKey + * @see Certificate + * @see Signature#initVerify(PublicKey) + * @see DSAPrivateKey + * @see RSAPrivateKey + * @see RSAPrivateCrtKey + * @since 1.1 + * @status updated to 1.4 + */ +public interface PrivateKey extends Key +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 6034044314589513430L; +} // interface PrivateKey diff --git a/libjava/classpath/java/security/PrivilegedAction.java b/libjava/classpath/java/security/PrivilegedAction.java new file mode 100644 index 000000000..1a51eaade --- /dev/null +++ b/libjava/classpath/java/security/PrivilegedAction.java @@ -0,0 +1,64 @@ +/* PrivilegedAction.java -- Perform a privileged action + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This interface specifes a single run method that + * executes a privileged operation. This method is called by + * AccessController.doPrivileged() after that method + * activiates the required privileges. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see AccessController + * @see PrivilegedExceptionAction + * @since 1.1 + * @status updated to 1.5 + */ +public interface PrivilegedAction +{ + /** + * This method performs an operation that requires higher privileges to + * perform. It is called when a section of code invokes + * AccessController.doPrivileged(). + * + * @return obj An implementation dependent return value + * @see AccessController#doPrivileged(PrivilegedAction) + * @see AccessController#doPrivileged(PrivilegedAction, AccessControlContext) + */ + T run(); +} // interface PrivilegedAction diff --git a/libjava/classpath/java/security/PrivilegedActionException.java b/libjava/classpath/java/security/PrivilegedActionException.java new file mode 100644 index 000000000..3f08c8130 --- /dev/null +++ b/libjava/classpath/java/security/PrivilegedActionException.java @@ -0,0 +1,109 @@ +/* PrivilegedActionException.java -- wrap an exception in a privileged action + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when an exception is thrown during a + * privileged action being performed with the + * AccessController.doPrivileged() method. It wraps the + * actual exception thrown in the privileged code. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see PrivilegedExceptionAction + * @see AccessController#doPrivileged(PrivilegedExceptionAction) + * @see AccessController#doPrivileged(PrivilegedExceptionAction, AccessControlContext) + * @status updated to 1.4 + */ +public class PrivilegedActionException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 4724086851538908602L; + + /** + * This is the actual exception that occurred. + * + * @serial the wrapped exception + */ + private Exception exception; + + /** + * Create a new instance that wraps the specified Exception. + * + * @param e the Exception to wrap + */ + public PrivilegedActionException(Exception e) + { + super(e); + exception = e; + } + + /** + * Get the underlying Exception that caused this one. This + * is a legacy method, the preferred way is {@link #getCause()}. + * + * @return the cause + */ + public Exception getException() + { + return exception; + } + + /** + * Gets the cause of this exception. + * + * @return the cause + * @since 1.4 + */ + public Throwable getCause() + { + return exception; + } + + /** + * Convert this to a String. + * + * @return the string representation + */ + public String toString() + { + return super.toString(); + } +} diff --git a/libjava/classpath/java/security/PrivilegedExceptionAction.java b/libjava/classpath/java/security/PrivilegedExceptionAction.java new file mode 100644 index 000000000..351438e0b --- /dev/null +++ b/libjava/classpath/java/security/PrivilegedExceptionAction.java @@ -0,0 +1,65 @@ +/* PrivilegedExceptionAction.java -- Perform a privileged operation + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This interface defines a method that is called by + * AccessController.doPrivileged() in order to perform a + * privileged operation with higher privileges enabled. This interface + * differs from PrivilegedAction in that the run + * method in this interface may throw a checked exception. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.1 + * @status updated to 1.5 + */ +public interface PrivilegedExceptionAction +{ + /** + * This method performs an operation that requires higher privileges to + * successfully complete. It is called when a section of code invokes + * AccessController.doPrivileged(). + * + * @return obj An implementation defined return value + * @throws Exception An implementation specific exception + * @see AccessController#doPrivileged(PrivilegedExceptionAction) + * @see AccessController#doPrivileged(PrivilegedExceptionAction, + * AccessControlContext) + */ + T run() throws Exception; +} // interface PrivilegedExceptionAction diff --git a/libjava/classpath/java/security/ProtectionDomain.java b/libjava/classpath/java/security/ProtectionDomain.java new file mode 100644 index 000000000..d5d657d61 --- /dev/null +++ b/libjava/classpath/java/security/ProtectionDomain.java @@ -0,0 +1,252 @@ +/* ProtectionDomain.java -- A security domain + Copyright (C) 1998, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.CPStringBuilder; + +/** + * This class represents a group of classes, along with their granted + * permissions. The classes are identified by a {@link CodeSource}. Thus, any + * class loaded from the specified {@link CodeSource} is treated as part of + * this domain. The set of permissions is represented by an instance of + * {@link PermissionCollection}. + * + *

        Every class in the system will belong to one and only one + * ProtectionDomain.

        + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @version 0.0 + */ +public class ProtectionDomain +{ + /** This is the CodeSource for this protection domain. */ + private CodeSource code_source; + + /** This is the set of permissions granted to this domain. */ + private PermissionCollection perms; + + /** The {@link ClassLoader} associated with this domain. */ + private ClassLoader classloader; + + /** The array of Principals associated with this domain.. */ + private Principal[] principals; + + /** Post 1.4 the policy may be refreshed! use false for pre 1.4. */ + private boolean staticBinding; + + /** + * Initializes a new instance of ProtectionDomain representing + * the specified {@link CodeSource} and set of permissions. No permissions + * can be added later to the {@link PermissionCollection} and this contructor + * will call the setReadOnly method on the specified set of + * permissions. + * + * @param codesource + * The {@link CodeSource} for this domain. + * @param permissions + * The set of permissions for this domain. + * @see PermissionCollection#setReadOnly() + */ + public ProtectionDomain(CodeSource codesource, PermissionCollection permissions) + { + this(codesource, permissions, null, null, true); + } + + /** + * This method initializes a new instance of ProtectionDomain + * given its {@link CodeSource}, granted permissions, associated + * {@link ClassLoader} and {@link Principal}s. + * + *

        Similar to the previous constructor, if the designated set of + * permissions is not null, the setReadOnly method + * is called on that set.

        + * + * @param codesource + * The {@link CodeSource} for this domain. + * @param permissions + * The permission set for this domain. + * @param classloader + * the ClassLoader associated with this domain. + * @param principals + * the array of {@link Principal}s associated with this domain. + * @since 1.4 + * @see PermissionCollection#setReadOnly() + */ + public ProtectionDomain(CodeSource codesource, + PermissionCollection permissions, + ClassLoader classloader, Principal[] principals) + { + this(codesource, permissions, classloader, principals, false); + } + + private ProtectionDomain(CodeSource codesource, + PermissionCollection permissions, + ClassLoader classloader, Principal[] principals, + boolean staticBinding) + { + super(); + + code_source = codesource; + if (permissions != null) + { + perms = permissions; + perms.setReadOnly(); + } + + this.classloader = classloader; + this.principals = + (principals != null ? (Principal[]) principals.clone() : new Principal[0]); + this.staticBinding = staticBinding; + } + + /** + * Returns the {@link CodeSource} of this domain. + * + * @return the {@link CodeSource} of this domain. + * @since 1.2 + */ + public final CodeSource getCodeSource() + { + return code_source; + } + + /** + * Returns the {@link ClassLoader} of this domain. + * + * @return the {@link ClassLoader} of this domain. + * @since 1.4 + */ + public final ClassLoader getClassLoader() + { + return this.classloader; + } + + /** + * Returns a clone of the {@link Principal}s of this domain. + * + * @return a clone of the {@link Principal}s of this domain. + * @since 1.4 + */ + public final Principal[] getPrincipals() + { + return (Principal[]) principals.clone(); + } + + /** + * Returns the {@link PermissionCollection} of this domain. + * + * @return The {@link PermissionCollection} of this domain. + */ + public final PermissionCollection getPermissions() + { + return perms; + } + + /** + * Tests whether or not the specified {@link Permission} is implied by the + * set of permissions granted to this domain. + * + * @param permission + * the {@link Permission} to test. + * @return true if the specified {@link Permission} is implied + * for this domain, false otherwise. + */ + public boolean implies(Permission permission) + { + if (staticBinding) + return (perms == null ? false : perms.implies(permission)); + // Else dynamically bound. Do we have it? + // NOTE: this will force loading of Policy.currentPolicy + return Policy.getCurrentPolicy().implies(this, permission); + } + + /** + * Returns a string representation of this object. It will include the + * {@link CodeSource} and set of permissions associated with this domain. + * + * @return A string representation of this object. + */ + public String toString() + { + String linesep = SystemProperties.getProperty("line.separator"); + CPStringBuilder sb = new CPStringBuilder("ProtectionDomain (").append(linesep); + + if (code_source == null) + sb.append("CodeSource:null"); + else + sb.append(code_source); + + sb.append(linesep); + if (classloader == null) + sb.append("ClassLoader:null"); + else + sb.append(classloader); + + sb.append(linesep); + sb.append("Principals:"); + if (principals != null && principals.length > 0) + { + sb.append("["); + Principal pal; + for (int i = 0; i < principals.length; i++) + { + pal = principals[i]; + sb.append("'").append(pal.getName()) + .append("' of type ").append(pal.getClass().getName()); + if (i < principals.length-1) + sb.append(", "); + } + sb.append("]"); + } + else + sb.append("none"); + + sb.append(linesep); + if (!staticBinding) // include all but dont force loading Policy.currentPolicy + if (Policy.isLoaded()) + sb.append(Policy.getCurrentPolicy().getPermissions(this)); + else // fallback on this one's permissions + sb.append(perms); + else + sb.append(perms); + + return sb.append(linesep).append(")").append(linesep).toString(); + } +} diff --git a/libjava/classpath/java/security/Provider.java b/libjava/classpath/java/security/Provider.java new file mode 100644 index 000000000..b1d6d9ce2 --- /dev/null +++ b/libjava/classpath/java/security/Provider.java @@ -0,0 +1,218 @@ +/* Provider.java -- Security provider information + Copyright (C) 1998, 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.Serializable; +import java.util.Properties; + +/** + * This class represents a Java security architecture service provider. The + * services provided by a such a provider can range from security algorithms to + * key generation. + *

        + * Providers are installed by name and version number. See the static + * initializer of the {@link java.security.Security} class for the default + * security providers installed by this class library. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class Provider + extends Properties + implements Serializable +{ + private static final long serialVersionUID = -4298000515446427739L; + + /** + * This is a textual description of the provider + */ + private String info; + + /** + * This is the name of the provider + */ + private String name; + + /** + * This is the version number of the provider + */ + private double version; + + /** + * This method initializes a new instance of Provider to have + * the specified name, version, and description information. + * + * @param name The name to assign to this Provider. + * @param version The version number for this Provider. + * @param info A textual description of this provider. + */ + protected Provider(String name, double version, String info) + { + this.name = name; + this.version = version; + this.info = info; + } + + /** + * This method returns the name assigned to this Provider. + * + * @return The Provider's name. + */ + public String getName() + { + return (name); + } + + /** + * This method retunrs the version number of this Provider. + * + * @return The Provider's version number. + */ + public double getVersion() + { + return (version); + } + + /** + * This method returns a textual description of the Provider. + * + * @return A description of the Provider. + */ + public String getInfo() + { + return (info); + } + + /** + * Maps a key property to a designated value. + *

        + * If there is an installed {@link SecurityManager} object in the underlying + * VM, its {@link SecurityManager#checkSecurityAccess(String)} method is + * called with the string "putProviderProperty." + name, where + * name is this provider's name. For the default implementation + * this translates into a {@link SecurityManager#checkPermission(Permission)} + * for a SecurityPermission("putProviderProperty." + name). + * + * @param key The property key. + * @param value The property value. + * @return The previous value of the specified property (key), + * or null if it did not have one. + * @throws SecurityException If a security manager is installed and its + * {@link SecurityManager#checkSecurityAccess(String)} method + * disallows adding properties at run-time. + * @since Classpath 0.4+cvs, JDK 1.2 + * @see java.lang.Object#equals(Object) + * @see java.util.Hashtable#get(Object) + */ + public Object put(Object key, Object value) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("putProviderProperty." + this.name); + return super.put(toCanonicalKey(key), value); + } + + // overrides same in java.util.Hashtable + public Object get(Object key) + { + return super.get(toCanonicalKey(key)); + } + + /** + * This method removes the specified key entry (and its associated value) + * from the property mapping collection. + *

        + * If there is an installed {@link SecurityManager} object in the underlying + * VM, its {@link SecurityManager#checkSecurityAccess(String)} method is + * called with the string "removeProviderProperty." + name, where + * name is this provider's name. For the default implementation + * this translates into a {@link SecurityManager#checkPermission(Permission)} + * for a SecurityPermission("removeProviderProperty." + name). + * + * @param key The key to remove + * @return The previous value for this key, or null if no + * previous value. + */ + public Object remove(Object key) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("removeProviderProperty." + this.name); + return super.remove(toCanonicalKey(key)); + } + + /** + * This method clears the entire property collection such that it no longer + * contains the properties used to look up the services provided by + * this Provider. + *

        + * If there is an installed {@link SecurityManager} object in the underlying + * VM, its {@link SecurityManager#checkSecurityAccess(String)} method is + * called with the string "clearProviderProperties." + name, + * where name is this provider's name. For the default + * implementation this translates into a + * {@link SecurityManager#checkPermission(Permission)} for a + * SecurityPermission("clearProviderProperties." + name). + */ + public void clear() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("clearProviderProperties." + this.name); + super.clear(); + } + + /** + * This method returns a String representation of this + * object. This will include the Provider name and + * version number. + * + * @return A String representation of this object. + */ + public String toString() + { + return (getClass().getName() + ": name=" + getName() + " version=" + + version); + } + + private Object toCanonicalKey(Object key) + { + if (key.getClass().isAssignableFrom(String.class)) // is it ours? + return ((String) key).toUpperCase(); // use default locale + return key; + } +} diff --git a/libjava/classpath/java/security/ProviderException.java b/libjava/classpath/java/security/ProviderException.java new file mode 100644 index 000000000..a2b469a6a --- /dev/null +++ b/libjava/classpath/java/security/ProviderException.java @@ -0,0 +1,92 @@ +/* ProviderException.java -- Generic security provider runtime exception + Copyright (C) 1998, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception indicates that a runtime problem was encounterd with + * a security provider. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class ProviderException extends RuntimeException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5256023526693665674L; + + /** + * Create an instance with no descriptive error message. + */ + public ProviderException() + { + } + + /** + * Create an instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public ProviderException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public ProviderException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public ProviderException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/PublicKey.java b/libjava/classpath/java/security/PublicKey.java new file mode 100644 index 000000000..9bf145840 --- /dev/null +++ b/libjava/classpath/java/security/PublicKey.java @@ -0,0 +1,60 @@ +/* PublicKey.java -- tagging interface for all public keys + Copyright (C) 1998, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This interface specified no methods. In simply provides a common + * super-interface for all algorithm specific public key values. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Key + * @see PrivateKey + * @see Certificate + * @see Signature#initVerify(PublicKey) + * @see DSAPublicKey + * @see RSAPublicKey + * @since 1.1 + * @status updated to 1.4 + */ +public interface PublicKey extends Key +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 7187392471159151072L; +} // interface PublicKey diff --git a/libjava/classpath/java/security/SecureClassLoader.java b/libjava/classpath/java/security/SecureClassLoader.java new file mode 100644 index 000000000..1480b7116 --- /dev/null +++ b/libjava/classpath/java/security/SecureClassLoader.java @@ -0,0 +1,148 @@ +/* SecureClassLoader.java --- A Secure Class Loader + Copyright (C) 1999, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +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 + * they are retrieved by the system policy handler. + * + * @since 1.2 + * + * @author Mark Benvenuto + */ +public class SecureClassLoader extends ClassLoader +{ + private final HashMap protectionDomainCache + = new HashMap(); + + protected SecureClassLoader(ClassLoader parent) + { + super(parent); + } + + protected SecureClassLoader() + { + } + + /** + * Creates a class using an array of bytes 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 off the offset into the data where the classfile starts. + * @param len the length of the classfile data in the array. + * @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. + */ + 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) + { + synchronized (protectionDomainCache) + { + protectionDomain = protectionDomainCache.get(cs); + } + + if (protectionDomain == null) + { + protectionDomain + = new ProtectionDomain(cs, getPermissions(cs), this, null); + synchronized (protectionDomainCache) + { + ProtectionDomain domain = protectionDomainCache.get(cs); + if (domain == null) + protectionDomainCache.put(cs, protectionDomain); + else + protectionDomain = domain; + } + } + } + return protectionDomain; + } + + /** + * Returns a PermissionCollection for the specified CodeSource. + * The default implementation invokes + * java.security.Policy.getPermissions. + * + * This method is called by defineClass that takes a CodeSource + * argument to build a proper ProtectionDomain for the class + * being defined. + */ + protected PermissionCollection getPermissions(CodeSource cs) + { + Policy policy = Policy.getCurrentPolicy(); + return policy.getPermissions(cs); + } +} diff --git a/libjava/classpath/java/security/SecureRandom.java b/libjava/classpath/java/security/SecureRandom.java new file mode 100644 index 000000000..abf4ff308 --- /dev/null +++ b/libjava/classpath/java/security/SecureRandom.java @@ -0,0 +1,420 @@ +/* SecureRandom.java --- Secure Random class implementation + Copyright (C) 1999, 2001, 2002, 2003, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import gnu.classpath.SystemProperties; +import gnu.java.lang.CPStringBuilder; +import gnu.java.security.Engine; +import gnu.java.security.action.GetSecurityPropertyAction; +import gnu.java.security.jce.prng.SecureRandomAdapter; +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; +import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * An interface to a cryptographically secure pseudo-random number + * generator (PRNG). Random (or at least unguessable) numbers are used + * in all areas of security and cryptography, from the generation of + * keys and initialization vectors to the generation of random padding + * bytes. + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + * @author Casey Marshall + */ +public class SecureRandom extends Random +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for PRNGs. */ + private static final String SECURE_RANDOM = "SecureRandom"; + + private static final long serialVersionUID = 4940670005562187L; + + //Serialized Field + long counter = 0; //Serialized + Provider provider = null; + byte[] randomBytes = null; //Always null + int randomBytesUsed = 0; + SecureRandomSpi secureRandomSpi = null; + byte[] state = null; + private String algorithm; + + private boolean isSeeded = false; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + Default constructor for SecureRandom. It constructs a + new SecureRandom by instantating the first SecureRandom + algorithm in the default security provier. + + It is not seeded and should be seeded using setSeed or else + on the first call to getnextBytes it will force a seed. + + It is maintained for backwards compatibility and programs + should use {@link #getInstance(java.lang.String)}. + */ + public SecureRandom() + { + Provider[] p = Security.getProviders(); + + //Format of Key: SecureRandom.algname + String key; + + String classname = null; + int i; + Enumeration e; + for (i = 0; i < p.length; i++) + { + e = p[i].propertyNames(); + while (e.hasMoreElements()) + { + key = (String) e.nextElement(); + if (key.startsWith("SECURERANDOM.")) + { + if ((classname = p[i].getProperty(key)) != null) + { + try + { + secureRandomSpi = (SecureRandomSpi) Class. + forName(classname).newInstance(); + provider = p[i]; + algorithm = key.substring(13); // Minus SecureRandom. + return; + } + catch (ThreadDeath death) + { + throw death; + } + catch (Throwable t) + { + // Ignore. + } + } + } + } + } + + // Nothing found. Fall back to SHA1PRNG + secureRandomSpi = new Sha160RandomSpi(); + algorithm = "Sha160"; + } + + /** + A constructor for SecureRandom. It constructs a new + SecureRandom by instantating the first SecureRandom algorithm + in the default security provier. + + It is seeded with the passed function and is useful if the user + has access to hardware random device (like a radiation detector). + + It is maintained for backwards compatibility and programs + should use getInstance. + + @param seed Seed bytes for class + */ + public SecureRandom(byte[] seed) + { + this(); + setSeed(seed); + } + + /** + A constructor for SecureRandom. It constructs a new + SecureRandom using the specified SecureRandomSpi from + the specified security provier. + + @param secureRandomSpi A SecureRandomSpi class + @param provider A Provider class + */ + protected SecureRandom(SecureRandomSpi secureRandomSpi, Provider provider) + { + this(secureRandomSpi, provider, "unknown"); + } + + /** + * Private constructor called from the getInstance() method. + */ + private SecureRandom(SecureRandomSpi secureRandomSpi, Provider provider, + String algorithm) + { + this.secureRandomSpi = secureRandomSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + /** + * Returns an instance of a SecureRandom 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. + * @throws IllegalArgumentException if algorithm is + * null or is an empty string. + */ + public static SecureRandom 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns an instance of a SecureRandom 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. + * @throws NoSuchAlgorithmException If the named provider does not implement + * the algorithm, or if the implementation cannot be instantiated. + * @throws NoSuchProviderException If no provider named provider + * is currently installed. + * @throws IllegalArgumentException if either algorithm or + * provider is null or empty. + */ + public static SecureRandom getInstance(String algorithm, String provider) + 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); + } + + /** + * Returns an instance of a SecureRandom for the specified + * algorithm from the given provider. + * + * @param algorithm The SecureRandom 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 algorithm or + * provider is null, or if + * algorithm is an empty string. + */ + public static SecureRandom getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("SecureRandom for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; + try + { + Object spi = Engine.getInstance(SECURE_RANDOM, algorithm, provider); + return new SecureRandom((SecureRandomSpi) spi, provider, algorithm); + } + catch (InvocationTargetException x) + { + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + } + catch (ClassCastException x) + { + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; + } + + /** + Returns the provider being used by the current SecureRandom class. + + @return The provider from which this SecureRandom was attained + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Returns the algorithm name used or "unknown" when the algorithm + * used couldn't be determined (as when constructed by the protected + * 2 argument constructor). + * + * @since 1.5 + */ + public String getAlgorithm() + { + return algorithm; + } + + /** + Seeds the SecureRandom. The class is re-seeded for each call and + each seed builds on the previous seed so as not to weaken security. + + @param seed seed bytes to seed with + */ + public void setSeed(byte[] seed) + { + secureRandomSpi.engineSetSeed(seed); + isSeeded = true; + } + + /** + Seeds the SecureRandom. The class is re-seeded for each call and + each seed builds on the previous seed so as not to weaken security. + + @param seed 8 seed bytes to seed with + */ + public void setSeed(long seed) + { + // This particular setSeed will be called by Random.Random(), via + // our own constructor, before secureRandomSpi is initialized. In + // this case we can't call a method on secureRandomSpi, and we + // definitely don't want to throw a NullPointerException. + // Therefore we test. + if (secureRandomSpi != null) + { + byte[] tmp = { (byte) (0xff & (seed >> 56)), + (byte) (0xff & (seed >> 48)), + (byte) (0xff & (seed >> 40)), + (byte) (0xff & (seed >> 32)), + (byte) (0xff & (seed >> 24)), + (byte) (0xff & (seed >> 16)), + (byte) (0xff & (seed >> 8)), + (byte) (0xff & seed) + }; + secureRandomSpi.engineSetSeed(tmp); + isSeeded = true; + } + } + + /** + Generates a user specified number of bytes. This function + is the basis for all the random functions. + + @param bytes array to store generated bytes in + */ + public void nextBytes(byte[] bytes) + { + if (!isSeeded) + setSeed(getSeed(32)); + randomBytesUsed += bytes.length; + counter++; + secureRandomSpi.engineNextBytes(bytes); + } + + /** + Generates an integer containing the user specified + number of random bits. It is right justified and padded + with zeros. + + @param numBits number of random bits to get, 0 <= numBits <= 32; + + @return the random bits + */ + protected final int next(int numBits) + { + if (numBits == 0) + return 0; + + byte[] tmp = new byte[(numBits + 7) / 8]; + this.nextBytes(tmp); + int ret = 0; + for (int i = 0; i < tmp.length; i++) + ret |= (tmp[i] & 0xFF) << (8 * i); + + long mask = (1L << numBits) - 1; + return (int) (ret & mask); + } + + /** + Returns the given number of seed bytes. This method is + maintained only for backwards capability. + + @param numBytes number of seed bytes to get + + @return an array containing the seed bytes + */ + public static byte[] getSeed(int numBytes) + { + return SecureRandomAdapter.getSeed(numBytes); + } + + /** + Returns the specified number of seed bytes. + + @param numBytes number of seed bytes to get + + @return an array containing the seed bytes + */ + public byte[] generateSeed(int numBytes) + { + return secureRandomSpi.engineGenerateSeed(numBytes); + } + +} diff --git a/libjava/classpath/java/security/SecureRandomSpi.java b/libjava/classpath/java/security/SecureRandomSpi.java new file mode 100644 index 000000000..08488ced7 --- /dev/null +++ b/libjava/classpath/java/security/SecureRandomSpi.java @@ -0,0 +1,85 @@ +/* SecureRandomSpi.java --- Secure Random Service Provider Interface + Copyright (C) 1999, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; +import java.io.Serializable; + +/** + SecureRandomSpi is the Service Provider Interface for SecureRandom + providers. It provides an interface for providers to the + SecureRandom engine to write their own pseudo-random number + generator. + + @since JDK 1.2 + + @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public abstract class SecureRandomSpi implements Serializable +{ + private static final long serialVersionUID = -2991854161009191830L; + + /** + Default Constructor for SecureRandomSpi + */ + public SecureRandomSpi() + { + } + + /** + Updates the seed for SecureRandomSpi but does not reset seed. + It does to this so repeated called never decrease randomness. + */ + protected abstract void engineSetSeed(byte[] seed); + + /** + Gets a user specified number of bytes depending on the length + of the array? + + @param bytes array to fill with random bytes + */ + protected abstract void engineNextBytes(byte[] bytes); + + /** + Gets a user specified number of bytes specified by the + parameter. + + @param numBytes number of random bytes to generate + + @return an array full of random bytes + */ + protected abstract byte[] engineGenerateSeed(int numBytes); +} diff --git a/libjava/classpath/java/security/Security.java b/libjava/classpath/java/security/Security.java new file mode 100644 index 000000000..6cd98b0fb --- /dev/null +++ b/libjava/classpath/java/security/Security.java @@ -0,0 +1,711 @@ +/* Security.java --- Java base security class implementation + Copyright (C) 1999, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.classpath.SystemProperties; + +import gnu.classpath.Configuration; +import gnu.classpath.VMStackWalker; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.Vector; + +/** + * This class centralizes all security properties and common security methods. + * One of its primary uses is to manage security providers. + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public final class Security +{ + private static final String ALG_ALIAS = "Alg.Alias."; + + private static Vector providers = new Vector(); + private static Properties secprops = new Properties(); + + static + { + String base = SystemProperties.getProperty("gnu.classpath.home.url"); + String vendor = SystemProperties.getProperty("gnu.classpath.vm.shortname"); + + // Try VM specific security file + boolean loaded = loadProviders (base, vendor); + + // Append classpath standard provider if possible + if (!loadProviders (base, "classpath") + && !loaded + && providers.size() == 0) + { + if (Configuration.DEBUG) + { + /* No providers found and both security files failed to + * load properly. Give a warning in case of DEBUG is + * enabled. Could be done with java.util.logging later. + */ + System.err.println + ("WARNING: could not properly read security provider files:"); + System.err.println + (" " + base + "/security/" + vendor + + ".security"); + System.err.println + (" " + base + "/security/" + "classpath" + + ".security"); + System.err.println + (" Falling back to standard GNU security provider"); + } + // Note that this matches our classpath.security file. + providers.addElement (new gnu.java.security.provider.Gnu()); + providers.addElement(new gnu.javax.crypto.jce.GnuCrypto()); + providers.addElement(new gnu.javax.crypto.jce.GnuSasl()); + providers.addElement(new gnu.javax.net.ssl.provider.Jessie()); + providers.addElement(new gnu.javax.security.auth.callback.GnuCallbacks()); + } + } + // This class can't be instantiated. + private Security() + { + } + + /** + * Tries to load the vender specific security providers from the given base + * URL. Returns true if the resource could be read and completely parsed + * successfully, false otherwise. + */ + private static boolean loadProviders(String baseUrl, String vendor) + { + if (baseUrl == null || vendor == null) + return false; + + boolean result = true; + String secfilestr = baseUrl + "/security/" + vendor + ".security"; + try + { + InputStream fin = new URL(secfilestr).openStream(); + secprops.load(fin); + + int i = 1; + String name; + while ((name = secprops.getProperty("security.provider." + i)) != null) + { + Exception exception = null; + try + { + ClassLoader sys = ClassLoader.getSystemClassLoader(); + providers.addElement(Class.forName(name, true, sys).newInstance()); + } + catch (ClassNotFoundException x) + { + exception = x; + } + catch (InstantiationException x) + { + exception = x; + } + catch (IllegalAccessException x) + { + exception = x; + } + + if (exception != null) + { + System.err.println ("WARNING: Error loading security provider " + + name + ": " + exception); + result = false; + } + i++; + } + } + catch (IOException ignored) + { + result = false; + } + + return result; + } + + /** + * Returns the value associated to a designated property name for a given + * algorithm. + * + * @param algName + * the algorithm name. + * @param propName + * the name of the property to return. + * @return the value of the specified property or null if none + * found. + * @deprecated Use the provider-based and algorithm-independent + * {@link AlgorithmParameters} and {@link KeyFactory} engine + * classes instead. + */ + public static String getAlgorithmProperty(String algName, String propName) + { + if (algName == null || propName == null) + return null; + + String property = String.valueOf(propName) + "." + String.valueOf(algName); + Provider p; + for (Iterator i = providers.iterator(); i.hasNext(); ) + { + p = (Provider) i.next(); + for (Iterator j = p.keySet().iterator(); j.hasNext(); ) + { + String key = (String) j.next(); + if (key.equalsIgnoreCase(property)) + return p.getProperty(key); + } + } + return null; + } + + /** + * Inserts a new designated {@link Provider} at a designated (1-based) + * position in the current list of installed {@link Provider}s, + * + * @param provider + * the new {@link Provider} to add. + * @param position + * the position (starting from 1) of where to install + * provider. + * @return the actual position, in the list of installed Providers. Returns + * -1 if provider was laready in the + * list. The actual position may be different than the desired + * position. + * @throws SecurityException + * if a {@link SecurityManager} is installed and it disallows this + * operation. + * @see #getProvider(String) + * @see #removeProvider(String) + * @see SecurityPermission + */ + public static int insertProviderAt(Provider provider, int position) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("insertProvider." + provider.getName()); + + position--; + int max = providers.size (); + for (int i = 0; i < max; i++) + { + if (((Provider) providers.elementAt(i)).getName().equals(provider.getName())) + return -1; + } + + if (position < 0) + position = 0; + if (position > max) + position = max; + + providers.insertElementAt(provider, position); + + return position + 1; + } + + /** + * Appends the designated new {@link Provider} to the current list of + * installed {@link Provider}s. + * + * @param provider + * the new {@link Provider} to append. + * @return the position (starting from 1) of provider in the + * current list of {@link Provider}s, or -1 if + * provider was already there. + * @throws SecurityException + * if a {@link SecurityManager} is installed and it disallows this + * operation. + * @see #getProvider(String) + * @see #removeProvider(String) + * @see SecurityPermission + */ + public static int addProvider(Provider provider) + { + return insertProviderAt (provider, providers.size () + 1); + } + + /** + * Removes an already installed {@link Provider}, given its name, from the + * current list of installed {@link Provider}s. + * + * @param name + * the name of an already installed {@link Provider} to remove. + * @throws SecurityException + * if a {@link SecurityManager} is installed and it disallows this + * operation. + * @see #getProvider(String) + * @see #addProvider(Provider) + */ + public static void removeProvider(String name) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("removeProvider." + name); + + int max = providers.size (); + for (int i = 0; i < max; i++) + { + if (((Provider) providers.elementAt(i)).getName().equals(name)) + { + providers.remove(i); + break; + } + } + } + + /** + * Returns the current list of installed {@link Provider}s as an array + * ordered according to their installation preference order. + * + * @return an array of all the installed providers. + */ + public static Provider[] getProviders() + { + Provider[] array = new Provider[providers.size ()]; + providers.copyInto (array); + return array; + } + + /** + * Returns an already installed {@link Provider} given its name. + * + * @param name + * the name of an already installed {@link Provider}. + * @return the {@link Provider} known by name. Returns + * null if the current list of {@link Provider}s does + * not include one named name. + * @see #removeProvider(String) + * @see #addProvider(Provider) + */ + public static Provider getProvider(String name) + { + if (name == null) + return null; + else + { + name = name.trim(); + if (name.length() == 0) + return null; + } + Provider p; + int max = providers.size (); + for (int i = 0; i < max; i++) + { + p = (Provider) providers.elementAt(i); + if (p.getName().equals(name)) + return p; + } + return null; + } + + /** + * Returns the value associated with a Security propery. + * + * @param key + * the key of the property to fetch. + * @return the value of the Security property associated with + * key. Returns null if no such property + * was found. + * @throws SecurityException + * if a {@link SecurityManager} is installed and it disallows this + * operation. + * @see #setProperty(String, String) + * @see SecurityPermission + */ + public static String getProperty(String key) + { + // XXX To prevent infinite recursion when the SecurityManager calls us, + // don't do a security check if the caller is trusted (by virtue of having + // been loaded by the bootstrap class loader). + SecurityManager sm = System.getSecurityManager(); + if (sm != null && VMStackWalker.getCallingClassLoader() != null) + sm.checkSecurityAccess("getProperty." + key); + + return secprops.getProperty(key); + } + + /** + * Sets or changes a designated Security property to a designated value. + * + * @param key + * the name of the property to set. + * @param datum + * the new value of the property. + * @throws SecurityException + * if a {@link SecurityManager} is installed and it disallows this + * operation. + * @see #getProperty(String) + * @see SecurityPermission + */ + public static void setProperty(String key, String datum) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setProperty." + key); + + if (datum == null) + secprops.remove(key); + else + secprops.put(key, datum); + } + + /** + * For a given service (e.g. Signature, MessageDigest, etc...) this + * method returns the {@link Set} of all available algorithm names (instances + * of {@link String}, from all currently installed {@link Provider}s. + * + * @param serviceName + * the case-insensitive name of a service (e.g. Signature, + * MessageDigest, etc). + * @return a {@link Set} of {@link String}s containing the names of all + * algorithm names provided by all of the currently installed + * {@link Provider}s. + * @since 1.4 + */ + public static Set getAlgorithms(String serviceName) + { + HashSet result = new HashSet(); + if (serviceName == null || serviceName.length() == 0) + return result; + + serviceName = serviceName.trim(); + if (serviceName.length() == 0) + return result; + + serviceName = serviceName.toUpperCase()+"."; + Provider[] providers = getProviders(); + int ndx; + for (int i = 0; i < providers.length; i++) + for (Enumeration e = providers[i].propertyNames(); e.hasMoreElements(); ) + { + String service = ((String) e.nextElement()).trim(); + if (service.toUpperCase().startsWith(serviceName)) + { + service = service.substring(serviceName.length()).trim(); + ndx = service.indexOf(' '); // get rid of attributes + if (ndx != -1) + service = service.substring(0, ndx); + result.add(service); + } + } + return Collections.unmodifiableSet(result); + } + + /** + * Returns an array of currently installed {@link Provider}s, ordered + * according to their installation preference order, which satisfy a given + * selection criterion. + * + *

        This implementation recognizes a selection criterion written in + * one of two following forms:

        + * + *
          + *
        • <crypto_service>.<algorithm_or_type>: Where + * crypto_service is a case-insensitive string, similar to what has + * been described in the {@link #getAlgorithms(String)} method, and + * algorithm_or_type is a known case-insensitive name of an + * Algorithm, or one of its aliases. + * + *

          For example, "CertificateFactory.X.509" would return all the installed + * {@link Provider}s which provide a CertificateFactory + * implementation of X.509.

        • + * + *
        • <crypto_service>.<algorithm_or_type> <attribute_name>:<value>: + * Where crypto_service is a case-insensitive string, similar to what + * has been described in the {@link #getAlgorithms(String)} method, + * algorithm_or_type is a case-insensitive known name of an Algorithm + * or one of its aliases, attribute_name is a case-insensitive + * property name with no whitespace characters, and no dots, in-between, and + * value is a {@link String} with no whitespace characters in-between. + * + *

          For example, "Signature.Sha1WithDSS KeySize:1024" would return all the + * installed {@link Provider}s which declared their ability to provide + * Signature services, using the Sha1WithDSS algorithm with + * key sizes of 1024.

        • + *
        + * + * @param filter + * the selection criterion for selecting among the installed + * {@link Provider}s. + * @return all the installed {@link Provider}s which satisfy the selection + * criterion. Returns null if no installed + * {@link Provider}s were found which satisfy the selection + * criterion. Returns ALL installed {@link Provider}s if + * filter is null or is an empty string. + * @throws InvalidParameterException + * if an exception occurs while parsing the filter. + * @see #getProviders(Map) + */ + public static Provider[] getProviders(String filter) + { + if (providers == null || providers.isEmpty()) + return null; + + if (filter == null || filter.length() == 0) + return getProviders(); + + HashMap map = new HashMap(1); + int i = filter.indexOf(':'); + if (i == -1) // . + map.put(filter, ""); + else // . : + map.put(filter.substring(0, i), filter.substring(i+1)); + + return getProviders(map); + } + + /** + * Returns an array of currently installed {@link Provider}s which satisfy a + * set of selection criteria. + * + *

        The selection criteria are defined in a {@link Map} where each + * element specifies a selection querry. The Keys in this + * {@link Map} must be in one of the two following forms:

        + * + *
          + *
        • <crypto_service>.<algorithm_or_type>: Where + * crypto_service is a case-insensitive string, similar to what has + * been described in the {@link #getAlgorithms(String)} method, and + * algorithm_or_type is a case-insensitive known name of an + * Algorithm, or one of its aliases. The value of the entry in the + * {@link Map} for such a Key MUST be the empty string. + * {@link Provider}s which provide an implementation for the designated + * service algorithm are included in the result.
        • + * + *
        • <crypto_service>.<algorithm_or_type> <attribute_name>: + * Where crypto_service is a case-insensitive string, similar to what + * has been described in the {@link #getAlgorithms(String)} method, + * algorithm_or_type is a case-insensitive known name of an Algorithm + * or one of its aliases, and attribute_name is a case-insensitive + * property name with no whitespace characters, and no dots, in-between. The + * value of the entry in this {@link Map} for such a Key MUST + * NOT be null or an empty string. {@link Provider}s which + * declare the designated attribute_name and value for the + * designated service algorithm are included in the result.
        • + *
        + * + * @param filter + * a {@link Map} of selection querries. + * @return all currently installed {@link Provider}s which satisfy ALL the + * selection criteria defined in filter. + * Returns ALL installed {@link Provider}s if filter + * is null or empty. + * @throws InvalidParameterException + * if an exception is encountered while parsing the syntax of the + * {@link Map}'s keys. + * @see #getProviders(String) + */ + public static Provider[] getProviders(Map filter) + { + if (providers == null || providers.isEmpty()) + return null; + + if (filter == null) + return getProviders(); + + Set querries = filter.keySet(); + if (querries == null || querries.isEmpty()) + return getProviders(); + + LinkedHashSet result = new LinkedHashSet(providers); // assume all + int dot, ws; + String querry, service, algorithm, attribute, value; + LinkedHashSet serviceProviders = new LinkedHashSet(); // preserve insertion order + for (Iterator i = querries.iterator(); i.hasNext(); ) + { + querry = (String) i.next(); + if (querry == null) // all providers + continue; + + querry = querry.trim(); + if (querry.length() == 0) // all providers + continue; + + dot = querry.indexOf('.'); + if (dot == -1) // syntax error + throw new InvalidParameterException( + "missing dot in '" + String.valueOf(querry)+"'"); + + value = filter.get(querry); + // deconstruct querry into [service, algorithm, attribute] + if (value == null || value.trim().length() == 0) // . + { + value = null; + attribute = null; + service = querry.substring(0, dot).trim(); + algorithm = querry.substring(dot+1).trim(); + } + else // . + { + ws = querry.indexOf(' '); + if (ws == -1) + throw new InvalidParameterException( + "value (" + String.valueOf(value) + + ") is not empty, but querry (" + String.valueOf(querry) + + ") is missing at least one space character"); + value = value.trim(); + attribute = querry.substring(ws+1).trim(); + // was the dot in the attribute? + if (attribute.indexOf('.') != -1) + throw new InvalidParameterException( + "attribute_name (" + String.valueOf(attribute) + + ") in querry (" + String.valueOf(querry) + ") contains a dot"); + + querry = querry.substring(0, ws).trim(); + service = querry.substring(0, dot).trim(); + algorithm = querry.substring(dot+1).trim(); + } + + // service and algorithm must not be empty + if (service.length() == 0) + throw new InvalidParameterException( + " in querry (" + String.valueOf(querry) + + ") is empty"); + + if (algorithm.length() == 0) + throw new InvalidParameterException( + " in querry (" + String.valueOf(querry) + + ") is empty"); + + selectProviders(service, algorithm, attribute, value, result, serviceProviders); + result.retainAll(serviceProviders); // eval next retaining found providers + if (result.isEmpty()) // no point continuing + break; + } + + if (result.isEmpty()) + return null; + + return (Provider[]) result.toArray(new Provider[result.size()]); + } + + private static void selectProviders(String svc, String algo, String attr, + String val, LinkedHashSet providerSet, + LinkedHashSet result) + { + result.clear(); // ensure we start with an empty result set + for (Iterator i = providerSet.iterator(); i.hasNext(); ) + { + Provider p = (Provider) i.next(); + if (provides(p, svc, algo, attr, val)) + result.add(p); + } + } + + private static boolean provides(Provider p, String svc, String algo, + String attr, String val) + { + Iterator it; + String serviceDotAlgorithm = null; + String key = null; + String realVal; + boolean found = false; + // if . is in the set then so is . + // but it may be stored under an alias . resolve + outer: for (int r = 0; r < 3; r++) // guard against circularity + { + serviceDotAlgorithm = (svc+"."+String.valueOf(algo)).trim(); + for (it = p.keySet().iterator(); it.hasNext(); ) + { + key = (String) it.next(); + if (key.equalsIgnoreCase(serviceDotAlgorithm)) // eureka + { + found = true; + break outer; + } + // it may be there but as an alias + if (key.equalsIgnoreCase(ALG_ALIAS + serviceDotAlgorithm)) + { + algo = p.getProperty(key); + continue outer; + } + // else continue inner + } + } + + if (!found) + return false; + + // found a candidate for the querry. do we have an attr to match? + if (val == null) // . querry + return true; + + // . ; find the key entry that match + String realAttr; + int limit = serviceDotAlgorithm.length() + 1; + for (it = p.keySet().iterator(); it.hasNext(); ) + { + key = (String) it.next(); + if (key.length() <= limit) + continue; + + if (key.substring(0, limit).equalsIgnoreCase(serviceDotAlgorithm+" ")) + { + realAttr = key.substring(limit).trim(); + if (! realAttr.equalsIgnoreCase(attr)) + continue; + + // eveything matches so far. do the value + realVal = p.getProperty(key); + if (realVal == null) + return false; + + realVal = realVal.trim(); + // is it a string value? + if (val.equalsIgnoreCase(realVal)) + return true; + + // assume value is a number. cehck for greater-than-or-equal + return (Integer.parseInt(val) >= Integer.parseInt(realVal)); + } + } + + return false; + } +} diff --git a/libjava/classpath/java/security/SecurityPermission.java b/libjava/classpath/java/security/SecurityPermission.java new file mode 100644 index 000000000..6aba18f34 --- /dev/null +++ b/libjava/classpath/java/security/SecurityPermission.java @@ -0,0 +1,178 @@ +/* SecurityPermission.java -- Class for named security permissions + Copyright (C) 1998, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This class provides a mechanism for specified named permissions + * related to the Java security framework. These permissions have no + * associated actions list. They are either granted or not granted. + * + *

        The list of valid permission names is:
        + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
        Permission NamePermission AllowsRisks + *
        createAccessControlContextAllows creation of an AccessControlContextThe new control context can have a rogue DomainCombiner, leading + * to a privacy leak
        getDomainCombinerGet a DomainCombiner from an AccessControlContextAccess to a DomainCombiner can lead to a privacy leak
        getPolicyAllows retrieval of the system security policyMalicious code can use information from the policy to better plan + * an attack
        setPolicyAllows the security policy to be changedMalicious code can give itself any permission it wants
        getProperty.keyRetrieve the property specified by the keyMalicious code can use information from the property to better plan + * an attack
        setProperty.keyAllows changing of the value of all properties implied by keyMalicious code can insert rogue classes to steal keys or recreate + * the security policy with whatever permissions it desires
        insertProvider.keyAllows the named provider to be addedMalicious code can insert rogue providers that steal data
        removeProvider.keyAllows the named provider to be removedA missing provider can cripple code that relies on it
        setSystemScopeAllows the system identity scope to be setMalicious code can add certificates not available in the original + * identity scope, to gain more permissions
        setIdentityPublicKeyAllows the public key of an Identity to be setMalicious code can install its own key to gain permissions not + * allowed by the original identity scope
        SetIdentityInfoAllows the description of an Identity to be setMalicious code can spoof users into trusting a fake identity
        addIdentityCertificateAllows a certificate to be set for the public key of an identityThe public key can become trusted to a wider audience than originally + * intended
        removeIdentityCertificateAllows removal of a certificate from an identity's public keyThe public key can become less trusted than it should be
        printIdentityView the name of the identity and scope, and whether they are + * trustedThe scope may include a filename, which provides an entry point for + * further security breaches
        clearProviderProperties.keyAllows the properties of the named provider to be clearedThis can disable parts of the program which depend on finding the + * provider
        putProviderProperty.keyAllows the properties of the named provider to be changedMalicious code can replace the implementation of a provider
        removeProviderProperty.keyAllows the properties of the named provider to be deletedThis can disable parts of the program which depend on finding the + * provider
        getSignerPrivateKeyAllows the retrieval of the private key for a signerAnyone that can access the private key can claim to be the + * Signer
        setSignerKeyPairAllows the public and private key of a Signer to be changedThe replacement might be a weaker encryption, or the attacker + * can use knowledge of the replaced key to decrypt an entire + * communication session
        + * + *

        There is some degree of security risk in granting any of these + * permissions. Some of them can completely compromise system security. + * Please exercise extreme caution in granting these permissions. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Permission + * @see SecurityManager + * @since 1.1 + * @status updated to 1.4 + */ +public final class SecurityPermission extends BasicPermission +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5236109936224050470L; + + /** + * Create a new instance with the specified name. + * + * @param name the name to assign to this permission + */ + public SecurityPermission(String name) + { + super(name); + } + + /** + * Create a new instance with the specified name. As SecurityPermission + * carries no actions, the second parameter is ignored. + * + * @param name the name to assign to this permission + * @param actions ignored + */ + public SecurityPermission(String name, String actions) + { + super(name); + } +} // class SecurityPermission diff --git a/libjava/classpath/java/security/Signature.java b/libjava/classpath/java/security/Signature.java new file mode 100644 index 000000000..d7186395f --- /dev/null +++ b/libjava/classpath/java/security/Signature.java @@ -0,0 +1,593 @@ +/* Signature.java --- Signature Class + Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Engine; + +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; + +/** + * Signature is used to provide an interface to digital signature + * algorithms. Digital signatures provide authentication and data integrity of + * digital data. + * + *

        The GNU provider provides the NIST standard DSA which uses DSA and SHA-1. + * It can be specified by SHA/DSA, SHA-1/DSA or its OID. If the RSA signature + * algorithm is provided then it could be MD2/RSA. MD5/RSA, or SHA-1/RSA. The + * algorithm must be specified because there is no default.

        + * + *

        Signature provides implementation-independent algorithms which are + * requested by the user through the getInstance() methods. It can + * be requested by specifying just the algorithm name or by specifying both the + * algorithm name and provider name.

        + * + *

        The three phases of using Signature are:

        + * + *
          + *
        1. Initializing: + *
            + *
          • It must be initialized with a private key for signing.
          • + *
          • It must be initialized with a public key for verifying.
          • + * + * + *
          • Updating: + *

            Update the bytes for signing or verifying with calls to update.

            + *
          • + * + *
          • Signing or Verify the signature on the currently stored bytes by + * calling sign or verify.
          • + *
        + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + */ +public abstract class Signature extends SignatureSpi +{ + /** Service name for signatures. */ + private static final String SIGNATURE = "Signature"; + + /** + * Possible state value which signifies that this instance has not yet been + * initialized. + */ + protected static final int UNINITIALIZED = 0; + + /** + * Possible state value which signifies that this instance has been + * initialized for signing purposes. + */ + protected static final int SIGN = 2; + + /** + * Possible state value which signifies that this instance has been + * initialized for verification purposes. + */ + protected static final int VERIFY = 3; + + /** Current sate of this instance. */ + protected int state = UNINITIALIZED; + + private String algorithm; + Provider provider; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Constructs a new Signature instance for a designated digital + * signature algorithm. + * + * @param algorithm + * the algorithm to use. + */ + protected Signature(String algorithm) + { + this.algorithm = algorithm; + state = UNINITIALIZED; + } + + /** + * Returns an instance of Signature representing the specified + * signature. + * + * @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 IllegalArgumentException if algorithm is + * null or is an empty string. + */ + public static Signature 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns an instance of Signature representing the specified + * signature from the named provider. + * + * @param algorithm the algorithm to use. + * @param provider the name of the provider to use. + * @return a new instance repesenting the desired algorithm. + * @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 algorithm or + * provider is null or empty. + */ + public static Signature getInstance(String algorithm, String provider) + 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); + } + + /** + * Returns an instance of Signature representing the specified + * signature from the specified {@link Provider}. + * + * @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 IllegalArgumentException if either algorithm or + * provider is null, or if + * algorithm is an empty string. + */ + public static Signature getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("Signature algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; + try + { + o = Engine.getInstance(SIGNATURE, algorithm, provider); + } + catch (InvocationTargetException x) + { + 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); + else if (o instanceof Signature) + { + result = (Signature) o; + result.algorithm = algorithm; + } + else + { + sb.append("is of an unexpected Type: ").append(o.getClass().getName()); + throw new NoSuchAlgorithmException(sb.toString()); + } + result.provider = provider; + return result; + } + + /** + * Returns the {@link Provider} of this instance. + * + * @return the {@link Provider} of this instance. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Initializes this instance with the public key for verification purposes. + * + * @param publicKey + * the public key to verify with. + * @throws InvalidKeyException + * if the key is invalid. + */ + public final void initVerify(PublicKey publicKey) throws InvalidKeyException + { + state = VERIFY; + engineInitVerify(publicKey); + } + + /** + * Verify a signature with a designated {@link Certificate}. This is a FIPS + * 140-1 compatible method since it verifies a signature with a certificate. + * + *

        If the {@link Certificate} is an X.509 one, has a KeyUsage + * parameter and that parameter indicates this key is not to be used for + * signing then an exception is thrown.

        + * + * @param certificate + * a {@link Certificate} containing a public key to verify with. + * @throws InvalidKeyException if the key is invalid. + */ + public final void initVerify(Certificate certificate) + throws InvalidKeyException + { + state = VERIFY; + if (certificate.getType().equals("X509")) + { + X509Certificate cert = (X509Certificate) certificate; + boolean[]array = cert.getKeyUsage(); + if (array != null && array[0] == false) + throw new InvalidKeyException( + "KeyUsage of this Certificate indicates it cannot be used for digital signing"); + } + this.initVerify(certificate.getPublicKey()); + } + + /** + * Initializes this class with the private key for signing purposes. + * + * @param privateKey + * the private key to sign with. + * @throws InvalidKeyException + * if the key is invalid. + */ + public final void initSign(PrivateKey privateKey) throws InvalidKeyException + { + state = SIGN; + engineInitSign(privateKey); + } + + /** + * Initializes this class with the private key and source of randomness for + * signing purposes. + * + * @param privateKey + * the private key to sign with. + * @param random + * the {@link SecureRandom} to use. + * @throws InvalidKeyException + * if the key is invalid. + */ + public final void initSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException + { + state = SIGN; + engineInitSign(privateKey, random); + } + + /** + * Returns the signature bytes of all the data fed to this instance. The + * format of the output depends on the underlying signature algorithm. + * + * @return the signature bytes. + * @throws SignatureException + * if the engine is not properly initialized. + */ + public final byte[] sign() throws SignatureException + { + if (state == SIGN) + return engineSign(); + else + throw new SignatureException(); + } + + /** + * Generates signature bytes of all the data fed to this instance and stores + * it in the designated array. The format of the result depends on the + * underlying signature algorithm. + * + *

        After calling this method, the instance is reset to its initial state + * and can then be used to generate additional signatures.

        + * + *

        IMPLEMENTATION NOTE: Neither this method nor the GNU provider + * will return partial digests. If len is less than the + * signature length, this method will throw a {@link SignatureException}. If + * it is greater than or equal then it is ignored.

        + * + * @param outbuf + * array of bytes of where to store the resulting signature bytes. + * @param offset + * the offset to start at in the array. + * @param len + * the number of the bytes to use in the array. + * @return the real number of bytes used. + * @throws SignatureException + * if the engine is not properly initialized. + * @since 1.2 + */ + public final int sign(byte[] outbuf, int offset, int len) + throws SignatureException + { + if (state == SIGN) + return engineSign(outbuf, offset, len); + else + throw new SignatureException(); + } + + /** + * Verifies a designated signature. + * + * @param signature + * the signature bytes to verify. + * @return true if verified, false otherwise. + * @throws SignatureException + * if the engine is not properly initialized or the signature does + * not check. + */ + public final boolean verify(byte[]signature) throws SignatureException + { + if (state == VERIFY) + return engineVerify(signature); + else + throw new SignatureException(); + } + + /** + * Verifies a designated signature. + * + * @param signature + * the signature bytes to verify. + * @param offset + * the offset to start at in the array. + * @param length + * the number of the bytes to use from the array. + * @return true if verified, false otherwise. + * @throws IllegalArgumentException + * if the signature byte array is null, + * or the offset or length is less + * than 0, or the sum of the offset + * and length is greater than the length of the + * signature byte array. + * @throws SignatureException + * if the engine is not properly initialized or the signature does + * not check. + */ + public final boolean verify(byte[] signature, int offset, int length) + throws SignatureException + { + if (state != VERIFY) + throw new SignatureException("illegal state"); + + if (signature == null) + throw new IllegalArgumentException("signature is null"); + if (offset < 0) + throw new IllegalArgumentException("offset is less than 0"); + if (length < 0) + throw new IllegalArgumentException("length is less than 0"); + if (offset + length < signature.length) + throw new IllegalArgumentException("range is out of bounds"); + + return engineVerify(signature, offset, length); + } + + /** + * Updates the data to be signed or verified with the specified byte. + * + * @param b + * the byte to update with. + * @throws SignatureException + * if the engine is not properly initialized. + */ + public final void update(byte b) throws SignatureException + { + if (state != UNINITIALIZED) + engineUpdate(b); + else + throw new SignatureException(); + } + + /** + * Updates the data to be signed or verified with the specified bytes. + * + * @param data + * the array of bytes to use. + * @throws SignatureException + * if the engine is not properly initialized. + */ + public final void update(byte[]data) throws SignatureException + { + if (state != UNINITIALIZED) + engineUpdate(data, 0, data.length); + else + throw new SignatureException(); + } + + /** + * Updates the data to be signed or verified with the specified bytes. + * + * @param data + * an array of bytes to use. + * @param off + * the offset to start at in the array. + * @param len + * the number of bytes to use from the array. + * @throws SignatureException + * if the engine is not properly initialized. + */ + public final void update(byte[]data, int off, int len) + throws SignatureException + { + if (state != UNINITIALIZED) + engineUpdate(data, off, len); + else + throw new SignatureException(); + } + + /** + * Update this signature with the {@link java.nio.Buffer#remaining()} + * bytes of the input buffer. + * + * @param input The input buffer. + * @throws SignatureException If this instance was not properly + * initialized. + */ + public final void update(ByteBuffer input) throws SignatureException + { + if (state != UNINITIALIZED) + engineUpdate(input); + else + throw new SignatureException("not initialized"); + } + + /** + * Returns the name of the algorithm currently used. The names of algorithms + * are usually SHA/DSA or SHA/RSA. + * + * @return name of algorithm. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Returns a rstring representation of this instance. + * + * @return a rstring representation of this instance. + */ + public String toString() + { + return (algorithm + " Signature"); + } + + /** + * Sets the specified algorithm parameter to the specified value. + * + * @param param + * the parameter name. + * @param value + * the parameter value. + * @throws InvalidParameterException + * if the parameter is invalid, the parameter is already set and + * can not be changed, a security exception occured, etc. + * @deprecated use the other setParameter + */ + public final void setParameter(String param, Object value) + throws InvalidParameterException + { + engineSetParameter(param, value); + } + + /** + * Sets the signature engine with the specified {@link AlgorithmParameterSpec}. + * + *

        By default, and unless overriden by the concrete SPI, this method always + * throws an {@link UnsupportedOperationException}.

        + * + * @param params + * the parameters to use for intializing this instance. + * @throws InvalidParameterException + * if the parameter is invalid, the parameter is already set and + * cannot be changed, a security exception occured, etc. + */ + public final void setParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { + engineSetParameter(params); + } + + /** + * Return the parameters of the algorithm used in this instance as an + * {@link AlgorithmParameters}. + * + * @return the parameters used with this instance, or null if + * this instance does not use any parameters. + */ + public final AlgorithmParameters getParameters() + { + return engineGetParameters(); + } + + /** + * Returns the value for the specified algorithm parameter. + * + * @param param + * the parameter name. + * @return the parameter value. + * @throws InvalidParameterException + * if the parameter is invalid. + * @deprecated use the other getParameter + */ + public final Object getParameter(String param) + throws InvalidParameterException + { + return engineGetParameter(param); + } + + /** + * Returns a clone of this instance. + * + * @return a clone of this instace. + * @throws CloneNotSupportedException + * if the implementation does not support cloning. + */ + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } +} diff --git a/libjava/classpath/java/security/SignatureException.java b/libjava/classpath/java/security/SignatureException.java new file mode 100644 index 000000000..b097bacfc --- /dev/null +++ b/libjava/classpath/java/security/SignatureException.java @@ -0,0 +1,92 @@ +/* SignatureException.java -- Generic error in signature + Copyright (C) 1998, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when a problem is encountered with a + * digital signature. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class SignatureException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 7509989324975124438L; + + /** + * Create an instance with no descriptive error message. + */ + public SignatureException() + { + } + + /** + * Create an instance with a descriptive error message. + * + * @param msg the message + */ + public SignatureException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public SignatureException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public SignatureException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/SignatureSpi.java b/libjava/classpath/java/security/SignatureSpi.java new file mode 100644 index 000000000..1ed078c0b --- /dev/null +++ b/libjava/classpath/java/security/SignatureSpi.java @@ -0,0 +1,316 @@ +/* SignatureSpi.java --- Signature Service Provider Interface + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.nio.ByteBuffer; +import java.security.spec.AlgorithmParameterSpec; + +/** + * SignatureSpi defines the Service Provider Interface (SPI) for + * the {@link Signature} class. The signature class provides an interface to a + * digital signature algorithm. Digital signatures are used for authentication + * and integrity of data. + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + * @since 1.2 + * @see Signature + */ +public abstract class SignatureSpi +{ + /** Source of randomness. */ + protected SecureRandom appRandom; + + /** + * Creates a new instance of SignatureSpi. + */ + public SignatureSpi() + { + appRandom = null; + } + + /** + * Initializes this instance with the public key for verification purposes. + * + * @param publicKey + * the public key to verify with. + * @throws InvalidKeyException + * if the key is invalid. + */ + protected abstract void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException; + + /** + * Initializes this instance with the private key for signing purposes. + * + * @param privateKey + * the private key to sign with. + * @throws InvalidKeyException + * if the key is invalid. + */ + protected abstract void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException; + + /** + * Initializes this instance with the private key and source of randomness for + * signing purposes. + * + *

        This method cannot be abstract for backward compatibility reasons.

        + * + * @param privateKey + * the private key to sign with. + * @param random + * the {@link SecureRandom} to use. + * @throws InvalidKeyException + * if the key is invalid. + * @since 1.2 + */ + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException + { + appRandom = random; + engineInitSign(privateKey); + } + + /** + * Updates the data to be signed or verified with the specified byte. + * + * @param b + * byte to update with. + * @throws SignatureException + * if the engine is not properly initialized. + */ + protected abstract void engineUpdate(byte b) throws SignatureException; + + /** + * Updates the data to be signed or verified with the specified bytes. + * + * @param b + * the array of bytes to use. + * @param off + * the offset to start at in the array. + * @param len + * the number of the bytes to use from the array. + * @throws SignatureException + * if the engine is not properly initialized. + */ + protected abstract void engineUpdate(byte[] b, int off, int len) + throws SignatureException; + + /** + * Update this signature with the {@link java.nio.Buffer#remaining()} + * bytes of the given buffer. + * + * @param input The input buffer. + * @throws IllegalStateException if the engine is not properly initialized. + */ + protected void engineUpdate(ByteBuffer input) + { + byte[] buf = new byte[4096]; + while (input.hasRemaining()) + { + int l = Math.min(input.remaining(), buf.length); + input.get(buf, 0, l); + try + { + engineUpdate(buf, 0, l); + } + catch (SignatureException se) + { + throw new IllegalStateException(se); + } + } + } + + /** + * Returns the signature bytes of all the data fed to this instance. The + * format of the output depends on the underlying signature algorithm. + * + * @return the signature bytes. + * @throws SignatureException + * if the engine is not properly initialized. + */ + protected abstract byte[] engineSign() throws SignatureException; + + /** + * Generates signature bytes of all the data fed to this instance and stores + * the result in the designated array. The format of the output depends on + * the underlying signature algorithm. + * + *

        This method cannot be abstract for backward compatibility reasons. + * After calling this method, the signature is reset to its initial state and + * can be used to generate additional signatures.

        + * + *

        IMPLEMENTATION NOTE:: Neither this method nor the GNU provider + * will return partial digests. If len is less than the + * signature length, this method will throw a {@link SignatureException}. If + * it is greater than or equal then it is ignored.

        + * + * @param outbuf + * the array of bytes to store the result in. + * @param offset + * the offset to start at in the array. + * @param len + * the number of the bytes to use in the array. + * @return the real number of bytes used. + * @throws SignatureException + * if the engine is not properly initialized. + * @since 1.2 + */ + protected int engineSign(byte[] outbuf, int offset, int len) + throws SignatureException + { + byte[] tmp = engineSign(); + if (tmp.length > len) + throw new SignatureException("Invalid Length"); + + System.arraycopy(outbuf, offset, tmp, 0, tmp.length); + return tmp.length; + } + + /** + * Verifies a designated signature. + * + * @param sigBytes + * the signature bytes to verify. + * @return true if verified, false otherwise. + * @throws SignatureException + * if the engine is not properly initialized or if it is the wrong + * signature. + */ + protected abstract boolean engineVerify(byte[] sigBytes) + throws SignatureException; + + /** + * Convenience method which calls the method with the same name and one + * argument after copying the designated bytes into a temporary byte array. + * Subclasses may override this method for performance reasons. + * + * @param sigBytes + * the array of bytes to use. + * @param offset + * the offset to start from in the array of bytes. + * @param length + * the number of bytes to use, starting at offset. + * @return true if verified, false otherwise. + * @throws SignatureException + * if the engine is not properly initialized. + */ + protected boolean engineVerify(byte[] sigBytes, int offset, int length) + throws SignatureException + { + byte[] tmp = new byte[length]; + System.arraycopy(sigBytes, offset, tmp, 0, length); + return engineVerify(tmp); + } + + /** + * Sets the specified algorithm parameter to the specified value. + * + * @param param + * the parameter name. + * @param value + * the parameter value. + * @throws InvalidParameterException + * if the parameter invalid, the parameter is already set and + * cannot be changed, a security exception occured, etc. + * @deprecated use the other setParameter. + */ + protected abstract void engineSetParameter(String param, Object value) + throws InvalidParameterException; + + /** + * Sets the signature engine with the specified {@link AlgorithmParameterSpec}. + * + *

        This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

        + * + * @param params + * the parameters. + * @throws InvalidParameterException + * if the parameter is invalid, the parameter is already set and + * cannot be changed, a security exception occured, etc. + */ + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException + { + throw new UnsupportedOperationException(); + } + + /** + * The default implementaion of this method always throws a + * {@link UnsupportedOperationException}. It MUST be overridden by concrete + * implementations to return the appropriate {@link AlgorithmParameters} for + * this signature engine (or null when that engine does not use + * any parameters. + * + * @return the parameters used with this signature engine, or + * null if it does not use any parameters. + * @throws UnsupportedOperationException + * always. + */ + protected AlgorithmParameters engineGetParameters() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the value for the specified algorithm parameter. + * + * @param param + * the parameter name. + * @return the parameter value. + * @throws InvalidParameterException + * if the parameter is invalid. + * @deprecated use the other getParameter + */ + protected abstract Object engineGetParameter(String param) + throws InvalidParameterException; + + /** + * Returns a clone of this instance. + * + * @return a clone of this instance. + * @throws CloneNotSupportedException + * if the implementation does not support cloning. + */ + public Object clone() throws CloneNotSupportedException + { + return super.clone(); + } +} diff --git a/libjava/classpath/java/security/SignedObject.java b/libjava/classpath/java/security/SignedObject.java new file mode 100644 index 000000000..79f551cce --- /dev/null +++ b/libjava/classpath/java/security/SignedObject.java @@ -0,0 +1,203 @@ +/* SignedObject.java --- Signed Object Class + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * SignedObject is used for storing runtime objects whose + * integrity cannot be compromised without being detected. + * + *

        SignedObject contains a {@link Serializable} object which is + * yet to be signed and a digital signature of that object.

        + * + *

        The signed copy is a "deep copy" (in serialized form) of an original + * object. Any changes to that original instance are not reflected in the + * enclosed copy inside this SignedObject.

        + * + *

        Several things to note are that, first there is no need to initialize the + * signature engine as this class will handle that automatically. Second, + * verification will only succeed if the public key corresponds to the private + * key used to generate the digital signature inside this + * SignedObject.

        + * + *

        For fexibility, the signature engine can be specified in the constructor + * or the verify() method. Programmers wishing to verify + * SignedObjects should be aware of the {@link Signature} engine + * they use. A malicious or flawed {@link Signature} implementation may always + * return true on verification thus circumventing the intended secrity check + * provided by the SignedObject.

        + * + *

        The GNU security provider offers an implementation of the standard NIST + * DSA which uses "DSA" and "SHA-1". It can be specified by "SHA/DSA", + * "SHA-1/DSA" or its OID. If the RSA signature algorithm is provided then it + * could be "MD2/RSA". "MD5/RSA", or "SHA-1/RSA". The algorithm must be + * specified because there is no default.

        + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + * @since 1.2 + * @see Signature + */ +public final class SignedObject implements Serializable +{ + private static final long serialVersionUID = 720502720485447167L; + + /** @serial */ + private byte[] content; + /** @serial */ + private byte[] signature; + /** @serial */ + private String thealgorithm; + + /** + * Constructs a new instance of SignedObject from a + * {@link Serializable} object. The object is signed with a designated + * private key and a signature engine. + * + * @param object + * the object to sign. + * @param signingKey + * the key to use. + * @param signingEngine + * the signature engine to use. + * @throws IOException + * if a serialization error occurred. + * @throws InvalidKeyException + * if the key is invalid. + * @throws SignatureException + * if a signing error occurs. + */ + public SignedObject(Serializable object, PrivateKey signingKey, + Signature signingEngine) + throws IOException, InvalidKeyException, SignatureException + { + thealgorithm = signingEngine.getAlgorithm(); + + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + ObjectOutputStream p = new ObjectOutputStream(ostream); + p.writeObject(object); + p.flush(); + p.close(); + + content = ostream.toByteArray(); + + signingEngine.initSign(signingKey); + signingEngine.update(content); + signature = signingEngine.sign(); + } + + /** + * Returns the encapsulated object. The object is de-serialized before being + * returned. + * + * @return the encapsulated object. + * @throws IOException + * if a de-serialization error occurs. + * @throws ClassNotFoundException + * if the encapsulated object's class was not found. + */ + public Object getObject() throws IOException, ClassNotFoundException + { + ByteArrayInputStream bais = new ByteArrayInputStream(content); + ObjectInput oi = new ObjectInputStream(bais); + Object obj = oi.readObject(); + oi.close(); + bais.close(); + + return obj; + } + + /** + * Returns the signature bytes of the encapsulated object. + * + * @return the signature bytes of the encapsulated object. + */ + public byte[] getSignature() + { + return (byte[]) signature.clone(); + + } + + /** + * Returns the name of the signature algorithm. + * + * @return the name of the signature algorithm. + */ + public String getAlgorithm() + { + return thealgorithm; + } + + /** + * Verifies the encapsulated digital signature by checking that it was + * generated by the owner of a designated public key. + * + * @param verificationKey + * the public key to use. + * @param verificationEngine + * the signature engine to use. + * @return true if signature is correct, false + * otherwise. + * @throws InvalidKeyException + * if the key is invalid. + * @throws SignatureException + * if verification fails. + */ + public boolean verify(PublicKey verificationKey, Signature verificationEngine) + throws InvalidKeyException, SignatureException + { + verificationEngine.initVerify(verificationKey); + verificationEngine.update(content); + return verificationEngine.verify(signature); + } + + /** Called to restore the state of the SignedObject from a stream. */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + content = (byte[]) content.clone(); + signature = (byte[]) signature.clone(); + } +} diff --git a/libjava/classpath/java/security/Signer.java b/libjava/classpath/java/security/Signer.java new file mode 100644 index 000000000..18259c863 --- /dev/null +++ b/libjava/classpath/java/security/Signer.java @@ -0,0 +1,148 @@ +/* Signer.java --- Signer Class + Copyright (C) 1999, 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * Signer is a subclass of {@link Identity}. It is used to store a + * digital signature key with an Identity. + * + * @author Mark Benvenuto (ivymccough@worldnet.att.net) + * @deprecated Replaced by java.security.KeyStore, the + * java.security.cert package, and java.security.Principal. + */ +public abstract class Signer extends Identity +{ + private static final long serialVersionUID = -1763464102261361480L; + private PrivateKey privateKey = null; + + /** Trivial constructor for serialization purposes. */ + protected Signer() + { + } + + /** + * Constructs a new instance of Signer with the specified + * identity name. + * + * @param name + * the name of the identity to use. + */ + public Signer(String name) + { + super(name); + } + + /** + * Constructs a new instance of Signer with the specified + * identity name and {@link IdentityScope}. + * + * @param name + * the name of the the identity to use. + * @param scope + * the {@link IdentityScope} to use. + * @throws KeyManagementException + * if a duplicate identity name exists within + * scope. + */ + public Signer(String name, IdentityScope scope) throws KeyManagementException + { + super(name, scope); + } + + /** + * Returns the private key of this Signer. + * + * @returns the private key of this Signer. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public PrivateKey getPrivateKey() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("getSignerPrivateKey"); + + return privateKey; + } + + /** + * Specifies the {@link KeyPair} associated with this Signer. + * + * @param pair + * the {@link KeyPair} to use. + * @throws InvalidParameterException + * if the key-pair is invalid. + * @throws KeyException + * if any another key-related error occurs. + * @throws SecurityException + * if a {@link SecurityManager} is installed which disallows this + * operation. + */ + public final void setKeyPair(KeyPair pair) + throws InvalidParameterException, KeyException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkSecurityAccess("setSignerKeyPair"); + + try + { + if (pair.getPublic() != null) + setPublicKey(pair.getPublic()); + else + throw new InvalidParameterException(); + + } + catch (KeyManagementException kme) + { + throw new KeyException(); + } + + if (pair.getPrivate() != null) + privateKey = pair.getPrivate(); + else + throw new InvalidParameterException(); + } + + /** @returns a string representing this Signer. */ + public String toString() + { + return (getName() + ": " + privateKey); + } +} diff --git a/libjava/classpath/java/security/UnrecoverableKeyException.java b/libjava/classpath/java/security/UnrecoverableKeyException.java new file mode 100644 index 000000000..6759c3c7b --- /dev/null +++ b/libjava/classpath/java/security/UnrecoverableKeyException.java @@ -0,0 +1,71 @@ +/* UnrecoverableKeyException.java -- Cannot recover a key from the key store + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security; + +/** + * This exception is thrown when a key cannot be recovered from the key + * store. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + * @status updated to 1.4 + */ +public class UnrecoverableKeyException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7275063078190151277L; + + /** + * Create an instance with no descriptive error message. + */ + public UnrecoverableKeyException() + { + } + + /** + * Create an instance with a descriptive error message. + * + * @param msg the descriptive error message + */ + public UnrecoverableKeyException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/UnresolvedPermission.java b/libjava/classpath/java/security/UnresolvedPermission.java new file mode 100644 index 000000000..449454aaf --- /dev/null +++ b/libjava/classpath/java/security/UnresolvedPermission.java @@ -0,0 +1,345 @@ +/* UnresolvedPermission.java -- Placeholder for unresolved permissions + Copyright (C) 1998, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security; + +// All uses of Certificate in this file refer to the one in the listed +// package, not this one. +import java.security.cert.Certificate; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.NoSuchElementException; +import java.util.Vector; + +/** + * This class is used to hold instances of all permissions that cannot + * be resolved to available permission classes when the security + * Policy object is instantiated. This may happen when the + * necessary security class has not yet been downloaded from the network. + * + *

        Instances of this class are re-resolved when + * AccessController check is done. At that time, a scan is + * made of all existing UnresolvedPermission objects and they + * are converted to objects of the appropriate permission type if the class + * for that type is then available. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Permission + * @see Permissions + * @see PermissionCollection + * @see Policy + * @since 1.1 + * @status updated to 1.4 + */ +public final class UnresolvedPermission extends Permission +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4821973115467008846L; + + /** + * The list of actions associated with this permission object. + * + * @serial the permission actions + */ + private final String actions; + + /** + * The list of Certificates associated with this object. + */ + private final transient Certificate[] certs; + + /** + * The name of the class this object should be resolved to. + * + * @serial the fully-qualified classname of the resolved type + */ + // Package visible for use by UnresolvedPermissionCollection. + final String type; + + /** + * The name of the permission. + * + * @serial the permission name + */ + private final String name; + + /** + * Create a new instance with all the information necessary to resolve it + * to an instance of the proper class at a future time. + * + * @param type the fully-qualified name of the class of this permission + * @param name the name of this permission + * @param actions the action list for this permission + * @param certs the list of certificates that sign this permission + */ + public UnresolvedPermission(String type, String name, String actions, + Certificate[] certs) + { + super(name); + this.name = name; + this.type = type; + this.actions = actions; + this.certs = certs; + } + + /** + * This method returns false always to indicate that this + * permission does not imply the specified permission. An + * UnresolvedPermission never grants any permissions. + * + * @param perm the Permission object to test + * @return false; until a permission is resolved, it implies nothing + */ + public boolean implies(Permission perm) + { + return false; + } + + /** + * This method tests this permission for equality against the specified + * Object. This will be true if and only if the following + * conditions are met:

          + *
        • The specified Object is an UnresolvedPermission
        • + *
        • The specified permission has the same type (i.e., desired class name) + * as this permission.
        • + *
        • The specified permission has the same name as this one.
        • + *
        • The specified permissoin has the same action list as this one.
        • + *
        • The specified permission has the same certificate list as this + * one.
        • + *
        + * + * @param obj the Object to test for equality + * @return true if the specified object is equal to this one + */ + public boolean equals(Object obj) + { + if (! (obj instanceof UnresolvedPermission)) + return (false); + UnresolvedPermission up = (UnresolvedPermission) obj; + return up.name.equals(name) && up.actions.equals(actions) + && up.type.equals(type) && Arrays.equals(up.certs, certs); + } + + /** + * Returns a hash code value for this object. Following the lead of + * Permission, this returns the hashcode of the permission name. + * + * @return A hash value + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * This method returns the list of actions associated with this + * permission. + * + * @return the action list + */ + public String getActions() + { + return actions; + } + + /** + * This method returns a String representation of this + * class. The format is: '(unresolved "ClassName "name" "actions")' + * + * @return String representation of this object + */ + public String toString() + { + return "(unresolved " + type + ' ' + name + ' ' + actions + ')'; + } + + /** + * This class returns a PermissionCollection object that can + * be used to store instances of UnresolvedPermission. + * + * @return a new PermissionCollection + */ + public PermissionCollection newPermissionCollection() + { + return new UnresolvedPermissionCollection(); + } + + /** + * Return the name of the class of the unresolved permission. + * @since 1.5 + */ + public String getUnresolvedType() + { + return type; + } + + /** + * Return the name of the unresolved permission. + * @since 1.5 + */ + public String getUnresolvedName() + { + return name; + } + + /** + * Return the actions of the unresolved permission, or null + * if there are no actions. + * @since 1.5 + */ + public String getUnresolvedActions() + { + return actions; + } + + /** + * Return the certificates of the unresolved permission. + * If there are no certificates, null is returned. Otherwise, + * a new array is returned. + * @since 1.5 + */ + public Certificate[] getUnresolvedCerts() + { + if (certs == null) + return null; + return (Certificate[]) certs.clone(); + } +} // class UnresolvedPermission + +/** + * Implements the permission collection for unresolved permissions, and + * obeys serialization of JDK. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ +class UnresolvedPermissionCollection extends PermissionCollection +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -7176153071733132400L; + + // Package-private to avoid a trampoline. + /** + * Hashtable where we store permissions. + * + * @serial map of typename to a Vector of permissions (you'd think Sun + * would document this better!) + */ + final Hashtable permissions = new Hashtable(); + + /** + * Add a permission. + * + * @param perm the permission to add + * @throws IllegalArgumentException if perm is not an UnresolvedPermission + * @throws SecurityException if the collection is read-only + */ + public void add(Permission perm) + { + if (isReadOnly()) + throw new SecurityException(); + if (! (perm instanceof UnresolvedPermission)) + throw new IllegalArgumentException(); + UnresolvedPermission up = (UnresolvedPermission) perm; + Vector v = (Vector) permissions.get(up.type); + if (v == null) + { + v = new Vector(); + permissions.put(up.type, v); + } + v.add(up); + } + + /** + * Returns true if perm is implied by the collection. + * + * @param perm the permission to check + * @return false; unresolved permissions imply nothing + */ + public boolean implies(Permission perm) + { + return false; + } + + /** + * Return the elements. + * + * @return the elements + */ + public Enumeration elements() + { + return new Enumeration() + { + Enumeration main_enum = permissions.elements(); + Enumeration sub_enum; + + public boolean hasMoreElements() + { + if (sub_enum == null) + { + if (main_enum == null) + return false; + if (! main_enum.hasMoreElements()) + { + main_enum = null; + return false; + } + Vector v = (Vector) main_enum.nextElement(); + sub_enum = v.elements(); + } + if (! sub_enum.hasMoreElements()) + { + sub_enum = null; + return hasMoreElements(); + } + return true; + } + + public Object nextElement() + { + if (! hasMoreElements()) + throw new NoSuchElementException(); + return sub_enum.nextElement(); + } + }; + } +} // class UnresolvedPermissionCollection diff --git a/libjava/classpath/java/security/acl/Acl.java b/libjava/classpath/java/security/acl/Acl.java new file mode 100644 index 000000000..10a59fdf5 --- /dev/null +++ b/libjava/classpath/java/security/acl/Acl.java @@ -0,0 +1,153 @@ +/* Acl.java -- An access control list + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +import java.security.Principal; +import java.util.Enumeration; + +/** + * A Java access control list (ACL) is a group of individual ACL entries. + * These entries consist of a Principal and a list of + * permissions this Principal is either granted or denied. + * A given Principal can have at most one positive ACL entry + * (i.e., one that grants permissions) and one negative ACL entry (i.e., one + * that denies permissions). If a given permission is both granted and + * denied, the ACL treats it as if it were never granted or denied. If + * both a Principal and a Group to which the + * Principal belongs have an ACL entry, the permissions for + * the individual Principal take precedence over the + * permissions of the Group if there is a conflict. + *

        + * Additionally, the ACL interface extends the Owner interface + * and so an ACL has owners. Actions which modify the ACL are restricted + * to owners. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Acl extends Owner +{ + + /** + * This method returns the name of this ACL. + * + * @return The name of this ACL + */ + String getName(); + + /** + * This method sets the name of the ACL + * + * @param caller The Principal requesting the action. + * @param name The new name for this ACL. + * + * @exception NotOwnerException If the caller is not an owner of this ACL. + */ + void setName(Principal caller, String name) + throws NotOwnerException; + + /** + * This method adds the specified entry to the ACL + * + * @param caller The Principal requesting the addition + * @param entry The ACL entry to add + * + * @return true if the entry was added, false + * if there is already an entry of the same type for the + * Principal. + * + * @exception NotOwnerException If the caller is not an owner of this ACL. + */ + boolean addEntry(Principal caller, AclEntry entry) + throws NotOwnerException; + + /** + * This method delets the specified entry from the ACL + * + * @param caller The Principal requesting the deletion. + * @param entry The ACL entry to delete + * + * @return true if the entry was deleted, or false + * if this entry was not part of the ACL to begin with + * + * @exception NotOwnerException If the caller is not an owner of this ACL. + */ + boolean removeEntry(Principal caller, AclEntry entry) + throws NotOwnerException; + + /** + * This method returns a list of all the entries in the ACL as an + * Enumeration. + * + * @return An enumeration of the ACL entries + */ + Enumeration entries(); + + /** + * This method tests whether or not the specified Principal + * has the specified Permission + * + * @param user The Principal to test + * @param perm The Permission to test for + * + * @return true if the user has been granted the permission, + * false otherwise + */ + boolean checkPermission(Principal user, Permission perm); + + /** + * This method returns a list of Permission's that are granted + * to a particular Principal. This includes any permissions + * that are granted to Group's to which the Principal + * belongs unless they are overridden by a negative ACL. This permission + * list is returned as an Enumeration. + * + * @param user The Principal to retrieve permissions for. + * + * @return A list of permissions for the Principal. + */ + Enumeration getPermissions(Principal user); + + /** + * This method returns the ACL as a String + * + * @return A String representation of this ACL + */ + String toString(); +} diff --git a/libjava/classpath/java/security/acl/AclEntry.java b/libjava/classpath/java/security/acl/AclEntry.java new file mode 100644 index 000000000..47154b285 --- /dev/null +++ b/libjava/classpath/java/security/acl/AclEntry.java @@ -0,0 +1,143 @@ +/* AclEntry.java -- An entry in an ACL list. + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +import java.security.Principal; +import java.util.Enumeration; + +/** + * This interface models an entry in an access control list (ACL). Java + * ACL's consist of a list of entries, where each consists of a + * Principal and a list of Permission's which + * have been granted to that Principal. An ACL can also + * be negative, which indicates that the list of + * Permission's is a list of permissions that are not + * granted to the Principal. A Principal can + * have at most one regular (or positive) ACL entry and one negative + * ACL entry. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface AclEntry extends Cloneable +{ + /** + * This method returns the Principal associated with this + * ACL entry. + * + * @return The Principal for this ACL entry + */ + Principal getPrincipal(); + + /** + * This method sets ths Principal associated with this + * ACL entry. This operation will only succeed if there is not already + * a Principal assigned. + * + * @param user The Principal for this ACL entry + * + * @return true if the Principal was successfully set or false if this entry already has a Principal. + */ + boolean setPrincipal(Principal user); + + /** + * This method sets this ACL entry to be a negative entry, indicating + * that it contains a list of permissions that are not granted + * to the entry's Principal. Note that there is no way to + * undo this operation. + */ + void setNegativePermissions(); + + /** + * This method tests whether or not this ACL entry is a negative entry or not. + * + * @return true if this ACL entry is negative, false otherwise + */ + boolean isNegative(); + + /** + * This method adds the specified permission to this ACL entry. + * + * @param permission The Permission to add + * + * @return true if the permission was added or false if it was already set for this entry + */ + boolean addPermission(Permission permission); + + /** + * This method deletes the specified permission to this ACL entry. + * + * @param perm The Permission to delete from this ACL entry. + * + * @return true if the permission was successfully deleted or false if the permission was not part of this ACL to begin with + */ + boolean removePermission(Permission perm); + + /** + * This method tests whether or not the specified permission is associated + * with this ACL entry. + * + * @param permission The Permission to test + * + * @return true if this permission is associated with this entry or false otherwise + */ + boolean checkPermission(Permission permission); + + /** + * This method returns a list of all Permission objects + * associated with this ACL entry as an Enumeration. + * + * @return A list of permissions for this ACL entry + */ + Enumeration permissions(); + + /** + * This method returns this object as a String. + * + * @return A String representation of this object + */ + String toString(); + + /** + * This method returns a clone of this ACL entry + * + * @return A clone of this ACL entry + */ + Object clone(); +} diff --git a/libjava/classpath/java/security/acl/AclNotFoundException.java b/libjava/classpath/java/security/acl/AclNotFoundException.java new file mode 100644 index 000000000..9a16d9c50 --- /dev/null +++ b/libjava/classpath/java/security/acl/AclNotFoundException.java @@ -0,0 +1,60 @@ +/* AclNotFoundException.java -- thrown when an ACL is not found + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +/** + * This exception is thrown when a requested access control list (ACL) is + * not found. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class AclNotFoundException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5684295034092681791L; + + /** + * Initializes a new instance of this class with no descriptive message + */ + public AclNotFoundException() + { + } +} diff --git a/libjava/classpath/java/security/acl/Group.java b/libjava/classpath/java/security/acl/Group.java new file mode 100644 index 000000000..a0df75526 --- /dev/null +++ b/libjava/classpath/java/security/acl/Group.java @@ -0,0 +1,90 @@ +/* Group.java -- Represents a group of Principals + Copyright (C) 1998, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +import java.security.Principal; +import java.util.Enumeration; + +/** + * This interface represents a group of Principals. Note that + * since this interface extends Principal, a Group + * can be used where ever a Principal is requested. This + * includes arguments to the methods in this interface. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Group extends Principal +{ + /** + * This method adds a new Principal to this group. + * + * @param user The new Principal to add + * + * @return true if the user was successfully added or false if the user is already a member + */ + boolean addMember(Principal user); + + /** + * This method deletes a member from the group. + * + * @param user The Principal to delete + * + * @return true if the user was successfully deleted or false if the user is not a member of the group + */ + boolean removeMember(Principal user); + + /** + * This method tests whether or not a given Principal is a + * member of this group. + * + * @param member The Principal to test for membership + * + * @return true if the user is member, false otherwise + */ + boolean isMember(Principal member); + + /** + * This method returns a list of all members of the group as an + * Enumeration. + * + * @return The list of all members of the group + */ + Enumeration members(); +} diff --git a/libjava/classpath/java/security/acl/LastOwnerException.java b/libjava/classpath/java/security/acl/LastOwnerException.java new file mode 100644 index 000000000..952724459 --- /dev/null +++ b/libjava/classpath/java/security/acl/LastOwnerException.java @@ -0,0 +1,62 @@ +/* LastOwnerException.java -- User attempted to delete last ACL owner + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +/** + * This exception is thrown when an attempt is made to delete the last owner + * of an access control list (ACL) + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @see Owner#deleteOwner(java.security.Principal, java.security.Principal) + * @status updated to 1.4 + */ +public class LastOwnerException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5141997548211140359L; + + /** + * Initialize a new instance of LastOwnerException that does + * not have a log message. + */ + public LastOwnerException() + { + } +} diff --git a/libjava/classpath/java/security/acl/NotOwnerException.java b/libjava/classpath/java/security/acl/NotOwnerException.java new file mode 100644 index 000000000..bea94763e --- /dev/null +++ b/libjava/classpath/java/security/acl/NotOwnerException.java @@ -0,0 +1,62 @@ +/* NotOwnerException.java -- Attempt to modify an unowned ACL + Copyright (C) 1998, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +/** + * This exception is thrown whenever an operation is attempted that requires + * the caller to be the owner of the access control list (ACL) when the caller + * is in fact not the owner of the ACL. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @status updated to 1.4 + */ +public class NotOwnerException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -5555597911163362399L; + + /** + * Initializes a new instance of NotOwnerException that does + * not have a descriptive message. + */ + public NotOwnerException() + { + } +} diff --git a/libjava/classpath/java/security/acl/Owner.java b/libjava/classpath/java/security/acl/Owner.java new file mode 100644 index 000000000..c671cd362 --- /dev/null +++ b/libjava/classpath/java/security/acl/Owner.java @@ -0,0 +1,95 @@ +/* Owner.java -- ACL owner + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +import java.security.Principal; + +/** + * This interface provides a mechanism for maintaining a list of owners + * of an access control list (ACL). Since a Principal must + * be an owner in order to modify the owner list, a mechanism must be + * provided to specify the initial owner of the ACL. The proper way to do + * this is for the implementing class to specify the initial owner in + * the contructor for that class. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Owner +{ + /** + * This method adds an owner to the access control list (ACL). Only a + * Principal who is already an owner can perform this operation. + * + * @param caller The Principal who is requesting that an owner be added + * @param owner The Principal to add as a new owner + * + * @param true if the new owner was successfully added or false if the specified new owner is already an owner + * + * @exception NotOwnerException If the caller is not already an owner of this ACL + */ + boolean addOwner(Principal caller, Principal owner) + throws NotOwnerException; + + /** + * This method delets an owner from the access control list (ACL). Only a + * Principal who is an owner can perform this operation. An + * owner can delete itself from the list. If there is only one + * owner remaining on this list, any attempt to delete it will throw an + * exception. + * + * @param caller The Principal who is requesting that an owner be deleted + * @param owner The Principal to delete as an owner + * + * @param true if the new owner was successfully deleted or false if the specified owner is not currently an owner + * + * @exception NotOwnerException If the caller is not already an owner of this ACL + * @exception LastOwnerException If completing the operation would delete the last ACL owner + */ + boolean deleteOwner(Principal caller, Principal owner) + throws NotOwnerException, LastOwnerException; + + /** + * This method tests whether or not a given Principal is an + * owner of this access control list (ACL). + * + * @return true if the Principal is an owner, false otherwise + */ + boolean isOwner(Principal owner); +} diff --git a/libjava/classpath/java/security/acl/Permission.java b/libjava/classpath/java/security/acl/Permission.java new file mode 100644 index 000000000..e5ba29138 --- /dev/null +++ b/libjava/classpath/java/security/acl/Permission.java @@ -0,0 +1,67 @@ +/* Permission.java -- Information about an ACL permission + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.acl; + +/** + * This interface provides information about a permission that can be + * granted. Note that this is not the same as the class + * java.security.Permission. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Permission +{ + /** + * This method tests whether or not a specified Permission + * (passed as an Object) is the same as this permission. + * + * @param perm The permission to check for equality + * + * @return true if the specified permission is the same as this one, false otherwise + */ + boolean equals (Object perm); + + /** + * This method returns this Permission as a String. + * + * @return A String representing this permission. + */ + String toString(); +} diff --git a/libjava/classpath/java/security/acl/package.html b/libjava/classpath/java/security/acl/package.html new file mode 100644 index 000000000..19facf190 --- /dev/null +++ b/libjava/classpath/java/security/acl/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.security.acl + + +

        + + + diff --git a/libjava/classpath/java/security/cert/CRL.java b/libjava/classpath/java/security/cert/CRL.java new file mode 100644 index 000000000..1eaa70fa9 --- /dev/null +++ b/libjava/classpath/java/security/cert/CRL.java @@ -0,0 +1,98 @@ +/* CRL.java --- Certificate Revocation List + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + Certificate Revocation List class for managing CRLs that + have different formats but the same general use. They + all serve as lists of revoked certificates and can + be queried for a given certificate. + + Specialized CRLs extend this class. + + @author Mark Benvenuto + + @since JDK 1.2 +*/ +public abstract class CRL +{ + + private String type; + + /** + Creates a new CRL for the specified type. An example + is "X.509". + + @param type the standard name for the CRL type. + */ + protected CRL(String type) + { + this.type = type; + } + + /** + Returns the CRL type. + + @return a string representing the CRL type + */ + public final String getType() + { + return type; + } + + /** + Returns a string representing the CRL. + + @return a string representing the CRL. + */ + public abstract String toString(); + + /** + Determines whether or not the specified Certificate + is revoked. + + @param cert A certificate to check if it is revoked + + @return true if the certificate is revoked, + false otherwise. + */ + public abstract boolean isRevoked(Certificate cert); + + +} diff --git a/libjava/classpath/java/security/cert/CRLException.java b/libjava/classpath/java/security/cert/CRLException.java new file mode 100644 index 000000000..10171c418 --- /dev/null +++ b/libjava/classpath/java/security/cert/CRLException.java @@ -0,0 +1,95 @@ +/* CRLException.java -- Certificate Revocation List Exception + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.GeneralSecurityException; + +/** + * Exception for a Certificate Revocation List. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.5 +*/ +public class CRLException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -6694728944094197147L; + + /** + * Constructs an CRLExceptionwithout a message string. + */ + public CRLException() + { + } + + /** + * Constructs an CRLException with a message string. + * + * @param msg a message to display with exception + */ + public CRLException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public CRLException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public CRLException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/cert/CRLSelector.java b/libjava/classpath/java/security/cert/CRLSelector.java new file mode 100644 index 000000000..6cd657c7f --- /dev/null +++ b/libjava/classpath/java/security/cert/CRLSelector.java @@ -0,0 +1,69 @@ +/* CRLSelector.java -- matches CRLs against criteria. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * A generic interface to classes that match certificate revocation + * lists (CRLs) to some given criteria. Implementations of this + * interface are useful for finding {@link CRL} objects in a {@link + * CertStore}. + * + * @see CertStore + * @see CertSelector + * @see X509CRLSelector + */ +public interface CRLSelector extends Cloneable +{ + + /** + * Returns a clone of this instance. + * + * @return The clone. + */ + Object clone(); + + /** + * Match a given certificate revocation list to this selector's + * criteria, returning true if it matches, false otherwise. + * + * @param crl The certificate revocation list to test. + * @return The boolean result of this test. + */ + boolean match(CRL crl); +} diff --git a/libjava/classpath/java/security/cert/CertPath.java b/libjava/classpath/java/security/cert/CertPath.java new file mode 100644 index 000000000..7211647a4 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPath.java @@ -0,0 +1,254 @@ +/* CertPath.java -- a sequence of certificates + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.cert; + +import gnu.java.lang.CPStringBuilder; + +import java.io.ByteArrayInputStream; +import java.io.NotSerializableException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.Iterator; +import java.util.List; + +/** + * This class represents an immutable sequence, or path, of security + * certificates. The path type must match the type of each certificate in the + * path, or in other words, for all instances of cert in a certpath object, + * cert.getType().equals(certpath.getType()) will return true. + * + *

        Since this class is immutable, it is thread-safe. During serialization, + * the path is consolidated into a {@link CertPathRep}, which preserves the + * data regardless of the underlying implementation of the path. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class CertPath implements Serializable +{ + /** + * The serialized representation of a path. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + protected static class CertPathRep implements Serializable + { + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 3015633072427920915L; + + /** + * The certificate type. + * + * @serial the type of the certificate path + */ + private final String type; + + /** + * The encoded form of the path. + * + * @serial the encoded form + */ + private final byte[] data; + + /** + * Create the new serial representation. + * + * @param type the path type + * @param data the encoded path data + */ + protected CertPathRep(String type, byte[] data) + { + this.type = type; + this.data = data; + } + + /** + * Decode the data into an actual {@link CertPath} upon deserialization. + * + * @return the replacement object + * @throws ObjectStreamException if replacement fails + */ + protected Object readResolve() throws ObjectStreamException + { + try + { + return CertificateFactory.getInstance(type) + .generateCertPath(new ByteArrayInputStream(data)); + } + catch (CertificateException e) + { + throw (ObjectStreamException) + new NotSerializableException("java.security.cert.CertPath: " + + type).initCause(e); + } + } + } // class CertPathRep + + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 6068470306649138683L; + + /** + * The path type. + * + * @serial the type of all certificates in this path + */ + private final String type; + + /** + * Create a certificate path with the given type. Most code should use + * {@link CertificateFactory} to create CertPaths. + * + * @param type the type of the path + */ + protected CertPath(String type) + { + this.type = type; + } + + /** + * Get the (non-null) type of all certificates in the path. + * + * @return the path certificate type + */ + public String getType() + { + return type; + } + + /** + * Get an immutable iterator over the path encodings (all String names), + * starting with the default encoding. The iterator will throw an + * UnsupportedOperationException if an attempt is made to + * remove items from the list. + * + * @return the iterator of supported encodings in the path + */ + public abstract Iterator getEncodings(); + + /** + * Compares this path to another for semantic equality. To be equal, both + * must be instances of CertPath, with the same type, and identical + * certificate lists. Overriding classes must not change this behavior. + * + * @param o the object to compare to + * @return true if the two are equal + */ + public boolean equals(Object o) + { + if (! (o instanceof CertPath)) + return false; + CertPath cp = (CertPath) o; + return type.equals(cp.type) + && getCertificates().equals(cp.getCertificates()); + } + + /** + * Returns the hashcode of this certificate path. This is defined as:
        + * 31 * getType().hashCode() + getCertificates().hashCode(). + * + * @return the hashcode + */ + public int hashCode() + { + return 31 * type.hashCode() + getCertificates().hashCode(); + } + + public String toString() + { + List l = getCertificates(); + int size = l.size(); + int i = 0; + CPStringBuilder result = new CPStringBuilder(type); + result.append(" Cert Path: length = ").append(size).append(".\n[\n"); + while (--size >= 0) + result.append(l.get(i++)).append('\n'); + return result.append("\n]").toString(); + } + + /** + * Returns the encoded form of this path, via the default encoding. + * + * @return the encoded form + * @throws CertificateEncodingException if encoding fails + */ + public abstract byte[] getEncoded() throws CertificateEncodingException; + + /** + * Returns the encoded form of this path, via the specified encoding. + * + * @param encoding the encoding to use + * @return the encoded form + * @throws CertificateEncodingException if encoding fails or does not exist + */ + public abstract byte[] getEncoded(String encoding) + throws CertificateEncodingException; + + /** + * Returns the immutable, thread-safe list of certificates in this path. + * + * @return the list of certificates, non-null but possibly empty + */ + public abstract List getCertificates(); + + /** + * Serializes the path in its encoded form, to ensure reserialization with + * the appropriate factory object without worrying about list implementation. + * The result will always be an instance of {@link CertPathRep}. + * + * @return the replacement object + * @throws ObjectStreamException if the replacement creation fails + */ + protected Object writeReplace() throws ObjectStreamException + { + try + { + return new CertPathRep(type, getEncoded()); + } + catch (CertificateEncodingException e) + { + throw (ObjectStreamException) + new NotSerializableException("java.security.cert.CertPath: " + + type).initCause(e); + } + } +} // class CertPath diff --git a/libjava/classpath/java/security/cert/CertPathBuilder.java b/libjava/classpath/java/security/cert/CertPathBuilder.java new file mode 100644 index 000000000..47bae6db8 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathBuilder.java @@ -0,0 +1,251 @@ +/* CertPathBuilder.java -- bulids CertPath objects from Certificates. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Engine; + +import java.lang.reflect.InvocationTargetException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.Security; + +/** + * This class builds certificate paths (also called certificate chains), + * which can be used to establish trust for a particular certificate by + * building a path from a trusted certificate (a trust anchor) to the + * untrusted certificate. + * + * @see CertPath + */ +public class CertPathBuilder +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for CertPathBuilder. */ + private static final String CERT_PATH_BUILDER = "CertPathBuilder"; + + /** The underlying implementation. */ + private CertPathBuilderSpi cpbSpi; + + /** The provider of this implementation. */ + private Provider provider; + + /** The name of this implementation. */ + private String algorithm; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Creates a new CertPathBuilder. + * + * @param cpbSpi The underlying implementation. + * @param provider The provider of the implementation. + * @param algorithm This implementation's name. + */ + protected CertPathBuilder(CertPathBuilderSpi cpbSpi, Provider provider, + String algorithm) + { + this.cpbSpi = cpbSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + // Class methods. + // ------------------------------------------------------------------------ + + /** + * Get the default cert path builder type. + * + *

        This value can be set at run-time by the security property + * "certpathbuilder.type". If this property is not set, + * then the value returned is "PKIX". + * + * @return The default CertPathBuilder algorithm. + */ + public static final String getDefaultType() + { + String type = Security.getProperty("certpathbuilder.type"); + if (type == null) + type = "PKIX"; + return type; + } + + /** + * Returns an instance of a named CertPathBuilder from the + * first provider that implements it. + * + * @param algorithm The name of the CertPathBuilder to create. + * @return The new instance. + * @throws NoSuchAlgorithmException If no installed provider implements the + * named algorithm. + * @throws IllegalArgumentException if algorithm is + * null or is an empty string. + */ + public static CertPathBuilder 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * Returns an instance of a named CertPathBuilder from a named + * provider. + * + * @param algorithm The name of the CertPathBuilder 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 IllegalArgumentException if either algorithm or + * provider is null, or if + * algorithm is an empty string. + */ + public static CertPathBuilder 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); + } + + /** + * Returns an instance of a named CertPathBuilder from the + * specified provider. + * + * @param algorithm The name of the CertPathBuilder 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 either algorithm or + * provider is null, or if + * algorithm is an empty string. + */ + public static CertPathBuilder getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("CertPathBuilder for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; + try + { + Object spi = Engine.getInstance(CERT_PATH_BUILDER, algorithm, provider); + return new CertPathBuilder((CertPathBuilderSpi) spi, provider, algorithm); + } + catch (InvocationTargetException x) + { + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + } + catch (ClassCastException x) + { + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; + } + + /** + * Return the name of this CertPathBuilder algorithm. + * + * @return The algorithm name. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Return the provider of this instance's implementation. + * + * @return The provider. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Builds a certificate path. The {@link CertPathParameters} parameter + * passed to this method is implementation-specific, but in general + * should contain some number of certificates and some number of + * trusted certificates (or "trust anchors"). + * + * @param params The parameters. + * @retrun The certificate path result. + * @throws CertPathBuilderException If the certificate path cannot be + * built. + * @throws InvalidAlgorithmParameterException If the implementation + * rejects the specified parameters. + */ + public final CertPathBuilderResult build(CertPathParameters params) + throws CertPathBuilderException, InvalidAlgorithmParameterException + { + return cpbSpi.engineBuild(params); + } +} diff --git a/libjava/classpath/java/security/cert/CertPathBuilderException.java b/libjava/classpath/java/security/cert/CertPathBuilderException.java new file mode 100644 index 000000000..985151010 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathBuilderException.java @@ -0,0 +1,159 @@ +/* CertPathBuilderException.java -- wraps an exception during certificate + path building + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.GeneralSecurityException; + +/** + * Indicates a problem while using a CertPathBuilder, wrapping + * the lower exception. This class is not thread-safe. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see CertPathBuilder + * @since 1.4 + * @status updated to 1.4 +*/ +public class CertPathBuilderException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 5316471420178794402L; + + /** + * Create an exception without a message. The cause may be initialized. + */ + public CertPathBuilderException() + { + } + + /** + * Create an exception with a message. The cause may be initialized. + * + * @param msg a message to display with exception + */ + public CertPathBuilderException(String msg) + { + super(msg); + } + + /** + * Create an exception with a cause. The message will be + * cause == null ? null : cause.toString(). + * + * @param cause the cause + */ + public CertPathBuilderException(Throwable cause) + { + this(cause == null ? null : cause.toString(), cause); + } + + /** + * Create an exception with a cause and a message. + * + * @param msg the message + * @param cause the cause + */ + public CertPathBuilderException(String msg, Throwable cause) + { + super(msg); + initCause(cause); + } + + /** + * Get the detail message. + * + * @return the detail message + */ + public String getMessage() + { + return super.getMessage(); + } + + /** + * Get the cause, null if unknown. + * + * @return the cause + */ + public Throwable getCause() + { + return super.getCause(); + } + + /** + * Convert this to a string, including its cause. + * + * @return the string conversion + */ + public String toString() + { + return super.toString(); + } + + /** + * Print the stack trace to System.err. + */ + public void printStackTrace() + { + super.printStackTrace(); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintWriter stream) + { + super.printStackTrace(stream); + } +} diff --git a/libjava/classpath/java/security/cert/CertPathBuilderResult.java b/libjava/classpath/java/security/cert/CertPathBuilderResult.java new file mode 100644 index 000000000..edae88f64 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathBuilderResult.java @@ -0,0 +1,63 @@ +/* CertPathBuilderResult -- results from building cert paths. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * A standard interface for the result of building a certificate path. + * All implementations of this class must provide a way to get the + * certificate path, but may also define additional methods for + * returning other result data generated by the certificate path + * builder. + */ +public interface CertPathBuilderResult extends Cloneable { + + /** + * Creates a copy of this builder result. + * + * @return The copy. + */ + Object clone(); + + /** + * Get the certificate path that was built. + * + * @retrn The certificate path. + */ + CertPath getCertPath(); +} diff --git a/libjava/classpath/java/security/cert/CertPathBuilderSpi.java b/libjava/classpath/java/security/cert/CertPathBuilderSpi.java new file mode 100644 index 000000000..afc7fc073 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathBuilderSpi.java @@ -0,0 +1,74 @@ +/* CertPathBuilderSpi -- CertPathBuilder service provider interface. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.cert; + +/** + * The {@link CertPathBuilder} Service Provider Interface + * (SPI). + * + * @see CertPathBuilder + */ +public abstract class CertPathBuilderSpi { + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Creates a new CertPathBuilderSpi. + */ + public CertPathBuilderSpi() { + super(); + } + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + * Creates a certificate path from the specified parameters. + * + * @param params The parameters to use. + * @return The certificate path result. + * @throws CertPathBuilderException If the certificate path cannot be + * built. + * @throws java.security.InvalidAlgorithmParameterException If the + * implementation rejects the specified parameters. + */ + public abstract CertPathBuilderResult engineBuild(CertPathParameters params) + throws CertPathBuilderException, + java.security.InvalidAlgorithmParameterException; +} diff --git a/libjava/classpath/java/security/cert/CertPathParameters.java b/libjava/classpath/java/security/cert/CertPathParameters.java new file mode 100644 index 000000000..62a5cb6a6 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathParameters.java @@ -0,0 +1,58 @@ +/* CertPathParameters.java -- parameters for CertPathBuilder. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.cert; + +/** + * Parameters for generating and validating certificate paths. This + * class does not define any methods (except a required cloneable + * interface) and is provided only to provide type safety for + * implementations. Concrete implementations implement this interface + * in accord with thier own needs. + * + * @see CertPathBuilder + * @see CertPathValidator + */ +public interface CertPathParameters extends Cloneable { + + /** + * Makes a copy of this CertPathParameters instance. + * + * @return The copy. + */ + Object clone(); +} diff --git a/libjava/classpath/java/security/cert/CertPathValidator.java b/libjava/classpath/java/security/cert/CertPathValidator.java new file mode 100644 index 000000000..8bd7b58e8 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathValidator.java @@ -0,0 +1,264 @@ +/* CertPathValidator -- validates certificate paths. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Engine; + +import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.Security; + +/** + * Generic interface to classes that validate certificate paths. + * + *

        Using this class is similar to all the provider-based security + * classes; the method of interest, {@link + * #validate(java.security.cert.CertPath,java.security.cert.CertPathParameters)}, + * which takes provider-specific implementations of {@link + * CertPathParameters}, and return provider-specific implementations of + * {@link CertPathValidatorResult}. + * + * @since JDK 1.4 + * @see CertPath + */ +public class CertPathValidator { + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for CertPathValidator. */ + private static final String CERT_PATH_VALIDATOR = "CertPathValidator"; + + /** The underlying implementation. */ + private final CertPathValidatorSpi validatorSpi; + + /** The provider of this implementation. */ + private final Provider provider; + + /** The algorithm's name. */ + private final String algorithm; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Creates a new CertPathValidator. + * + * @param validatorSpi The underlying implementation. + * @param provider The provider of the implementation. + * @param algorithm The algorithm name. + */ + protected CertPathValidator(CertPathValidatorSpi validatorSpi, + Provider provider, String algorithm) + { + this.validatorSpi = validatorSpi; + this.provider = provider; + this.algorithm = algorithm; + } + + // Class methods. + // ------------------------------------------------------------------------ + + /** + * Returns the default validator type. + * + *

        This value may be set at run-time via the security property + * "certpathvalidator.type", or the value "PKIX" if this property is + * not set. + * + * @return The default validator type. + */ + public static synchronized String getDefaultType() { + String type = (String) AccessController.doPrivileged( + new PrivilegedAction() + { + public Object run() + { + return Security.getProperty("certpathvalidator.type"); + } + } + ); + if (type == null) + type = "PKIX"; + return type; + } + + /** + * 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 IllegalArgumentException if algorithm is + * null 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(algorithm); + } + + /** + * 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. + * @return The new instance. + * @throws NoSuchAlgorithmException If the named provider does not implement + * the algorithm. + * @throws NoSuchProviderException If no provider named provider is + * installed. + * @throws IllegalArgumentException if either algorithm or + * provider is null, or if + * algorithm is an empty string. + */ + 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); + } + + /** + * 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. + * @return The new instance. + * @throws NoSuchAlgorithmException If the provider does not implement the + * algorithm. + * @throws IllegalArgumentException if either algorithm or + * provider is null, or if + * algorithm is an empty string. + */ + public static CertPathValidator getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("CertPathValidator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; + try + { + Object spi = Engine.getInstance(CERT_PATH_VALIDATOR, algorithm, provider); + return new CertPathValidator((CertPathValidatorSpi) spi, provider, algorithm); + } + catch (InvocationTargetException x) + { + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + } + catch (ClassCastException x) + { + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; + } + + /** + * Return the name of this validator. + * + * @return This validator's name. + */ + public final String getAlgorithm() + { + return algorithm; + } + + /** + * Return the provider of this implementation. + * + * @return The provider. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Attempt to validate a certificate path. + * + * @param certPath The path to validate. + * @param params The algorithm-specific parameters. + * @return The result of this validation attempt. + * @throws CertPathValidatorException If the certificate path cannot + * be validated. + * @throws InvalidAlgorithmParameterException If this implementation + * rejects the specified parameters. + */ + public final CertPathValidatorResult validate(CertPath certPath, + CertPathParameters params) + throws CertPathValidatorException, InvalidAlgorithmParameterException + { + return validatorSpi.engineValidate(certPath, params); + } +} diff --git a/libjava/classpath/java/security/cert/CertPathValidatorException.java b/libjava/classpath/java/security/cert/CertPathValidatorException.java new file mode 100644 index 000000000..f3195be29 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathValidatorException.java @@ -0,0 +1,226 @@ +/* CertPathValidatorException.java -- wraps an exception during validation + of a CertPath + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.GeneralSecurityException; + +/** + * Indicates a problem while validating a certification path. In addition, + * it can store the path an index in that path that caused the problem. This + * class is not thread-safe. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see CertPathValidator + * @since 1.4 + * @status updated to 1.4 +*/ +public class CertPathValidatorException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = -3083180014971893139L; + + /** + * The index of the certificate path that failed, or -1. + * + * @serial the failed index + */ + private final int index; + + /** + * The CertPath that failed. + * + * @serial the object being validated at time of failure + */ + private final CertPath certPath; + + /** + * Create an exception without a message. The cause may be initialized. The + * index is set to -1 and the failed CertPath object to null. + */ + public CertPathValidatorException() + { + this((String) null); + } + + /** + * Create an exception with a message. The cause may be initialized. The + * index is set to -1 and the failed CertPath object to null. + * + * @param msg a message to display with exception + */ + public CertPathValidatorException(String msg) + { + super(msg); + index = -1; + certPath = null; + } + + /** + * Create an exception with a cause. The message will be + * cause == null ? null : cause.toString(). The index is set + * to -1 and the failed CertPath object to null. + * + * @param cause the cause + */ + public CertPathValidatorException(Throwable cause) + { + this(cause == null ? null : cause.toString(), cause, null, -1); + } + + /** + * Create an exception with a cause and a message. The index is set to -1 + * and the failed CertPath object to null. + * + * @param msg the message + * @param cause the cause + */ + public CertPathValidatorException(String msg, Throwable cause) + { + this(msg, cause, null, -1); + } + + /** + * Create an exception with a cause, message, failed object, and index of + * failure in that CertPath. + * + * @param msg the message + * @param cause the cause + * @param certPath the path that was being validated, or null + * @param index the index of the path, or -1 + * @throws IndexOutOfBoundsException if index is < -1 or + * > certPath.getCertificates().size() + * @throws IllegalArgumentException if certPath is null but index != -1 + */ + public CertPathValidatorException(String msg, Throwable cause, + CertPath certPath, int index) + { + super(msg); + initCause(cause); + if (index < -1 || (certPath != null + && index >= certPath.getCertificates().size())) + throw new IndexOutOfBoundsException(); + if ((certPath == null) != (index == -1)) + throw new IllegalArgumentException(); + this.certPath = certPath; + this.index = index; + } + + /** + * Get the detail message. + * + * @return the detail message + */ + public String getMessage() + { + return super.getMessage(); + } + + /** + * Get the certificate path that had the failure, or null. + * + * @return the culprit path + */ + public CertPath getCertPath() + { + return certPath; + } + + /** + * Get the index that failed, or -1. + * + * @return the colprit index + */ + public int getIndex() + { + return index; + } + + /** + * Get the cause, null if unknown. + * + * @return the cause + */ + public Throwable getCause() + { + return super.getCause(); + } + + /** + * Convert this to a string, including its cause. + * + * @return the string conversion + */ + public String toString() + { + return super.toString(); + } + + /** + * Print the stack trace to System.err. + */ + public void printStackTrace() + { + super.printStackTrace(); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintWriter stream) + { + super.printStackTrace(stream); + } +} diff --git a/libjava/classpath/java/security/cert/CertPathValidatorResult.java b/libjava/classpath/java/security/cert/CertPathValidatorResult.java new file mode 100644 index 000000000..0ccd1be78 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathValidatorResult.java @@ -0,0 +1,63 @@ +/* CertPathValidatorResult -- result of validating certificate paths + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Interface to the result of calling {@link + * CertPathValidator#validate(java.security.cert.CertPath,java.security.cert.CertPathParameters)}. + * + *

        This interface defines no methods other than the required + * {@link java.lang.Cloneable} interface, and is intended to group and + * provide type safety for validator results. Providers that implement + * a certificate path validator must also provide an implementation of + * this interface, possibly defining additional methods. + * + * @since JDK 1.4 + * @see CertPathValidator + */ +public interface CertPathValidatorResult extends Cloneable +{ + + /** + * Returns a copy of this validator result. + * + * @return The copy. + */ + Object clone(); +} diff --git a/libjava/classpath/java/security/cert/CertPathValidatorSpi.java b/libjava/classpath/java/security/cert/CertPathValidatorSpi.java new file mode 100644 index 000000000..d4531e716 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertPathValidatorSpi.java @@ -0,0 +1,81 @@ +/* CertPathValidatorSpi -- cert path validator service provider interface + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.InvalidAlgorithmParameterException; + +/** + * The service provider interface (SPI) for the {@link + * CertPathValidator} class. Providers implementing certificate path + * validators must subclass this class and implement its abstract + * methods. + */ +public abstract class CertPathValidatorSpi +{ + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Default constructor. + */ + public CertPathValidatorSpi() + { + super(); + } + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + * Attempt to validate a certificate path. + * + * @param certPath The path to validate. + * @param params The algorithm-specific parameters. + * @return The result of this validation attempt. + * @throws CertPathValidatorException If the certificate path cannot + * be validated. + * @throws InvalidAlgorithmParameterException If this implementation + * rejects the specified parameters. + */ + public abstract CertPathValidatorResult + engineValidate(CertPath certPath, CertPathParameters params) + throws CertPathValidatorException, + InvalidAlgorithmParameterException; +} diff --git a/libjava/classpath/java/security/cert/CertSelector.java b/libjava/classpath/java/security/cert/CertSelector.java new file mode 100644 index 000000000..4a2e7d921 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertSelector.java @@ -0,0 +1,58 @@ +/* CertSelector.java -- certificate selector interface. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +public interface CertSelector extends Cloneable +{ + + /** + * Returns a copy of this CertSelector. + * + * @return The copy. + */ + Object clone(); + + /** + * Match a certificate according to this selector's criteria. + * + * @param cert The certificate to match. + * @return true if the certificate matches thin criteria. + */ + boolean match(Certificate cert); +} diff --git a/libjava/classpath/java/security/cert/CertStore.java b/libjava/classpath/java/security/cert/CertStore.java new file mode 100644 index 000000000..630e96762 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertStore.java @@ -0,0 +1,305 @@ +/* CertStore -- stores and retrieves certificates. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.security.Engine; + +import java.lang.reflect.InvocationTargetException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.Security; +import java.util.Collection; + +/** + * A CertStore is a read-only repository for certificates and + * certificate revocation lists. + * + * @since 1.4 + */ +public class CertStore +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** Service name for CertStore. */ + private static final String CERT_STORE = "CertStore"; + + /** The underlying implementation. */ + private CertStoreSpi storeSpi; + + /** This implementation's provider. */ + private Provider provider; + + /** The name of this key store type. */ + private String type; + + /** The parameters used to initialize this instance, if any. */ + private CertStoreParameters params; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Create a new CertStore. + * + * @param storeSpi The underlying implementation. + * @param provider The provider of this implementation. + * @param type The type of CertStore this class represents. + * @param params The parameters used to initialize this instance, if any. + */ + protected CertStore(CertStoreSpi storeSpi, Provider provider, String type, + CertStoreParameters params) + { + this.storeSpi = storeSpi; + this.provider = provider; + this.type = type; + this.params = params; + } + + // Class methods. + // ------------------------------------------------------------------------ + + /** + * Returns the default certificate store type. + * + *

        This value can be set at run-time via the security property + * "certstore.type"; if not specified than the default type will be + * "LDAP". + * + * @return The default CertStore type. + */ + public static final synchronized String getDefaultType() + { + String type = null; + type = (String) java.security.AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return Security.getProperty("certstore.type"); + } + } + ); + if (type == null) + type = "LDAP"; + return type; + } + + /** + * 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. + * @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 type is + * null 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(type); + } + + /** + * 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 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 NoSuchProviderException If no provider named provider is + * installed. + * @throws IllegalArgumentException if either type or + * provider is null, or if + * type 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); + return getInstance(type, params, p); + } + + /** + * Returns an instance of the given certificate store type from a given + * provider. + * + * @param type The type of CertStore to create. + * @param params The parameters to initialize this cert store with. + * @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 either type or + * provider is null, or if + * type is an empty string. + */ + public static CertStore getInstance(String type, CertStoreParameters params, + Provider provider) + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException + { + CPStringBuilder sb = new CPStringBuilder("CertStore of type [") + .append(type).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; + try + { + Object[] args = new Object[] { params }; + Object spi = Engine.getInstance(CERT_STORE, type, provider, args); + return new CertStore((CertStoreSpi) spi, provider, type, params); + } + catch (InvocationTargetException x) + { + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + } + catch (ClassCastException x) + { + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; + } + + /** + * Return the type of certificate store this instance represents. + * + * @return The CertStore type. + */ + public final String getType() + { + return type; + } + + /** + * Return the provider of this implementation. + * + * @return The provider. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Get the parameters this instance was created with, if any. The + * parameters will be cloned before they are returned. + * + * @return The parameters, or null. + */ + public final CertStoreParameters getCertStoreParameters() + { + return params != null ? (CertStoreParameters) params.clone() : null; + } + + /** + * Get a collection of certificates from this CertStore, optionally + * filtered by the specified CertSelector. The Collection returned may + * be empty, but will never be null. + * + *

        Implementations may not allow a null argument, even if no + * filtering is desired. + * + * @param selector The certificate selector. + * @return The collection of certificates. + * @throws CertStoreException If the certificates cannot be retrieved. + */ + public final Collection getCertificates(CertSelector selector) + throws CertStoreException + { + return storeSpi.engineGetCertificates(selector); + } + + /** + * Get a collection of certificate revocation lists from this CertStore, + * optionally filtered by the specified CRLSelector. The Collection + * returned may be empty, but will never be null. + * + *

        Implementations may not allow a null argument, even if no + * filtering is desired. + * + * @param selector The certificate selector. + * @return The collection of certificate revocation lists. + * @throws CertStoreException If the CRLs cannot be retrieved. + */ + public final Collection getCRLs(CRLSelector selector) + throws CertStoreException + { + return storeSpi.engineGetCRLs(selector); + } +} diff --git a/libjava/classpath/java/security/cert/CertStoreException.java b/libjava/classpath/java/security/cert/CertStoreException.java new file mode 100644 index 000000000..a4d8b7a46 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertStoreException.java @@ -0,0 +1,159 @@ +/* CertStoreException.java -- wraps an exception during certificate storage + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.security.GeneralSecurityException; + +/** + * Indicates a problem while retrieving certificates and CRLs from + * CertStore, wrapping the lower exception. This class is not + * thread-safe. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see CertStore + * @since 1.4 + * @status updated to 1.4 +*/ +public class CertStoreException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.4+. + */ + private static final long serialVersionUID = 2395296107471573245L; + + /** + * Create an exception without a message. The cause may be initialized. + */ + public CertStoreException() + { + } + + /** + * Create an exception with a message. The cause may be initialized. + * + * @param msg a message to display with exception + */ + public CertStoreException(String msg) + { + super(msg); + } + + /** + * Create an exception with a cause. The message will be + * cause == null ? null : cause.toString(). + * + * @param cause the cause + */ + public CertStoreException(Throwable cause) + { + this(cause == null ? null : cause.toString(), cause); + } + + /** + * Create an exception with a cause and a message. + * + * @param msg the message + * @param cause the cause + */ + public CertStoreException(String msg, Throwable cause) + { + super(msg); + initCause(cause); + } + + /** + * Get the detail message. + * + * @return the detail message + */ + public String getMessage() + { + return super.getMessage(); + } + + /** + * Get the cause, null if unknown. + * + * @return the cause + */ + public Throwable getCause() + { + return super.getCause(); + } + + /** + * Convert this to a string, including its cause. + * + * @return the string conversion + */ + public String toString() + { + return super.toString(); + } + + /** + * Print the stack trace to System.err. + */ + public void printStackTrace() + { + super.printStackTrace(); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + } + + /** + * Print the stack trace to a stream. + * + * @param stream the stream + */ + public void printStackTrace(PrintWriter stream) + { + super.printStackTrace(stream); + } +} diff --git a/libjava/classpath/java/security/cert/CertStoreParameters.java b/libjava/classpath/java/security/cert/CertStoreParameters.java new file mode 100644 index 000000000..71bcd6109 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertStoreParameters.java @@ -0,0 +1,60 @@ +/* CertStoreParameters -- interface to CertStore parameters. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Parameters used when creating instances of {@link CertStore}. This + * class does not define any methods (except a required cloneable + * interface) and is provided only to provide type safety for + * implementations. Concrete implementations implement this interface + * in accord with thier own needs. + * + * @see LDAPCertStoreParameters + * @see CollectionCertStoreParameters + */ +public interface CertStoreParameters extends Cloneable +{ + + /** + * Create a copy of these parameters. + * + * @return The copy. + */ + Object clone(); +} diff --git a/libjava/classpath/java/security/cert/CertStoreSpi.java b/libjava/classpath/java/security/cert/CertStoreSpi.java new file mode 100644 index 000000000..a47978a22 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertStoreSpi.java @@ -0,0 +1,103 @@ +/* CertStoreSpi -- certificate store service provider interface. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.InvalidAlgorithmParameterException; +import java.util.Collection; + +/** + * The service provider interface (SPI) for the {@link + * CertStore} class. + * + *

        Providers wishing to implement a CertStore must subclass this + * class, implementing all the abstract methods. Providers may also + * implement the {@link CertStoreParameters} interface, if they require + * parameters. + * + * @since 1.4 + * @see CertStore + * @see CollectionCertStoreParameters + * @see LDAPCertStoreParameters + */ +public abstract class CertStoreSpi +{ + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Creates a new CertStoreSpi. + * + * @param params The parameters to initialize this instance with, or + * null if no parameters are required. + * @throws InvalidAlgorithmParameterException If the specified + * parameters are inappropriate for this class. + */ + public CertStoreSpi(CertStoreParameters params) + throws InvalidAlgorithmParameterException + { + super(); + } + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + * Get the certificates from this store, filtering them through the + * specified CertSelector. + * + * @param selector The CertSelector to filter certificates. + * @return A (non-null) collection of certificates. + * @throws CertStoreException If the certificates cannot be retrieved. + */ + public abstract Collection engineGetCertificates(CertSelector selector) + throws CertStoreException; + + /** + * Get the certificate revocation list from this store, filtering them + * through the specified CRLSelector. + * + * @param selector The CRLSelector to filter certificate revocation + * lists. + * @return A (non-null) collection of certificate revocation list. + * @throws CertStoreException If the CRLs cannot be retrieved. + */ + public abstract Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException; +} diff --git a/libjava/classpath/java/security/cert/Certificate.java b/libjava/classpath/java/security/cert/Certificate.java new file mode 100644 index 000000000..be1713cbf --- /dev/null +++ b/libjava/classpath/java/security/cert/Certificate.java @@ -0,0 +1,306 @@ +/* Certificate.java --- Certificate class + Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.ByteArrayInputStream; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.SignatureException; + +/** + * The Certificate class is an abstract class used to manage + * identity certificates. An identity certificate is a + * combination of a principal and a public key which is + * certified by another principal. This is the puprose of + * Certificate Authorities (CA). + * + *

        This class is used to manage different types of certificates + * but have important common puposes. Different types of + * certificates like X.509 and OpenPGP share general certificate + * functions (like encoding and verifying) and information like + * public keys. + * + *

        X.509, OpenPGP, and SDSI can be implemented by subclassing this + * class even though they differ in storage methods and information + * stored. + * + * @see CertificateFactory + * @see X509Certificate + * @since JDK 1.2 + * @author Mark Benvenuto + * @author Casey Marshall + */ +public abstract class Certificate implements Serializable +{ + private static final long serialVersionUID = -3585440601605666277L; + + private String type; + + /** + Constructs a new certificate of the specified type. An example + is "X.509". + + @param type a valid standard name for a certificate. + */ + protected Certificate(String type) + { + this.type = type; + } + + /** + Returns the Certificate type. + + @return a string representing the Certificate type + */ + public final String getType() + { + return type; + } + + /** + Compares this Certificate to other. It checks if the + object if instanceOf Certificate and then checks if + the encoded form matches. + + @param other An Object to test for equality + + @return true if equal, false otherwise + */ + public boolean equals(Object other) + { + if( other instanceof Certificate ) { + try { + Certificate x = (Certificate) other; + if( getEncoded().length != x.getEncoded().length ) + return false; + + byte[] b1 = getEncoded(); + byte[] b2 = x.getEncoded(); + + for( int i = 0; i < b1.length; i++ ) + if( b1[i] != b2[i] ) + return false; + + } catch( CertificateEncodingException cee ) { + return false; + } + return true; + } + return false; + } + + /** + Returns a hash code for this Certificate in its encoded + form. + + @return A hash code of this class + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + Gets the DER ASN.1 encoded format for this Certificate. + It assumes each certificate has only one encoding format. + Ex: X.509 is encoded as ASN.1 DER + + @return byte array containg encoded form + + @throws CertificateEncodingException if an error occurs + */ + public abstract byte[] getEncoded() throws CertificateEncodingException; + + /** + Verifies that this Certificate was properly signed with the + PublicKey that corresponds to its private key. + + @param key PublicKey to verify with + + @throws CertificateException encoding error + @throws NoSuchAlgorithmException unsupported algorithm + @throws InvalidKeyException incorrect key + @throws NoSuchProviderException no provider + @throws SignatureException signature error + */ + public abstract void verify(PublicKey key) + throws CertificateException, + NoSuchAlgorithmException, + InvalidKeyException, + NoSuchProviderException, + SignatureException; + + /** + Verifies that this Certificate was properly signed with the + PublicKey that corresponds to its private key and uses + the signature engine provided by the provider. + + @param key PublicKey to verify with + @param sigProvider Provider to use for signature algorithm + + @throws CertificateException encoding error + @throws NoSuchAlgorithmException unsupported algorithm + @throws InvalidKeyException incorrect key + @throws NoSuchProviderException incorrect provider + @throws SignatureException signature error + */ + public abstract void verify(PublicKey key, + String sigProvider) + throws CertificateException, + NoSuchAlgorithmException, + InvalidKeyException, + NoSuchProviderException, + SignatureException; + + /** + Returns a string representing the Certificate. + + @return a string representing the Certificate. + */ + public abstract String toString(); + + + /** + Returns the public key stored in the Certificate. + + @return The public key + */ + public abstract PublicKey getPublicKey(); + + // Protected methods. + // ------------------------------------------------------------------------ + + /** + * Returns a replacement for this certificate to be serialized. This + * method returns the equivalent to the following for this class: + * + *

        + *
        new CertificateRep(getType(), getEncoded());
        + *
        + * + *

        This thusly replaces the certificate with its name and its + * encoded form, which can be deserialized later with the {@link + * CertificateFactory} implementation for this certificate's type. + * + * @return The replacement object to be serialized. + * @throws ObjectStreamException If the replacement could not be + * created. + */ + protected Object writeReplace() throws ObjectStreamException + { + try + { + return new CertificateRep(getType(), getEncoded()); + } + catch (CertificateEncodingException cee) + { + throw new InvalidObjectException(cee.toString()); + } + } + + // Inner class. + // ------------------------------------------------------------------------ + + /** + Certificate.CertificateRep is an inner class used to provide an alternate + storage mechanism for serialized Certificates. + */ + protected static class CertificateRep implements java.io.Serializable + { + + /** From JDK1.4. */ + private static final long serialVersionUID = -8563758940495660020L; + + /** The certificate type, e.g. "X.509". */ + private String type; + + /** The encoded certificate data. */ + private byte[] data; + + /** + * Create an alternative representation of this certificate. The + * (type, data) pair is typically the certificate's + * type as returned by {@link Certificate#getType()} (i.e. the + * canonical name of the certificate type) and the encoded form as + * returned by {@link Certificate#getEncoded()}. + * + *

        For example, X.509 certificates would create an instance of + * this class with the parameters "X.509" and the ASN.1 + * representation of the certificate, encoded as DER bytes. + * + * @param type The certificate type. + * @param data The encoded certificate data. + */ + protected CertificateRep(String type, byte[] data) + { + this.type = type; + this.data = data; + } + + /** + * Deserialize this certificate replacement into the appropriate + * certificate object. That is, this method attempts to create a + * {@link CertificateFactory} for this certificate's type, then + * attempts to parse the encoded data with that factory, returning + * the resulting certificate. + * + * @return The deserialized certificate. + * @throws ObjectStreamException If there is no appropriate + * certificate factory for the given type, or if the encoded form + * cannot be parsed. + */ + protected Object readResolve() throws ObjectStreamException + { + try + { + CertificateFactory fact = CertificateFactory.getInstance(type); + return fact.generateCertificate(new ByteArrayInputStream(data)); + } + catch (Exception e) + { + throw new InvalidObjectException(e.toString()); + } + } + } +} diff --git a/libjava/classpath/java/security/cert/CertificateEncodingException.java b/libjava/classpath/java/security/cert/CertificateEncodingException.java new file mode 100644 index 000000000..3f871691d --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateEncodingException.java @@ -0,0 +1,93 @@ +/* CertificateEncodingException.java -- Certificate Encoding Exception + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Exception for a Certificate Encoding. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.5 + */ +public class CertificateEncodingException extends CertificateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 6219492851589449162L; + + /** + * Constructs an exception without a message string. + */ + public CertificateEncodingException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg A message to display with exception + */ + public CertificateEncodingException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public CertificateEncodingException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public CertificateEncodingException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateException.java b/libjava/classpath/java/security/cert/CertificateException.java new file mode 100644 index 000000000..8a6f383bb --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateException.java @@ -0,0 +1,96 @@ +/* CertificateException.java -- Certificate Exception + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.GeneralSecurityException; + +/** + * Exception for a Certificate. + * + * @author Mark Benvenuto + * @see Certificate + * @since 1.2 + * @status updated to 1.5 + */ +public class CertificateException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 3192535253797119798L; + + /** + * Constructs an exception without a message string. + */ + public CertificateException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg a message to display with exception + */ + public CertificateException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public CertificateException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public CertificateException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateExpiredException.java b/libjava/classpath/java/security/cert/CertificateExpiredException.java new file mode 100644 index 000000000..5b37142b5 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateExpiredException.java @@ -0,0 +1,71 @@ +/* CertificateExpiredException.java --- Certificate Expired Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Exception for a Certificate Expiring. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.4 + */ +public class CertificateExpiredException extends CertificateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 9071001339691533771L; + + /** + * Constructs an exception without a message string. + */ + public CertificateExpiredException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg a message to display with exception + */ + public CertificateExpiredException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateFactory.java b/libjava/classpath/java/security/cert/CertificateFactory.java new file mode 100644 index 000000000..4fd5b3965 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateFactory.java @@ -0,0 +1,355 @@ +/* CertificateFactory.java -- Certificate Factory Class + Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.security.Engine; + +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.Security; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * This class implements the CertificateFactory class interface used to + * generate certificates, certificate revocation lists (CRLs), and certificate + * paths objects from their encoded forms. + * + * @author Mark Benvenuto + * @author Casey Marshall + * @since 1.2 + * @status Fully compatible with JDK 1.4. + */ +public class CertificateFactory +{ + + /** The service name for certificate factories. */ + private static final String CERTIFICATE_FACTORY = "CertificateFactory"; + + private CertificateFactorySpi certFacSpi; + private Provider provider; + private String type; + + /** + * Creates an instance of CertificateFactory. + * + * @param certFacSpi The underlying CertificateFactory engine. + * @param provider The provider of this implementation. + * @param type The type of Certificate this factory creates. + */ + protected CertificateFactory(CertificateFactorySpi certFacSpi, + Provider provider, String type) + { + this.certFacSpi = certFacSpi; + this.provider = provider; + this.type = type; + } + + /** + * Returns an instance of a CertificateFactory representing the + * specified certificate factory type. + * + * @param type The type of certificate factory to create. + * @return A CertificateFactory of the desired type. + * @throws CertificateException If the type of certificate factory is not + * implemented by any installed provider. + * @throws IllegalArgumentException if type is + * null or is an empty string. + */ + public static final CertificateFactory getInstance(String type) + 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 x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new CertificateException(type); + } + + /** + * Returns an instance of a CertificateFactory 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 CertificateFactory 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 type or + * provider is null, or if + * type 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) + throw new NoSuchProviderException(provider); + return getInstance(type, p); + } + + /** + * Returns an instance of a CertificateFactory 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 either type or + * provider is null, or if + * type is an empty string. + */ + public static final CertificateFactory getInstance(String type, + Provider provider) + throws CertificateException + { + Throwable cause; + try + { + Object spi = Engine.getInstance(CERTIFICATE_FACTORY, type, provider); + return new CertificateFactory((CertificateFactorySpi) spi, provider, type); + } + catch (ClassCastException x) + { + cause = x; + } + catch (InvocationTargetException x) + { + cause = x.getCause() != null ? x.getCause() : x; + } + catch (NoSuchAlgorithmException x) + { + cause = x; + } + CertificateException x = new CertificateException(type); + x.initCause(cause); + throw x; + } + + /** + * Gets the provider of this implementation. + * + * @return The provider of this implementation. + */ + public final Provider getProvider() + { + return provider; + } + + /** + * Returns the type of the certificate this factory creates. + * + * @return A string with the type of certificate + */ + public final String getType() + { + return type; + } + + /** + * Generates a Certificate from the encoded data read + * from an InputStream. + * + *

        The input stream must contain only one certificate. + * + *

        If there exists a specialized certificate class for the + * certificate format handled by the certificate factory + * then the return Ceritificate should be a typecast of it. + * Ex: A X.509 CertificateFactory should return X509Certificate. + * + *

        For X.509 certificates, the certificate in inStream must be + * DER encoded and supplied in binary or printable (Base64) + * encoding. If the certificate is in Base64 encoding, it must be + * bounded by -----BEGINCERTIFICATE-----, and + * -----END CERTIFICATE-----. + * + * @param inStream An input stream containing the certificate data. + * @return A certificate initialized from the decoded InputStream data. + * @throws CertificateException If an error occurs decoding the + * certificate. + */ + public final Certificate generateCertificate(InputStream inStream) + throws CertificateException + { + return certFacSpi.engineGenerateCertificate(inStream); + } + + /** + * Returns a collection of certificates that were read from the + * input stream. It may be empty, have only one, or have + * multiple certificates. + * + * For a X.509 certificate factory, the stream may contain a + * single DER encoded certificate or a PKCS#7 certificate + * chain. This is a PKCS#7 SignedData object with the + * most significant field being certificates. If no + * CRLs are present, then an empty collection is returned. + * + * @param inStream An input stream containing the certificate data. + * @return A collection of certificates initialized from the decoded + * InputStream data. + * @throws CertificateException If an error occurs decoding the + * certificates. + */ + public final Collection generateCertificates(InputStream inStream) + throws CertificateException + { + return certFacSpi.engineGenerateCertificates(inStream); + } + + /** + * Generates a CRL based on the encoded data read + * from the InputStream. + * + *

        The input stream must contain only one CRL. + * + *

        If there exists a specialized CRL class for the + * CRL format handled by the certificate factory + * then the return CRL should be a typecast of it. + * Ex: A X.509 CertificateFactory should return X509CRL. + * + * @param inStream An input stream containing the CRL data. + * @return A CRL initialized from the decoded InputStream data. + * @throws CRLException If an error occurs decoding the CRL. + */ + public final CRL generateCRL(InputStream inStream) + throws CRLException + { + return certFacSpi.engineGenerateCRL(inStream); + } + + /** + *

        Generates CRLs based on the encoded data read + * from the InputStream. + * + *

        For a X.509 certificate factory, the stream may contain a + * single DER encoded CRL or a PKCS#7 CRL set. This is a + * PKCS#7 SignedData object with the most significant + * field being crls. If no CRLs are present, then an + * empty collection is returned. + * + * @param inStream an input stream containing the CRLs. + * @return a collection of CRLs initialized from the decoded + * InputStream data. + * @throws CRLException If an error occurs decoding the CRLs. + */ + public final Collection generateCRLs(InputStream inStream) + throws CRLException + { + return certFacSpi.engineGenerateCRLs( inStream ); + } + + /** + * Generate a {@link CertPath} and initialize it with data parsed from + * the input stream. The default encoding of this factory is used. + * + * @param inStream The InputStream containing the CertPath data. + * @return A CertPath initialized from the input stream data. + * @throws CertificateException If an error occurs decoding the + * CertPath. + */ + public final CertPath generateCertPath(InputStream inStream) + throws CertificateException + { + return certFacSpi.engineGenerateCertPath(inStream); + } + + /** + * Generate a {@link CertPath} and initialize it with data parsed from + * the input stream, using the specified encoding. + * + * @param inStream The InputStream containing the CertPath data. + * @param encoding The encoding of the InputStream data. + * @return A CertPath initialized from the input stream data. + * @throws CertificateException If an error occurs decoding the + * CertPath. + */ + public final CertPath generateCertPath(InputStream inStream, String encoding) + throws CertificateException + { + return certFacSpi.engineGenerateCertPath(inStream, encoding); + } + + /** + * Generate a {@link CertPath} and initialize it with the certificates + * in the {@link java.util.List} argument. + * + * @param certificates The list of certificates with which to create + * the CertPath. + * @return A CertPath initialized from the certificates. + * @throws CertificateException If an error occurs generating the + * CertPath. + */ + public final CertPath generateCertPath(List certificates) + throws CertificateException + { + return certFacSpi.engineGenerateCertPath(certificates); + } + + /** + * Returns an Iterator of CertPath encodings supported by this + * factory, with the default encoding first. The returned Iterator + * cannot be modified. + * + * @return The Iterator of supported encodings. + */ + public final Iterator getCertPathEncodings() + { + return certFacSpi.engineGetCertPathEncodings(); + } +} // class CertificateFactory diff --git a/libjava/classpath/java/security/cert/CertificateFactorySpi.java b/libjava/classpath/java/security/cert/CertificateFactorySpi.java new file mode 100644 index 000000000..2c9ca5d38 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateFactorySpi.java @@ -0,0 +1,224 @@ +/* CertificateFactorySpi.java --- Certificate Factory Class + Copyright (C) 1999,2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.io.InputStream; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + CertificateFactorySpi is the abstract class Service Provider + Interface (SPI) for the CertificateFactory class. A provider + must implement all the abstract methods if they wish to + supply a certificate factory for a particular certificate + type. Ex: X.509 + + Certificate factories are used to generate certificates and + certificate revocation lists (CRL) from their encoding. + + @since 1.2 + + @author Mark Benvenuto + */ +public abstract class CertificateFactorySpi +{ + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Constructs a new CertificateFactorySpi + */ + public CertificateFactorySpi() + {} + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + Generates a Certificate based on the encoded data read + from the InputStream. + + The input stream must contain only one certificate. + + If there exists a specialized certificate class for the + certificate format handled by the certificate factory + then the return Ceritificate should be a typecast of it. + Ex: A X.509 CertificateFactory should return X509Certificate. + + For X.509 certificates, the certificate in inStream must be + DER encoded and supplied in binary or printable (Base64) + encoding. If the certificate is in Base64 encoding, it must be + bounded by -----BEGIN CERTIFICATE-----, and + -----END CERTIFICATE-----. + + @param inStream an input stream containing the certificate data + + @return a certificate initialized with InputStream data. + + @throws CertificateException Certificate parsing error + */ + public abstract Certificate engineGenerateCertificate(InputStream inStream) + throws CertificateException; + + /** + Returns a collection of certificates that were read from the + input stream. It may be empty, have only one, or have + multiple certificates. + + For a X.509 certificate factory, the stream may contain a + single DER encoded certificate or a PKCS#7 certificate + chain. This is a PKCS#7 SignedData object with the + most significant field being certificates. If no + CRLs are present, then an empty collection is returned. + + @param inStream an input stream containing the certificates + + @return a collection of certificates initialized with + the InputStream data. + + @throws CertificateException Certificate parsing error + */ + public abstract Collection engineGenerateCertificates(InputStream inStream) + throws CertificateException; + + /** + Generates a CRL based on the encoded data read + from the InputStream. + + The input stream must contain only one CRL. + + If there exists a specialized CRL class for the + CRL format handled by the certificate factory + then the return CRL should be a typecast of it. + Ex: A X.509 CertificateFactory should return X509CRL. + + @param inStream an input stream containing the CRL data + + @return a CRL initialized with InputStream data. + + @throws CRLException CRL parsing error + */ + public abstract CRL engineGenerateCRL(InputStream inStream) + throws CRLException; + + /** + Generates CRLs based on the encoded data read + from the InputStream. + + For a X.509 certificate factory, the stream may contain a + single DER encoded CRL or a PKCS#7 CRL set. This is a + PKCS#7 SignedData object with the most significant + field being crls. If no CRLs are present, then an + empty collection is returned. + + @param inStream an input stream containing the CRLs + + @return a collection of CRLs initialized with + the InputStream data. + + @throws CRLException CRL parsing error + */ + public abstract Collection engineGenerateCRLs(InputStream inStream) + throws CRLException; + + // 1.4 instance methods. + // ------------------------------------------------------------------------ + + /** + * Generate a {@link CertPath} and initialize it with data parsed from + * the input stream. The default encoding of this factory is used. + * + * @param inStream The InputStream containing the CertPath data. + * @return A CertPath initialized from the input stream data. + * @throws CertificateException If an error occurs decoding the + * CertPath. + */ + public CertPath engineGenerateCertPath(InputStream inStream) + throws CertificateException + { + throw new UnsupportedOperationException("not implemented"); + } + + /** + * Generate a {@link CertPath} and initialize it with data parsed from + * the input stream, using the specified encoding. + * + * @param inStream The InputStream containing the CertPath data. + * @param encoding The encoding of the InputStream data. + * @return A CertPath initialized from the input stream data. + * @throws CertificateException If an error occurs decoding the + * CertPath. + */ + public CertPath engineGenerateCertPath(InputStream inStream, String encoding) + throws CertificateException + { + throw new UnsupportedOperationException("not implemented"); + } + + /** + * Generate a {@link CertPath} and initialize it with the certificates + * in the {@link java.util.List} argument. + * + * @param certificates The list of certificates with which to create + * the CertPath. + * @return A CertPath initialized from the certificates. + * @throws CertificateException If an error occurs generating the + * CertPath. + */ + public CertPath engineGenerateCertPath(List certificates) + throws CertificateException + { + throw new UnsupportedOperationException("not implemented"); + } + + /** + * Returns an Iterator of CertPath encodings supported by this + * factory, with the default encoding first. The returned Iterator + * cannot be modified. + * + * @return The Iterator of supported encodings. + */ + public Iterator engineGetCertPathEncodings() + { + throw new UnsupportedOperationException("not implemented"); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateNotYetValidException.java b/libjava/classpath/java/security/cert/CertificateNotYetValidException.java new file mode 100644 index 000000000..dfb4b4837 --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateNotYetValidException.java @@ -0,0 +1,71 @@ +/* CertificateNotYetValidException.java -- Certificate Not Yet Valid Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Exception for a Certificate that is not yet valid. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.4 +*/ +public class CertificateNotYetValidException extends CertificateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 4355919900041064702L; + + /** + * Constructs an exception without a message string. + */ + public CertificateNotYetValidException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg A message to display with exception + */ + public CertificateNotYetValidException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/cert/CertificateParsingException.java b/libjava/classpath/java/security/cert/CertificateParsingException.java new file mode 100644 index 000000000..5a930f41b --- /dev/null +++ b/libjava/classpath/java/security/cert/CertificateParsingException.java @@ -0,0 +1,93 @@ +/* CertificateParsingException.java -- Certificate Parsing Exception + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Exception for parsing a DER-encoded Certificate. + * + * @author Mark Benvenuto + * @since 1.2 + * @status updated to 1.5 +*/ +public class CertificateParsingException extends CertificateException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -7989222416793322029L; + + /** + * Constructs an exception without a message string. + */ + public CertificateParsingException() + { + } + + /** + * Constructs an exception with a message string. + * + * @param msg a message to display with exception + */ + public CertificateParsingException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public CertificateParsingException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public CertificateParsingException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/cert/CollectionCertStoreParameters.java b/libjava/classpath/java/security/cert/CollectionCertStoreParameters.java new file mode 100644 index 000000000..389874854 --- /dev/null +++ b/libjava/classpath/java/security/cert/CollectionCertStoreParameters.java @@ -0,0 +1,122 @@ +/* CollectionCertStoreParameters -- collection-based cert store parameters + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +/** + * An implementation of {@link CertStoreParameters} with a simple, + * in-memory {@link Collection} of certificates and certificate + * revocation list. + * + *

        Note that this class is not thread-safe, and its underlying + * collection may be changed at any time. + * + * @see CertStore + * @since 1.4 + */ +public class CollectionCertStoreParameters implements CertStoreParameters +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** The underlying collection. */ + private final Collection collection; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Creates a new CollectionCertStoreParameters with an empty, + * immutable collection. + */ + public CollectionCertStoreParameters() + { + this(Collections.EMPTY_LIST); + } + + /** + * Create a new CollectionCertStoreParameters with the specified + * collection. The argument is not copied, and subsequent changes to + * the collection will change this class's collection. + * + * @param collection The collection. + * @throws NullPointerException If collection is null. + */ + public CollectionCertStoreParameters(Collection collection) + { + if (collection == null) + throw new NullPointerException(); + this.collection = collection; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + public Object clone() + { + return new CollectionCertStoreParameters(new ArrayList(collection)); + } + + /** + * Return the underlying collection. The collection is not copied + * before being returned, so callers may update the collection that is + * returned. + * + * @return The collection. + */ + public Collection getCollection() + { + return collection; + } + + /** + * Return a string representation of these parameters. + * + * @return The string representation of these parameters. + */ + public String toString() + { + return "CollectionCertStoreParameters: [ collection: " + + collection + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/LDAPCertStoreParameters.java b/libjava/classpath/java/security/cert/LDAPCertStoreParameters.java new file mode 100644 index 000000000..f2dff764a --- /dev/null +++ b/libjava/classpath/java/security/cert/LDAPCertStoreParameters.java @@ -0,0 +1,140 @@ +/* LDAPCertStoreParameters.java -- LDAP CertStore parameters. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +/** + * Parameters for CertStores that are retrieved via the lightweight + * directory access protocol (LDAP). + * + * @see CertStore + */ +public class LDAPCertStoreParameters implements CertStoreParameters +{ + + // Constants and fields. + // ------------------------------------------------------------------------ + + /** The default LDAP port. */ + private static final int LDAP_PORT = 389; + + /** The server name. */ + private final String serverName; + + /** The LDAP port. */ + private final int port; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new LDAPCertStoreParameters object, with a servername of + * "localhost" and a port of 389. + */ + public LDAPCertStoreParameters() + { + this("localhost", LDAP_PORT); + } + + /** + * Create a new LDAPCertStoreParameters object, with a specified + * server name and a port of 389. + * + * @param serverName The LDAP server name. + * @throws NullPointerException If serverName is null. + */ + public LDAPCertStoreParameters(String serverName) + { + this(serverName, LDAP_PORT); + } + + /** + * Create a new LDAPCertStoreParameters object, with a specified + * server name and port. + * + * @param serverName The LDAP server name. + * @param port The LDAP port. + * @throws NullPointerException If serverName is null. + */ + public LDAPCertStoreParameters(String serverName, int port) + { + if (serverName == null) + throw new NullPointerException(); + this.serverName = serverName; + this.port = port; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + public Object clone() + { + return new LDAPCertStoreParameters(serverName, port); + } + + /** + * Return the server name. + * + * @return The server name. + */ + public String getServerName() + { + return serverName; + } + + /** + * Return the port. + * + * @return the port. + */ + public int getPort() + { + return port; + } + + /** + * Return a string representation of these parameters. + * + * @return The string representation of these parameters. + */ + public String toString() + { + return "LDAPCertStoreParameters: [ serverName: " + serverName + + "; port: " + port + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/PKIXBuilderParameters.java b/libjava/classpath/java/security/cert/PKIXBuilderParameters.java new file mode 100644 index 000000000..3a29b5218 --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXBuilderParameters.java @@ -0,0 +1,149 @@ +/* PKIXBuilderParameters.java -- parameters for PKIX cert path builders + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.lang.CPStringBuilder; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; + +import java.util.Set; + +/** + * Parameters for building certificate paths using the PKIX algorithm. + * + * @see CertPathBuilder + * @since 1.4 + */ +public class PKIXBuilderParameters extends PKIXParameters +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The maximum path length. */ + private int maxPathLength; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new PKIXBuilderParameters object, populating the trusted + * certificates set with all X.509 certificates found in the given key + * store. All certificates found in the key store are assumed to be + * trusted by this constructor. + * + * @param keystore The key store. + * @param targetConstraints The target certificate constraints. + * @throws KeyStoreException If the certificates cannot be retrieved + * from the key store. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the key store. + * @throws NullPointerException If keystore is null. + */ + public PKIXBuilderParameters(KeyStore keystore, + CertSelector targetConstraints) + throws KeyStoreException, InvalidAlgorithmParameterException + { + super(keystore); + setTargetCertConstraints(targetConstraints); + maxPathLength = 5; + } + + /** + * Create a new PKIXBuilderParameters object, populating the trusted + * certificates set with the elements of the given set, each of which + * must be a {@link TrustAnchor}. + * + * @param trustAnchors The set of trust anchors. + * @param targetConstraints The target certificate constraints. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the set. + * @throws NullPointerException If trustAnchors is null. + * @throws ClassCastException If every element in trustAnchors + * is not a {@link TrustAnchor}. + */ + public PKIXBuilderParameters(Set trustAnchors, + CertSelector targetConstraints) + throws InvalidAlgorithmParameterException + { + super(trustAnchors); + setTargetCertConstraints(targetConstraints); + maxPathLength = 5; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the maximum length of certificate paths to build. + * + *

        If this value is 0 it is taken to mean that the certificate path + * should contain only one certificate. A value of -1 means that the + * certificate path length is unconstrained. The default value is 5. + * + * @return The maximum path length. + */ + public int getMaxPathLength() + { + return maxPathLength; + } + + /** + * Sets the maximum length of certificate paths to build. + * + * @param maxPathLength The new path length. + * @throws IllegalArgumentException If maxPathLength is less + * than -1. + */ + public void setMaxPathLength(int maxPathLength) + { + if (maxPathLength < -1) + throw new IllegalArgumentException(); + this.maxPathLength = maxPathLength; + } + + public String toString() + { + CPStringBuilder buf = new CPStringBuilder(super.toString()); + buf.insert(buf.length() - 2, "; Max Path Length=" + maxPathLength); + return buf.toString(); + } +} diff --git a/libjava/classpath/java/security/cert/PKIXCertPathBuilderResult.java b/libjava/classpath/java/security/cert/PKIXCertPathBuilderResult.java new file mode 100644 index 000000000..52984b543 --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXCertPathBuilderResult.java @@ -0,0 +1,104 @@ +/* PKIXCertPathBuilderResult.java -- PKIX cert path bulider result + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.lang.CPStringBuilder; + +/** + * The result of calling the {@link + * CertPathBuilder#build(java.security.cert.CertPathParameters)} method + * of PKIX {@link CertPathBuilder}s. + * + * @see CertPathBuilder + * @see CertPathBuilderResult + */ +public class PKIXCertPathBuilderResult extends PKIXCertPathValidatorResult + implements CertPathBuilderResult +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The certificate path. */ + private CertPath certPath; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Creates a new PKIXCertPathBuilderResult. + * + * @param certPath The certificate path. + * @param trustAnchor The trust anchor. + * @param policyTree The root node of the policy tree. + * @param subjectPublicKey The public key. + * @throws NullPointerException If certPath, trustAnchor or + * subjectPublicKey is null. + */ + public PKIXCertPathBuilderResult(CertPath certPath, + TrustAnchor trustAnchor, + PolicyNode policyTree, + java.security.PublicKey subjectPublicKey) + { + super(trustAnchor, policyTree, subjectPublicKey); + if (certPath == null) + throw new NullPointerException(); + this.certPath = certPath; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the certificate path that was built. + * + * @return The certificate path that was built. + */ + public CertPath getCertPath() + { + return certPath; + } + + public String toString() + { + CPStringBuilder buf = new CPStringBuilder(super.toString()); + buf.insert(buf.length() - 2, "; CertPath=" + certPath); + return buf.toString(); + } +} diff --git a/libjava/classpath/java/security/cert/PKIXCertPathChecker.java b/libjava/classpath/java/security/cert/PKIXCertPathChecker.java new file mode 100644 index 000000000..0bedf401a --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXCertPathChecker.java @@ -0,0 +1,134 @@ +/* PKIXCertPathChecker.java -- checks X.509 certificate paths. + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.util.Collection; +import java.util.Set; + +/** + * A validator for X.509 certificates when approving certificate chains. + * + *

        Concrete subclasses can be passed to the {@link + * PKIXParameters#setCertPathCheckers(java.util.List)} and {@link + * PKIXParameters#addCertPathChecker(java.security.cert.PKIXCertPathChecker)} + * methods, which are then used to set up PKIX certificate chain + * builders or validators. These classes then call the {@link + * #check(java.security.cert.Certificate,java.util.Collection)} method + * of this class, performing whatever checks on the certificate, + * throwing an exception if any check fails. + * + *

        Subclasses of this must be able to perform their checks in the + * backward direction -- from the most-trusted certificate to the target + * -- and may optionally support forward checking -- from the target to + * the most-trusted certificate. + * + * @see PKIXParameters + * @since 1.4 + */ +public abstract class PKIXCertPathChecker implements Cloneable +{ + + // Constructor. + // ------------------------------------------------------------------------ + + /** Default constructor. */ + protected PKIXCertPathChecker() + { + super(); + } + + // Cloneable interface. + // ------------------------------------------------------------------------ + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException cnse) + { + throw new InternalError(cnse.getMessage()); + } + } + + // Abstract methods. + // ------------------------------------------------------------------------ + + /** + * Initialize this PKIXCertPathChecker. If subclasses support forward + * checking, a value of true can be passed to this method, and + * certificates can be validated from the target certificate to the + * most-trusted certifcate. + * + * @param forward The direction of this PKIXCertPathChecker. + * @throws CertPathValidatorException If forward is true and + * this class does not support forward checking. + */ + public abstract void init(boolean forward) throws CertPathValidatorException; + + /** + * Returns whether or not this class supports forward checking. + * + * @return Whether or not this class supports forward checking. + */ + public abstract boolean isForwardCheckingSupported(); + + /** + * Returns an immutable set of X.509 extension object identifiers (OIDs) + * supported by this PKIXCertPathChecker. + * + * @return An immutable set of Strings of the supported X.509 OIDs, or + * null if no extensions are supported. + */ + public abstract Set getSupportedExtensions(); + + /** + * Checks a certificate, removing any critical extensions that are + * resolved in this check. + * + * @param cert The certificate to check. + * @param unresolvedCritExts The (mutable) collection of as-of-yet + * unresolved critical extensions, as OID strings. + * @throws CertPathValidatorException If this certificate fails this + * check. + */ + public abstract void check(Certificate cert, Collection unresolvedCritExts) + throws CertPathValidatorException; +} diff --git a/libjava/classpath/java/security/cert/PKIXCertPathValidatorResult.java b/libjava/classpath/java/security/cert/PKIXCertPathValidatorResult.java new file mode 100644 index 000000000..17b5c86f8 --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXCertPathValidatorResult.java @@ -0,0 +1,142 @@ +/* PKIXCertPathValidatorResult.java -- PKIX cert path builder result + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.PublicKey; + +/** + * Results returned by the {@link + * CertPathValidator#validate(java.security.cert.CertPath,java.security.cert.CertPathParameters)} + * method for PKIX {@link CertPathValidator}s. + * + * @see CertPathValidator + */ +public class PKIXCertPathValidatorResult implements CertPathValidatorResult +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The trust anchor. */ + private final TrustAnchor trustAnchor; + + /** The root node of the policy tree. */ + private final PolicyNode policyTree; + + /** The subject's public key. */ + private final PublicKey subjectPublicKey; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Creates a new PKIXCertPathValidatorResult. + * + * @param trustAnchor The trust anchor. + * @param policyTree The root node of the policy tree. + * @param subjectPublicKey The public key. + * @throws NullPointerException If either trustAnchor or + * subjectPublicKey is null. + */ + public PKIXCertPathValidatorResult(TrustAnchor trustAnchor, + PolicyNode policyTree, + PublicKey subjectPublicKey) + { + if (trustAnchor == null || subjectPublicKey == null) + throw new NullPointerException(); + this.trustAnchor = trustAnchor; + this.policyTree = policyTree; + this.subjectPublicKey = subjectPublicKey; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the trust anchor. + * + * @return The trust anchor. + */ + public TrustAnchor getTrustAnchor() + { + return trustAnchor; + } + + /** + * Returns the root node of the policy tree. + * + * @return The root node of the policy tree. + */ + public PolicyNode getPolicyTree() + { + return policyTree; + } + + /** + * Returns the subject public key. + * + * @return The subject public key. + */ + public PublicKey getPublicKey() + { + return subjectPublicKey; + } + + /** + * Returns a copy of this object. + * + * @return The copy. + */ + public Object clone() + { + return new PKIXCertPathValidatorResult(trustAnchor, policyTree, + subjectPublicKey); + } + + /** + * Returns a printable string representation of this result. + * + * @return A printable string representation of this result. + */ + public String toString() + { + return "[ Trust Anchor=" + trustAnchor + "; Policy Tree=" + + policyTree + "; Subject Public Key=" + subjectPublicKey + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/PKIXParameters.java b/libjava/classpath/java/security/cert/PKIXParameters.java new file mode 100644 index 000000000..bbb75571f --- /dev/null +++ b/libjava/classpath/java/security/cert/PKIXParameters.java @@ -0,0 +1,547 @@ +/* PKIXParameters.java -- parameters for the PKIX cert path algorithm + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; + +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * Parameters for verifying certificate paths using the PKIX + * (Public-Key Infrastructure (X.509)) algorithm. + * + * @see CertPathBuilder + * @since 1.4 + */ +public class PKIXParameters implements CertPathParameters +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The trusted certificates. */ + private final Set trustAnchors; + + /** The set of initial policy identifiers. */ + private final Set initPolicies; + + /** The list of certificate stores. */ + private final List certStores; + + /** The list of path checkers. */ + private final List pathCheckers; + + /** The revocation enabled flag. */ + private boolean revocationEnabled; + + /** The explicit policy required flag. */ + private boolean exPolicyRequired; + + /** The policy mapping inhibited flag. */ + private boolean policyMappingInhibited; + + /** The any policy inhibited flag. */ + private boolean anyPolicyInhibited; + + /** The policy qualifiers rejected flag. */ + private boolean policyQualRejected; + + /** The target validation date. */ + private Date date; + + /** The signature algorithm provider. */ + private String sigProvider; + + /** The target constraints. */ + private CertSelector targetConstraints; + + // Constructors. + // ------------------------------------------------------------------------ + + /** + * Create a new PKIXParameters object, populating the trusted + * certificates set with all certificates found in the given key + * store. All certificates found in the key store are assumed to be + * trusted by this constructor. + * + * @param keystore The key store. + * @throws KeyStoreException If the certificates cannot be retrieved + * from the key store. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the key store. + * @throws NullPointerException If keystore is null. + */ + public PKIXParameters(KeyStore keystore) + throws KeyStoreException, InvalidAlgorithmParameterException + { + this(); + for (Enumeration e = keystore.aliases(); e.hasMoreElements(); ) + { + String alias = (String) e.nextElement(); + if (!keystore.isCertificateEntry(alias)) + continue; + Certificate cert = keystore.getCertificate(alias); + if (cert instanceof X509Certificate) + trustAnchors.add(new TrustAnchor((X509Certificate) cert, null)); + } + if (trustAnchors.isEmpty()) + throw new InvalidAlgorithmParameterException("no certs in the key store"); + } + + /** + * Create a new PKIXParameters object, populating the trusted + * certificates set with the elements of the given set, each of which + * must be a {@link TrustAnchor}. + * + * @param trustAnchors The set of trust anchors. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the set. + * @throws NullPointerException If trustAnchors is null. + * @throws ClassCastException If every element in trustAnchors + * is not a {@link TrustAnchor}. + */ + public PKIXParameters(Set trustAnchors) + throws InvalidAlgorithmParameterException + { + this(); + setTrustAnchors(trustAnchors); + } + + /** + * Default constructor. + */ + private PKIXParameters() + { + trustAnchors = new HashSet(); + initPolicies = new HashSet(); + certStores = new LinkedList(); + pathCheckers = new LinkedList(); + revocationEnabled = true; + exPolicyRequired = false; + policyMappingInhibited = false; + anyPolicyInhibited = false; + policyQualRejected = true; + } + + /** + * Copying constructor for cloning. + * + * @param that The instance being cloned. + */ + private PKIXParameters(PKIXParameters that) + { + this(); + this.trustAnchors.addAll(that.trustAnchors); + this.initPolicies.addAll(that.initPolicies); + this.certStores.addAll(that.certStores); + this.pathCheckers.addAll(that.pathCheckers); + this.revocationEnabled = that.revocationEnabled; + this.exPolicyRequired = that.exPolicyRequired; + this.policyMappingInhibited = that.policyMappingInhibited; + this.anyPolicyInhibited = that.anyPolicyInhibited; + this.policyQualRejected = that.policyQualRejected; + this.date = that.date; + this.sigProvider = that.sigProvider; + this.targetConstraints = that.targetConstraints != null + ? (CertSelector) that.targetConstraints.clone() : null; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns an immutable set of trust anchors. The set returned will + * never be null and will never be empty. + * + * @return A (never null, never empty) immutable set of trust anchors. + */ + public Set getTrustAnchors() + { + return Collections.unmodifiableSet(trustAnchors); + } + + /** + * Sets the trust anchors of this class, replacing the current trust + * anchors with those in the given set. The supplied set is copied to + * prevent modification. + * + * @param trustAnchors The new set of trust anchors. + * @throws InvalidAlgorithmParameterException If there are no + * certificates in the set. + * @throws NullPointerException If trustAnchors is null. + * @throws ClassCastException If every element in trustAnchors + * is not a {@link TrustAnchor}. + */ + public void setTrustAnchors(Set trustAnchors) + throws InvalidAlgorithmParameterException + { + if (trustAnchors.isEmpty()) + throw new InvalidAlgorithmParameterException("no trust anchors"); + this.trustAnchors.clear(); + for (Iterator i = trustAnchors.iterator(); i.hasNext(); ) + { + this.trustAnchors.add((TrustAnchor) i.next()); + } + } + + /** + * Returns the set of initial policy identifiers (as OID strings). If + * any policy is accepted, this method returns the empty set. + * + * @return An immutable set of initial policy OID strings, or the + * empty set if any policy is acceptable. + */ + public Set getInitialPolicies() + { + return Collections.unmodifiableSet(initPolicies); + } + + /** + * Sets the initial policy identifiers (as OID strings). If the + * argument is null or the empty set, then any policy identifier will + * be accepted. + * + * @param initPolicies The new set of policy strings, or null. + * @throws ClassCastException If any element in initPolicies is + * not a string. + */ + public void setInitialPolicies(Set initPolicies) + { + this.initPolicies.clear(); + if (initPolicies == null) + return; + for (Iterator i = initPolicies.iterator(); i.hasNext(); ) + { + this.initPolicies.add((String) i.next()); + } + } + + /** + * Add a {@link CertStore} to the list of cert stores. + * + * @param store The CertStore to add. + */ + public void addCertStore(CertStore store) + { + if (store != null) + certStores.add(store); + } + + /** + * Returns an immutable list of cert stores. This method never returns + * null. + * + * @return The list of cert stores. + */ + public List getCertStores() + { + return Collections.unmodifiableList(certStores); + } + + /** + * Set the cert stores. If the argument is null the list of cert + * stores will be empty. + * + * @param certStores The cert stores. + */ + public void setCertStores(List certStores) + { + this.certStores.clear(); + if (certStores == null) + return; + for (Iterator i = certStores.iterator(); i.hasNext(); ) + { + this.certStores.add((CertStore) i.next()); + } + } + + /** + * Returns the value of the revocation enabled flag. The default + * value for this flag is true. + * + * @return The revocation enabled flag. + */ + public boolean isRevocationEnabled() + { + return revocationEnabled; + } + + /** + * Sets the value of the revocation enabled flag. + * + * @param value The new value. + */ + public void setRevocationEnabled(boolean value) + { + revocationEnabled = value; + } + + /** + * Returns the value of the explicit policy required flag. The + * default value of this flag is false. + * + * @return The explicit policy required flag. + */ + public boolean isExplicitPolicyRequired() + { + return exPolicyRequired; + } + + /** + * Sets the value of the explicit policy required flag. + * + * @param value The new value. + */ + public void setExplicitPolicyRequired(boolean value) + { + exPolicyRequired = value; + } + + /** + * Returns the value of the policy mapping inhibited flag. The + * default value of this flag is false. + * + * @return The policy mapping inhibited flag. + */ + public boolean isPolicyMappingInhibited() + { + return policyMappingInhibited; + } + + /** + * Sets the value of the policy mapping inhibited flag. + * + * @param value The new value. + */ + public void setPolicyMappingInhibited(boolean value) + { + policyMappingInhibited = value; + } + + /** + * Returns the value of the any policy inhibited flag. The + * default value of this flag is false. + * + * @return The any policy inhibited flag. + */ + public boolean isAnyPolicyInhibited() + { + return anyPolicyInhibited; + } + + /** + * Sets the value of the any policy inhibited flag. + * + * @param value The new value. + */ + public void setAnyPolicyInhibited(boolean value) + { + anyPolicyInhibited = value; + } + + /** + * Returns the value of the policy qualifiers enabled flag. The + * default value of this flag is true. + * + * @return The policy qualifiers enabled flag. + */ + public boolean getPolicyQualifiersRejected() + { + return policyQualRejected; + } + + /** + * Sets the value of the policy qualifiers enabled flag. + * + * @param value The new value. + */ + public void setPolicyQualifiersRejected(boolean value) + { + policyQualRejected = value; + } + + /** + * Returns the date for which the certificate path should be + * validated, or null if the current time should be used. The date + * object is copied to prevent subsequent modification. + * + * @return The date, or null if not set. + */ + public Date getDate() + { + return date != null ? (Date) date.clone() : null; + } + + /** + * Sets the date for which the certificate path should be validated, + * or null if the current time should be used. + * + * @param date The new date, or null. + */ + public void setDate(Date date) + { + if (date != null) + this.date = (Date) date.clone(); + else + this.date = null; + } + + /** + * Add a certificate path checker. + * + * @param checker The certificate path checker to add. + */ + public void addCertPathChecker(PKIXCertPathChecker checker) + { + if (checker != null) + pathCheckers.add(checker); + } + + /** + * Returns an immutable list of all certificate path checkers. + * + * @return An immutable list of all certificate path checkers. + */ + public List getCertPathCheckers() + { + return Collections.unmodifiableList(pathCheckers); + } + + /** + * Sets the certificate path checkers. If the argument is null, the + * list of checkers will merely be cleared. + * + * @param pathCheckers The new list of certificate path checkers. + * @throws ClassCastException If any element of pathCheckers is + * not a {@link PKIXCertPathChecker}. + */ + public void setCertPathCheckers(List pathCheckers) + { + this.pathCheckers.clear(); + if (pathCheckers == null) + return; + for (Iterator i = pathCheckers.iterator(); i.hasNext(); ) + { + this.pathCheckers.add((PKIXCertPathChecker) i.next()); + } + } + + /** + * Returns the signature algorithm provider, or null if not set. + * + * @return The signature algorithm provider, or null if not set. + */ + public String getSigProvider() + { + return sigProvider; + } + + /** + * Sets the signature algorithm provider, or null if there is no + * preferred provider. + * + * @param sigProvider The signature provider name. + */ + public void setSigProvider(String sigProvider) + { + this.sigProvider = sigProvider; + } + + /** + * Returns the constraints placed on the target certificate, or null + * if there are none. The target constraints are copied to prevent + * subsequent modification. + * + * @return The target constraints, or null. + */ + public CertSelector getTargetCertConstraints() + { + return targetConstraints != null + ? (CertSelector) targetConstraints.clone() : null; + } + + /** + * Sets the constraints placed on the target certificate. + * + * @param targetConstraints The target constraints. + */ + public void setTargetCertConstraints(CertSelector targetConstraints) + { + this.targetConstraints = targetConstraints != null + ? (CertSelector) targetConstraints.clone() : null; + } + + /** + * Returns a copy of these parameters. + * + * @return The copy. + */ + public Object clone() + { + return new PKIXParameters(this); + } + + /** + * Returns a printable representation of these parameters. + * + * @return A printable representation of these parameters. + */ + public String toString() { + return "[ Trust Anchors: " + trustAnchors + "; Initial Policy OIDs=" + + (initPolicies != null ? initPolicies.toString() : "any") + + "; Validity Date=" + date + "; Signature Provider=" + + sigProvider + "; Default Revocation Enabled=" + revocationEnabled + + "; Explicit Policy Required=" + exPolicyRequired + + "; Policy Mapping Inhibited=" + policyMappingInhibited + + "; Any Policy Inhibited=" + anyPolicyInhibited + + "; Policy Qualifiers Rejected=" + policyQualRejected + + "; Target Cert Contstraints=" + targetConstraints + + "; Certification Path Checkers=" + pathCheckers + + "; CertStores=" + certStores + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/PolicyNode.java b/libjava/classpath/java/security/cert/PolicyNode.java new file mode 100644 index 000000000..5da78c188 --- /dev/null +++ b/libjava/classpath/java/security/cert/PolicyNode.java @@ -0,0 +1,108 @@ +/* PolicyNode.java -- a single node in a policy tree + Copyright (C) 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.util.Iterator; +import java.util.Set; + +/** + * @since 1.4 + */ +public interface PolicyNode +{ + + /** + * Get the iterator of the child nodes of this node. The returned + * iterator is (naturally) unmodifiable. + * + * @return An iterator over the child nodes. + */ + Iterator getChildren(); + + /** + * Get the depth of this node within the tree, starting at 0 for the + * root node. + * + * @return The depth of this node. + */ + int getDepth(); + + /** + * Returns a set of policies (string OIDs) that will satisfy this + * node's policy. The root node should always return the singleton set + * with the element "any-policy". + * + * @return The set of expected policies. + */ + Set getExpectedPolicies(); + + /** + * Returns the parent node of this node, or null if this is the root + * node. + * + * @return The parent node, or null. + */ + PolicyNode getParent(); + + /** + * Returns a set of {@link PolicyQualifierInfo} objects that qualify + * the valid policy of this node. The root node should always return + * the empty set. + * + * @return The set of {@link PolicyQualifierInfo} objects. + */ + Set getPolicyQualifiers(); + + /** + * Get the policy OID this node represents. The root node should return + * the special value "any-policy". + * + * @return The policy of this node. + */ + String getValidPolicy(); + + /** + * Return the criticality flag of this policy node. Nodes who return + * true for this method should be considered critical. The root node + * is never critical. + * + * @return The criticality flag. + */ + boolean isCritical(); +} diff --git a/libjava/classpath/java/security/cert/PolicyQualifierInfo.java b/libjava/classpath/java/security/cert/PolicyQualifierInfo.java new file mode 100644 index 000000000..b53faa935 --- /dev/null +++ b/libjava/classpath/java/security/cert/PolicyQualifierInfo.java @@ -0,0 +1,169 @@ +/* PolicyQualifierInfo.java -- policy qualifier info object. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.io.ASN1ParsingException; +import gnu.java.security.OID; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * The PolicyQualifierInfo X.509 certificate extension. + * PolicyQualifierInfo objects are represented by the ASN.1 structure: + * + *

        + * PolicyQualifierInfo ::= SEQUENCE {
        + *    policyQualifierId   PolicyQualifierId,
        + *    qualifier           ANY DEFINED BY policyQualifierId
        + * }
        + *
        + * PolicyQualifierId ::= OBJECT IDENTIFIER
        + * 
        + * + * @since 1.4 + * @specnote this class was final in 1.4, but beginning with 1.5 is not + */ +public class PolicyQualifierInfo +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The policyQualifierId field. */ + private OID oid; + + /** The DER encoded form of this object. */ + private byte[] encoded; + + /** The DER encoded form of the qualifier field. */ + private DERValue qualifier; + + // Constructor. + // ------------------------------------------------------------------------ + + /** + * Create a new PolicyQualifierInfo object from the DER encoded form + * passed in the byte array. The argument is copied. + * + *

        The ASN.1 form of PolicyQualifierInfo is: +

        +PolicyQualifierInfo ::= SEQUENCE {
        +   policyQualifierId     PolicyQualifierId,
        +   qualifier             ANY DEFINED BY policyQualifierId
        +}
        +
        +PolicyQualifierId ::= OBJECT IDENTIFIER
        +
        + * + * @param encoded The DER encoded form. + * @throws IOException If the structure cannot be parsed from the + * encoded bytes. + */ + public PolicyQualifierInfo(byte[] encoded) throws IOException + { + if (encoded == null) + throw new IOException("null bytes"); + this.encoded = (byte[]) encoded.clone(); + DERReader in = new DERReader(new ByteArrayInputStream(this.encoded)); + DERValue qualInfo = in.read(); + if (!qualInfo.isConstructed()) + throw new ASN1ParsingException("malformed PolicyQualifierInfo"); + DERValue val = in.read(); + if (!(val.getValue() instanceof OID)) + throw new ASN1ParsingException("value read not an OBJECT IDENTIFIER"); + oid = (OID) val.getValue(); + if (val.getEncodedLength() < val.getLength()) + qualifier = in.read(); + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the policyQualifierId field of this structure, + * as a dotted-decimal representation of the object identifier. + * + * @return This structure's OID field. + */ + public final String getPolicyQualifierId() + { + return oid.toString(); + } + + /** + * Returns the DER encoded form of this object; the contents of the + * returned byte array are equivalent to those that were passed to the + * constructor. The byte array is cloned every time this method is + * called. + * + * @return The encoded form. + */ + public final byte[] getEncoded() + { + return (byte[]) encoded.clone(); + } + + /** + * Get the qualifier field of this object, as a DER + * encoded byte array. The byte array returned is cloned every time + * this method is called. + * + * @return The encoded qualifier. + */ + public final byte[] getPolicyQualifier() + { + if (qualifier == null) + return new byte[0]; + return qualifier.getEncoded(); + } + + /** + * Returns a printable string representation of this object. + * + * @return The string representation. + */ + public String toString() + { + return "PolicyQualifierInfo { policyQualifierId ::= " + oid + + ", qualifier ::= " + qualifier + " }"; + } +} diff --git a/libjava/classpath/java/security/cert/TrustAnchor.java b/libjava/classpath/java/security/cert/TrustAnchor.java new file mode 100644 index 000000000..2110ed518 --- /dev/null +++ b/libjava/classpath/java/security/cert/TrustAnchor.java @@ -0,0 +1,185 @@ +/* TrustAnchor.java -- an ultimately-trusted certificate. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.java.security.x509.X500DistinguishedName; + +import java.security.PublicKey; + +/** + * An ultimately-trusted certificate to serve as the root of a + * certificate chain. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public class TrustAnchor +{ + + // Fields. + // ------------------------------------------------------------------------ + + /** The certificate authority's distinguished name. */ + private final X500DistinguishedName caName; + + /** The certficate authority's public key. */ + private final PublicKey caKey; + + /** The certficate authority's certificate. */ + private final X509Certificate trustedCert; + + /** The encoded name constraints bytes. */ + private final byte[] nameConstraints; + + // Constnuctors. + // ------------------------------------------------------------------------ + + /** + * Create a new trust anchor from a certificate and (optional) name + * constraints. + * + *

        If the nameConstraints argument in non-null, it will be + * copied to prevent modification. + * + * @param trustedCert The trusted certificate. + * @param nameConstraints The encoded nameConstraints. + */ + public TrustAnchor(X509Certificate trustedCert, byte[] nameConstraints) + { + if (trustedCert == null) + throw new NullPointerException(); + this.trustedCert = trustedCert; + caName = null; + caKey = null; + if (nameConstraints != null) + this.nameConstraints = (byte[]) nameConstraints.clone(); + else + this.nameConstraints = null; + } + + /** + * Create a new trust anchor from a certificate authority's + * distinguished name, public key, and (optional) name constraints. + * + *

        If the nameConstraints argument in non-null, it will be + * copied to prevent modification. + * + * @params caName The CA's distinguished name. + * @params caKey The CA's public key. + * @params nameConstraints The encoded nameConstraints. + */ + public TrustAnchor(String caName, PublicKey caKey, byte[] nameConstraints) + { + if (caName == null || caKey == null) + throw new NullPointerException(); + if (caName.length() == 0) + throw new IllegalArgumentException(); + trustedCert = null; + this.caName = new X500DistinguishedName(caName); + this.caKey = caKey; + if (nameConstraints != null) + this.nameConstraints = (byte[]) nameConstraints.clone(); + else + this.nameConstraints = null; + } + + // Instance methods. + // ------------------------------------------------------------------------ + + /** + * Return the trusted certificate, or null if none was specified. + * + * @return The trusted certificate. + */ + public final X509Certificate getTrustedCert() + { + return trustedCert; + } + + /** + * Return the certificate authority's distinguished name, or null if + * none was specified. + * + * @return The CA's distinguished name. + */ + public final String getCAName() + { + if (caName != null) + return caName.toString(); + return null; + } + + /** + * Return the certificate authority's public key, or null if none was + * specified. + * + * @return The CA's public key. + */ + public final PublicKey getCAPublicKey() + { + return caKey; + } + + /** + * Return the encoded name constraints, or null if none was specified. + * + *

        The name constraints byte array is copied when this method is + * called to prevent modification. + * + * @return The encoded name constraints. + */ + public final byte[] getNameConstraints() + { + if (nameConstraints == null) + return null; + return (byte[]) nameConstraints.clone(); + } + + /** + * Return a printable representation of this trust anchor. + * + * @return The printable representation. + */ + public String toString() + { + if (trustedCert == null) + return "[ Trusted CA Public Key=" + caKey + ", Trusted CA Issuer Name=" + + caName.toString() + " ]"; + return "[ Trusted CA Certificate=" + trustedCert + " ]"; + } +} diff --git a/libjava/classpath/java/security/cert/X509CRL.java b/libjava/classpath/java/security/cert/X509CRL.java new file mode 100644 index 000000000..895ba33e7 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509CRL.java @@ -0,0 +1,397 @@ +/* X509CRL.java --- X.509 Certificate Revocation List + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.SignatureException; +import java.util.Date; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +/** + The X509CRL class is the abstract class used to manage + X.509 Certificate Revocation Lists. The CRL is a list of + time stamped entries which indicate which lists have been + revoked. The list is signed by a Certificate Authority (CA) + and made publically available in a repository. + + Each revoked certificate in the CRL is identified by its + certificate serial number. When a piece of code uses a + certificate, the certificates validity is checked by + validating its signature and determing that it is not + only a recently acquired CRL. The recently aquired CRL + is depends on the local policy in affect. The CA issues + a new CRL periodically and entries are removed as the + certificate expiration date is reached + + + A description of the X.509 v2 CRL follows below from rfc2459. + + "The X.509 v2 CRL syntax is as follows. For signature calculation, + the data that is to be signed is ASN.1 DER encoded. ASN.1 DER + encoding is a tag, length, value encoding system for each element. + + CertificateList ::= SEQUENCE { + tbsCertList TBSCertList, + signatureAlgorithm AlgorithmIdentifier, + signatureValue BIT STRING } + + TBSCertList ::= SEQUENCE { + version Version OPTIONAL, + -- if present, shall be v2 + signature AlgorithmIdentifier, + issuer Name, + thisUpdate Time, + nextUpdate Time OPTIONAL, + revokedCertificates SEQUENCE OF SEQUENCE { + userCertificate CertificateSerialNumber, + revocationDate Time, + crlEntryExtensions Extensions OPTIONAL + -- if present, shall be v2 + } OPTIONAL, + crlExtensions [0] EXPLICIT Extensions OPTIONAL + -- if present, shall be v2 + }" + + @author Mark Benvenuto + + @since 1.2 +*/ +public abstract class X509CRL extends CRL implements X509Extension +{ + + /** + Constructs a new X509CRL. + */ + protected X509CRL() + { + super("X.509"); + } + + /** + Compares this X509CRL to other. It checks if the + object if instanceOf X509CRL and then checks if + the encoded form matches. + + @param other An Object to test for equality + + @return true if equal, false otherwise + */ + public boolean equals(Object other) + { + if( other instanceof X509CRL ) { + try { + X509CRL x = (X509CRL) other; + if( getEncoded().length != x.getEncoded().length ) + return false; + + byte[] b1 = getEncoded(); + byte[] b2 = x.getEncoded(); + + for( int i = 0; i < b1.length; i++ ) + if( b1[i] != b2[i] ) + return false; + + } catch( CRLException crle ) { + return false; + } + return true; + } + return false; + } + + /** + Returns a hash code for this X509CRL in its encoded + form. + + @return A hash code of this class + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + Gets the DER ASN.1 encoded format for this X.509 CRL. + + @return byte array containg encoded form + + @throws CRLException if an error occurs + */ + public abstract byte[] getEncoded() throws CRLException; + + /** + Verifies that this CRL was properly signed with the + PublicKey that corresponds to its private key. + + @param key PublicKey to verify with + + @throws CRLException encoding error + @throws NoSuchAlgorithmException unsupported algorithm + @throws InvalidKeyException incorrect key + @throws NoSuchProviderException no provider + @throws SignatureException signature error + */ + public abstract void verify(PublicKey key) + throws CRLException, + NoSuchAlgorithmException, + InvalidKeyException, + NoSuchProviderException, + SignatureException; + + /** + Verifies that this CRL was properly signed with the + PublicKey that corresponds to its private key and uses + the signature engine provided by the provider. + + @param key PublicKey to verify with + @param sigProvider Provider to use for signature algorithm + + @throws CRLException encoding error + @throws NoSuchAlgorithmException unsupported algorithm + @throws InvalidKeyException incorrect key + @throws NoSuchProviderException incorrect provider + @throws SignatureException signature error + */ + public abstract void verify(PublicKey key, + String sigProvider) + throws CRLException, + NoSuchAlgorithmException, + InvalidKeyException, + NoSuchProviderException, + SignatureException; + + /** + Gets the version of this CRL. + + The ASN.1 encoding is: + + version Version OPTIONAL, + -- if present, shall be v2 + + Version ::= INTEGER { v1(0), v2(1), v3(2) } + + Consult rfc2459 for more information. + + @return the version number, Ex: 1 or 2 + */ + public abstract int getVersion(); + + /** + Returns the issuer (issuer distinguished name) of the CRL. + The issuer is the entity who signed and issued the + Certificate Revocation List. + + The ASN.1 DER encoding is: + + issuer Name, + + Name ::= CHOICE { + RDNSequence } + + RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + + RelativeDistinguishedName ::= + SET OF AttributeTypeAndValue + + AttributeTypeAndValue ::= SEQUENCE { + type AttributeType, + value AttributeValue } + + AttributeType ::= OBJECT IDENTIFIER + + AttributeValue ::= ANY DEFINED BY AttributeType + + DirectoryString ::= CHOICE { + teletexString TeletexString (SIZE (1..MAX)), + printableString PrintableString (SIZE (1..MAX)), + universalString UniversalString (SIZE (1..MAX)), + utf8String UTF8String (SIZE (1.. MAX)), + bmpString BMPString (SIZE (1..MAX)) } + + Consult rfc2459 for more information. + + @return the issuer in the Principal class + */ + public abstract Principal getIssuerDN(); + + /** + Returns the thisUpdate date of the CRL. + + The ASN.1 DER encoding is: + + thisUpdate Time, + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Consult rfc2459 for more information. + + @return the thisUpdate date + */ + public abstract Date getThisUpdate(); + + /* + Gets the nextUpdate field + + The ASN.1 DER encoding is: + + nextUpdate Time OPTIONAL, + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Consult rfc2459 for more information. + + @return the nextUpdate date + */ + public abstract Date getNextUpdate(); + + /** + Gets the requeste dX509Entry for the specified + certificate serial number. + + @return a X509CRLEntry representing the X.509 CRL entry + */ + public abstract X509CRLEntry getRevokedCertificate(BigInteger serialNumber); + + /** + Returns a Set of revoked certificates. + + @return a set of revoked certificates. + */ + public abstract Set getRevokedCertificates(); + + /** + Returns the DER ASN.1 encoded tbsCertList which is + the basic information of the list and associated certificates + in the encoded state. See top for more information. + + The ASN.1 DER encoding is: + + tbsCertList TBSCertList, + + Consult rfc2459 for more information. + + @return byte array representing tbsCertList + */ + public abstract byte[] getTBSCertList() throws CRLException; + + + /** + Returns the signature for the CRL. + + The ASN.1 DER encoding is: + + signatureValue BIT STRING + + Consult rfc2459 for more information. + */ + public abstract byte[] getSignature(); + + /** + Returns the signature algorithm used to sign the CRL. + An examples is "SHA-1/DSA". + + The ASN.1 DER encoding is: + + signatureAlgorithm AlgorithmIdentifier, + + AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + + Consult rfc2459 for more information. + + The algorithm name is determined from the OID. + + @return a string with the signature algorithm name + */ + public abstract String getSigAlgName(); + + /** + Returns the OID for the signature algorithm used. + Example "1.2.840.10040.4.3" is return for SHA-1 with DSA.\ + + The ASN.1 DER encoding for the example is: + + id-dsa-with-sha1 ID ::= { + iso(1) member-body(2) us(840) x9-57 (10040) + x9cm(4) 3 } + + Consult rfc2459 for more information. + + @return a string containing the OID. + */ + public abstract String getSigAlgOID(); + + /** + Returns the AlgorithmParameters in the encoded form + for the signature algorithm used. + + If access to the parameters is need, create an + instance of AlgorithmParameters. + + @return byte array containing algorithm parameters, null + if no parameters are present in CRL + */ + public abstract byte[] getSigAlgParams(); + + // 1.4 instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the X.500 distinguished name of this CRL's issuer. + * + * @return The issuer's X.500 distinguished name. + * @since JDK 1.4 + */ + public X500Principal getIssuerX500Principal() + { + throw new UnsupportedOperationException(); + } +} diff --git a/libjava/classpath/java/security/cert/X509CRLEntry.java b/libjava/classpath/java/security/cert/X509CRLEntry.java new file mode 100644 index 000000000..ac5ef4714 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509CRLEntry.java @@ -0,0 +1,169 @@ +/* X509CRLEntry.java --- X.509 Certificate Revocation List Entry + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.math.BigInteger; +import java.util.Date; + +/** + Abstract class for entries in the CRL (Certificate Revocation + List). The ASN.1 definition for revokedCertificates is + + revokedCertificates SEQUENCE OF SEQUENCE { + userCertificate CertificateSerialNumber, + revocationDate Time, + crlEntryExtensions Extensions OPTIONAL + -- if present, shall be v2 + } OPTIONAL, + + CertificateSerialNumber ::= INTEGER + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + + Extension ::= SEQUENCE { + extnID OBJECT IDENTIFIER, + critical BOOLEAN DEFAULT FALSE, + extnValue OCTET STRING } + + For more information consult rfc2459. + + @author Mark Benvenuto + + @since JDK 1.2 +*/ +public abstract class X509CRLEntry implements X509Extension +{ + + /** + Creates a new X509CRLEntry + */ + public X509CRLEntry() + {} + + /** + Compares this X509CRLEntry to other. It checks if the + object if instanceOf X509CRLEntry and then checks if + the encoded form( the inner SEQUENCE) matches. + + @param other An Object to test for equality + + @return true if equal, false otherwise + */ + public boolean equals(Object other) + { + if( other instanceof X509CRLEntry ) { + try { + X509CRLEntry xe = (X509CRLEntry) other; + if( getEncoded().length != xe.getEncoded().length ) + return false; + + byte[] b1 = getEncoded(); + byte[] b2 = xe.getEncoded(); + + for( int i = 0; i < b1.length; i++ ) + if( b1[i] != b2[i] ) + return false; + + } catch( CRLException crle ) { + return false; + } + return true; + } + return false; + } + + /** + Returns a hash code for this X509CRLEntry in its encoded + form. + + @return A hash code of this class + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + Gets the DER ASN.1 encoded format for this CRL Entry, + the inner SEQUENCE. + + @return byte array containg encoded form + + @throws CRLException if an error occurs + */ + public abstract byte[] getEncoded() throws CRLException; + + /** + Gets the serial number for userCertificate in + this X509CRLEntry. + + @return the serial number for this X509CRLEntry. + */ + public abstract BigInteger getSerialNumber(); + + + /** + Gets the revocation date in revocationDate for + this X509CRLEntry. + + @return the revocation date for this X509CRLEntry. + */ + public abstract Date getRevocationDate(); + + + /** + Checks if this X509CRLEntry has extensions. + + @return true if it has extensions, false otherwise + */ + public abstract boolean hasExtensions(); + + + /** + Returns a string that represents this X509CRLEntry. + + @return a string representing this X509CRLEntry. + */ + public abstract String toString(); + +} diff --git a/libjava/classpath/java/security/cert/X509CRLSelector.java b/libjava/classpath/java/security/cert/X509CRLSelector.java new file mode 100644 index 000000000..d412a1ae3 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509CRLSelector.java @@ -0,0 +1,442 @@ +/* X509CRLSelector.java -- selects X.509 CRLs by criteria. + Copyright (C) 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.classpath.SystemProperties; +import gnu.java.lang.CPStringBuilder; +import gnu.java.security.der.DERReader; +import gnu.java.security.der.DERValue; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.security.auth.x500.X500Principal; + +/** + * A class for matching X.509 certificate revocation lists by criteria. + * + *

        Use of this class requires extensive knowledge of the Internet + * Engineering Task Force's Public Key Infrastructure (X.509). The primary + * document describing this standard is RFC 3280: Internet X.509 + * Public Key Infrastructure Certificate and Certificate Revocation List + * (CRL) Profile. + * + *

        Note that this class is not thread-safe. If multiple threads will + * use or modify this class then they need to synchronize on the object. + * + * @author Casey Marshall (csm@gnu.org) + * @since 1.4 + */ +public class X509CRLSelector implements CRLSelector, Cloneable +{ + + // Fields. + // ------------------------------------------------------------------------- + + private static final String CRL_NUMBER_ID = "2.5.29.20"; + + private List issuerNames; + private BigInteger maxCrlNumber; + private BigInteger minCrlNumber; + private Date date; + private X509Certificate cert; + + // Constructor. + // ------------------------------------------------------------------------- + + /** + * Creates a new CRL selector with no criteria enabled; i.e., every CRL + * will be matched. + */ + public X509CRLSelector() + { + } + + // Instance methods. + // ------------------------------------------------------------------------- + + /** + * Add an issuer name to the set of issuer names criteria, as the DER + * encoded form. + * + * @param name The name to add, as DER bytes. + * @throws IOException If the argument is not a valid DER-encoding. + */ + public void addIssuerName(byte[] name) throws IOException + { + X500Principal p = null; + try + { + p = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name"); + ioe.initCause(iae); + throw ioe; + } + if (issuerNames == null) + issuerNames = new LinkedList(); + issuerNames.add(p); + } + + /** + * Add an issuer name to the set of issuer names criteria, as a + * String representation. + * + * @param name The name to add. + * @throws IOException If the argument is not a valid name. + */ + public void addIssuerName(String name) throws IOException + { + X500Principal p = null; + try + { + p = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name: " + name); + ioe.initCause(iae); + throw ioe; + } + if (issuerNames == null) + issuerNames = new LinkedList(); + issuerNames.add(p); + } + + /** + * Sets the issuer names criterion. Pass null to clear this + * value. CRLs matched by this selector must have an issuer name in this + * set. + * + * @param names The issuer names. + * @throws IOException If any of the elements in the collection is not + * a valid name. + */ + public void setIssuerNames(Collection names) throws IOException + { + if (names == null) + { + issuerNames = null; + return; + } + List l = new ArrayList(names.size()); + for (Iterator it = names.iterator(); it.hasNext(); ) + { + Object o = it.next(); + if (o instanceof X500Principal) + l.add(o); + else if (o instanceof String) + { + try + { + l.add(new X500Principal((String) o)); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name: " + o); + ioe.initCause(iae); + throw ioe; + } + } + else if (o instanceof byte[]) + { + try + { + l.add(new X500Principal((byte[]) o)); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name"); + ioe.initCause(iae); + throw ioe; + } + } + else if (o instanceof InputStream) + { + try + { + l.add(new X500Principal((InputStream) o)); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed name"); + ioe.initCause(iae); + throw ioe; + } + } + else + throw new IOException("not a valid name: " + + (o != null ? o.getClass().getName() : "null")); + + } + issuerNames = l; + } + + /** + * Returns the set of issuer names that are matched by this selector, + * or null if this criteria is not set. The returned + * collection is not modifiable. + * + * @return The set of issuer names. + */ + public Collection getIssuerNames() + { + if (issuerNames != null) + return Collections.unmodifiableList(issuerNames); + else + return null; + } + + /** + * Returns the maximum value of the CRLNumber extension present in + * CRLs matched by this selector, or null if this + * criteria is not set. + * + * @return The maximum CRL number. + */ + public BigInteger getMaxCRL() + { + return maxCrlNumber; + } + + /** + * Returns the minimum value of the CRLNumber extension present in + * CRLs matched by this selector, or null if this + * criteria is not set. + * + * @return The minimum CRL number. + */ + public BigInteger getMinCRL() + { + return minCrlNumber; + } + + /** + * Sets the maximum value of the CRLNumber extension present in CRLs + * matched by this selector. Specify null to clear this + * criterion. + * + * @param maxCrlNumber The maximum CRL number. + */ + public void setMaxCRLNumber(BigInteger maxCrlNumber) + { + this.maxCrlNumber = maxCrlNumber; + } + + /** + * Sets the minimum value of the CRLNumber extension present in CRLs + * matched by this selector. Specify null to clear this + * criterion. + * + * @param minCrlNumber The minimum CRL number. + */ + public void setMinCRLNumber(BigInteger minCrlNumber) + { + this.minCrlNumber = minCrlNumber; + } + + /** + * Returns the date when this CRL must be valid; that is, the date + * must be after the thisUpdate date, but before the nextUpdate date. + * Returns null if this criterion is not set. + * + * @return The date. + */ + public Date getDateAndTime() + { + return date != null ? (Date) date.clone() : null; + } + + /** + * Sets the date at which this CRL must be valid. Specify + * null to clear this criterion. + * + * @param date The date. + */ + public void setDateAndTime(Date date) + { + this.date = date != null ? (Date) date.clone() : null; + } + + /** + * Returns the certificate being checked, or null if this + * value is not set. + * + * @return The certificate. + */ + public X509Certificate getCertificateChecking() + { + return cert; + } + + /** + * Sets the certificate being checked. This is not a criterion, but + * info used by certificate store implementations to aid in searching. + * + * @param cert The certificate. + */ + public void setCertificateChecking(X509Certificate cert) + { + this.cert = cert; + } + + /** + * Returns a string representation of this selector. The string will + * only describe the enabled criteria, so if none are enabled this will + * return a string that contains little else besides the class name. + * + * @return The string. + */ + public String toString() + { + CPStringBuilder str = new CPStringBuilder(X509CRLSelector.class.getName()); + String nl = SystemProperties.getProperty("line.separator"); + String eol = ";" + nl; + + str.append(" {").append(nl); + if (issuerNames != null) + str.append(" issuer names = ").append(issuerNames).append(eol); + if (maxCrlNumber != null) + str.append(" max CRL = ").append(maxCrlNumber).append(eol); + if (minCrlNumber != null) + str.append(" min CRL = ").append(minCrlNumber).append(eol); + if (date != null) + str.append(" date = ").append(date).append(eol); + if (cert != null) + str.append(" certificate = ").append(cert).append(eol); + str.append("}").append(nl); + return str.toString(); + } + + /** + * Checks a CRL against the criteria of this selector, returning + * true if the given CRL matches all the criteria. + * + * @param _crl The CRL being checked. + * @return True if the CRL matches, false otherwise. + */ + public boolean match(CRL _crl) + { + if (!(_crl instanceof X509CRL)) + return false; + X509CRL crl = (X509CRL) _crl; + if (issuerNames != null) + { + if (!issuerNames.contains(crl.getIssuerX500Principal())) + return false; + } + BigInteger crlNumber = null; + if (maxCrlNumber != null) + { + byte[] b = crl.getExtensionValue(CRL_NUMBER_ID); + if (b == null) + return false; + try + { + DERValue val = DERReader.read(b); + if (!(val.getValue() instanceof BigInteger)) + return false; + crlNumber = (BigInteger) val.getValue(); + } + catch (IOException ioe) + { + return false; + } + if (maxCrlNumber.compareTo(crlNumber) < 0) + return false; + } + if (minCrlNumber != null) + { + if (crlNumber == null) + { + byte[] b = crl.getExtensionValue(CRL_NUMBER_ID); + if (b == null) + return false; + try + { + DERValue val = DERReader.read(b); + if (!(val.getValue() instanceof BigInteger)) + return false; + crlNumber = (BigInteger) val.getValue(); + } + catch (IOException ioe) + { + return false; + } + } + if (minCrlNumber.compareTo(crlNumber) > 0) + return false; + } + if (date != null) + { + if (date.compareTo(crl.getThisUpdate()) < 0 || + date.compareTo(crl.getNextUpdate()) > 0) + return false; + } + return true; + } + + /** + * Returns a copy of this object. + * + * @return The copy. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException shouldNotHappen) + { + throw new Error(shouldNotHappen); + } + } +} diff --git a/libjava/classpath/java/security/cert/X509CertSelector.java b/libjava/classpath/java/security/cert/X509CertSelector.java new file mode 100644 index 000000000..8c1230afb --- /dev/null +++ b/libjava/classpath/java/security/cert/X509CertSelector.java @@ -0,0 +1,1319 @@ +/* X509CertSelector.java -- selects X.509 certificates by criteria. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import gnu.classpath.SystemProperties; +import gnu.java.lang.CPStringBuilder; +import gnu.java.security.OID; +import gnu.java.security.x509.GnuPKIExtension; +import gnu.java.security.x509.ext.CertificatePolicies; +import gnu.java.security.x509.ext.Extension; +import gnu.java.security.x509.ext.GeneralName; +import gnu.java.security.x509.ext.GeneralSubtree; +import gnu.java.security.x509.ext.NameConstraints; +import gnu.java.security.x509.ext.GeneralName.Kind; + +import java.io.IOException; +import java.math.BigInteger; +import java.net.InetAddress; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +/** + * A concrete implementation of {@link CertSelector} for X.509 certificates, + * which allows a number of criteria to be set when accepting certificates, + * from validity dates, to issuer and subject distinguished names, to some + * of the various X.509 extensions. + * + *

        Use of this class requires extensive knowledge of the Internet + * Engineering Task Force's Public Key Infrastructure (X.509). The primary + * document describing this standard is RFC 3280: Internet X.509 + * Public Key Infrastructure Certificate and Certificate Revocation List + * (CRL) Profile. + * + *

        Note that this class is not thread-safe. If multiple threads will + * use or modify this class then they need to synchronize on the object. + * + * @author Casey Marshall (csm@gnu.org) + * @since 1.4 + */ +public class X509CertSelector implements CertSelector, Cloneable +{ + + // Constants and fields. + // ------------------------------------------------------------------------- + + private static final String AUTH_KEY_ID = "2.5.29.35"; + private static final String SUBJECT_KEY_ID = "2.5.29.14"; + private static final String NAME_CONSTRAINTS_ID = "2.5.29.30"; + + private static boolean checkOid(int[] oid) + { + return (oid != null && oid.length > 2 && + (oid[0] >= 0 && oid[0] <= 2) && (oid[1] >= 0 && oid[1] <= 39)); + } + + private static GeneralName makeName(int id, String name) throws IOException + { + byte[] nameBytes = null; + GeneralName.Kind kind = GeneralName.Kind.forTag(id); + switch (Kind.forTag(id)) + { + case dNSName: + case rfc822Name: + case uniformResourceIdentifier: + nameBytes = name.getBytes("ASCII"); + break; + + case iPAddress: + InetAddress addr = InetAddress.getByName(name); + nameBytes = addr.getAddress(); + break; + + case registeredId: + OID oid = new OID(name); + nameBytes = oid.getDER(); + break; + + case directoryName: + X500Principal xname = new X500Principal(name); + nameBytes = xname.getEncoded(); + break; + + case ediPartyName: + case x400Address: + case otherName: + throw new IOException("cannot decode string representation of " + + kind); + } + return new GeneralName(kind, nameBytes); + } + + private int basicConstraints; + private X509Certificate cert; + private BigInteger serialNo; + private X500Principal issuer; + private X500Principal subject; + private byte[] subjectKeyId; + private byte[] authKeyId; + private boolean[] keyUsage; + private Date certValid; + private OID sigId; + private PublicKey subjectKey; + private X509EncodedKeySpec subjectKeySpec; + private Set keyPurposeSet; + private List altNames; + private boolean matchAllNames; + private byte[] nameConstraints; + private Set policy; + private List pathToNames; + + /** + * Creates a new X.509 certificate selector. The new selector will be + * empty, and will accept any certificate (provided that it is an + * {@link X509Certificate}). + */ + public X509CertSelector() + { + basicConstraints = -1; + } + + /** + * Add a name to match in the NameConstraints extension. The argument is + * the DER-encoded bytes of a GeneralName structure. + * + * See the method {@link #addSubjectAlternativeName(int, byte[])} for the + * format of the GeneralName structure. + * + * @param id The name identifier. Must be between 0 and 8. + * @param name The DER-encoded bytes of the name to match. + * @throws IOException If the name DER is malformed. + */ + public void addPathToName(int id, byte[] name) throws IOException + { + GeneralName generalName = new GeneralName(GeneralName.Kind.forTag(id), name); + if (pathToNames == null) + pathToNames = new LinkedList(); + pathToNames.add(generalName); + } + + /** + * Add a name to match in the NameConstraints extension. This method will + * only recognize certain types of name that have convenient string + * encodings. For robustness, you should use the {@link + * #addPathToName(int, byte[])} method whenever possible. + * + * @param id The name identifier. Must be between 0 and 8. + * @param name The name. + * @throws IOException If the name cannot be decoded. + */ + public void addPathToName(int id, String name) throws IOException + { + GeneralName generalName = makeName(id, name); + if (pathToNames == null) + pathToNames = new LinkedList(); + pathToNames.add(generalName); + } + + /** + * Add a name, as DER-encoded bytes, to the subject alternative names + * criterion. + * + * The name is a GeneralName structure, which has the ASN.1 format: + * + *

        +  GeneralName ::= CHOICE {
        +    otherName                       [0]     OtherName,
        +    rfc822Name                      [1]     IA5String,
        +    dNSName                         [2]     IA5String,
        +    x400Address                     [3]     ORAddress,
        +    directoryName                   [4]     Name,
        +    ediPartyName                    [5]     EDIPartyName,
        +    uniformResourceIdentifier       [6]     IA5String,
        +    iPAddress                       [7]     OCTET STRING,
        +    registeredID                    [8]     OBJECT IDENTIFIER }
        +
        + * + * @param id The type of name this is. + * @param name The DER-encoded name. + * @throws IOException If the name is not a valid DER sequence. + */ + public void addSubjectAlternativeName(int id, byte[] name) + throws IOException + { + GeneralName generalName = new GeneralName(GeneralName.Kind.forTag(id), name); + if (altNames == null) + altNames = new LinkedList(); + altNames.add(generalName); + } + + /** + * Add a name to the subject alternative names criterion. This method will + * only recognize certain types of name that have convenient string + * encodings. For robustness, you should use the {@link + * #addSubjectAlternativeName(int, byte[])} method whenever possible. + * + * This method can only decode certain name kinds of names as strings. + * + * @param id The type of name this is. Must be in the range [0,8]. + * @param name The name. + * @throws IOException If the id is out of range, or if the name + * is null. + */ + public void addSubjectAlternativeName(int id, String name) + throws IOException + { + GeneralName generalName = makeName(id, name); + if (altNames == null) + altNames = new LinkedList(); + altNames.add(generalName); + } + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException shouldNotHappen) + { + throw new Error(shouldNotHappen); + } + } + + /** + * Returns the authority key identifier criterion, or null if + * this value was not set. Note that the byte array is cloned to prevent + * modification. + * + * @return The authority key identifier. + */ + public byte[] getAuthorityKeyIdentifier() + { + if (authKeyId != null) + return (byte[]) authKeyId.clone(); + else + return null; + } + + /** + * Returns the basic constraints criterion, or -1 if this value is not set. + * + * @return The basic constraints. + */ + public int getBasicConstraints() + { + return basicConstraints; + } + + /** + * Returns the certificate criterion, or null if this value + * was not set. + * + * @return The certificate. + */ + public X509Certificate getCertificate() + { + return cert; + } + + /** + * Returns the date at which certificates must be valid, or null + * if this criterion was not set. + * + * @return The target certificate valitity date. + */ + public Date getCertificateValid() + { + if (certValid != null) + return (Date) certValid.clone(); + else + return null; + } + + /** + * Returns the set of extended key purpose IDs, as an unmodifiable set + * of OID strings. Returns null if this criterion is not + * set. + * + * @return The set of key purpose OIDs (strings). + */ + public Set getExtendedKeyUsage() + { + if (keyPurposeSet != null) + return Collections.unmodifiableSet(keyPurposeSet); + else + return null; + } + + /** + * Returns the issuer criterion as a sequence of DER bytes, or + * null if this value was not set. + * + * @return The issuer. + */ + public byte[] getIssuerAsBytes() throws IOException + { + if (issuer != null) + return issuer.getEncoded(); + else + return null; + } + + /** + * Returns the issuer criterion as a string, or null if this + * value was not set. + * + * @return The issuer. + */ + public String getIssuerAsString() + { + if (issuer != null) + return issuer.getName(); + else + return null; + } + + /** + * Returns the public key usage criterion, or null if this + * value is not set. Note that the array is cloned to prevent modification. + * + * @return The public key usage. + */ + public boolean[] getKeyUsage() + { + if (keyUsage != null) + return (boolean[]) keyUsage.clone(); + else + return null; + } + + /** + * Returns whether or not all specified alternative names must match. + * If false, a certificate is considered a match if one of the + * specified alternative names matches. + * + * @return true if all names must match. + */ + public boolean getMatchAllSubjectAltNames() + { + return matchAllNames; + } + + /** + * Returns the name constraints criterion, or null if this + * value is not set. Note that the byte array is cloned to prevent + * modification. + * + * @return The name constraints. + */ + public byte[] getNameConstraints() + { + if (nameConstraints != null) + return (byte[]) nameConstraints.clone(); + else + return null; + } + + public Collection> getPathToNames() + { + if (pathToNames != null) + { + List> names = new ArrayList>(pathToNames.size()); + for (GeneralName name : pathToNames) + { + List n = new ArrayList(2); + n.add(name.kind().tag()); + n.add(name.name()); + names.add(n); + } + + return names; + } + return null; + } + + /** + * Returns the certificate policy extension that will be matched by this + * selector, or null if the certificate policy will not be matched. + * + * @return The policy to be matched, or null. + */ + public Set getPolicy() + { + Set p = this.policy; + if (p != null) + { + Set strings = new HashSet(p.size()); + for (OID o : p) + { + strings.add(o.toString()); + } + return strings; + } + return null; + } + + /** + * This method, and its related X.509 certificate extension — the + * private key usage period — is not supported under the Internet + * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this + * method is not supported either. + * + *

        Do not use this method. It is not deprecated, as it is not deprecated + * in the Java standard, but it is basically a no-operation and simply + * returns null. + * + * @return Null. + */ + public Date getPrivateKeyValid() + { + return null; + } + + /** + * Returns the serial number criterion, or null if this + * value was not set. + * + * @return The serial number. + */ + public BigInteger getSerialNumber() + { + return serialNo; + } + + /** + * Get the subject alternative names criterion. The collection returned + * is a collection of pairs: the first element is an {@link Integer} + * containing the name type, and the second is a byte array containing + * the DER-encoded name bytes. + * + * @return The subject alternative names criterion. Returns null if this + * criterion is not set. + */ + public Collection> getSubjectAlternativeNames() + { + if (altNames != null) + { + List> names = new ArrayList>(altNames.size()); + for (GeneralName name : altNames) + { + List n = new ArrayList(2); + n.add(name.kind().tag()); + n.add(name.name()); + names.add(n); + } + return names; + } + return null; + } + + /** + * Returns the subject criterion as a sequence of DER bytes, or + * null if this value is not set. + * + * @return The subject. + */ + public byte[] getSubjectAsBytes() throws IOException + { + if (subject != null) + return subject.getEncoded(); + else + return null; + } + + /** + * Returns the subject criterion as a string, of null if + * this value was not set. + * + * @return The subject. + */ + public String getSubjectAsString() + { + if (subject != null) + return subject.getName(); + else + return null; + } + + /** + * Returns the subject key identifier criterion, or null if + * this value was not set. Note that the byte array is cloned to prevent + * modification. + * + * @return The subject key identifier. + */ + public byte[] getSubjectKeyIdentifier() + { + if (subjectKeyId != null) + return (byte[]) subjectKeyId.clone(); + else + return null; + } + + /** + * Returns the subject public key criterion, or null if this + * value is not set. + * + * @return The subject public key. + */ + public PublicKey getSubjectPublicKey() + { + return subjectKey; + } + + /** + * Returns the public key algorithm ID that matching certificates must have, + * or null if this criterion was not set. + * + * @return The public key algorithm ID. + */ + public String getSubjectPublicKeyAlgID() + { + return String.valueOf(sigId); + } + + /** + * Match a certificate. This method will check the given certificate + * against all the enabled criteria of this selector, and will return + * true if the given certificate matches. + * + * @param certificate The certificate to check. + * @return true if the certificate matches all criteria. + */ + public boolean match(Certificate certificate) + { + if (!(certificate instanceof X509Certificate)) + return false; + X509Certificate cert = (X509Certificate) certificate; + if (this.cert != null) + { + try + { + byte[] e1 = this.cert.getEncoded(); + byte[] e2 = cert.getEncoded(); + if (!Arrays.equals(e1, e2)) + return false; + } + catch (CertificateEncodingException cee) + { + return false; + } + } + if (serialNo != null) + { + if (!serialNo.equals(cert.getSerialNumber())) + return false; + } + if (certValid != null) + { + try + { + cert.checkValidity(certValid); + } + catch (CertificateException ce) + { + return false; + } + } + if (issuer != null) + { + if (!issuer.equals(cert.getIssuerX500Principal())) + return false; + } + if (subject != null) + { + if (!subject.equals(cert.getSubjectX500Principal())) + return false; + } + if (sigId != null) + { + if (!sigId.toString().equals(cert.getSigAlgOID())) + return false; + } + if (subjectKeyId != null) + { + byte[] b = cert.getExtensionValue(SUBJECT_KEY_ID); + if (!Arrays.equals(b, subjectKeyId)) + return false; + } + if (authKeyId != null) + { + byte[] b = cert.getExtensionValue(AUTH_KEY_ID); + if (!Arrays.equals(b, authKeyId)) + return false; + } + if (keyUsage != null) + { + boolean[] b = cert.getKeyUsage(); + if (!Arrays.equals(b, keyUsage)) + return false; + } + if (basicConstraints >= 0) + { + if (cert.getBasicConstraints() != basicConstraints) + return false; + } + if (keyPurposeSet != null) + { + List kp = null; + try + { + kp = cert.getExtendedKeyUsage(); + } + catch (CertificateParsingException cpe) + { + return false; + } + if (kp == null) + return false; + for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) + { + if (!kp.contains(it.next())) + return false; + } + } + if (altNames != null) + { + Collection> an = null; + try + { + an = cert.getSubjectAlternativeNames(); + } + catch (CertificateParsingException cpe) + { + return false; + } + if (an == null) + return false; + int match = 0; + for (GeneralName name : altNames) + { + for (List list : an) + { + try + { + Integer id = (Integer) list.get(0); + Object val = list.get(1); + GeneralName n = null; + if (val instanceof String) + n = makeName(id, (String) val); + else if (val instanceof byte[]) + { + n = new GeneralName(GeneralName.Kind.forTag(id), + (byte[]) val); + } + else + continue; + if (name.equals(n)) + match++; + } + catch (Exception e) + { + continue; + } + } + if (match == 0 || (matchAllNames && match < altNames.size())) + return false; + } + } + if (nameConstraints != null) + { + byte[] nc = cert.getExtensionValue(NAME_CONSTRAINTS_ID); + if (!Arrays.equals(nameConstraints, nc)) + return false; + } + + if (policy != null) + { + CertificatePolicies policies = null; + if (cert instanceof GnuPKIExtension) + { + policies = (CertificatePolicies) + ((GnuPKIExtension) cert).getExtension(CertificatePolicies.ID).getValue(); + } + else + { + byte[] policiesDer = + cert.getExtensionValue(CertificatePolicies.ID.toString()); + try + { + policies = new CertificatePolicies(policiesDer); + } + catch (IOException ioe) + { + // ignored + } + } + + if (policies == null) + return false; + if (!policies.getPolicies().containsAll(policy)) + return false; + } + + if (pathToNames != null) + { + NameConstraints nc = null; + if (cert instanceof GnuPKIExtension) + { + Extension e = + ((GnuPKIExtension) cert).getExtension(NameConstraints.ID); + if (e != null) + nc = (NameConstraints) e.getValue(); + } + else + { + byte[] b = cert.getExtensionValue(NameConstraints.ID.toString()); + if (b != null) + { + try + { + nc = new NameConstraints(b); + } + catch (IOException ioe) + { + } + } + } + + if (nc == null) + return false; + + int match = 0; + for (GeneralName name : pathToNames) + { + for (GeneralSubtree subtree : nc.permittedSubtrees()) + { + if (name.equals(subtree.base())) + match++; + } + } + if (match == 0 || (matchAllNames && match < pathToNames.size())) + return false; + } + + return true; + } + + /** + * Sets the authority key identifier criterion, or null to clear + * this criterion. Note that the byte array is cloned to prevent modification. + * + * @param authKeyId The authority key identifier. + */ + public void setAuthorityKeyIdentifier(byte[] authKeyId) + { + this.authKeyId = authKeyId != null ? (byte[]) authKeyId.clone() : null; + } + + /** + * Sets the basic constraints criterion. Specify -1 to clear this parameter. + * + * @param basicConstraints The new basic constraints value. + */ + public void setBasicConstraints(int basicConstraints) + { + if (basicConstraints < -1) + basicConstraints = -1; + this.basicConstraints = basicConstraints; + } + + /** + * Sets the certificate criterion. If set, only certificates that are + * equal to the certificate passed here will be accepted. + * + * @param cert The certificate. + */ + public void setCertificate(X509Certificate cert) + { + this.cert = cert; + } + + /** + * Sets the date at which certificates must be valid. Specify + * null to clear this criterion. + * + * @param certValid The certificate validity date. + */ + public void setCertificateValid(Date certValid) + { + this.certValid = certValid != null ? (Date) certValid.clone() : null; + } + + /** + * Sets the extended key usage criterion, as a set of OID strings. Specify + * null to clear this value. + * + * @param keyPurposeSet The set of key purpose OIDs. + * @throws IOException If any element of the set is not a valid OID string. + */ + public void setExtendedKeyUsage(Set keyPurposeSet) throws IOException + { + if (keyPurposeSet == null) + { + this.keyPurposeSet = null; + return; + } + Set s = new HashSet(); + for (Iterator it = keyPurposeSet.iterator(); it.hasNext(); ) + { + Object o = it.next(); + if (!(o instanceof String)) + throw new IOException("not a string: " + o); + try + { + OID oid = new OID((String) o); + int[] comp = oid.getIDs(); + if (!checkOid(comp)) + throw new IOException("malformed OID: " + o); + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed OID: " + o); + ioe.initCause(iae); + throw ioe; + } + } + this.keyPurposeSet = s; + } + + /** + * Sets the issuer, specified as the DER encoding of the issuer's + * distinguished name. Only certificates issued by this issuer will + * be accepted. + * + * @param name The DER encoding of the issuer's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setIssuer(byte[] name) throws IOException + { + if (name != null) + { + try + { + issuer = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + issuer = null; + } + + /** + * Sets the issuer, specified as a string representation of the issuer's + * distinguished name. Only certificates issued by this issuer will + * be accepted. + * + * @param name The string representation of the issuer's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setIssuer(String name) throws IOException + { + if (name != null) + { + try + { + issuer = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + issuer = null; + } + + /** + * Sets the public key usage criterion. Specify null to clear + * this value. + * + * @param keyUsage The public key usage. + */ + public void setKeyUsage(boolean[] keyUsage) + { + this.keyUsage = keyUsage != null ? (boolean[]) keyUsage.clone() : null; + } + + /** + * Sets whether or not all subject alternative names must be matched. + * If false, then a certificate will be considered a match if one + * alternative name matches. + * + * @param matchAllNames Whether or not all alternative names must be + * matched. + */ + public void setMatchAllSubjectAltNames(boolean matchAllNames) + { + this.matchAllNames = matchAllNames; + } + + /** + * Sets the name constraints criterion; specify null to + * clear this criterion. Note that if non-null, the argument will be + * cloned to prevent modification. + * + * @param nameConstraints The new name constraints. + * @throws IOException If the argument is not a valid DER-encoded + * name constraints. + */ + public void setNameConstraints(byte[] nameConstraints) + throws IOException + { + // Check if the input is well-formed... + new NameConstraints(nameConstraints); + + // But we just compare raw byte arrays. + this.nameConstraints = nameConstraints != null + ? (byte[]) nameConstraints.clone() : null; + } + + /** + * Sets the pathToNames criterion. The argument is a collection of + * pairs, the first element of which is an {@link Integer} giving + * the ID of the name, and the second element is either a {@link String} + * or a byte array. + * + * See {@link #addPathToName(int, byte[])} and {@link #addPathToName(int, String)} + * for how these arguments are handled. + * + * @param names The names. + * @throws IOException If any argument is malformed. + */ + public void setPathToNames(Collection> names) throws IOException + { + if (names == null || names.size() == 0) + { + pathToNames = null; + } + else + { + pathToNames = new ArrayList(names.size()); + for (List name : names) + { + Integer id = (Integer) name.get(0); + Object name2 = name.get(1); + if (name2 instanceof String) + addPathToName(id, (String) name2); + else if (name2 instanceof byte[]) + addPathToName(id, (byte[]) name2); + else + throw new IOException("invalid name type: " + + name2.getClass().getName()); + } + } + } + + /** + * Sets the certificate policy to match, or null if this criterion should + * not be checked. Each element if the set must be a dotted-decimal form + * of certificate policy object identifier. + * + * @param policy The policy to match. + * @throws IOException If some element of the policy is not a valid + * policy extenison OID. + */ + public void setPolicy(Set policy) throws IOException + { + if (policy != null) + { + HashSet p = new HashSet(policy.size()); + for (String s : policy) + { + try + { + OID oid = new OID(s); + int[] i = oid.getIDs(); + if (!checkOid(i)) + throw new IOException("invalid OID"); + p.add(oid); + } + catch (IOException ioe) + { + throw ioe; + } + catch (Exception x) + { + IOException ioe = new IOException("invalid OID"); + ioe.initCause(x); + throw ioe; + } + } + this.policy = p; + } + else + this.policy = null; + } + + /** + * This method, and its related X.509 certificate extension — the + * private key usage period — is not supported under the Internet + * PKI for X.509 certificates (PKIX), described in RFC 3280. As such, this + * method is not supported either. + * + *

        Do not use this method. It is not deprecated, as it is not deprecated + * in the Java standard, but it is basically a no-operation. + * + * @param UNUSED Is silently ignored. + */ + public void setPrivateKeyValid(Date UNUSED) + { + } + + /** + * Sets the serial number of the desired certificate. Only certificates that + * contain this serial number are accepted. + * + * @param serialNo The serial number. + */ + public void setSerialNumber(BigInteger serialNo) + { + this.serialNo = serialNo; + } + + /** + * Sets the subject, specified as the DER encoding of the subject's + * distinguished name. Only certificates with the given subject will + * be accepted. + * + * @param name The DER encoding of the subject's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setSubject(byte[] name) throws IOException + { + if (name != null) + { + try + { + subject = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + subject = null; + } + + /** + * Sets the subject, specified as a string representation of the + * subject's distinguished name. Only certificates with the given + * subject will be accepted. + * + * @param name The string representation of the subject's distinguished name. + * @throws IOException If the given name is incorrectly formatted. + */ + public void setSubject(String name) throws IOException + { + if (name != null) + { + try + { + subject = new X500Principal(name); + } + catch (IllegalArgumentException iae) + { + throw new IOException(iae.getMessage()); + } + } + else + subject = null; + } + + /** + * Sets the subject alternative names critertion. Each element of the + * argument must be a {@link java.util.List} that contains exactly two + * elements: the first an {@link Integer}, representing the type of + * name, and the second either a {@link String} or a byte array, + * representing the name itself. + * + * @param altNames The alternative names. + * @throws IOException If any element of the argument is invalid. + */ + public void setSubjectAlternativeNames(Collection> altNames) + throws IOException + { + if (altNames == null || altNames.isEmpty()) + { + this.altNames = null; + return; + } + List l = new ArrayList(altNames.size()); + for (List list : altNames) + { + Integer id = (Integer) list.get(0); + Object value = list.get(1); + GeneralName name = null; + if (value instanceof String) + name = makeName(id, (String) value); + else if (value instanceof byte[]) + name = new GeneralName(GeneralName.Kind.forTag(id), (byte[]) value); + else + throw new IOException("invalid name type: " + value.getClass().getName()); + l.add(name); + } + this.altNames = l; + } + + /** + * Sets the subject key identifier criterion, or null to clear + * this criterion. Note that the byte array is cloned to prevent modification. + * + * @param subjectKeyId The subject key identifier. + */ + public void setSubjectKeyIdentifier(byte[] subjectKeyId) + { + this.subjectKeyId = subjectKeyId != null ? (byte[]) subjectKeyId.clone() : + null; + } + + /** + * Sets the subject public key criterion as a DER-encoded key. Specify + * null to clear this value. + * + * @param key The DER-encoded key bytes. + * @throws IOException If the argument is not a valid DER-encoded key. + */ + public void setSubjectPublicKey(byte[] key) throws IOException + { + if (key == null) + { + subjectKey = null; + subjectKeySpec = null; + return; + } + try + { + subjectKeySpec = new X509EncodedKeySpec(key); + KeyFactory enc = KeyFactory.getInstance("X.509"); + subjectKey = enc.generatePublic(subjectKeySpec); + } + catch (Exception x) + { + subjectKey = null; + subjectKeySpec = null; + IOException ioe = new IOException(x.getMessage()); + ioe.initCause(x); + throw ioe; + } + } + + /** + * Sets the subject public key criterion as an opaque representation. + * Specify null to clear this criterion. + * + * @param key The public key. + */ + public void setSubjectPublicKey(PublicKey key) + { + this.subjectKey = key; + if (key == null) + { + subjectKeySpec = null; + return; + } + try + { + KeyFactory enc = KeyFactory.getInstance("X.509"); + subjectKeySpec = (X509EncodedKeySpec) + enc.getKeySpec(key, X509EncodedKeySpec.class); + } + catch (Exception x) + { + subjectKey = null; + subjectKeySpec = null; + } + } + + /** + * Sets the public key algorithm ID that matching certificates must have. + * Specify null to clear this criterion. + * + * @param sigId The public key ID. + * @throws IOException If the specified ID is not a valid object identifier. + */ + public void setSubjectPublicKeyAlgID(String sigId) throws IOException + { + if (sigId != null) + { + try + { + OID oid = new OID(sigId); + int[] comp = oid.getIDs(); + if (!checkOid(comp)) + throw new IOException("malformed OID: " + sigId); + this.sigId = oid; + } + catch (IllegalArgumentException iae) + { + IOException ioe = new IOException("malformed OID: " + sigId); + ioe.initCause(iae); + throw ioe; + } + } + else + this.sigId = null; + } + + public String toString() + { + CPStringBuilder str = new CPStringBuilder(X509CertSelector.class.getName()); + String nl = SystemProperties.getProperty("line.separator"); + String eol = ";" + nl; + str.append(" {").append(nl); + if (cert != null) + str.append(" certificate = ").append(cert).append(eol); + if (basicConstraints >= 0) + str.append(" basic constraints = ").append(basicConstraints).append(eol); + if (serialNo != null) + str.append(" serial number = ").append(serialNo).append(eol); + if (certValid != null) + str.append(" valid date = ").append(certValid).append(eol); + if (issuer != null) + str.append(" issuer = ").append(issuer).append(eol); + if (subject != null) + str.append(" subject = ").append(subject).append(eol); + if (sigId != null) + str.append(" signature OID = ").append(sigId).append(eol); + if (subjectKey != null) + str.append(" subject public key = ").append(subjectKey).append(eol); + if (subjectKeyId != null) + { + str.append(" subject key ID = "); + for (int i = 0; i < subjectKeyId.length; i++) + { + str.append(Character.forDigit((subjectKeyId[i] & 0xF0) >>> 8, 16)); + str.append(Character.forDigit((subjectKeyId[i] & 0x0F), 16)); + if (i < subjectKeyId.length - 1) + str.append(':'); + } + str.append(eol); + } + if (authKeyId != null) + { + str.append(" authority key ID = "); + for (int i = 0; i < authKeyId.length; i++) + { + str.append(Character.forDigit((authKeyId[i] & 0xF0) >>> 8, 16)); + str.append(Character.forDigit((authKeyId[i] & 0x0F), 16)); + if (i < authKeyId.length - 1) + str.append(':'); + } + str.append(eol); + } + if (keyUsage != null) + { + str.append(" key usage = "); + for (int i = 0; i < keyUsage.length; i++) + str.append(keyUsage[i] ? '1' : '0'); + str.append(eol); + } + if (keyPurposeSet != null) + str.append(" key purpose = ").append(keyPurposeSet).append(eol); + if (altNames != null) + str.append(" alternative names = ").append(altNames).append(eol); + if (nameConstraints != null) + str.append(" name constraints = ").append(eol); + if (policy != null) + str.append(" policy = ").append(policy).append(eol); + if (pathToNames != null) + str.append(" pathToNames = ").append(pathToNames).append(eol); + str.append("}").append(nl); + return str.toString(); + } +} diff --git a/libjava/classpath/java/security/cert/X509Certificate.java b/libjava/classpath/java/security/cert/X509Certificate.java new file mode 100644 index 000000000..ab9e1be37 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509Certificate.java @@ -0,0 +1,589 @@ +/* X509Certificate.java --- X.509 Certificate class + Copyright (C) 1999,2003, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; + +import java.math.BigInteger; +import java.security.Principal; +import java.util.Date; +import java.util.List; + +/** + * X509Certificate is the abstract class for X.509 certificates. + * This provides a stanard class interface for accessing all + * the attributes of X.509 certificates. + * + *

        In June 1996, the basic X.509 v3 format was finished by + * ISO/IEC and ANSI X.9. The ASN.1 DER format is below: + * + *

        + * Certificate  ::=  SEQUENCE  {
        + *   tbsCertificate       TBSCertificate,
        + *   signatureAlgorithm   AlgorithmIdentifier,
        + *   signatureValue       BIT STRING  }
        + * 
        + * + *

        These certificates are widely used in various Internet + * protocols to support authentication. It is used in + * Privacy Enhanced Mail (PEM), Transport Layer Security (TLS), + * Secure Sockets Layer (SSL), code signing for trusted software + * distribution, and Secure Electronic Transactions (SET). + * + *

        The certificates are managed and vouched for by + * Certificate Authorities (CAs). CAs are companies or + * groups that create certificates by placing the data in the + * X.509 certificate format and signing it with their private + * key. CAs serve as trusted third parties by certifying that + * the person or group specified in the certificate is who + * they say they are. + * + *

        The ASN.1 defintion for tbsCertificate is + * + *

        + * TBSCertificate  ::=  SEQUENCE  {
        + *   version         [0]  EXPLICIT Version DEFAULT v1,
        + *   serialNumber         CertificateSerialNumber,
        + *   signature            AlgorithmIdentifier,
        + *   issuer               Name,
        + *   validity             Validity,
        + *   subject              Name,
        + *   subjectPublicKeyInfo SubjectPublicKeyInfo,
        + *   issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
        + *                        -- If present, version shall be v2 or v3
        + *   subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
        + *                        -- If present, version shall be v2 or v3
        + *   extensions      [3]  EXPLICIT Extensions OPTIONAL
        + *                        -- If present, version shall be v3
        + * }
        + *
        + * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
        + *
        + * CertificateSerialNumber  ::=  INTEGER
        + *
        + * Validity ::= SEQUENCE {
        + *   notBefore      Time,
        + *   notAfter       Time }
        + *
        + * Time ::= CHOICE {
        + *   utcTime        UTCTime,
        + *   generalTime    GeneralizedTime }
        + *
        + * UniqueIdentifier  ::=  BIT STRING
        + *
        + * SubjectPublicKeyInfo  ::=  SEQUENCE  {
        + *   algorithm            AlgorithmIdentifier,
        + *   subjectPublicKey     BIT STRING  }
        + *
        + * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
        + *
        + * Extension  ::=  SEQUENCE  {
        + *   extnID      OBJECT IDENTIFIER,
        + *   critical    BOOLEAN DEFAULT FALSE,
        + *   extnValue   OCTET STRING  }
        + * 
        + * + * Certificates are created with the CertificateFactory. + * + *

        References: + * + *

          + *
        1. Olivier Dubuisson, Philippe Fouquart (Translator) ASN.1 - + * Communication between heterogeneous systems, (C) September 2000, + * Morgan Kaufmann Publishers, ISBN 0-12-6333361-0. Available on-line at + * http://www.oss.com/asn1/dubuisson.html
        2. + *
        3. R. Housley et al, RFC + * 3280: Internet X.509 Public Key Infrastructure Certificate and CRL + * Profile.
        4. + *
        + * + * @since 1.2 + * @author Mark Benvenuto + * @author Casey Marshall (rsdio@metastatic.org) + */ +public abstract class X509Certificate + extends Certificate + implements X509Extension +{ + private static final long serialVersionUID = -2491127588187038216L; + + /** + * Constructs a new certificate of the specified type. + */ + protected X509Certificate() + { + super( "X.509" ); + } + + /** + Checks the validity of the X.509 certificate. It is valid + if the current date and time are within the period specified + by the certificate. + + The ASN.1 DER encoding is: + + validity Validity, + + Validity ::= SEQUENCE { + notBefore Time, + notAfter Time } + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Consult rfc2459 for more information. + + @throws CertificateExpiredException if the certificate expired + @throws CertificateNotYetValidException if the certificate is + not yet valid + */ + public abstract void checkValidity() + throws CertificateExpiredException, + CertificateNotYetValidException; + + /** + Checks the validity of the X.509 certificate for the + specified time and date. It is valid if the specified + date and time are within the period specified by + the certificate. + + @throws CertificateExpiredException if the certificate expired + based on the date + @throws CertificateNotYetValidException if the certificate is + not yet valid based on the date + */ + public abstract void checkValidity(Date date) + throws CertificateExpiredException, + CertificateNotYetValidException; + + /** + Returns the version of this certificate. + + The ASN.1 DER encoding is: + + version [0] EXPLICIT Version DEFAULT v1, + + Version ::= INTEGER { v1(0), v2(1), v3(2) } + + Consult rfc2459 for more information. + + @return version number of certificate + */ + public abstract int getVersion(); + + /** + Gets the serial number for serial Number in + this Certifcate. It must be a unique number + unique other serial numbers from the granting CA. + + The ASN.1 DER encoding is: + + serialNumber CertificateSerialNumber, + + CertificateSerialNumber ::= INTEGER + + Consult rfc2459 for more information. + + @return the serial number for this X509CRLEntry. + */ + public abstract BigInteger getSerialNumber(); + + /** + Returns the issuer (issuer distinguished name) of the + Certificate. The issuer is the entity who signed + and issued the Certificate. + + The ASN.1 DER encoding is: + + issuer Name, + + Name ::= CHOICE { + RDNSequence } + + RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + + RelativeDistinguishedName ::= + SET OF AttributeTypeAndValue + + AttributeTypeAndValue ::= SEQUENCE { + type AttributeType, + value AttributeValue } + + AttributeType ::= OBJECT IDENTIFIER + + AttributeValue ::= ANY DEFINED BY AttributeType + + DirectoryString ::= CHOICE { + teletexString TeletexString (SIZE (1..MAX)), + printableString PrintableString (SIZE (1..MAX)), + universalString UniversalString (SIZE (1..MAX)), + utf8String UTF8String (SIZE (1.. MAX)), + bmpString BMPString (SIZE (1..MAX)) } + + Consult rfc2459 for more information. + + @return the issuer in the Principal class + */ + public abstract Principal getIssuerDN(); + + /** + Returns the subject (subject distinguished name) of the + Certificate. The subject is the entity who the Certificate + identifies. + + The ASN.1 DER encoding is: + + subject Name, + + Consult rfc2459 for more information. + + @return the issuer in the Principal class + */ + public abstract Principal getSubjectDN(); + + /** + Returns the date that this certificate is not to be used + before, notBefore. + + The ASN.1 DER encoding is: + + validity Validity, + + Validity ::= SEQUENCE { + notBefore Time, + notAfter Time } + + Time ::= CHOICE { + utcTime UTCTime, + generalTime GeneralizedTime } + + Consult rfc2459 for more information. + + @return the date notBefore + */ + public abstract Date getNotBefore(); + + /** + Returns the date that this certificate is not to be used + after, notAfter. + + @return the date notAfter + */ + public abstract Date getNotAfter(); + + + /** + Returns the tbsCertificate from the certificate. + + @return the DER encoded tbsCertificate + + @throws CertificateEncodingException if encoding error occurred + */ + public abstract byte[] getTBSCertificate() throws CertificateEncodingException; + + /** + Returns the signature in its raw DER encoded format. + + The ASN.1 DER encoding is: + + signatureValue BIT STRING + + Consult rfc2459 for more information. + + @return byte array representing signature + */ + public abstract byte[] getSignature(); + + /** + Returns the signature algorithm used to sign the CRL. + An examples is "SHA-1/DSA". + + The ASN.1 DER encoding is: + + signatureAlgorithm AlgorithmIdentifier, + + AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + + Consult rfc2459 for more information. + + The algorithm name is determined from the OID. + + @return a string with the signature algorithm name + */ + public abstract String getSigAlgName(); + + + /** + Returns the OID for the signature algorithm used. + Example "1.2.840.10040.4.3" is return for SHA-1 with DSA.\ + + The ASN.1 DER encoding for the example is: + + id-dsa-with-sha1 ID ::= { + iso(1) member-body(2) us(840) x9-57 (10040) + x9cm(4) 3 } + + Consult rfc2459 for more information. + + @return a string containing the OID. + */ + public abstract String getSigAlgOID(); + + + /** + Returns the AlgorithmParameters in the encoded form + for the signature algorithm used. + + If access to the parameters is need, create an + instance of AlgorithmParameters. + + @return byte array containing algorithm parameters, null + if no parameters are present in certificate + */ + public abstract byte[] getSigAlgParams(); + + + /** + Returns the issuer unique ID for this certificate. + + The ASN.1 DER encoding is: + + issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + -- If present, version shall be v2 or v3 + + UniqueIdentifier ::= BIT STRING + + Consult rfc2459 for more information. + + @return bit representation of issuerUniqueID + */ + public abstract boolean[] getIssuerUniqueID(); + + /** + Returns the subject unique ID for this certificate. + + The ASN.1 DER encoding is: + + subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + -- If present, version shall be v2 or v3 + + UniqueIdentifier ::= BIT STRING + + Consult rfc2459 for more information. + + @return bit representation of subjectUniqueID + */ + public abstract boolean[] getSubjectUniqueID(); + + /** + Returns a boolean array representing the KeyUsage + extension for the certificate. The KeyUsage (OID = 2.5.29.15) + defines the purpose of the key in the certificate. + + The ASN.1 DER encoding is: + + id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } + + KeyUsage ::= BIT STRING { + digitalSignature (0), + nonRepudiation (1), + keyEncipherment (2), + dataEncipherment (3), + keyAgreement (4), + keyCertSign (5), + cRLSign (6), + encipherOnly (7), + decipherOnly (8) } + + Consult rfc2459 for more information. + + @return bit representation of KeyUsage + */ + public abstract boolean[] getKeyUsage(); + + /** + Returns the certificate constraints path length from the + critical BasicConstraints extension, (OID = 2.5.29.19). + + The basic constraints extensions is used to determine if + the subject of the certificate is a Certificate Authority (CA) + and how deep the certification path may exist. The + pathLenConstraint only takes affect if cA + is set to true. "A value of zero indicates that only an + end-entity certificate may follow in the path." (rfc2459) + + The ASN.1 DER encoding is: + + id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } + + BasicConstraints ::= SEQUENCE { + cA BOOLEAN DEFAULT FALSE, + pathLenConstraint INTEGER (0..MAX) OPTIONAL } + + Consult rfc2459 for more information. + + @return the length of the path constraint if BasicConstraints + is present and cA is TRUE. Otherwise returns -1. + */ + public abstract int getBasicConstraints(); + + // 1.4 instance methods. + // ------------------------------------------------------------------------ + + /** + * Returns the ExtendedKeyUsage extension of this + * certificate, or null if there is no extension present. The returned + * value is a {@link java.util.List} strings representing the object + * identifiers of the extended key usages. This extension has the OID + * 2.5.29.37. + * + *

        The ASN.1 definition for this extension is: + * + *

        +   * ExtendedKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
        +   *
        +   * KeyPurposeId ::= OBJECT IDENTIFIER
        +   * 
        + * + * @return The list of extension OIDs, or null if there are none + * present in this certificate. + * @throws CertificateParsingException If this extension cannot be + * parsed from its encoded form. + */ + public java.util.List getExtendedKeyUsage() + throws CertificateParsingException + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the alternative names for this certificate's subject (the + * owner), or null if there are none. + * + *

        This is an X.509 extension with OID 2.5.29.17 and is defined by + * the ASN.1 construction: + * + *

        +   * SubjectAltNames ::= GeneralNames
        +   *
        +   * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
        +   *
        +   * GeneralName ::= CHOICE {
        +   *   otherName                 [0]   OtherName,
        +   *   rfc822Name                [1]   IA5String,
        +   *   dNSName                   [2]   IA5String,
        +   *   x400Address               [3]   ORAddress,
        +   *   directoryName             [4]   Name,
        +   *   ediPartyName              [5]   EDIPartyName,
        +   *   uniformResourceIdentifier [6]   IA5String,
        +   *   iPAddress                 [7]   OCTET STRING,
        +   *   registeredID              [8]   OBJECT IDENTIFIER
        +   * }
        +   * 
        + * + *

        The returned collection contains one or more two-element Lists, + * with the first object being an Integer representing the choice + * above (with value 0 through 8) and the second being an (a) String + * if the GeneralName is a rfc822Name, dNSName, + * uniformResourceIdentifier, iPAddress, or registeredID, or (b) a + * byte array of the DER encoded form for any others. + * + * @return The collection of alternative names, or null if there are + * none. + * @throws CertificateParsingException If the encoded extension cannot + * be parsed. + * @since JDK 1.4 + */ + public java.util.Collection> getSubjectAlternativeNames() + throws CertificateParsingException + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the alternative names for this certificate's issuer, or + * null if there are none. + * + *

        This is an X.509 extension with OID 2.5.29.18, and is defined by + * the ASN.1 construction: + * + *

        +   * IssuerAltNames ::= GeneralNames
        +   * 
        + * + *

        The GeneralNames construct and the form of the + * returned collection are the same as with {@link + * #getSubjectAlternativeNames()}. + * + * @return The collection of alternative names, or null if there are + * none. + * @throws CertificateParsingException If the encoded extension cannot + * be parsed. + * @since JDK 1.4 + */ + public java.util.Collection> getIssuerAlternativeNames() + throws CertificateParsingException + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the X.500 distinguished name of this certificate's subject. + * + * @return The subject's X.500 distinguished name. + * @since JDK 1.4 + */ + public javax.security.auth.x500.X500Principal getSubjectX500Principal() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns the X.500 distinguished name of this certificate's issuer. + * + * @return The issuer's X.500 distinguished name. + * @since JDK 1.4 + */ + public javax.security.auth.x500.X500Principal getIssuerX500Principal() + { + throw new UnsupportedOperationException(); + } +} diff --git a/libjava/classpath/java/security/cert/X509Extension.java b/libjava/classpath/java/security/cert/X509Extension.java new file mode 100644 index 000000000..a0c24f429 --- /dev/null +++ b/libjava/classpath/java/security/cert/X509Extension.java @@ -0,0 +1,113 @@ +/* X509Extension.java --- X.509 Extension + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.cert; +import java.util.Set; + +/** + Public interface for the X.509 Extension. + + This is used for X.509 v3 Certificates and CRL v2 (Certificate + Revocation Lists) for managing attributes assoicated with + Certificates, for managing the hierarchy of certificates, + and for managing the distribution of CRL. This extension + format is used to define private extensions. + + Each extensions for a certificate or CRL must be marked + either critical or non-critical. If the certificate/CRL + system encounters a critical extension not recognized then + it must reject the certificate. A non-critical extension + may be just ignored if not recognized. + + + The ASN.1 definition for this class is: + + Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + + Extension ::= SEQUENCE { + extnId OBJECT IDENTIFIER, + critical BOOLEAN DEFAULT FALSE, + extnValue OCTET STRING + -- contains a DER encoding of a value + -- of the type registered for use with + -- the extnId object identifier value + } + + @author Mark Benvenuto + + @since 1.2 +*/ +public interface X509Extension +{ + + /** + Returns true if the certificate contains a critical extension + that is not supported. + + @return true if has unsupported extension, false otherwise + */ + boolean hasUnsupportedCriticalExtension(); + + /** + Returns a set of the CRITICAL extension OIDs from the + certificate/CRL that the object implementing this interface + manages. + + @return A Set containing the OIDs. If there are no CRITICAL + extensions or extensions at all this returns null. + */ + Set getCriticalExtensionOIDs(); + + /** + Returns a set of the NON-CRITICAL extension OIDs from the + certificate/CRL that the object implementing this interface + manages. + + @return A Set containing the OIDs. If there are no NON-CRITICAL + extensions or extensions at all this returns null. + */ + Set getNonCriticalExtensionOIDs(); + + /** + Returns the DER encoded OCTET string for the specified + extension value identified by a OID. The OID is a string + of number separated by periods. Ex: 12.23.45.67 + */ + byte[] getExtensionValue(String oid); + +} diff --git a/libjava/classpath/java/security/cert/package.html b/libjava/classpath/java/security/cert/package.html new file mode 100644 index 000000000..14b12d16c --- /dev/null +++ b/libjava/classpath/java/security/cert/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.security.cert + + +

        + + + diff --git a/libjava/classpath/java/security/interfaces/DSAKey.java b/libjava/classpath/java/security/interfaces/DSAKey.java new file mode 100644 index 000000000..c6e819eb0 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAKey.java @@ -0,0 +1,56 @@ +/* DSAKey.java -- Interface for Digital Signature Algorithm key + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +/** + * This interface is implemented by a class to return the parameters + * of a Digital Signature Algorithm (DSA) public or private key. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAKey +{ + /** + * This method returns non-secret parameters of the DSA key + * + * @return The DSA parameters + */ + DSAParams getParams(); +} diff --git a/libjava/classpath/java/security/interfaces/DSAKeyPairGenerator.java b/libjava/classpath/java/security/interfaces/DSAKeyPairGenerator.java new file mode 100644 index 000000000..e657c54b4 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAKeyPairGenerator.java @@ -0,0 +1,85 @@ +/* DSAKeyPairGenerator.java -- Initialize a DSA key generator + Copyright (C) 1998, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.security.InvalidParameterException; +import java.security.SecureRandom; + +/** + * This interface contains methods for intializing a Digital Signature + * Algorithm key generation engine. The initialize methods may be called + * any number of times. If no explicity initialization call is made, then + * the engine defaults to generating 1024-bit keys using pre-calculated + * base, prime, and subprime values. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAKeyPairGenerator +{ + /** + * Initializes the key generator with the specified DSA parameters and + * random bit source + * + * @param params The DSA parameters to use + * @param random The random bit source to use + * + * @exception InvalidParameterException If the parameters passed are not valid + */ + void initialize (DSAParams params, SecureRandom random) + throws InvalidParameterException; + + /** + * Initializes the key generator to a give modulus. If the genParams + * value is true then new base, prime, and subprime values + * will be generated for the given modulus. If not, the pre-calculated + * values will be used. If no pre-calculated values exist for the specified + * modulus, an exception will be thrown. It is guaranteed that there will + * always be pre-calculated values for all modulus values between 512 and + * 1024 bits inclusives. + * + * @param modlen The modulus length + * @param genParams true to generate new DSA parameters, false otherwise + * @param random The random bit source to use + * + * @exception InvalidParameterException If a parameter is invalid + */ + void initialize (int modlen, boolean genParams, SecureRandom random) + throws InvalidParameterException; +} diff --git a/libjava/classpath/java/security/interfaces/DSAParams.java b/libjava/classpath/java/security/interfaces/DSAParams.java new file mode 100644 index 000000000..42baeeb95 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAParams.java @@ -0,0 +1,72 @@ +/* DSAParams.java -- Digital Signature Algorithm parameter access + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; + +/** + * This interface allows the Digital Signature Algorithm (DSA) parameters + * to be queried. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAParams +{ + /** + * Returns the base, or 'g' value + * + * @return The DSA base value + */ + BigInteger getG(); + + /** + * Returns the prime, or 'p' value + * + * @return The DSA prime value + */ + BigInteger getP(); + + /** + * Returns the subprime, or 'q' value + * + * @return The DSA subprime value + */ + BigInteger getQ(); +} diff --git a/libjava/classpath/java/security/interfaces/DSAPrivateKey.java b/libjava/classpath/java/security/interfaces/DSAPrivateKey.java new file mode 100644 index 000000000..d79b34b90 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAPrivateKey.java @@ -0,0 +1,61 @@ +/* DSAPublicKey.java -- A Digital Signature Algorithm private key + Copyright (C) 1998, 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.PrivateKey; + +/** + * This interface models a Digital Signature Algorithm (DSA) private key + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAPrivateKey extends DSAKey, PrivateKey +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 7776497482533790279L; + + /** + * This method returns the value of the DSA private key + */ + BigInteger getX(); +} diff --git a/libjava/classpath/java/security/interfaces/DSAPublicKey.java b/libjava/classpath/java/security/interfaces/DSAPublicKey.java new file mode 100644 index 000000000..d73e189f6 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/DSAPublicKey.java @@ -0,0 +1,61 @@ +/* DSAPublicKey.java -- A Digital Signature Algorithm public key + Copyright (C) 1998, 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.PublicKey; + +/** + * This interface models a Digital Signature Algorithm (DSA) public key + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface DSAPublicKey extends DSAKey, PublicKey +{ + /** + * The version identifier used for serialization. + */ + long serialVersionUID = 1234526332779022332L; + + /** + * This method returns the value of the DSA public key + */ + BigInteger getY(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAKey.java b/libjava/classpath/java/security/interfaces/RSAKey.java new file mode 100644 index 000000000..485fa81e0 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAKey.java @@ -0,0 +1,57 @@ +/* RSAKey.java --- A generic RSA Key interface + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; + +/** + A generic RSA Key interface for public and private keys + + @since JDK 1.3 + + @author Mark Benvenuto + */ +public interface RSAKey +{ + /** + Generates a modulus. + + @returns a modulus + */ + BigInteger getModulus(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java b/libjava/classpath/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java new file mode 100644 index 000000000..da7d7479d --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAMultiPrimePrivateCrtKey.java @@ -0,0 +1,112 @@ +/* RSAMultiPrimePrivateCrtKey.java -- + Copyright (C) 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.spec.RSAOtherPrimeInfo; + +/** + * The interface to an RSA multi-prime private key, as defined in the PKCS#1 + * v2.1, using the Chinese Remainder Theorem (CRT) information values. + * + * @since 1.4 + * @see java.security.spec.RSAPrivateKeySpec + * @see java.security.spec.RSAMultiPrimePrivateCrtKeySpec + * @see RSAPrivateKey + * @see RSAPrivateCrtKey + */ +public interface RSAMultiPrimePrivateCrtKey extends RSAPrivateKey +{ + // Constants + // -------------------------------------------------------------------------- + + long serialVersionUID = 618058533534628008L; + + // Methods + // -------------------------------------------------------------------------- + + /** + * Returns the public exponent. + * + * @return the public exponent. + */ + BigInteger getPublicExponent(); + + /** + * Returns the prime p. + * + * @return the prime p. + */ + BigInteger getPrimeP(); + + /** + * Returns the prime q. + * + * @return the prime q. + */ + BigInteger getPrimeQ(); + + /** + * Returns the prime's exponent p. + * + * @return the prime's exponent p. + */ + BigInteger getPrimeExponentP(); + + /** + * Returns the prime's exponent q. + * + * @return the prime's exponent q. + */ + BigInteger getPrimeExponentQ(); + + /** + * Returns the CRT Coefficient. + * + * @return the CRT Coefficient. + */ + BigInteger getCrtCoefficient(); + + /** + * Returns the OtherPrimeInfo triplet MPIs or null if + * there are only two known prime factors (p and q). + * + * @return the OtherPrimeInfo INTEGERs. + */ + RSAOtherPrimeInfo[] getOtherPrimeInfo(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAPrivateCrtKey.java b/libjava/classpath/java/security/interfaces/RSAPrivateCrtKey.java new file mode 100644 index 000000000..96a1496cf --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAPrivateCrtKey.java @@ -0,0 +1,95 @@ +/* RSAPrivateCrtKey.java -- An RSA private key in CRT format + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; + +/** + * This interface provides access to information about an RSA private + * key in Chinese Remainder Theorem (CRT) format. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface RSAPrivateCrtKey extends RSAPrivateKey +{ + long serialVersionUID = -5682214253527700368L; + + /** + * Returns the public exponent for this key + * + * @return The public exponent for this key + */ + BigInteger getPublicExponent(); + + /** + * Returns the primeP value + * + * @return The primeP value + */ + BigInteger getPrimeP(); + + /** + * Returns the primeQ value + * + * @return The primeQ value + */ + BigInteger getPrimeQ(); + + /** + * Returns the primeExponentP + * + * @return The primeExponentP + */ + BigInteger getPrimeExponentP(); + + /** + * Returns the primeExponentQ + * + * @return The primeExponentQ + */ + BigInteger getPrimeExponentQ(); + + /** + * Returns the CRT coefficient + * + * @return The CRT coefficient + */ + BigInteger getCrtCoefficient(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAPrivateKey.java b/libjava/classpath/java/security/interfaces/RSAPrivateKey.java new file mode 100644 index 000000000..514987625 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAPrivateKey.java @@ -0,0 +1,60 @@ +/* RSAPrivateKey.java -- An RSA private key + Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.PrivateKey; + +/** + * This interface provides access to information about an RSA private key. + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface RSAPrivateKey extends PrivateKey, RSAKey +{ + long serialVersionUID = 5187144804936595022L; + + /** + * Returns the private exponent value for this key + * + * @return The private exponent value for this key + */ + BigInteger getPrivateExponent(); +} diff --git a/libjava/classpath/java/security/interfaces/RSAPublicKey.java b/libjava/classpath/java/security/interfaces/RSAPublicKey.java new file mode 100644 index 000000000..5fb569d1d --- /dev/null +++ b/libjava/classpath/java/security/interfaces/RSAPublicKey.java @@ -0,0 +1,60 @@ +/* RSAPublicKey.java -- An RSA public key + Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.interfaces; + +import java.math.BigInteger; +import java.security.PublicKey; + +/** + * This interface provides access to information about an RSA public key. + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface RSAPublicKey extends PublicKey, RSAKey +{ + long serialVersionUID = -8727434096241101194L; + + /** + * Returns the public exponent value for this key + * + * @return The public exponent value for this key + */ + BigInteger getPublicExponent(); +} diff --git a/libjava/classpath/java/security/interfaces/package.html b/libjava/classpath/java/security/interfaces/package.html new file mode 100644 index 000000000..aab0d6375 --- /dev/null +++ b/libjava/classpath/java/security/interfaces/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.security.interfaces + + +

        + + + diff --git a/libjava/classpath/java/security/package.html b/libjava/classpath/java/security/package.html new file mode 100644 index 000000000..328b7044b --- /dev/null +++ b/libjava/classpath/java/security/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.security + + +

        + + + diff --git a/libjava/classpath/java/security/spec/AlgorithmParameterSpec.java b/libjava/classpath/java/security/spec/AlgorithmParameterSpec.java new file mode 100644 index 000000000..bc877e312 --- /dev/null +++ b/libjava/classpath/java/security/spec/AlgorithmParameterSpec.java @@ -0,0 +1,52 @@ +/* AlgorithmParameterSpec.java --- Algorithm Parameter Spec Interface + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + A transparent interface for Algorithm Parameter Specifications. + It contains no member functions. It is used to group + algorithm parameter classes. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public interface AlgorithmParameterSpec +{ +} diff --git a/libjava/classpath/java/security/spec/DSAParameterSpec.java b/libjava/classpath/java/security/spec/DSAParameterSpec.java new file mode 100644 index 000000000..f7f673110 --- /dev/null +++ b/libjava/classpath/java/security/spec/DSAParameterSpec.java @@ -0,0 +1,101 @@ +/* DSAParameterSpec.java --- DSA Parameter Specificaton class + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +import java.math.BigInteger; +import java.security.interfaces.DSAParams; + +/** + * DSA Parameter class Specification. Used to maintain the DSA + * Parameters. + * + * @since 1.2 + * + * @author Mark Benvenuto +*/ +public class DSAParameterSpec implements AlgorithmParameterSpec, DSAParams +{ + private BigInteger p = null; + private BigInteger q = null; + private BigInteger g = null; + + /** + * Constructs a new DSAParameterSpec with the specified p, q, and g. + * + * @param p the prime + * @param q the sub-prime + * @param g the base + */ + public DSAParameterSpec(BigInteger p, BigInteger q, BigInteger g) + { + this.p = p; + this.q = q; + this.g = g; + } + + /** + * Returns p for the DSA algorithm. + * + * @return Returns the requested BigInteger + */ + public BigInteger getP() + { + return this.p; + } + + /** + * Returns p for the DSA algorithm. + * + * @return Returns the requested BigInteger + */ + public BigInteger getQ() + { + return this.q; + } + + /** + * Returns g for the DSA algorithm. + * + * @return Returns the requested BigInteger + */ + public BigInteger getG() + { + return this.g; + } +} diff --git a/libjava/classpath/java/security/spec/DSAPrivateKeySpec.java b/libjava/classpath/java/security/spec/DSAPrivateKeySpec.java new file mode 100644 index 000000000..19af107e6 --- /dev/null +++ b/libjava/classpath/java/security/spec/DSAPrivateKeySpec.java @@ -0,0 +1,113 @@ +/* DSAPrivateKeySpec.java --- DSA Private Key Specificaton class + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + DSA Private Key class Specification. Used to maintain the DSA + Private Keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class DSAPrivateKeySpec implements KeySpec +{ + private BigInteger x = null; + private BigInteger p = null; + private BigInteger q = null; + private BigInteger g = null; + + /** + Constructs a new DSAPrivateKeySpec with the specified x, p, q, and g. + + @param x the private key + @param p the prime + @param q the sub-prime + @param g the base + */ + public DSAPrivateKeySpec(BigInteger x, BigInteger p, BigInteger q, BigInteger g) + { + this.x = x; + this.p = p; + this.q = q; + this.g = g; + } + + /** + Returns private key x for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getX() + { + return this.x; + } + + /** + Returns p for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getP() + { + return this.p; + } + + /** + Returns p for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getQ() + { + return this.q; + } + + /** + Returns g for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getG() + { + return this.g; + } + +} diff --git a/libjava/classpath/java/security/spec/DSAPublicKeySpec.java b/libjava/classpath/java/security/spec/DSAPublicKeySpec.java new file mode 100644 index 000000000..751844bcc --- /dev/null +++ b/libjava/classpath/java/security/spec/DSAPublicKeySpec.java @@ -0,0 +1,113 @@ +/* DSAPublicKeySpec.java --- DSA Public Key Specificaton class + Copyright (C) 1999, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + DSA Public Key class Specification. Used to maintain the DSA + Public Keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class DSAPublicKeySpec implements KeySpec +{ + private BigInteger y = null; + private BigInteger p = null; + private BigInteger q = null; + private BigInteger g = null; + + /** + Constructs a new DSAPublicKeySpec with the specified y, p, q, and g. + + @param y the public key + @param p the prime + @param q the sub-prime + @param g the base + */ + public DSAPublicKeySpec(BigInteger y, BigInteger p, BigInteger q, BigInteger g) + { + this.y = y; + this.p = p; + this.q = q; + this.g = g; + } + + /** + Returns public key y for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getY() + { + return this.y; + } + + /** + Returns p for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getP() + { + return this.p; + } + + /** + Returns p for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getQ() + { + return this.q; + } + + /** + Returns g for the DSA algorithm. + + @return Returns the requested BigInteger + */ + public BigInteger getG() + { + return this.g; + } + +} diff --git a/libjava/classpath/java/security/spec/EncodedKeySpec.java b/libjava/classpath/java/security/spec/EncodedKeySpec.java new file mode 100644 index 000000000..93e158385 --- /dev/null +++ b/libjava/classpath/java/security/spec/EncodedKeySpec.java @@ -0,0 +1,85 @@ +/* EncodedKeySpec.java --- Encoded Key Specificaton class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + Encoded Key Specification class which is used to store + byte encoded keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public abstract class EncodedKeySpec implements KeySpec +{ + + private byte[] encodedKey; + + /** + Constructs a new EncodedKeySpec with the specified encoded key. + + @param encodedKey A key to store + */ + public EncodedKeySpec(byte[] encodedKey) + { + this.encodedKey = encodedKey; + } + + /** + Gets the encoded key in byte format. + + @returns the encoded key + */ + public byte[] getEncoded() + { + return this.encodedKey; + } + + /** + Returns the name of the key format used. + + This name is the format such as "PKCS#8" or "X.509" which + if it matches a Key class name of the same type can be + transformed using the apporiate KeyFactory. + + @return a string representing the name + */ + public abstract String getFormat(); + +} diff --git a/libjava/classpath/java/security/spec/InvalidKeySpecException.java b/libjava/classpath/java/security/spec/InvalidKeySpecException.java new file mode 100644 index 000000000..bbbbcc6d7 --- /dev/null +++ b/libjava/classpath/java/security/spec/InvalidKeySpecException.java @@ -0,0 +1,96 @@ +/* InvalidKeySpecException.java -- invalid KeySpec Exception + Copyright (C) 1999, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +import java.security.GeneralSecurityException; + +/** + * Exception for an invalid key specification. + * + * @author Mark Benvenuto + * @see KeySpec + * @since 1.2 + * @status updated to 1.5 + */ +public class InvalidKeySpecException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 3546139293998810778L; + + /** + * Constructs an InvalidKeySpecException without a message string. + */ + public InvalidKeySpecException() + { + } + + /** + * Constructs an InvalidKeySpecException with a message string. + * + * @param msg a message to display with exception + */ + public InvalidKeySpecException(String msg) + { + super(msg); + } + + /** + * Create a new instance with a descriptive error message and + * a cause. + * @param s the descriptive error message + * @param cause the cause + * @since 1.5 + */ + public InvalidKeySpecException(String s, Throwable cause) + { + super(s, cause); + } + + /** + * Create a new instance with a cause. + * @param cause the cause + * @since 1.5 + */ + public InvalidKeySpecException(Throwable cause) + { + super(cause); + } +} diff --git a/libjava/classpath/java/security/spec/InvalidParameterSpecException.java b/libjava/classpath/java/security/spec/InvalidParameterSpecException.java new file mode 100644 index 000000000..ff34565f1 --- /dev/null +++ b/libjava/classpath/java/security/spec/InvalidParameterSpecException.java @@ -0,0 +1,76 @@ +/* InvalidParameterSpecException.java --- invalid ParameterSpec Exception + Copyright (C) 1999, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +import java.security.GeneralSecurityException; + +/** + * Exception for an invalid algorithm specification. + * + * @author Mark Benvenuto + * @see AlogorithmParameters + * @see AlogorithmParameterSpec + * @see DSAParameterSpec + * @since 1.2 + * @status updated to 1.4 +*/ +public class InvalidParameterSpecException extends GeneralSecurityException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = -970468769593399342L; + + /** + * Constructs an InvalidParameterSpecException without a message string. + */ + public InvalidParameterSpecException() + { + } + + /** + * Constructs an InvalidParameterSpecException with a message string. + * + * @param msg a message to display with exception + */ + public InvalidParameterSpecException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/security/spec/KeySpec.java b/libjava/classpath/java/security/spec/KeySpec.java new file mode 100644 index 000000000..13c7dad42 --- /dev/null +++ b/libjava/classpath/java/security/spec/KeySpec.java @@ -0,0 +1,52 @@ +/* KeySpec.java --- Key Specification interface + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + A transparent interface for Key Specifications. + It contains no member functions. It is used to group + key classes. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public interface KeySpec +{ +} diff --git a/libjava/classpath/java/security/spec/PKCS8EncodedKeySpec.java b/libjava/classpath/java/security/spec/PKCS8EncodedKeySpec.java new file mode 100644 index 000000000..53b68de47 --- /dev/null +++ b/libjava/classpath/java/security/spec/PKCS8EncodedKeySpec.java @@ -0,0 +1,81 @@ +/* PKCS8EncodedKeySpec.java --- PKCS8 Encoded Key Specificaton class + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + PKCS8 Encoded Key Specification class which is used to store + "PKCS#8" byte encoded keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class PKCS8EncodedKeySpec extends EncodedKeySpec +{ + /** + Constructs a new PKCS8EncodedKeySpec with the specified encoded key. + + @param encodedKey A key to store, assumed to be "PKCS#8" + */ + public PKCS8EncodedKeySpec(byte[] encodedKey) + { + super( encodedKey ); + } + + /** + Gets the encoded key in byte format. + + @returns the encoded key +*/ + public byte[] getEncoded() + { + return super.getEncoded(); + } + + /** + Returns the name of the key format used which is "PKCS#8" + + @return a string representing the name +*/ + public final String getFormat() + { + return "PKCS#8"; + } + +} diff --git a/libjava/classpath/java/security/spec/PSSParameterSpec.java b/libjava/classpath/java/security/spec/PSSParameterSpec.java new file mode 100644 index 000000000..92a6c9edd --- /dev/null +++ b/libjava/classpath/java/security/spec/PSSParameterSpec.java @@ -0,0 +1,87 @@ +/* PSSParameterSpec.java -- + Copyright (C) 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.spec; + +/** + * An implementation of {@link AlgorithmParameterSpec} for the RSA PSS encoding + * scheme. + * + * @since 1.4 + * @see AlgorithmParameterSpec + * @see java.security.Signature + */ +public class PSSParameterSpec implements AlgorithmParameterSpec +{ + // Constants and fields + // -------------------------------------------------------------------------- + + private int saltLen; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** + * Construct a new instance of PSSParameterSpec given a salt + * length. + * + * @param saltLen + * the length in bits of the salt. + * @throws IllegalArgumentException + * if saltLen is less than 0. + */ + public PSSParameterSpec(int saltLen) + { + super(); + + if (saltLen < 0) + throw new IllegalArgumentException(); + this.saltLen = saltLen; + } + + // Class methods + // -------------------------------------------------------------------------- + + // Instance methods + // -------------------------------------------------------------------------- + + /** @return the length (in bits) of the salt. */ + public int getSaltLength() + { + return this.saltLen; + } +} diff --git a/libjava/classpath/java/security/spec/RSAKeyGenParameterSpec.java b/libjava/classpath/java/security/spec/RSAKeyGenParameterSpec.java new file mode 100644 index 000000000..5a1dafe2a --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAKeyGenParameterSpec.java @@ -0,0 +1,97 @@ +/* RSAKeyGenParameterSpec.java --- RSA Key Generator Parameter Spec Class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + This class generates a set of RSA Key parameters used in the generation + of RSA keys. + + @since JDK 1.3 + + @author Mark Benvenuto +*/ +public class RSAKeyGenParameterSpec implements AlgorithmParameterSpec +{ + private int keysize; + private BigInteger publicExponent; + + /** + Public Exponent F0 = 3 + */ + public static final BigInteger F0 = BigInteger.valueOf(3); + + /** + Public Exponent F4 = 3 + */ + public static final BigInteger F4 = BigInteger.valueOf(65537L); + + /** + Create a new RSAKeyGenParameterSpec to store the RSA key's keysize + and public exponent + + @param keysize Modulus size of key in bits + @param publicExponent - the exponent + */ + public RSAKeyGenParameterSpec(int keysize, BigInteger publicExponent) + { + this.keysize = keysize; + this.publicExponent = publicExponent; + } + + /** + Return the size of the key. + + @return the size of the key. + */ + public int getKeysize() + { + return keysize; + } + + /** + Return the public exponent. + + @return the public exponent. + */ + public BigInteger getPublicExponent() + { + return publicExponent; + } +} diff --git a/libjava/classpath/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java b/libjava/classpath/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java new file mode 100644 index 000000000..09b8438df --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAMultiPrimePrivateCrtKeySpec.java @@ -0,0 +1,223 @@ +/* PSSParameterSpec.java -- + Copyright (C) 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.spec; + +import java.math.BigInteger; + +/** + * This class represents an RSA multi-prime private key, as defined in the + * PKCS#1 v2.1, using the Chinese Remainder Theorem (CRT) information + * values. + * + * @since 1.4 + * @see java.security.Key + * @see java.security.KeyFactory + * @see KeySpec + * @see PKCS8EncodedKeySpec + * @see RSAPrivateKeySpec + * @see RSAPublicKeySpec + * @see RSAOtherPrimeInfo + */ +public class RSAMultiPrimePrivateCrtKeySpec extends RSAPrivateKeySpec +{ + // Constants and fields + // -------------------------------------------------------------------------- + + private BigInteger publicExponent; + private BigInteger primeP; + private BigInteger primeQ; + private BigInteger primeExponentP; + private BigInteger primeExponentQ; + private BigInteger crtCoefficient; + private RSAOtherPrimeInfo[] otherPrimeInfo; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** + * Constructs a new instance of RSAMultiPrimePrivateCrtKeySpec + * given the various PKCS#1 v2.1 parameters. + * + *

        Note that otherPrimeInfo is cloned when constructing this + * object.

        + * + * @param modulus + * the modulus n. + * @param publicExponent + * the public exponent e. + * @param privateExponent + * the private exponent d. + * @param primeP + * the prime factor p of n. + * @param primeQ + * the prime factor q of n. + * @param primeExponentP + * this is d mod (p-1). + * @param primeExponentQ + * this is d mod (q-1). + * @param crtCoefficient + * the Chinese Remainder Theorem coefficient q-1 mod p. + * @param otherPrimeInfo + * triplets of the rest of primes, null can be + * specified if there are only two prime factors (p and q). + * @throws NullPointerException + * if any of the parameters is null. + * @throws IllegalArgumentException + * if an empty otherPrimeInfo is specified. + */ + public RSAMultiPrimePrivateCrtKeySpec(BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger primeP, + BigInteger primeQ, + BigInteger primeExponentP, + BigInteger primeExponentQ, + BigInteger crtCoefficient, + RSAOtherPrimeInfo[] otherPrimeInfo) + { + super(modulus, privateExponent); + + if (modulus == null) + throw new NullPointerException("modulus"); + if (publicExponent == null) + throw new NullPointerException("publicExponent"); + if (privateExponent == null) + throw new NullPointerException("privateExponent"); + if (primeP == null) + throw new NullPointerException("primeP"); + if (primeQ == null) + throw new NullPointerException("primeQ"); + if (primeExponentP == null) + throw new NullPointerException("primeExponentP"); + if (primeExponentQ == null) + throw new NullPointerException("primeExponentQ"); + if (crtCoefficient == null) + throw new NullPointerException("crtCoefficient"); + if (otherPrimeInfo != null) + if (otherPrimeInfo.length == 0) + throw new IllegalArgumentException(); + else + this.otherPrimeInfo = (RSAOtherPrimeInfo[]) otherPrimeInfo.clone(); + + this.publicExponent = publicExponent; + this.primeP = primeP; + this.primeQ = primeQ; + this.primeExponentP = primeExponentP; + this.primeExponentQ = primeExponentQ; + this.crtCoefficient = crtCoefficient; + } + + // Class methods + // -------------------------------------------------------------------------- + + // Instance methods + // -------------------------------------------------------------------------- + + /** + * Returns the public exponent. + * + * @return the public exponent. + */ + public BigInteger getPublicExponent() + { + return this.publicExponent; + } + + /** + * Returns the prime p. + * + * @return the prime p. + */ + public BigInteger getPrimeP() + { + return this.primeP; + } + + /** + * Returns the prime q. + * + * @return the prime q. + */ + public BigInteger getPrimeQ() + { + return this.primeQ; + } + + /** + * Returns d mod (p-1). + * + * @return d mod (p-1). + */ + public BigInteger getPrimeExponentP() + { + return this.primeExponentP; + } + + /** + * Returns d mod (q-1). + * + * @return d mod (q-1). + */ + public BigInteger getPrimeExponentQ() + { + return this.primeExponentQ; + } + + /** + * Returns the CRT Coefficient q-1 mod p. + * + * @return the CRT Coefficient q-1 mod p. + */ + public BigInteger getCrtCoefficient() + { + return this.crtCoefficient; + } + + /** + * Returns a clone of otherPrimeInfo or null if + * it was null at construction time. + * + * @return a cloned copy of otherPrimeInfo. + */ + public RSAOtherPrimeInfo[] getOtherPrimeInfo() + { + return this.otherPrimeInfo == null + ? null + : (RSAOtherPrimeInfo[]) this.otherPrimeInfo.clone(); + } +} diff --git a/libjava/classpath/java/security/spec/RSAOtherPrimeInfo.java b/libjava/classpath/java/security/spec/RSAOtherPrimeInfo.java new file mode 100644 index 000000000..45dd53fab --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAOtherPrimeInfo.java @@ -0,0 +1,126 @@ +/* RSAOtherPrimeInfo.java -- + Copyright (C) 2003, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.security.spec; + +import java.math.BigInteger; + +/** + * An in-memory representation of the RSA triplet (prime, exponent, and + * coefficient) inside a PKCS#1 v2.1 OtherPrimeInfo structure. + * + * @since 1.4 + * @see RSAPrivateCrtKeySpec + * @see java.security.interfaces.RSAMultiPrimePrivateCrtKey + */ +public class RSAOtherPrimeInfo +{ + // Constants and fields + // -------------------------------------------------------------------------- + + private BigInteger prime; + private BigInteger primeExponent; + private BigInteger crtCoefficient; + + // Constructor(s) + // -------------------------------------------------------------------------- + + /** + * Constructs a new RSAOtherPrimeInfo given the PKCS#1 MPIs. + * + * @param prime + * the prime factor of n. + * @param primeExponent + * the exponent. + * @param crtCoefficient + * the Chinese Remainder Theorem coefficient. + * @throws NullPointerException + * if any of the parameters is null. + */ + public RSAOtherPrimeInfo(BigInteger prime, BigInteger primeExponent, + BigInteger crtCoefficient) + { + super(); + + if (prime == null) + throw new NullPointerException("prime"); + if (primeExponent == null) + throw new NullPointerException("primeExponent"); + if (crtCoefficient == null) + throw new NullPointerException("crtCoefficient"); + + this.prime = prime; + this.primeExponent = primeExponent; + this.crtCoefficient = crtCoefficient; + } + + // Class methods + // -------------------------------------------------------------------------- + + // Instance methods + // -------------------------------------------------------------------------- + + /** + * Returns the prime. + * + * @return the prime. + */ + public final BigInteger getPrime() + { + return this.prime; + } + + /** + * Returns the prime's exponent. + * + * @return the primeExponent. + */ + public final BigInteger getExponent() + { + return this.primeExponent; + } + + /** + * Returns the CRT Coefficient. + * + * @return the CRT Coefficient. + */ + public final BigInteger getCrtCoefficient() + { + return this.crtCoefficient; + } +} diff --git a/libjava/classpath/java/security/spec/RSAPrivateCrtKeySpec.java b/libjava/classpath/java/security/spec/RSAPrivateCrtKeySpec.java new file mode 100644 index 000000000..6d327e62b --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAPrivateCrtKeySpec.java @@ -0,0 +1,151 @@ +/* RSAPrivateCrtKeySpec.java --- RSA Private Certificate Key Specificaton class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + RSA Private Certificate Key class Specification. Used to + maintain the RSA Private Certificate Keys with the + Chinese Remainder Theorem(CRT) as specified by PKCS#1. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class RSAPrivateCrtKeySpec extends RSAPrivateKeySpec +{ + private BigInteger publicExponent; + private BigInteger primeP; + private BigInteger primeQ; + private BigInteger primeExponentP; + private BigInteger primeExponentQ; + private BigInteger crtCoefficient; + + /** + Constructs a new RSAPrivateKeySpec with the specified + variables. + + @param modulus the RSA modulus + @param publicExponent the public key exponent + @param privateExponent the private key exponent + @param primeP the prime P + @param primeQ the prime Q + @param primeExponentP the prime exponent P + @param primeExponentQ the prime exponent P + @param crtCoefficient the CRT coefficient + */ + public RSAPrivateCrtKeySpec(BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger primeP, + BigInteger primeQ, + BigInteger primeExponentP, + BigInteger primeExponentQ, + BigInteger crtCoefficient) + { + super( modulus, privateExponent); + this.publicExponent = publicExponent; + this.primeP = primeP; + this.primeQ = primeQ; + this.primeExponentP = primeExponentP; + this.primeExponentQ = primeExponentQ; + this.crtCoefficient = crtCoefficient; + } + + /** + Gets the RSA public exponent. + + @return the RSA public exponent + */ + public BigInteger getPublicExponent() + { + return this.publicExponent; + } + + /** + Gets the RSA prime P. + + @return the RSA prime P + */ + public BigInteger getPrimeP() + { + return this.primeP; + } + + /** + Gets the RSA prime Q. + + @return the RSA prime Q + */ + public BigInteger getPrimeQ() + { + return this.primeQ; + } + + /** + Gets the RSA prime exponent P. + + @return the RSA prime exponent P + */ + public BigInteger getPrimeExponentP() + { + return this.primeExponentP; + } + + /** + Gets the RSA prime exponent P. + + @return the RSA prime exponent Q + */ + public BigInteger getPrimeExponentQ() + { + return this.primeExponentQ; + } + + /** + Gets the RSA CRT coefficient. + + @return the RSA CRT coefficient + */ + public BigInteger getCrtCoefficient() + { + return this.crtCoefficient; + } + +} diff --git a/libjava/classpath/java/security/spec/RSAPrivateKeySpec.java b/libjava/classpath/java/security/spec/RSAPrivateKeySpec.java new file mode 100644 index 000000000..f812766ba --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAPrivateKeySpec.java @@ -0,0 +1,88 @@ +/* RSAPrivateKeySpec.java --- RSA Private Key Specificaton class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + RSA Private Key class Specification. Used to maintain the RSA + Private Keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class RSAPrivateKeySpec implements KeySpec +{ + private BigInteger modulus; + private BigInteger privateExponent; + + /** + Constructs a new RSAPrivateKeySpec with the specified + modulus and privateExponent. + + @param modulus the RSA modulus + @param privateExponent the private key exponent + */ + public RSAPrivateKeySpec(BigInteger modulus, BigInteger privateExponent) + { + this.modulus = modulus; + this.privateExponent = privateExponent; + } + + /** + Gets the RSA modulus. + + @return the RSA modulus + */ + public BigInteger getModulus() + { + return this.modulus; + } + + /** + Gets the RSA private exponent. + + @return the RSA private exponent + */ + public BigInteger getPrivateExponent() + { + return this.privateExponent; + } + +} diff --git a/libjava/classpath/java/security/spec/RSAPublicKeySpec.java b/libjava/classpath/java/security/spec/RSAPublicKeySpec.java new file mode 100644 index 000000000..acee6bcdf --- /dev/null +++ b/libjava/classpath/java/security/spec/RSAPublicKeySpec.java @@ -0,0 +1,88 @@ +/* RSAPublicKeySpec.java --- RSA Public Key Specificaton class + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; +import java.math.BigInteger; + +/** + RSA Public Key class Specification. Used to maintain the RSA + Public Keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class RSAPublicKeySpec implements KeySpec +{ + private BigInteger modulus; + private BigInteger publicExponent; + + /** + Constructs a new RSAPublicKeySpec with the specified + modulus and publicExponent. + + @param modulus the RSA modulus + @param publicExponent the public key exponent + */ + public RSAPublicKeySpec(BigInteger modulus, BigInteger publicExponent) + { + this.modulus = modulus; + this.publicExponent = publicExponent; + } + + /** + Gets the RSA modulus. + + @return the RSA modulus + */ + public BigInteger getModulus() + { + return this.modulus; + } + + /** + Gets the RSA public exponent. + + @return the RSA public exponent + */ + public BigInteger getPublicExponent() + { + return this.publicExponent; + } + +} diff --git a/libjava/classpath/java/security/spec/X509EncodedKeySpec.java b/libjava/classpath/java/security/spec/X509EncodedKeySpec.java new file mode 100644 index 000000000..8b50aaae1 --- /dev/null +++ b/libjava/classpath/java/security/spec/X509EncodedKeySpec.java @@ -0,0 +1,82 @@ +/* X509EncodedKeySpec.java --- X.509 Encoded Key Specificaton class + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.security.spec; + +/** + X.509 Encoded Key Specification class which is used to store + "X.509" byte encoded keys. + + @since JDK 1.2 + + @author Mark Benvenuto +*/ +public class X509EncodedKeySpec extends EncodedKeySpec +{ + + /** + Constructs a new X509EncodedKeySpec with the specified encoded key. + + @param encodedKey A key to store, assumed to be "X.509" + */ + public X509EncodedKeySpec(byte[] encodedKey) + { + super( encodedKey ); + } + + /** + Gets the encoded key in byte format. + + @returns the encoded key + */ + public byte[] getEncoded() + { + return super.getEncoded(); + } + + /** + Returns the name of the key format used which is "X.509" + + @return a string representing the name + */ + public final String getFormat() + { + return "X.509"; + } + +} diff --git a/libjava/classpath/java/security/spec/package.html b/libjava/classpath/java/security/spec/package.html new file mode 100644 index 000000000..8e818896a --- /dev/null +++ b/libjava/classpath/java/security/spec/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.security.spec + + +

        + + + diff --git a/libjava/classpath/java/sql/Array.java b/libjava/classpath/java/sql/Array.java new file mode 100644 index 000000000..5cea23e8a --- /dev/null +++ b/libjava/classpath/java/sql/Array.java @@ -0,0 +1,186 @@ +/* Array.java -- Interface for accessing SQL array object + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.util.Map; + +/** + * This interface provides methods for accessing SQL array types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Array +{ + /** + * Returns the name of the SQL type of the elements in this + * array. This name is database specific. + * + * @return The name of the SQL type of the elements in this array. + * @exception SQLException If an error occurs. + */ + String getBaseTypeName() throws SQLException; + + /** + * Returns the JDBC type identifier of the elements in this + * array. This will be one of the values defined in the + * Types class. + * + * @return The JDBC type of the elements in this array. + * @exception SQLException If an error occurs. + * @see Types + */ + int getBaseType() throws SQLException; + + /** + * Returns the contents of this array. This object returned + * will be an array of Java objects of the appropriate types. + * + * @return The contents of the array as an array of Java objects. + * @exception SQLException If an error occurs. + */ + Object getArray() throws SQLException; + + /** + * Returns the contents of this array. The specified + * Map will be used to override selected mappings + * between SQL types and Java classes. + * + * @param map A mapping of SQL types to Java classes. + * @return The contents of the array as an array of Java objects. + * @exception SQLException If an error occurs. + */ + Object getArray(Map> map) throws SQLException; + + /** + * Returns a portion of this array starting at start + * into the array and continuing for count + * elements. Fewer than the requested number of elements will be + * returned if the array does not contain the requested number of elements. + * The object returned will be an array of Java objects of + * the appropriate types. + * + * @param start The offset into this array to start returning elements from. + * @param count The requested number of elements to return. + * @return The requested portion of the array. + * @exception SQLException If an error occurs. + */ + Object getArray(long start, int count) throws SQLException; + + /** + * This method returns a portion of this array starting at start + * into the array and continuing for count + * elements. Fewer than the requested number of elements will be + * returned if the array does not contain the requested number of elements. + * The object returned will be an array of Java objects. The specified + * Map will be used for overriding selected SQL type to + * Java class mappings. + * + * @param start The offset into this array to start returning elements from. + * @param count The requested number of elements to return. + * @param map A mapping of SQL types to Java classes. + * @return The requested portion of the array. + * @exception SQLException If an error occurs. + */ + Object getArray(long start, int count, Map> map) + throws SQLException; + + /** + * Returns the elements in the array as a ResultSet. + * Each row of the result set will have two columns. The first will be + * the index into the array of that row's contents. The second will be + * the actual value of that array element. + * + * @return The elements of this array as a ResultSet. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + ResultSet getResultSet() throws SQLException; + + /** + * This method returns the elements in the array as a ResultSet. + * Each row of the result set will have two columns. The first will be + * the index into the array of that row's contents. The second will be + * the actual value of that array element. The specified Map + * will be used to override selected default mappings of SQL types to + * Java classes. + * + * @param map A mapping of SQL types to Java classes. + * @return The elements of this array as a ResultSet. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + ResultSet getResultSet(Map> map) throws SQLException; + + /** + * This method returns a portion of the array as a ResultSet. + * The returned portion will start at start into the + * array and up to count elements will be returned. + *

        + * Each row of the result set will have two columns. The first will be + * the index into the array of that row's contents. The second will be + * the actual value of that array element. + * + * @param start The index into the array to start returning elements from. + * @param count The requested number of elements to return. + * @return The requested elements of this array as a ResultSet. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + ResultSet getResultSet(long start, int count) throws SQLException; + + /** + * This method returns a portion of the array as a ResultSet. + * The returned portion will start at start into the + * array and up to count elements will be returned. + * + *

        Each row of the result set will have two columns. The first will be + * the index into the array of that row's contents. The second will be + * the actual value of that array element. The specified Map + * will be used to override selected default mappings of SQL types to + * Java classes.

        + * + * @param start The index into the array to start returning elements from. + * @param count The requested number of elements to return. + * @param map A mapping of SQL types to Java classes. + * @return The requested elements of this array as a ResultSet. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + ResultSet getResultSet(long start, int count, Map> map) + throws SQLException; +} diff --git a/libjava/classpath/java/sql/BatchUpdateException.java b/libjava/classpath/java/sql/BatchUpdateException.java new file mode 100644 index 000000000..6b88bbad7 --- /dev/null +++ b/libjava/classpath/java/sql/BatchUpdateException.java @@ -0,0 +1,141 @@ +/* BatchUpdateException.java -- Exception for batch oriented SQL errors + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +/** + * This class extends SQLException to count the successful + * updates in each statement in a batch that was successfully updated prior + * to the error. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class BatchUpdateException extends SQLException +{ + static final long serialVersionUID = 5977529877145521757L; + + /** + * This is the array of update counts for the commands which completed + * successfully prior to the error. + */ + private int[] updateCounts; + + /** + * This method initializes a new instance of BatchUpdateException + * with the specified descriptive error message, SQL state, and update count + * information. The vendor specific error code will be initialized to 0. + * + * @param message The descriptive error message. + * @param SQLState The SQL state information for this error. + * @param vendorCode + * @param updateCounts The update count information for this error. + */ + public BatchUpdateException(String message, String SQLState, int vendorCode, + int[] updateCounts) + { + super(message, SQLState, vendorCode); + this.updateCounts = updateCounts; + } + + /** + * This method initializes a new instance of BatchUpdateException + * with the specified descriptive error message, SQL state, and update count + * information. The vendor specific error code will be initialized to 0. + * + * @param message The descriptive error message. + * @param SQLState The SQL state information for this error. + * @param updateCounts The update count information for this error. + */ + public BatchUpdateException(String message, String SQLState, + int[] updateCounts) + { + super(message, SQLState); + this.updateCounts = updateCounts; + } + + /** + * This method initializes a new instance of BatchUpdateException + * with the specified descriptive error message and update count information. + * The SQL state will be initialized to null and the vendor + * specific error code will be initialized to 0. + * + * @param message The descriptive error message. + * @param updateCounts The update count information for this error. + */ + public BatchUpdateException(String message, int[] updateCounts) + { + super(message); + this.updateCounts = updateCounts; + } + + /** + * Initializes a new instance of BatchUpdateException + * with the specified update count information and no descriptive error + * message. This SQL state will be initialized to null and + * the vendor specific error code will be initialized to 0. + * + * @param updateCounts The update count array. + */ + public BatchUpdateException(int[] updateCounts) + { + this.updateCounts = updateCounts; + } + + /** + * Initializes a new instance of BatchUpdateException + * with no descriptive error message. The SQL state and update count will + * be initialized to null and the vendor specific error code will + * initialized to 0. + */ + public BatchUpdateException() + { + super(); + } + + /** + * This method returns the update count information for this error. If + * not null this is an array of int's that are + * the update accounts for each command that was successfully executed. + * The array elements are in the order that the commands were executed. + * + * @return The update count information, which may be null. + */ + public int[] getUpdateCounts() + { + return updateCounts; + } +} diff --git a/libjava/classpath/java/sql/Blob.java b/libjava/classpath/java/sql/Blob.java new file mode 100644 index 000000000..00fe49f70 --- /dev/null +++ b/libjava/classpath/java/sql/Blob.java @@ -0,0 +1,157 @@ +/* Blob.java -- Access a SQL Binary Large OBject. + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This interface specified methods for accessing a SQL BLOB (Binary Large + * OBject) type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + */ +public interface Blob +{ + /** + * This method returns the number of bytes in this Blob. + * + * @return The number of bytes in this Blob. + * @exception SQLException If an error occurs. + */ + long length() throws SQLException; + + /** + * This method returns up to the requested bytes of this Blob + * as a byte array. + * + * @param start The index into this Blob to start returning + * bytes from. + * @param count The requested number of bytes to return. + * @return The requested bytes from this Blob. + * @exception SQLException If an error occurs. + */ + byte[] getBytes(long start, int count) throws SQLException; + + /** + * This method returns a stream that will read the bytes of this + * Blob. + * + * @return A stream that will read the bytes of this Blob. + * @exception SQLException If an error occurs. + */ + InputStream getBinaryStream() throws SQLException; + + /** + * This method returns the index into this Blob at which the + * first instance of the specified bytes occur. The searching starts at the + * specified index into this Blob. + * + * @param pattern The byte pattern to search for. + * @param start The index into this Blob to start searching for + * the pattern. + * @return The offset at which the pattern is first found, or -1 if the + * pattern is not found. + * @exception SQLException If an error occurs. + */ + long position(byte[] pattern, long start) throws SQLException; + + /** + * This method returns the index into this Blob at which the + * first instance of the specified pattern occurs. The searching starts at the + * specified index into this Blob. The bytes in the specified + * Blob are used as the search pattern. + * + * @param pattern The Blob containing the byte pattern to + * search for. + * @param start The index into this Blob to start searching for + * the pattern. + * @return The offset at which the pattern is first found, or -1 if the + * pattern is not found. + * @exception SQLException If an error occurs. + */ + long position(Blob pattern, long start) throws SQLException; + + /** + * Writes the specified data into this Blob, starting at the + * specified index. + * + * @param start The index at which the writing starts. + * @param bytes The data to write. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int setBytes(long start, byte[] bytes) throws SQLException; + + /** + * Writes a portion of the specified data into this Blob, + * starting at the specified index. + * + * @param startWrite The index into this Blob at which writing + * starts. + * @param bytes The data to write a portion of. + * @param startRead The offset into the data where the portion to copy starts. + * @param count The number of bytes to write. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int setBytes(long startWrite, byte[] bytes, int startRead, int count) + throws SQLException; + + /** + * Returns a binary stream that writes into this Blob, + * starting at the specified index. + * + * @param start The index at which the writing starts. + * @return A binary stream to write into this Blob. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + OutputStream setBinaryStream(long start) throws SQLException; + + /** + * Truncates this Blob to be at most the specified number of + * bytes long. + * + * @param count The length this Blob is truncated to. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void truncate(long count) throws SQLException; +} diff --git a/libjava/classpath/java/sql/CallableStatement.java b/libjava/classpath/java/sql/CallableStatement.java new file mode 100644 index 000000000..09d58704e --- /dev/null +++ b/libjava/classpath/java/sql/CallableStatement.java @@ -0,0 +1,961 @@ +/* CallableStatement.java -- A statement for calling stored procedures. + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.util.Calendar; +import java.util.Map; + +/** + * This interface provides a mechanism for calling stored procedures. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface CallableStatement extends PreparedStatement +{ + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. + * + * @param index The index of the parameter to register as output. + * @param sqlType The SQL type value from Types. + * @exception SQLException If an error occurs. + */ + void registerOutParameter(int index, int sqlType) + throws SQLException; + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type and scale. + * + * @param index The index of the parameter to register as output. + * @param sqlType The SQL type value from Types. + * @param scale The scale of the value that will be returned. + * @exception SQLException If an error occurs. + */ + void registerOutParameter(int index, int sqlType, int scale) + throws SQLException; + + /** + * This method tests whether the value of the last parameter that was fetched + * was actually a SQL NULL value. + * + * @return true if the last parameter fetched was a NULL, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean wasNull() throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * String. + * + * @param index The index of the parameter to return. + * @return The parameter value as a String. + * @exception SQLException If an error occurs. + */ + String getString(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * boolean. + * + * @param index The index of the parameter to return. + * @return The parameter value as a boolean. + * @exception SQLException If an error occurs. + */ + boolean getBoolean(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * byte. + * + * @param index The index of the parameter to return. + * @return The parameter value as a byte. + * @exception SQLException If an error occurs. + */ + byte getByte(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * short. + * + * @param index The index of the parameter to return. + * @return The parameter value as a short. + * @exception SQLException If an error occurs. + */ + short getShort(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * int. + * + * @param index The index of the parameter to return. + * @return The parameter value as a int. + * @exception SQLException If an error occurs. + */ + int getInt(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * long. + * + * @param index The index of the parameter to return. + * @return The parameter value as a long. + * @exception SQLException If an error occurs. + */ + long getLong(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * float. + * + * @param index The index of the parameter to return. + * @return The parameter value as a float. + * @exception SQLException If an error occurs. + */ + float getFloat(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * double. + * + * @param index The index of the parameter to return. + * @return The parameter value as a double. + * @exception SQLException If an error occurs. + */ + double getDouble(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * BigDecimal. + * + * @param index The index of the parameter to return. + * @param scale The number of digits to the right of the decimal to return. + * @return The parameter value as a BigDecimal. + * @exception SQLException If an error occurs. + * @deprecated Use getBigDecimal(int index) + * or getBigDecimal(String name) instead. + */ + BigDecimal getBigDecimal(int index, int scale) + throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * byte array. + * + * @param index The index of the parameter to return. + * @return The parameter value as a byte array + * @exception SQLException If an error occurs. + */ + byte[] getBytes(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Date. + * + * @param index The index of the parameter to return. + * @return The parameter value as a java.sql.Date. + * @exception SQLException If an error occurs. + */ + Date getDate(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Time. + * + * @param index The index of the parameter to return. + * @return The parameter value as a java.sql.Time. + * @exception SQLException If an error occurs. + */ + Time getTime(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Timestamp. + * + * @param index The index of the parameter to return. + * @return The parameter value as a java.sql.Timestamp. + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Object. + * + * @param index The index of the parameter to return. + * @return The parameter value as an Object. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Object getObject(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * BigDecimal. + * + * @param index The index of the parameter to return. + * @return The parameter value as a BigDecimal. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + BigDecimal getBigDecimal(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Object. + * + * @param index The index of the parameter to return. + * @param map The mapping to use for conversion from SQL to Java types. + * @return The parameter value as an Object. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Object getObject(int index, Map> map) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Ref. + * + * @param index The index of the parameter to return. + * @return The parameter value as a Ref. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Ref getRef(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Blob. + * + * @param index The index of the parameter to return. + * @return The parameter value as a Blob. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Blob getBlob(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Clob. + * + * @param index The index of the parameter to return. + * @return The parameter value as a Clob. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Clob getClob(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Array. + * + * @param index The index of the parameter to return. + * @return The parameter value as a Array. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Array getArray(int index) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Date. + * + * @param index The index of the parameter to return. + * @param cal The Calendar to use for timezone and locale. + * @return The parameter value as a java.sql.Date. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Date getDate(int index, Calendar cal) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Time. + * + * @param index The index of the parameter to return. + * @param cal The Calendar to use for timezone and locale. + * @return The parameter value as a java.sql.Time. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Time getTime(int index, Calendar cal) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Timestamp. + * + * @param index The index of the parameter to return. + * @return The parameter value as a java.sql.Timestamp. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Timestamp getTimestamp(int index, Calendar cal) + throws SQLException; + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. + * + * @param index The index of the parameter to register as output. + * @param sqlType The SQL type value from Types. + * @param typeName The user defined data type name. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + void registerOutParameter(int index, int sqlType, String typeName) + throws SQLException; + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. + * + * @param name The name of the parameter to register as output. + * @param sqlType The SQL type value from Types. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void registerOutParameter(String name, int sqlType) + throws SQLException; + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. This version of registerOutParameter is used + * for NUMERIC or DECIMAL types. + * + * @param name The name of the parameter to register as output. + * @param sqlType The SQL type value from Types. + * @param scale Number of digits to the right of the decimal point. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void registerOutParameter(String name, int sqlType, int scale) + throws SQLException; + + + /** + * This method registers the specified parameter as an output parameter + * of the specified SQL type. This version of registerOutParameter is used + * for user-named or REF types. If the type of the output parameter does + * not have such a type, the typeName argument is ignored. + * + * @param name The name of the parameter to register as output. + * @param sqlType The SQL type value from Types. + * @param typeName The SQL structured type name. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void registerOutParameter(String name, int sqlType, String typeName) + throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.net.URL. + * + * @param index The index of the parameter to return. + * @return The parameter value as a URL. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + URL getURL(int index) throws SQLException; + + /** + * This method sets the value of the specified parameter to the specified + * java.net.URL + * + * @param name The name of the parameter to set. + * @param value The value the parameter. + * @since 1.4 + */ + void setURL(String name, URL value) throws SQLException; + + /** + * This method populates the specified parameter with a SQL NULL value + * for the specified type. + * + * @param name The name of the parameter to set. + * @param sqlType The SQL type identifier of the parameter from + * Types + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setNull(String name, int sqlType) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * boolean value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setBoolean(String name, boolean value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * byte value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setByte(String name, byte value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * short value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setShort(String name, short value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * int value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setInt(String name, int value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * long value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setLong(String name, long value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * float value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setFloat(String name, float value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * double value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setDouble(String name, double value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * BigDecimal value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setBigDecimal(String name, BigDecimal value) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * String value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setString(String name, String value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * byte array value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setBytes(String name, byte[] value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Date value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setDate(String name, Date value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Time value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setTime(String name, Time value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Timestamp value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setTimestamp(String name, Timestamp value) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * ASCII InputStream value. + * + * @param name The name of the parameter value to set. + * @param stream The stream from which the parameter value is read. + * @param count The number of bytes in the stream. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setAsciiStream(String name, InputStream stream, int count) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * binary InputStream value. + * + * @param name The name of the parameter value to set. + * @param stream The stream from which the parameter value is read. + * @param count The number of bytes in the stream. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setBinaryStream(String name, InputStream stream, int count) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Object value. The specified SQL object type will be used. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @param sqlType The SQL type to use for the parameter, from + * Types + * @param scale The scale of the value, for numeric values only. + * @exception SQLException If an error occurs. + * @see Types + * @since 1.4 + */ + void setObject(String name, Object value, int sqlType, int scale) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Object value. The specified SQL object type will be used. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @param sqlType The SQL type to use for the parameter, from + * Types + * @exception SQLException If an error occurs. + * @see Types + * @since 1.4 + */ + void setObject(String name, Object value, int sqlType) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Object value. The default object type to SQL type mapping + * will be used. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setObject(String name, Object value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * character Reader value. + * + * @param name The name of the parameter value to set. + * @param reader The reader from which the parameter value is read. + * @param count The number of characters in the stream. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setCharacterStream(String name, Reader reader, int count) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Date value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @param cal The Calendar to use for timezone and locale. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setDate(String name, Date value, Calendar cal) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Time value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @param cal The Calendar to use for timezone and locale. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setTime(String name, Time value, Calendar cal) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Timestamp value. + * + * @param name The name of the parameter value to set. + * @param value The value of the parameter. + * @param cal The Calendar to use for timezone and locale. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setTimestamp(String name, Timestamp value, Calendar cal) + throws SQLException; + + /** + * This method populates the specified parameter with a SQL NULL value + * for the specified type. + * + * @param name The name of the parameter to set. + * @param sqlType The SQL type identifier of the parameter from + * Types + * @param typeName The name of the data type, for user defined types. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setNull(String name, int sqlType, String typeName) + throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * String. + * + * @param name The name of the parameter to return. + * @return The parameter value as a String. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + String getString(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * boolean. + * + * @param name The name of the parameter to return. + * @return The parameter value as a boolean. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + boolean getBoolean(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * byte. + * + * @param name The name of the parameter to return. + * @return The parameter value as a byte. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + byte getByte(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * short. + * + * @param name The name of the parameter to return. + * @return The parameter value as a short. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + short getShort(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * int. + * + * @param name The name of the parameter to return. + * @return The parameter value as a int. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int getInt(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * long. + * + * @param name The name of the parameter to return. + * @return The parameter value as a long. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + long getLong(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * float. + * + * @param name The name of the parameter to return. + * @return The parameter value as a float. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + float getFloat(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * double. + * + * @param name The name of the parameter to return. + * @return The parameter value as a double. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + double getDouble(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * byte array. + * + * @param name The name of the parameter to return. + * @return The parameter value as a byte[]. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + byte[] getBytes(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Date. + * + * @param name The name of the parameter to return. + * @return The parameter value as a java.sql.Date. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Date getDate(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Time. + * + * @param name The name of the parameter to return. + * @return The parameter value as a java.sql.Time. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Time getTime(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Timestamp. + * + * @param name The name of the parameter to return. + * @return The parameter value as a java.sql.Timestamp. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Timestamp getTimestamp(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Object. + * + * @param name The name of the parameter to return. + * @return The parameter value as a Object. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Object getObject(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * BigDecimal. + * + * @param name The name of the parameter to return. + * @return The parameter value as a BigDecimal. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + BigDecimal getBigDecimal(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Object using the specified mapping for conversion from + * SQL to Java types. + * + * @param name The name of the parameter to return. + * @param map The mapping to use for conversion from SQL to Java types. + * @return The parameter value as an Object. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Object getObject(String name, Map> map) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Ref. + * + * @param name The name of the parameter to return. + * @return The parameter value as a Ref. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Ref getRef(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Blob. + * + * @param name The name of the parameter to return. + * @return The parameter value as a Blob. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Blob getBlob(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Clob. + * + * @param name The name of the parameter to return. + * @return The parameter value as a Clob. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Clob getClob(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * Array. + * + * @param name The name of the parameter to return. + * @return The parameter value as a Array. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Array getArray(String name) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Date. + * + * @param name The name of the parameter to return. + * @param cal The Calendar to use for timezone and locale. + * @return The parameter value as a java.sql.Date. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Date getDate(String name, Calendar cal) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Time. + * + * @param name The name of the parameter to return. + * @param cal The Calendar to use for timezone and locale. + * @return The parameter value as a java.sql.Time. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Time getTime(String name, Calendar cal) throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.sql.Timestamp. + * + * @param name The name of the parameter to return. + * @param cal The Calendar to use for timezone and locale. + * @return The parameter value as a java.sql.Timestamp. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Timestamp getTimestamp(String name, Calendar cal) + throws SQLException; + + /** + * This method returns the value of the specified parameter as a Java + * java.net.URL. + * + * @param name The name of the parameter to return. + * @return The parameter value as a java.net.URL. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + URL getURL(String name) throws SQLException; +} diff --git a/libjava/classpath/java/sql/Clob.java b/libjava/classpath/java/sql/Clob.java new file mode 100644 index 000000000..483276e69 --- /dev/null +++ b/libjava/classpath/java/sql/Clob.java @@ -0,0 +1,187 @@ +/* Clob.java -- Access Character Large OBjects + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; + +/** + * This interface contains methods for accessing a SQL CLOB (Character Large + * OBject) type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Clob +{ + /** + * This method returns the number of characters in this Clob. + * + * @return The number of characters in this Clob. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + long length() throws SQLException; + + /** + * This method returns the specified portion of this Clob as a + * String. + * + * @param start The index into this Clob (index values + * start at 1) to start returning characters from. + * @param count The requested number of characters to return. + * @return The requested Clob section, as a String. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + String getSubString(long start, int count) throws SQLException; + + /** + * This method returns a character stream that reads the contents of this + * Clob. + * + * @return A character stream to read this Clob's contents. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + Reader getCharacterStream() throws SQLException; + + /** + * This method returns a byte stream that reads the contents of this + * Clob as a series of ASCII bytes. + * + * @return A stream to read this Clob's contents. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + InputStream getAsciiStream() throws SQLException; + + /** + * This method returns the index into this Clob of the first + * occurrence of the specified character pattern (supplied by the caller as a + * String). The search begins at the specified index. + * + * @param pattern The character pattern to search for, passed as a + * String. + * @param start The index into this Clob to start searching + * (indices start at 1). + * @return The index at which the pattern was found (indices start at 1), or + * -1 if the pattern was not found. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + long position(String pattern, long start) throws SQLException; + + /** + * This method returns the index into this Clob of the first + * occurrence of the specified character pattern (supplied by the caller as a + * Clob). The search begins at the specified index. + * + * @param pattern The character pattern to search for, passed as a + * Clob. + * @param start The index into this Clob to start searching + * (indices start at 1). + * @return The index at which the pattern was found (indices start at 1), or + * -1 if the pattern was not found. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + long position(Clob pattern, long start) throws SQLException; + + /** + * Writes the specified string into this Clob, starting at the + * specified index. + * + * @param start The index at which the writing starts. + * @param value The string to write. + * @return The number of characters written. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int setString(long start, String value) throws SQLException; + + /** + * Writes the specified portion of a string into this Clob, + * starting at the specified index. + * + * @param startWrite The index at which the writing starts. + * @param value The string to write a portion of. + * @param startRead The offset into the string where the portion to copy + * starts. + * @param count The number of characters to write. + * @return The number of characters written. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int setString(long startWrite, String value, int startRead, int count) + throws SQLException; + + /** + * Returns an ASCII text stream that writes into this Clob, + * starting at the specified index. + * + * @param start The index at which the writing starts. + * @return An ASCII text stream to write into this Clob. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + OutputStream setAsciiStream(long start) throws SQLException; + + /** + * Returns a character stream that writes into this Clob, + * starting at the specified index. + * + * @param start The index at which the writing starts. + * @return A character stream to write into this Clob. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Writer setCharacterStream(long start) throws SQLException; + + /** + * Truncates this Clob to be at most the specified number of + * characters long. + * + * @param count The length this Clob is truncated to. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void truncate(long count) throws SQLException; +} diff --git a/libjava/classpath/java/sql/Connection.java b/libjava/classpath/java/sql/Connection.java new file mode 100644 index 000000000..f37527625 --- /dev/null +++ b/libjava/classpath/java/sql/Connection.java @@ -0,0 +1,500 @@ +/* Connection.java -- Manage a database connection. + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.util.Map; + +/** + * This interface provides methods for managing a connection to a database. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Connection +{ + /** + * This transaction isolation level indicates that transactions are not + * supported. + */ + int TRANSACTION_NONE = 0; + + /** + * This transaction isolation level indicates that one transaction can + * read modifications by other transactions before the other transactions + * have committed their changes. This could result in invalid reads. + */ + int TRANSACTION_READ_UNCOMMITTED = 1; + + /** + * This transaction isolation level indicates that only committed data from + * other transactions will be read. If a transaction reads a row, then + * another transaction commits a change to that row, the first transaction + * would retrieve the changed row on subsequent reads of the same row. + */ + int TRANSACTION_READ_COMMITTED = 2; + + /** + * This transaction isolation level indicates that only committed data from + * other transactions will be read. It also ensures that data read from + * a row will not be different on a subsequent read even if another + * transaction commits a change. + */ + int TRANSACTION_REPEATABLE_READ = 4; + + /** + * This transaction isolation level indicates that only committed data from + * other transactions will be read. It also ensures that data read from + * a row will not be different on a subsequent read even if another + * transaction commits a change. Additionally, rows modified by other + * transactions will not affect the result set returned during subsequent + * executions of the same WHERE clause in this transaction. + */ + int TRANSACTION_SERIALIZABLE = 8; + + /** + * This method creates a new SQL statement. The default result set type + * and concurrency will be used. + * + * @return A new Statement object. + * @exception SQLException If an error occurs. + * @see Statement + */ + Statement createStatement() throws SQLException; + + /** + * This method creates a new PreparedStatement for the specified + * SQL string. This method is designed for use with parameterized + * statements. The default result set type and concurrency will be used. + * + * @param sql The SQL statement to use in creating this + * PreparedStatement. + * @return A new PreparedStatement. + * @exception SQLException If an error occurs. + * @see PreparedStatement + */ + PreparedStatement prepareStatement(String sql) throws SQLException; + + /** + * This method creates a new CallableStatement for the + * specified SQL string. Thie method is designed to be used with + * stored procedures. The default result set type and concurrency + * will be used. + * + * @param sql The SQL statement to use in creating this + * CallableStatement. + * @return A new CallableStatement. + * @exception SQLException If an error occurs. + * @see CallableStatement + */ + CallableStatement prepareCall(String sql) throws SQLException; + + /** + * This method converts the specified generic SQL statement into the + * native grammer of the database this object is connected to. + * + * @param sql The JDBC generic SQL statement. + * @return The native SQL statement. + * @exception SQLException If an error occurs. + */ + String nativeSQL(String sql) throws SQLException; + + /** + * This method turns auto commit mode on or off. In auto commit mode, + * every SQL statement is committed its own transaction. Otherwise a + * transaction must be explicitly committed or rolled back. + * + * @param autoCommit true to enable auto commit mode, + * false to disable it. + * @exception SQLException If an error occurs. + * @see #commit() + * @see #rollback() + */ + void setAutoCommit(boolean autoCommit) throws SQLException; + + /** + * This method tests whether or not auto commit mode is currently enabled. + * In auto commit mode, every SQL statement is committed its own transaction. + * Otherwise a transaction must be explicitly committed or rolled back. + * + * @return true if auto commit mode is enabled, + * false otherwise. + * @exception SQLException If an error occurs. + * @see #commit() + * @see #rollback() + */ + boolean getAutoCommit() throws SQLException; + + /** + * This method commits any SQL statements executed on this connection since + * the last commit or rollback. + * + * @exception SQLException If an error occurs. + */ + void commit() throws SQLException; + + /** + * This method rolls back any SQL statements executed on this connection + * since the last commit or rollback. + * + * @exception SQLException If an error occurs. + */ + void rollback() throws SQLException; + + /** + * This method immediately closes this database connection. + * + * @exception SQLException If an error occurs. + */ + void close() throws SQLException; + + /** + * This method tests whether or not this connection has been closed. + * + * @return true if the connection is closed, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isClosed() throws SQLException; + + /** + * This method returns the meta data for this database connection. + * + * @return The meta data for this database. + * @exception SQLException If an error occurs. + * @see DatabaseMetaData + */ + DatabaseMetaData getMetaData() throws SQLException; + + /** + * This method turns read only mode on or off. It may not be called while + * a transaction is in progress. + * + * @param readOnly true if this connection is read only, + * false otherwise. + * @exception SQLException If an error occurs. + */ + void setReadOnly(boolean readOnly) throws SQLException; + + /** + * This method tests whether or not this connection is in read only mode. + * + * @return true if the connection is read only false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isReadOnly() throws SQLException; + + /** + * This method sets the name of the catalog in use by this connection. + * Note that this method does nothing if catalogs are not supported by + * this database. + * + * @param catalog The name of the catalog to use for this connection. + * @exception SQLException If an error occurs. + */ + void setCatalog(String catalog) throws SQLException; + + /** + * This method returns the name of the catalog in use by this connection, + * if any. + * + * @return The name of the catalog, or null if none + * exists or catalogs are not supported by this database. + * @exception SQLException If an error occurs. + */ + String getCatalog() throws SQLException; + + /** + * This method sets the current transaction isolation mode. This must + * be one of the constants defined in this interface. + * + * @param level The transaction isolation level. + * @exception SQLException If an error occurs. + */ + void setTransactionIsolation(int level) throws SQLException; + + /** + * This method returns the current transaction isolation mode. This will + * be one of the constants defined in this interface. + * + * @return The transaction isolation level. + * @exception SQLException If an error occurs. + */ + int getTransactionIsolation() throws SQLException; + + /** + * This method returns the first warning that occurred on this connection, + * if any. If there were any subsequence warnings, they will be chained + * to the first one. + * + * @return The first SQLWarning that occurred, or + * null if there have been no warnings. + * @exception SQLException If an error occurs. + */ + SQLWarning getWarnings() throws SQLException; + + /** + * This method clears all warnings that have occurred on this connection. + * + * @exception SQLException If an error occurs. + */ + void clearWarnings() throws SQLException; + + /** + * This method creates a new SQL statement with the specified type and + * concurrency. Valid values for these parameters are specified in the + * ResultSet class. + * + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency The type of concurrency to be used in + * the result set for this statement. + * @return A new Statement object. + * @exception SQLException If an error occurs. + * @see Statement + * @see ResultSet + */ + Statement createStatement(int resultSetType, int resultSetConcurrency) + throws SQLException; + + /** + * This method creates a new PreparedStatement for the specified + * SQL string. This method is designed for use with parameterized + * statements. The specified result set type and concurrency will be used. + * Valid values for these parameters are specified in the + * ResultSet class. + * + * @param sql The SQL statement to use in creating this + * PreparedStatement. + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency The type of concurrency to be used in + * the result set for this statement. + * @return A new PreparedStatement. + * @exception SQLException If an error occurs. + * @see PreparedStatement + * @see ResultSet + */ + PreparedStatement prepareStatement(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException; + + /** + * This method creates a new CallableStatement for the + * specified SQL string. Thie method is designed to be used with + * stored procedures. The specified result set type and concurrency + * will be used. Valid values for these parameters are specified in the + * ResultSet class. + * + * @param sql The SQL statement to use in creating this + * PreparedStatement. + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency The type of concurrency to be used in + * the result set for this statement. + * @return A new CallableStatement. + * @exception SQLException If an error occurs. + * @see CallableStatement + * @see ResultSet + */ + CallableStatement prepareCall(String sql, int resultSetType, int + resultSetConcurrency) throws SQLException; + + /** + * This method returns the mapping of SQL types to Java classes + * currently in use by this connection. This mapping will have no + * entries unless they have been manually added. + * + * @return The SQL type to Java class mapping. + * @exception SQLException If an error occurs. + */ + Map> getTypeMap() throws SQLException; + + /** + * This method sets the mapping table for SQL types to Java classes. + * Any entries in this map override the defaults. + * + * @param map The new SQL mapping table. + * @exception SQLException If an error occurs. + */ + void setTypeMap(Map> map) throws SQLException; + + /** + * Sets the default holdability of ResultSetS that are created + * from StatementS using this Connection. + * + * @param holdability The default holdability value to set, this must be one + * of ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT. + * @exception SQLException If an error occurs. + * @see ResultSet + * @since 1.4 + */ + void setHoldability(int holdability) throws SQLException; + + /** + * Gets the default holdability of ResultSetS that are created + * from StatementS using this Connection. + * + * @return The current default holdability value, this must be one of + * ResultSet.HOLD_CURSORS_OVER_COMMIT or + * ResultSet.CLOSE_CURSORS_AT_COMMIT. + * @exception SQLException If an error occurs. + * @see ResultSet + * @since 1.4 + */ + int getHoldability() throws SQLException; + + /** + * Creates a new unnamed savepoint for this Connection + * + * @return The Savepoint object representing the savepoint. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Savepoint setSavepoint() throws SQLException; + + /** + * Creates a new savepoint with the specifiend name for this + * Connection. + * + * @param name The name of the savepoint. + * @return The Savepoint object representing the savepoint. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + Savepoint setSavepoint(String name) throws SQLException; + + /** + * Undoes all changes made after the specified savepoint was set. + * + * @param savepoint The safepoint to roll back to. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void rollback(Savepoint savepoint) throws SQLException; + + /** + * Removes the specified savepoint from this Connection. + * Refering to a savepoint after it was removed is an error and will throw an + * SQLException. + * + * @param savepoint The savepoint to release. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void releaseSavepoint(Savepoint savepoint) throws SQLException; + + /** + * This method creates a new SQL statement with the specified type, + * concurrency and holdability, instead of using the defaults. Valid values + * for these parameters are specified in the ResultSet class. + * + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency The type of concurrency to be used in + * the result set for this statement. + * @param resultSetHoldability The type of holdability to be usd in the + * result set for this statement. + * @return A new Statement + * @exception SQLException If an error occurs. + * @see ResultSet + * @since 1.4 + */ + Statement createStatement(int resultSetType, int + resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * This method creates a new PreparedStatement for the specified + * SQL string. This method is designed for use with parameterized + * statements. The specified result set type, concurrency and holdability + * will be used. Valid values for these parameters are specified in the + * ResultSet class. + * + * @param sql The SQL statement to use in creating this + * PreparedStatement. + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency The type of concurrency to be used in + * the result set for this statement. + * @param resultSetHoldability The type of holdability to be usd in the + * result set for this statement. + * @return A new PreparedStatement. + * @exception SQLException If an error occurs. + * @see PreparedStatement + * @see ResultSet + * @since 1.4 + */ + PreparedStatement prepareStatement(String sql, int resultSetType, int + resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * This method creates a new CallableStatement for the + * specified SQL string. Thie method is designed to be used with + * stored procedures. The specified result set type, concurrency and + * holdability will be used. Valid values for these parameters are specified + * in the ResultSet class. + * + * @param sql The SQL statement to use in creating this + * PreparedStatement. + * @param resultSetType The type of result set to use for this statement. + * @param resultSetConcurrency The type of concurrency to be used in + * the result set for this statement. + * @param resultSetHoldability The type of holdability to be used in the + * result set for this statement. + * @return A new CallableStatement. + * @exception SQLException If an error occurs. + * @see CallableStatement + * @see ResultSet + * @since 1.4 + */ + CallableStatement prepareCall(String sql, int resultSetType, int + resultSetConcurrency, int resultSetHoldability) throws SQLException; + + /** + * @since 1.4 + */ + PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) + throws SQLException; + + /** + * @since 1.4 + */ + PreparedStatement prepareStatement(String sql, int[] columnIndexes) + throws SQLException; + + /** + * @since 1.4 + */ + PreparedStatement prepareStatement(String sql, String[] columnNames) + throws SQLException; +} diff --git a/libjava/classpath/java/sql/DataTruncation.java b/libjava/classpath/java/sql/DataTruncation.java new file mode 100644 index 000000000..efa4487b3 --- /dev/null +++ b/libjava/classpath/java/sql/DataTruncation.java @@ -0,0 +1,157 @@ +/* DataTruncation.java -- Warning when data has been truncated. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +/** + * This exception is thrown when a piece of data is unexpectedly + * truncated in JDBC. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DataTruncation extends SQLWarning +{ + static final long serialVersionUID = 6464298989504059473L; + + /** + * The original size of the data. + */ + private int dataSize; + + /** + * The index of the parameter or column whose value was truncated. + */ + private int index; + + /** + * Indicates whether or not a parameter value was truncated. + */ + private boolean parameter; + + /** + * Indicates whether or not a data column value was truncated. + */ + private boolean read; + + /** + * This is the size of the data after truncation. + */ + private int transferSize; + + /** + * This method initializes a new instance of DataTruncation + * with the specified values. The descriptive error message for this + * exception will be "Data truncation", the SQL state will be "01004" + * and the vendor specific error code will be set to 0. + * + * @param index The index of the parameter or column that was truncated. + * @param parameter true if a parameter was truncated, + * false otherwise. + * @param read true if a data column was truncated, + * false otherwise. + * @param dataSize The original size of the data. + * @param transferSize The size of the data after truncation. + */ + public DataTruncation(int index, boolean parameter, boolean read, int + dataSize, int transferSize) + { + super("Data truncation", "01004"); + + this.index = index; + this.parameter = parameter; + this.read = read; + this.dataSize = dataSize; + this.transferSize = transferSize; + } + + /** + * This method returns the index of the column or parameter that was + * truncated. + * + * @return The index of the column or parameter that was truncated. + */ + public int getIndex() + { + return index; + } + + /** + * This method determines whether or not it was a parameter that was + * truncated. + * + * @return true if a parameter was truncated, false + * otherwise. + */ + public boolean getParameter() + { + return parameter; + } + + /** + * This method determines whether or not it was a column that was + * truncated. + * + * @return true if a column was truncated, false + * otherwise. + */ + public boolean getRead() + { + return read; + } + + /** + * This method returns the original size of the parameter or column that + * was truncated. + * + * @return The original size of the parameter or column that was truncated. + */ + public int getDataSize() + { + return dataSize; + } + + /** + * This method returns the size of the parameter or column after it was + * truncated. + * + * @return The size of the parameter or column after it was truncated. + */ + public int getTransferSize() + { + return transferSize; + } +} diff --git a/libjava/classpath/java/sql/DatabaseMetaData.java b/libjava/classpath/java/sql/DatabaseMetaData.java new file mode 100644 index 000000000..3ffd5b0df --- /dev/null +++ b/libjava/classpath/java/sql/DatabaseMetaData.java @@ -0,0 +1,2270 @@ +/* DatabaseMetaData.java -- Information about the database itself. + Copyright (C) 1999, 2000, 2001, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +public interface DatabaseMetaData +{ + /** + * It is unknown whether or not the procedure returns a result. + */ + int procedureResultUnknown = 0; + + /** + * The procedure does not return a result. + */ + int procedureNoResult = 1; + + /** + * The procedure returns a result. + */ + int procedureReturnsResult = 2; + + /** + * The column type is unknown. + */ + int procedureColumnUnknown = 0; + + /** + * The column type is input. + */ + int procedureColumnIn = 1; + + /** + * The column type is input/output. + */ + int procedureColumnInOut = 2; + + /** + * The column type is output + */ + int procedureColumnOut = 4; + + /** + * The column is used for return values. + */ + int procedureColumnReturn = 5; + + /** + * The column is used for storing results + */ + int procedureColumnResult = 3; + + /** + * NULL values are not allowed. + */ + int procedureNoNulls = 0; + + /** + * NULL values are allowed. + */ + int procedureNullable = 1; + + /** + * It is unknown whether or not NULL values are allowed. + */ + int procedureNullableUnknown = 2; + + /** + * The column does not allow NULL + */ + int columnNoNulls = 0; + + /** + * The column does allow NULL + */ + int columnNullable = 1; + + /** + * It is unknown whether or not the column allows NULL + */ + int columnNullableUnknown = 2; + + /** + * The best row's scope is only guaranteed to be valid so long as the + * row is actually being used. + */ + int bestRowTemporary = 0; + + /** + * The best row identifier is valid to the end of the transaction. + */ + int bestRowTransaction = 1; + + /** + * The best row identifier is valid to the end of the session. + */ + int bestRowSession = 2; + + /** + * The best row may or may not be a pseudo-column. + */ + int bestRowUnknown = 0; + + /** + * The best row identifier is not a pseudo-column. + */ + int bestRowNotPseudo = 1; + + /** + * The best row identifier is a pseudo-column. + */ + int bestRowPseudo = 2; + + /** + * It is unknown whether or not the version column is a pseudo-column. + */ + int versionColumnUnknown = 0; + + /** + * The version column is not a pseudo-column + */ + int versionColumnNotPseudo = 1; + + /** + * The version column is a pseudo-column + */ + int versionColumnPseudo = 2; + + /** + * Foreign key changes are cascaded in updates or deletes. + */ + int importedKeyCascade = 0; + + /** + * Column may not be updated or deleted in use as a foreign key. + */ + int importedKeyRestrict = 1; + + /** + * When primary key is updated or deleted, the foreign key is set to NULL. + */ + int importedKeySetNull = 2; + + /** + * If the primary key is a foreign key, it cannot be udpated or deleted. + */ + int importedKeyNoAction = 3; + + /** + * If the primary key is updated or deleted, the foreign key is set to + * a default value. + */ + int importedKeySetDefault = 4; + + /** + * Wish I knew what this meant. + */ + int importedKeyInitiallyDeferred = 5; + + /** + * Wish I knew what this meant. + */ + int importedKeyInitiallyImmediate = 6; + + /** + * Wish I knew what this meant. + */ + int importedKeyNotDeferrable = 7; + + /** + * A NULL value is not allowed for this data type. + */ + int typeNoNulls = 0; + + /** + * A NULL value is allowed for this data type. + */ + int typeNullable = 1; + + /** + * It is unknown whether or not NULL values are allowed for this data type. + */ + int typeNullableUnknown = 2; + + /** + * Where clauses are not supported for this type. + */ + int typePredNone = 0; + + /** + * Only "WHERE..LIKE" style WHERE clauses are allowed on this data type. + */ + int typePredChar = 1; + + /** + * All WHERE clauses except "WHERE..LIKE" style are allowed on this data type. + */ + int typePredBasic = 2; + + /** + * Any type of WHERE clause is allowed for this data type. + */ + int typeSearchable = 3; + + /** + * This column contains table statistics. + */ + short tableIndexStatistic = 0; + + /** + * This table index is clustered. + */ + short tableIndexClustered = 1; + + /** + * This table index is hashed. + */ + short tableIndexHashed = 2; + + /** + * This table index is of another type. + */ + short tableIndexOther = 3; + + /** + * A NULL value is not allowed for this attribute. + */ + short attributeNoNulls = 0; + + /** + * A NULL value is allowed for this attribute. + */ + short attributeNullable = 1; + + /** + * It is unknown whether or not NULL values are allowed for this attribute. + */ + short attributeNullableUnknown = 2; + + int sqlStateXOpen = 1; + + int sqlStateSQL99 = 2; + + /** + * This method tests whether or not all the procedures returned by + * the getProcedures method can be called by this user. + * + * @return true if all the procedures can be called, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean allProceduresAreCallable() throws SQLException; + + /** + * This method tests whether or not all the table returned by the + * getTables method can be selected by this user. + * + * @return true if all the procedures can be called, + * false otherwise. + * + * @exception SQLException If an error occurs. + */ + boolean allTablesAreSelectable() throws SQLException; + + /** + * This method returns the URL for this database. + * + * @return The URL string for this database, or null if it + * is not known. + * @exception SQLException If an error occurs. + */ + String getURL() throws SQLException; + + /** + * This method returns the database username for this connection. + * + * @return The database username. + * @exception SQLException If an error occurs. + */ + String getUserName() throws SQLException; + + /** + * This method tests whether or not the database is in read only mode. + * + * @return true if the database is in read only mode, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isReadOnly() throws SQLException; + + /** + * This method tests whether or not NULL's sort as high values. + * + * @return true if NULL's sort as high values, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullsAreSortedHigh() throws SQLException; + + /** + * This method tests whether or not NULL's sort as low values. + * + * @return true if NULL's sort as low values, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullsAreSortedLow() throws SQLException; + + /** + * This method tests whether or not NULL's sort as high values. + * + * @return true if NULL's sort as high values, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullsAreSortedAtStart() throws SQLException; + + /** + * This method test whether or not NULL's are sorted to the end + * of the list regardless of ascending or descending sort order. + * + * @return true if NULL's always sort to the end, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullsAreSortedAtEnd() throws SQLException; + + /** + * This method returns the name of the database product. + * + * @return The database product. + * @exception SQLException If an error occurs. + */ + String getDatabaseProductName() throws SQLException; + + /** + * This method returns the version of the database product. + * + * @return The version of the database product. + * @exception SQLException If an error occurs. + */ + String getDatabaseProductVersion() throws SQLException; + + /** + * This method returns the name of the JDBC driver. + * + * @return The name of the JDBC driver. + * @exception SQLException If an error occurs. + */ + String getDriverName() throws SQLException; + + /** + * This method returns the version of the JDBC driver. + * + * @return The version of the JDBC driver. + * @exception SQLException If an error occurs. + */ + String getDriverVersion() throws SQLException; + + /** + * This method returns the major version number of the JDBC driver. + * + * @return The major version number of the JDBC driver. + */ + int getDriverMajorVersion(); + + /** + * This method returns the minor version number of the JDBC driver. + * + * @return The minor version number of the JDBC driver. + */ + int getDriverMinorVersion(); + + /** + * This method tests whether or not the database uses local files to + * store tables. + * + * @return true if the database uses local files, + * false otherwise. + * + * @exception SQLException If an error occurs. + */ + boolean usesLocalFiles() throws SQLException; + + /** + * This method tests whether or not the database uses a separate file for + * each table. + * + * @return true if the database uses a separate file for each + * table false otherwise. + * + * @exception SQLException If an error occurs. + */ + boolean usesLocalFilePerTable() throws SQLException; + + /** + * This method tests whether or not the database supports identifiers + * with mixed case. + * + * @return true if the database supports mixed case identifiers, + * false otherwise. + * + * @exception SQLException If an error occurs. + */ + boolean supportsMixedCaseIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database treats mixed case + * identifiers as all upper case. + * + * @return true if the database treats all identifiers as + * upper case, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesUpperCaseIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database treats mixed case + * identifiers as all lower case. + * + * @return true if the database treats all identifiers as + * lower case, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesLowerCaseIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database stores mixed case + * identifers even if it treats them as case insensitive. + * + * @return true if the database stores mixed case identifiers, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesMixedCaseIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database supports quoted identifiers + * with mixed case. + * + * @return true if the database supports mixed case quoted + * identifiers, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsMixedCaseQuotedIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database treats mixed case + * quoted identifiers as all upper case. + * + * @return true if the database treats all quoted identifiers + * as upper case, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesUpperCaseQuotedIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database treats mixed case + * quoted identifiers as all lower case. + * + * @return true if the database treats all quoted identifiers + * as lower case, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesLowerCaseQuotedIdentifiers() throws SQLException; + + /** + * This method tests whether or not the database stores mixed case + * quoted identifers even if it treats them as case insensitive. + * + * @return true if the database stores mixed case quoted + * identifiers, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean storesMixedCaseQuotedIdentifiers() throws SQLException; + + /** + * This metohd returns the quote string for SQL identifiers. + * + * @return The quote string for SQL identifers, or a space if quoting + * is not supported. + * @exception SQLException If an error occurs. + */ + String getIdentifierQuoteString() throws SQLException; + + /** + * This method returns a comma separated list of all the SQL keywords in + * the database that are not in SQL92. + * + * @return The list of SQL keywords not in SQL92. + * @exception SQLException If an error occurs. + */ + String getSQLKeywords() throws SQLException; + + /** + * This method returns a comma separated list of math functions. + * + * @return The list of math functions. + * @exception SQLException If an error occurs. + */ + String getNumericFunctions() throws SQLException; + + /** + * This method returns a comma separated list of string functions. + * + * @return The list of string functions. + * @exception SQLException If an error occurs. + */ + String getStringFunctions() throws SQLException; + + /** + * This method returns a comma separated list of of system functions. + * + * @return A comma separated list of system functions. + * @exception SQLException If an error occurs. + */ + String getSystemFunctions() throws SQLException; + + /** + * This method returns comma separated list of time/date functions. + * + * @return The list of time/date functions. + * @exception SQLException If an error occurs. + */ + String getTimeDateFunctions() throws SQLException; + + /** + * This method returns the string used to escape wildcards in search strings. + * + * @return The string used to escape wildcards in search strings. + * @exception SQLException If an error occurs. + */ + String getSearchStringEscape() throws SQLException; + + /** + * This methods returns non-standard characters that can appear in + * unquoted identifiers. + * + * @return Non-standard characters that can appear in unquoted identifiers. + * @exception SQLException If an error occurs. + */ + String getExtraNameCharacters() throws SQLException; + + /** + * This method tests whether or not the database supports + * "ALTER TABLE ADD COLUMN" + * + * @return true if column add supported, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsAlterTableWithAddColumn() throws SQLException; + + /** + * This method tests whether or not the database supports + * "ALTER TABLE DROP COLUMN" + * + * @return true if column drop supported, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsAlterTableWithDropColumn() throws SQLException; + + /** + * This method tests whether or not column aliasing is supported. + * + * @return true if column aliasing is supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsColumnAliasing() throws SQLException; + + /** + * This method tests whether the concatenation of a NULL and non-NULL + * value results in a NULL. This will always be true in fully JDBC compliant + * drivers. + * + * @return true if concatenating NULL and a non-NULL value + * returns a NULL, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean nullPlusNonNullIsNull() throws SQLException; + + /** + * Tests whether or not CONVERT is supported. + * + * @return true if CONVERT is supported, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsConvert() throws SQLException; + + /** + * This method tests whether or not CONVERT can be performed between the + * specified types. The types are contants from Types. + * + * @param fromType The SQL type to convert from. + * @param toType The SQL type to convert to. + * @return true if the conversion can be performed, + * false otherwise. + * @see Types + */ + boolean supportsConvert(int fromType, int toType) throws + SQLException; + + /** + * This method tests whether or not table correlation names are + * supported. This will be always be true in a fully JDBC + * compliant driver. + * + * @return true if table correlation names are supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsTableCorrelationNames() throws SQLException; + + /** + * This method tests whether correlation names must be different from the + * name of the table. + * + * @return true if the correlation name must be different from + * the table name, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsDifferentTableCorrelationNames() throws SQLException; + + /** + * This method tests whether or not expressions are allowed in an + * ORDER BY lists. + * + * @return true if expressions are allowed in ORDER BY + * lists, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsExpressionsInOrderBy() throws SQLException; + + /** + * This method tests whether or ORDER BY on a non-selected column is + * allowed. + * + * @return true if a non-selected column can be used in an + * ORDER BY, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOrderByUnrelated() throws SQLException; + + /** + * This method tests whether or not GROUP BY is supported. + * + * @return true if GROUP BY is supported, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsGroupBy() throws SQLException; + + /** + * This method tests whether GROUP BY on a non-selected column is + * allowed. + * + * @return true if a non-selected column can be used in a + * GROUP BY, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsGroupByUnrelated() throws SQLException; + + /** + * This method tests whether or not a GROUP BY can add columns not in the + * select if it includes all the columns in the select. + * + * @return true if GROUP BY an add columns provided it includes + * all columns in the select, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsGroupByBeyondSelect() throws SQLException; + + /** + * This method tests whether or not the escape character is supported in + * LIKE expressions. A fully JDBC compliant driver will always return + * true. + * + * @return true if escapes are supported in LIKE expressions, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsLikeEscapeClause() throws SQLException; + + /** + * This method tests whether multiple result sets for a single statement are + * supported. + * + * @return true if multiple result sets are supported for a + * single statement, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsMultipleResultSets() throws SQLException; + + /** + * This method test whether or not multiple transactions may be open + * at once, as long as they are on different connections. + * + * @return true if multiple transactions on different + * connections are supported, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsMultipleTransactions() throws SQLException; + + /** + * This method tests whether or not columns can be defined as NOT NULL. A + * fully JDBC compliant driver always returns true. + * + * @return true if NOT NULL columns are supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsNonNullableColumns() throws SQLException; + + /** + * This method tests whether or not the minimum grammer for ODBC is supported. + * A fully JDBC compliant driver will always return true. + * + * @return true if the ODBC minimum grammar is supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsMinimumSQLGrammar() throws SQLException; + + /** + * This method tests whether or not the core grammer for ODBC is supported. + * + * @return true if the ODBC core grammar is supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCoreSQLGrammar() throws SQLException; + + /** + * This method tests whether or not the extended grammer for ODBC is supported. + * + * @return true if the ODBC extended grammar is supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsExtendedSQLGrammar() throws SQLException; + + /** + * This method tests whether or not the ANSI92 entry level SQL + * grammar is supported. A fully JDBC compliant drivers must return + * true. + * + * @return true if the ANSI92 entry level SQL grammar is + * supported, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsANSI92EntryLevelSQL() throws SQLException; + + /** + * This method tests whether or not the ANSI92 intermediate SQL + * grammar is supported. + * + * @return true if the ANSI92 intermediate SQL grammar is + * supported, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsANSI92IntermediateSQL() throws SQLException; + + /** + * This method tests whether or not the ANSI92 full SQL + * grammar is supported. + * + * @return true if the ANSI92 full SQL grammar is + * supported, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsANSI92FullSQL() throws SQLException; + + /** + * This method tests whether or not the SQL integrity enhancement + * facility is supported. + * + * @return true if the integrity enhancement facility is + * supported, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsIntegrityEnhancementFacility() throws SQLException; + + /** + * This method tests whether or not the database supports outer joins. + * + * @return true if outer joins are supported, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOuterJoins() throws SQLException; + + /** + * This method tests whether or not the database supports full outer joins. + * + * @return true if full outer joins are supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsFullOuterJoins() throws SQLException; + + /** + * This method tests whether or not the database supports limited outer joins. + * + * @return true if limited outer joins are supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsLimitedOuterJoins() throws SQLException; + + /** + * This method returns the vendor's term for "schema". + * + * @return The vendor's term for schema. + * @exception SQLException if an error occurs. + */ + String getSchemaTerm() throws SQLException; + + /** + * This method returns the vendor's term for "procedure". + * + * @return The vendor's term for procedure. + * @exception SQLException if an error occurs. + */ + String getProcedureTerm() throws SQLException; + + /** + * This method returns the vendor's term for "catalog". + * + * @return The vendor's term for catalog. + * @exception SQLException if an error occurs. + */ + String getCatalogTerm() throws SQLException; + + /** + * This method tests whether a catalog name appears at the beginning of + * a fully qualified table name. + * + * @return true if the catalog name appears at the beginning, + * false if it appears at the end. + * @exception SQLException If an error occurs. + */ + boolean isCatalogAtStart() throws SQLException; + + /** + * This method returns the separator between the catalog name and the + * table name. + * + * @return The separator between the catalog name and the table name. + * @exception SQLException If an error occurs. + */ + String getCatalogSeparator() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a data + * manipulation statement. + * + * @return true if a catalog name can appear in a data + * manipulation statement, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInDataManipulation() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a procedure + * call + * + * @return true if a catalog name can appear in a procedure + * call, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInProcedureCalls() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a table definition. + * + * @return true if a catalog name can appear in a table + * definition, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInTableDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in an index definition. + * + * @return true if a catalog name can appear in an index + * definition, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInIndexDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in privilege definitions. + * + * @return true if a catalog name can appear in privilege + * definition, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSchemasInPrivilegeDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a data + * manipulation statement. + * + * @return true if a catalog name can appear in a data + * manipulation statement, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInDataManipulation() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a procedure + * call + * + * @return true if a catalog name can appear in a procedure + * call, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInProcedureCalls() throws SQLException; + + /** + * This method tests whether a catalog name can appear in a table definition. + * + * @return true if a catalog name can appear in a table + * definition, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInTableDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in an index definition. + * + * @return true if a catalog name can appear in an index + * definition, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInIndexDefinitions() throws SQLException; + + /** + * This method tests whether a catalog name can appear in privilege definitions. + * + * @return true if a catalog name can appear in privilege + * definition, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException; + + /** + * This method tests whether or not that database supports positioned + * deletes. + * + * @return true if positioned deletes are supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsPositionedDelete() throws SQLException; + + /** + * This method tests whether or not that database supports positioned + * updates. + * + * @return true if positioned updates are supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsPositionedUpdate() throws SQLException; + + /** + * This method tests whether or not SELECT FOR UPDATE is supported by the + * database. + * + * @return true if SELECT FOR UPDATE is supported + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSelectForUpdate() throws SQLException; + + /** + * This method tests whether or not stored procedures are supported on + * this database. + * + * @return true if stored procedures are supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsStoredProcedures() throws SQLException; + + /** + * This method tests whether or not subqueries are allowed in comparisons. + * A fully JDBC compliant driver will always return true. + * + * @return true if subqueries are allowed in comparisons, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSubqueriesInComparisons() throws SQLException; + + /** + * This method tests whether or not subqueries are allowed in exists + * expressions. A fully JDBC compliant driver will always return + * true. + * + * @return true if subqueries are allowed in exists + * expressions, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSubqueriesInExists() throws SQLException; + + /** + * This method tests whether subqueries are allowed in IN statements. + * A fully JDBC compliant driver will always return true. + * + * @return true if the driver supports subqueries in IN + * statements, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSubqueriesInIns() throws SQLException; + + /** + * This method tests whether or not subqueries are allowed in quantified + * expressions. A fully JDBC compliant driver will always return + * true. + * + * @return true if subqueries are allowed in quantified + * expressions, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsSubqueriesInQuantifieds() throws SQLException; + + /** + * This method test whether or not correlated subqueries are allowed. A + * fully JDBC compliant driver will always return true. + * + * @return true if correlated subqueries are allowed, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsCorrelatedSubqueries() throws SQLException; + + /** + * This method tests whether or not the UNION statement is supported. + * + * @return true if UNION is supported, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsUnion() throws SQLException; + + /** + * This method tests whether or not the UNION ALL statement is supported. + * + * @return true if UNION ALL is supported, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsUnionAll() throws SQLException; + + /** + * This method tests whether or not the database supports cursors + * remaining open across commits. + * + * @return true if cursors can remain open across commits, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOpenCursorsAcrossCommit() throws SQLException; + + /** + * This method tests whether or not the database supports cursors + * remaining open across rollbacks. + * + * @return true if cursors can remain open across rollbacks, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOpenCursorsAcrossRollback() throws SQLException; + + /** + * This method tests whether or not the database supports statements + * remaining open across commits. + * + * @return true if statements can remain open across commits, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOpenStatementsAcrossCommit() throws SQLException; + + /** + * This method tests whether or not the database supports statements + * remaining open across rollbacks. + * + * @return true if statements can remain open across rollbacks, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsOpenStatementsAcrossRollback() throws SQLException; + + /** + * This method returns the number of hex characters allowed in an inline + * binary literal. + * + * @return The number of hex characters allowed in a binary literal, 0 meaning + * either an unknown or unlimited number. + * @exception SQLException If an error occurs. + */ + int getMaxBinaryLiteralLength() throws SQLException; + + /** + * This method returns the maximum length of a character literal. + * + * @return The maximum length of a character literal. + * @exception SQLException If an error occurs. + */ + int getMaxCharLiteralLength() throws SQLException; + + /** + * This method returns the maximum length of a column name. + * + * @return The maximum length of a column name. + * @exception SQLException If an error occurs. + */ + int getMaxColumnNameLength() throws SQLException; + + /** + * This method returns the maximum number of columns in a GROUP BY statement. + * + * @return The maximum number of columns in a GROUP BY statement. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInGroupBy() throws SQLException; + + /** + * This method returns the maximum number of columns in an index. + * + * @return The maximum number of columns in an index. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInIndex() throws SQLException; + + /** + * This method returns the maximum number of columns in an ORDER BY statement. + * + * @return The maximum number of columns in an ORDER BY statement. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInOrderBy() throws SQLException; + + /** + * This method returns the maximum number of columns in a SELECT statement. + * + * @return The maximum number of columns in a SELECT statement. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInSelect() throws SQLException; + + /** + * This method returns the maximum number of columns in a table. + * + * @return The maximum number of columns in a table. + * @exception SQLException If an error occurs. + */ + int getMaxColumnsInTable() throws SQLException; + + /** + * This method returns the maximum number of connections this client + * can have to the database. + * + * @return The maximum number of database connections. + * @SQLException If an error occurs. + */ + int getMaxConnections() throws SQLException; + + /** + * This method returns the maximum length of a cursor name. + * + * @return The maximum length of a cursor name. + * @exception SQLException If an error occurs. + */ + int getMaxCursorNameLength() throws SQLException; + + /** + * This method returns the maximum length of an index. + * + * @return The maximum length of an index. + * @exception SQLException If an error occurs. + */ + int getMaxIndexLength() throws SQLException; + + /** + * This method returns the maximum length of a schema name. + * + * @return The maximum length of a schema name. + * @exception SQLException If an error occurs. + */ + int getMaxSchemaNameLength() throws SQLException; + + /** + * This method returns the maximum length of a procedure name. + * + * @return The maximum length of a procedure name. + * @exception SQLException If an error occurs. + */ + int getMaxProcedureNameLength() throws SQLException; + + /** + * This method returns the maximum length of a catalog name. + * + * @return The maximum length of a catalog name. + * @exception SQLException If an error occurs. + */ + int getMaxCatalogNameLength() throws SQLException; + + /** + * This method returns the maximum size of a row in bytes. + * + * @return The maximum size of a row. + * @exception SQLException If an error occurs. + */ + int getMaxRowSize() throws SQLException; + + /** + * This method tests whether or not the maximum row size includes BLOB's + * + * @return true if the maximum row size includes BLOB's, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean doesMaxRowSizeIncludeBlobs() throws SQLException; + + /** + * This method includes the maximum length of a SQL statement. + * + * @return The maximum length of a SQL statement. + * @exception SQLException If an error occurs. + */ + int getMaxStatementLength() throws SQLException; + + /** + * This method returns the maximum number of statements that can be + * active at any time. + * + * @return The maximum number of statements that can be active at any time. + * @exception SQLException If an error occurs. + */ + int getMaxStatements() throws SQLException; + + /** + * This method returns the maximum length of a table name. + * + * @return The maximum length of a table name. + * @exception SQLException If an error occurs. + */ + int getMaxTableNameLength() throws SQLException; + + /** + * This method returns the maximum number of tables that may be referenced + * in a SELECT statement. + * + * @return The maximum number of tables allowed in a SELECT statement. + * @exception SQLException If an error occurs. + */ + int getMaxTablesInSelect() throws SQLException; + + /** + * This method returns the maximum length of a user name. + * + * @return The maximum length of a user name. + * @exception SQLException If an error occurs. + */ + int getMaxUserNameLength() throws SQLException; + + /** + * This method returns the default transaction isolation level of the + * database. + * + * @return The default transaction isolation level of the database. + * @exception SQLException If an error occurs. + * @see Connection + */ + int getDefaultTransactionIsolation() throws SQLException; + + /** + * This method tests whether or not the database supports transactions. + * + * @return true if the database supports transactions, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsTransactions() throws SQLException; + + /** + * This method tests whether or not the database supports the specified + * transaction isolation level. + * + * @param level The transaction isolation level. + * + * @return true if the specified transaction isolation level + * is supported, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsTransactionIsolationLevel(int level) throws + SQLException; + + /** + * This method tests whether or not DDL and DML statements allowed within + * the same transaction. + * + * @return true if DDL and DML statements are allowed in the + * same transaction, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsDataDefinitionAndDataManipulationTransactions() + throws SQLException; + + /** + * This method tests whether or not only DML statement are allowed + * inside a transaction. + * + * @return true if only DML statements are allowed in + * transactions, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsDataManipulationTransactionsOnly() throws + SQLException; + + /** + * This method tests whether or not a DDL statement will cause the + * current transaction to be automatically committed. + * + * @return true if DDL causes an immediate transaction commit, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean dataDefinitionCausesTransactionCommit() throws SQLException; + + /** + * This method tests whether or not DDL statements are ignored in + * transactions. + * + * @return true if DDL statements are ignored in transactions, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean dataDefinitionIgnoredInTransactions() throws SQLException; + + /** + * This method returns a list of all the stored procedures matching the + * specified pattern in the given schema and catalog. This is returned + * a ResultSet with the following columns: + *

        + *

          + *
        1. PROCEDURE_CAT - The catalog the procedure is in, which may be + * null.
        2. + *
        3. PROCEDURE_SCHEM - The schema the procedures is in, which may be + * null.
        4. + *
        5. PROCEDURE_NAME - The name of the procedure.
        6. + *
        7. Unused
        8. + *
        9. Unused
        10. + *
        11. Unused
        12. + *
        13. REMARKS - A description of the procedure
        14. + *
        15. PROCEDURE_TYPE - Indicates the return type of the procedure, which + * is one of the contstants defined in this class + * (procedureResultUnknown, procedureNoResult, or + * procedureReturnsResult).
        16. + *
        + * + * @param catalog The name of the catalog to return stored procedured from, + * or "" to return procedures from all catalogs. + * @param schemaPattern A schema pattern for the schemas to return stored + * procedures from, or "" to return procedures from all schemas. + * @param procedurePattern The pattern of procedure names to return. + * @returns A ResultSet with all the requested procedures. + * @exception SQLException If an error occurs. + */ + ResultSet getProcedures(String catalog, String schemaPattern, String + procedurePattern) throws SQLException; + + /** + * This method returns a list of the parameter and result columns for + * the requested stored procedures. This is returned in the form of a + * ResultSet with the following columns: + *

        + *

          + *
        1. PROCEDURE_CAT - The catalog the procedure is in, which may be + * null.
        2. + *
        3. PROCEDURE_SCHEM - The schema the procedures is in, which may be + * null.
        4. + *
        5. PROCEDURE_NAME - The name of the procedure.
        6. + *
        7. COLUMN_NAME - The name of the column
        8. + *
        9. COLUMN_TYPE - The type of the column, which will be one of the + * contants defined in this class (procedureColumnUnknown, + * procedureColumnIn, procedureColumnInOut, + * procedureColumnOut, procedureColumnReturn, + * or procedureColumnResult).
        10. + *
        11. DATA_TYPE - The SQL type of the column. This is one of the constants + * defined in Types.
        12. + *
        13. TYPE_NAME - The string name of the data type for this column.
        14. + *
        15. PRECISION - The precision of the column.
        16. + *
        17. LENGTH - The length of the column in bytes
        18. + *
        19. SCALE - The scale of the column.
        20. + *
        21. RADIX - The radix of the column.
        22. + *
        23. NULLABLE - Whether or not the column is NULLABLE. This is one of + * the constants defined in this class (procedureNoNulls, + * procedureNullable, or procedureNullableUnknown)
        24. + *
        25. REMARKS - A description of the column.
        26. + *
        + * + * @param catalog The name of the catalog to return stored procedured from, + * or "" to return procedures from all catalogs. + * @param schemaPattern A schema pattern for the schemas to return stored + * procedures from, or "" to return procedures from all schemas. + * @param procedurePattern The pattern of procedures names to return. + * @param columnPattern The pattern of column names to return. + * @returns A ResultSet with all the requested procedures. + * @exception SQLException If an error occurs. + */ + ResultSet getProcedureColumns(String catalog, String schemaPattern, + String procedurePattern, String columnPattern) throws + SQLException; + + /** + * This method returns a list of the requested table as a + * ResultSet with the following columns: + * + *
          + *
        1. TABLE_CAT - The catalog the table is in, which may be null.
        2. + *
        3. TABLE_SCHEM - The schema the table is in, which may be null.
        4. + *
        5. TABLE_NAME - The name of the table.
        6. + *
        7. TABLE_TYPE - A string describing the table type. This will be one + * of the values returned by the getTableTypes() method.
        8. + *
        9. REMARKS - Comments about the table.
        10. + *
        + * + * @param catalog The name of the catalog to return tables from, + * or "" to return tables from all catalogs. + * @param schemaPattern A schema pattern for the schemas to return tables + * from, or "" to return tables from all schemas. + * @param tablePattern The pattern of table names to return. + * @param types The list of table types to include; null returns all types. + * @returns A ResultSet with all the requested tables. + * @exception SQLException If an error occurs. + */ + ResultSet getTables(String catalog, String schemaPattern, String + tablePattern, String[] types) throws SQLException; + + /** + * This method returns the list of database schemas as a + * ResultSet, with one column - TABLE_SCHEM - that is the + * name of the schema. + * + * @return A ResultSet with all the requested schemas. + * @exception SQLException If an error occurs. + */ + ResultSet getSchemas() throws SQLException; + + /** + * This method returns the list of database catalogs as a + * ResultSet with one column - TABLE_CAT - that is the + * name of the catalog. + * + * @return A ResultSet with all the requested catalogs. + * @exception SQLException If an error occurs. + */ + ResultSet getCatalogs() throws SQLException; + + /** + * This method returns the list of database table types as a + * ResultSet with one column - TABLE_TYPE - that is the + * name of the table type. + * + * @return A ResultSet with all the requested table types. + * @exception SQLException If an error occurs. + */ + ResultSet getTableTypes() throws SQLException; + + /** + * This method returns a list of the tables columns for + * the requested tables. This is returned in the form of a + * ResultSet with the following columns: + *

        + *

          + *
        1. TABLE_CAT - The catalog the table is in, which may be + * null.
        2. + *
        3. TABLE_SCHEM - The schema the tables is in, which may be + * null.
        4. + *
        5. TABLE_NAME - The name of the table.
        6. + *
        7. COLUMN_NAME - The name of the column
        8. + *
        9. DATA_TYPE - The SQL type of the column. This is one of the constants + * defined in Types.
        10. + *
        11. TYPE_NAME - The string name of the data type for this column.
        12. + *
        13. COLUMN_SIZE - The size of the column.
        14. + *
        15. Unused
        16. + *
        17. NUM_PREC_RADIX - The radix of the column.
        18. + *
        19. NULLABLE - Whether or not the column is NULLABLE. This is one of + * the constants defined in this class (tableNoNulls, + * tableNullable, or tableNullableUnknown)
        20. + *
        21. REMARKS - A description of the column.
        22. + *
        23. COLUMN_DEF - The default value for the column, may be null.
        24. + *
        25. SQL_DATA_TYPE - Unused
        26. + *
        27. SQL_DATETIME_SUB - Unused
        28. + *
        29. CHAR_OCTET_LENGTH - For character columns, the maximum number of bytes + * in the column.
        30. + *
        31. ORDINAL_POSITION - The index of the column in the table.
        32. + *
        33. IS_NULLABLE - "NO" means no, "YES" means maybe, and an empty string + * means unknown.
        34. + *
        + * + * @param catalog The name of the catalog to return table from, + * or "" to return tables from all catalogs. + * @param schemaPattern A schema pattern for the schemas to return + * tables from, or "" to return tables from all schemas. + * @param tablePattern The pattern of table names to return. + * @param columnPattern The pattern of column names to return. + * @returns A ResultSet with all the requested tables. + * @exception SQLException If an error occurs. + */ + ResultSet getColumns(String catalog, String schemaPattern, String + tablePattern, String columnPattern) throws SQLException; + + /** + * This method returns the access rights that have been granted to the + * requested columns. This information is returned as a ResultSet + * with the following columns: + * + *
          + *
        1. TABLE_CAT - The catalog the table is in, which may be + * null.
        2. + *
        3. TABLE_SCHEM - The schema the tables is in, which may be + * null.
        4. + *
        5. TABLE_NAME - The name of the table.
        6. + *
        7. COLUMN_NAME - The name of the column.
        8. + *
        9. GRANTOR - The entity that granted the access.
        10. + *
        11. GRANTEE - The entity granted the access.
        12. + *
        13. PRIVILEGE - The name of the privilege granted.
        14. + *
        15. IS_GRANTABLE - "YES" if the grantee can grant the privilege to + * others, "NO" if not, and null if unknown.
        16. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or null + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tableName The table name to return information for. + * @param columnPattern A pattern of column names to return information for. + * @return A ResultSet with all the requested privileges. + * @exception SQLException If an error occurs. + */ + ResultSet getColumnPrivileges(String catalog, String schema, String + tableName, String columnPattern) throws SQLException; + + /** + * This method returns the access rights that have been granted to the + * requested tables. This information is returned as a ResultSet + * with the following columns: + * + *
          + *
        1. TABLE_CAT - The catalog the table is in, which may be + * null.
        2. + *
        3. TABLE_SCHEM - The schema the tables is in, which may be + * null.
        4. + *
        5. TABLE_NAME - The name of the table.
        6. + *
        7. GRANTOR - The entity that granted the access.
        8. + *
        9. GRANTEE - The entity granted the access.
        10. + *
        11. PRIVILEGE - The name of the privilege granted.
        12. + *
        13. IS_GRANTABLE - "YES" if the grantee can grant the privilege to + * others, "NO" if not, and null if unknown.
        14. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or null + * to return information from all catalogs. + * @param schemaPattern The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tablePattern The table name pattern of tables to return + * information for. + * @return A ResultSet with all the requested privileges. + * @exception SQLException If an error occurs. + */ + ResultSet getTablePrivileges(String catalog, String schemaPattern, + String tablePattern) throws SQLException; + + /** + * This method returns the best set of columns for uniquely identifying + * a row. It returns this information as a ResultSet with + * the following columns: + * + *
          + *
        1. SCOPE - The scope of the results returned. This is one of the + * constants defined in this class (bestRowTemporary, + * bestRowTransaction, or bestRowSession).
        2. + *
        3. COLUMN_NAME - The name of the column.
        4. + *
        5. DATA_TYPE - The SQL type of the column. This is one of the constants + * defined in Types.
        6. + *
        7. TYPE_NAME - The string name of the data type for this column.
        8. + *
        9. COLUMN_SIZE - The precision of the columns
        10. + *
        11. BUFFER_LENGTH - Unused
        12. + *
        13. DECIMAL_DIGITS - The scale of the column.
        14. + *
        15. PSEUDO_COLUMN - Whether or not the best row identifier is a + * pseudo_column. This is one of the constants defined in this class + * (bestRowUnknown, bestRowNotPseudo, or + * bestRowPseudo).
        16. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or null + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tableName The table name to return information for. + * @param scope One of the best row id scope constants from this class. + * @param nullable true to include columns that are nullable, + * false otherwise. + * @return A ResultSet with the best row identifier. + * @exception SQLException If an error occurs. + */ + ResultSet getBestRowIdentifier(String catalog, String schema, + String tableName, int scope, boolean nullable) throws SQLException; + + /** + * This method returns the set of columns that are automatically updated + * when the row is update. It returns this information as a + * ResultSet with the following columns: + * + *
          + *
        1. SCOPE - Unused
        2. + *
        3. COLUMN_NAME - The name of the column.
        4. + *
        5. DATA_TYPE - The SQL type of the column. This is one of the constants + * defined in Types.
        6. + *
        7. TYPE_NAME - The string name of the data type for this column.
        8. + *
        9. COLUMN_SIZE - The precision of the columns
        10. + *
        11. BUFFER_LENGTH - Unused
        12. + *
        13. DECIMAL_DIGITS - The scale of the column.
        14. + *
        15. PSEUDO_COLUMN - Whether or not the best row identifier is a + * pseudo_column. This is one of the constants defined in this class + * (versionRowUnknown, versionRowNotPseudo, or + * versionRowPseudo).
        16. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or null + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tableName The table name to return information for + * @return A ResultSet with the version columns. + * @exception SQLException If an error occurs. + */ + ResultSet getVersionColumns(String catalog, String schema, + String tableName) throws SQLException; + + /** + * This method returns a list of a table's primary key columns. These + * are returned as a ResultSet with the following columns. + * + *
          + *
        1. TABLE_CAT - The catalog of the table, which may be null.
        2. + *
        3. TABLE_SCHEM - The schema of the table, which may be null.
        4. + *
        5. TABLE_NAME - The name of the table.
        6. + *
        7. COLUMN_NAME - The name of the column.
        8. + *
        9. KEY_SEQ - The sequence number of the column within the primary key.
        10. + *
        11. PK_NAME - The name of the primary key, which may be null.
        12. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or null + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tableName The table name to return information for. + * @return A ResultSet with the primary key columns. + * @exception SQLException If an error occurs. + */ + ResultSet getPrimaryKeys(String catalog, String schema, String tableName) + throws SQLException; + + /** + * This method returns a list of the table's foreign keys. These are + * returned as a ResultSet with the following columns: + * + *
          + *
        1. PKTABLE_CAT - The catalog of the table the key was imported from.
        2. + *
        3. PKTABLE_SCHEM - The schema of the table the key was imported from.
        4. + *
        5. PKTABLE_NAME - The name of the table the key was imported from.
        6. + *
        7. PKCOLUMN_NAME - The name of the column that was imported.
        8. + *
        9. FKTABLE_CAT - The foreign key catalog name.
        10. + *
        11. FKTABLE_SCHEM - The foreign key schema name.
        12. + *
        13. FKTABLE_NAME - The foreign key table name.
        14. + *
        15. FKCOLUMN_NAME - The foreign key column name.
        16. + *
        17. KEY_SEQ - The sequence number of the column within the foreign key.
        18. + *
        19. UPDATE_RULE - How the foreign key behaves when the primary key is + * updated. This is one of the constants defined in this class + * (importedNoAction, importedKeyCascade, + * importedKeySetNull, importedKeySetDefault, or + * importedKeyRestrict).
        20. + *
        21. DELETE_RULE - How the foreign key behaves when the primary key is + * deleted. This is one of the constants defined in this class + * (importedNoAction, importedKeyCascade, + * importedKeySetNull, or importedKeySetDefault)
        22. + *
        23. FK_NAME - The name of the foreign key.
        24. + *
        25. PK_NAME - The name of the primary key.
        26. + *
        27. DEFERRABILITY - The deferrability value. This is one of the + * constants defined in this table (importedKeyInitiallyDeferred, + * importedKeyInitiallyImmediate, or + * importedKeyNotDeferrable).
        28. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or null + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tableName The table name to return information for. + * @return A ResultSet with the foreign key columns. + * @exception SQLException If an error occurs. + */ + ResultSet getImportedKeys(String catalog, String schema, + String tableName) throws SQLException; + + /** + * This method returns a list of the table's which use this table's + * primary key as a foreign key. The information is + * returned as a ResultSet with the following columns: + * + *
          + *
        1. PKTABLE_CAT - The catalog of the table the key was imported from.
        2. + *
        3. PKTABLE_SCHEM - The schema of the table the key was imported from.
        4. + *
        5. PKTABLE_NAME - The name of the table the key was imported from.
        6. + *
        7. PKCOLUMN_NAME - The name of the column that was imported.
        8. + *
        9. FKTABLE_CAT - The foreign key catalog name.
        10. + *
        11. FKTABLE_SCHEM - The foreign key schema name.
        12. + *
        13. FKTABLE_NAME - The foreign key table name.
        14. + *
        15. FKCOLUMN_NAME - The foreign key column name.
        16. + *
        17. KEY_SEQ - The sequence number of the column within the foreign key.
        18. + *
        19. UPDATE_RULE - How the foreign key behaves when the primary key is + * updated. This is one of the constants defined in this class + * (importedNoAction, importedKeyCascade, + * importedKeySetNull, importedKeySetDefault, or + * importedKeyRestrict).
        20. + *
        21. DELETE_RULE - How the foreign key behaves when the primary key is + * deleted. This is one of the constants defined in this class + * (importedNoAction, importedKeyCascade, + * importedKeySetNull, or importedKeySetDefault)
        22. + *
        23. FK_NAME - The name of the foreign key.
        24. + *
        25. PK_NAME - The name of the primary key.
        26. + *
        27. DEFERRABILITY - The deferrability value. This is one of the + * constants defined in this table (importedKeyInitiallyDeferred, + * importedKeyInitiallyImmediate, or + * importedKeyNotDeferrable).
        28. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or null + * to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tableName The table name to return information for. + * @return A ResultSet with the requested information + * @exception SQLException If an error occurs. + */ + ResultSet getExportedKeys(String catalog, String schema, + String tableName) throws SQLException; + + /** + * This method returns a description of how one table imports another + * table's primary key as a foreign key. The information is + * returned as a ResultSet with the following columns: + * + *
          + *
        1. PKTABLE_CAT - The catalog of the table the key was imported from.
        2. + *
        3. PKTABLE_SCHEM - The schema of the table the key was imported from.
        4. + *
        5. PKTABLE_NAME - The name of the table the key was imported from.
        6. + *
        7. PKCOLUMN_NAME - The name of the column that was imported.
        8. + *
        9. FKTABLE_CAT - The foreign key catalog name.
        10. + *
        11. FKTABLE_SCHEM - The foreign key schema name.
        12. + *
        13. FKTABLE_NAME - The foreign key table name.
        14. + *
        15. FKCOLUMN_NAME - The foreign key column name.
        16. + *
        17. KEY_SEQ - The sequence number of the column within the foreign key.
        18. + *
        19. UPDATE_RULE - How the foreign key behaves when the primary key is + * updated. This is one of the constants defined in this class + * (importedNoAction, importedKeyCascade, + * importedKeySetNull, importedKeySetDefault, or + * importedKeyRestrict).
        20. + *
        21. DELETE_RULE - How the foreign key behaves when the primary key is + * deleted. This is one of the constants defined in this class + * (importedNoAction, importedKeyCascade, + * importedKeySetNull, or importedKeySetDefault)
        22. + *
        23. FK_NAME - The name of the foreign key.
        24. + *
        25. PK_NAME - The name of the primary key.
        26. + *
        27. DEFERRABILITY - The deferrability value. This is one of the + * constants defined in this table (importedKeyInitiallyDeferred, + * importedKeyInitiallyImmediate, or + * importedKeyNotDeferrable).
        28. + *
        + * + * @param primaryCatalog The catalog to retrieve information from, or the + * empty string to return entities not associated with a catalog, or + * null to return information from all catalogs, on the + * exporting side. + * @param primarySchema The schema to retrieve information from, or the empty + * string to return entities not associated with a schema, on the + * exporting side. + * @param primaryTableName The table name to return information for, on the + * exporting side. + * @param foreignCatalog The catalog to retrieve information from, or the + * empty string to return entities not associated with a catalog, + * or null to return information from all catalogs, on + * the importing side. + * @param foreignSchema The schema to retrieve information from, or the + * empty string to return entities not associated with a schema on + * the importing side. + * @param foreignTableName The table name to return information for on the + * importing side. + * @return A ResultSet with the requested information + * @exception SQLException If an error occurs. + */ + ResultSet getCrossReference(String primaryCatalog, String + primarySchema, String primaryTableName, String foreignCatalog, String + foreignSchema, String foreignTableName) throws SQLException; + + /** + * This method returns a list of the SQL types supported by this + * database. The information is returned as a ResultSet + * with the following columns: + * + *
          + *
        1. TYPE_NAME - The name of the data type.
        2. + *
        3. DATA_TYPE - A data type constant from Types for this + * type.
        4. + *
        5. PRECISION - The maximum precision of this type.
        6. + *
        7. LITERAL_PREFIX - Prefix value used to quote a literal, which may be + * null.
        8. + *
        9. LITERAL_SUFFIX - Suffix value used to quote a literal, which may be + * null.
        10. + *
        11. CREATE_PARAMS - The parameters used to create the type, which may be + * null.
        12. + *
        13. NULLABLE - Whether or not this type supports NULL values. This will + * be one of the constants defined in this interface + * (typeNoNulls, typeNullable, or + * typeNullableUnknown).
        14. + *
        15. CASE_SENSITIVE - Whether or not the value is case sensitive.
        16. + *
        17. SEARCHABLE - Whether or not "LIKE" expressions are supported in + * WHERE clauses for this type. This will be one of the constants defined + * in this interface (typePredNone, typePredChar, + * typePredBasic, or typeSearchable).
        18. + *
        19. UNSIGNED_ATTRIBUTE - Is the value of this type unsigned.
        20. + *
        21. FIXED_PREC_SCALE - Whether or not this type can be used for money.
        22. + *
        23. AUTO_INCREMENT - Whether or not this type supports auto-incrementing.
        24. + *
        25. LOCAL_TYPE_NAME - A localized name for this data type.
        26. + *
        27. MINIMUM_SCALE - The minimum scale supported by this type.
        28. + *
        29. MAXIMUM_SCALE - The maximum scale supported by this type.
        30. + *
        31. SQL_DATA_TYPE - Unused.
        32. + *
        33. SQL_DATETIME_SUB - Unused.
        34. + *
        35. NUM_PREC_RADIX - The radix of this data type.
        36. + *
        + * + * @return A ResultSet with the list of available data types. + * @exception SQLException If an error occurs. + */ + ResultSet getTypeInfo() throws SQLException; + + /** + * This method returns information about a tables indices and statistics. + * It is returned as a ResultSet with the following columns: + * + *
          + *
        1. TABLE_CAT - The catalog of the table, which may be null.
        2. + *
        3. TABLE_SCHEM - The schema of the table, which may be null.
        4. + *
        5. TABLE_NAME - The name of the table.
        6. + *
        7. NON_UNIQUE - Are index values non-unique?
        8. + *
        9. INDEX_QUALIFIER The index catalog, which may be null
        10. + *
        11. INDEX_NAME - The name of the index.
        12. + *
        13. TYPE - The type of index, which will be one of the constants defined + * in this interface (tableIndexStatistic, + * tableIndexClustered, tableIndexHashed, or + * tableIndexOther).
        14. + *
        15. ORDINAL_POSITION - The sequence number of this column in the index. + * This will be 0 when the index type is tableIndexStatistic.
        16. + *
        17. COLUMN_NAME - The name of this column in the index.
        18. + *
        19. ASC_OR_DESC - "A" for an ascending sort sequence, "D" for a + * descending sort sequence or null if a sort sequence is not + * supported.
        20. + *
        21. CARDINALITY - The number of unique rows in the index, or the number + * of rows in the table if the index type is tableIndexStatistic.
        22. + *
        23. PAGES - The number of pages used for the index, or the number of pages + * in the table if the index type is tableIndexStatistic.
        24. + *
        25. FILTER_CONDITION - The filter condition for this index, which may be + * null.
        26. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or + * null to return information from all catalogs. + * @param schema The schema to retrieve information from, or the empty string + * to return entities not associated with a schema. + * @param tableName The table name to return information for. + * @param unique true to return only unique indexes, + * false otherwise. + * @param approximate true if data values can be approximations, + * false otherwise. + * @return A ResultSet with the requested index information + * @exception SQLException If an error occurs. + */ + ResultSet getIndexInfo(String catalog, String schema, String tableName, + boolean unique, boolean approximate) throws SQLException; + + /** + * This method tests whether or not the datbase supports the specified + * result type. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * + * @return true if the result set type is supported, + * false otherwise. + * + * @exception SQLException If an error occurs. + * + * @see ResultSet + */ + boolean supportsResultSetType(int type) throws SQLException; + + /** + * This method tests whether the specified result set type and result set + * concurrency type are supported by the database. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @param concurrency The desired concurrency type, which is one of the + * constants defined in ResultSet. + * @return true if the result set type is supported, + * false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean supportsResultSetConcurrency(int type, int concurrency) + throws SQLException; + + /** + * This method tests whether or not the specified result set type sees its + * own updates. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type sees its own updates, + * false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean ownUpdatesAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees its + * own deletes. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type sees its own deletes, + * false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean ownDeletesAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees its + * own inserts. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type sees its own inserts, + * false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean ownInsertsAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees + * updates committed by others. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type sees other updates, + * false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean othersUpdatesAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees + * deletes committed by others. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type sees other deletes, + * false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean othersDeletesAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type sees + * inserts committed by others. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type sees other inserts, + * false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean othersInsertsAreVisible(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type can detect + * a visible update by calling the rowUpdated method. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type can detect visible updates + * using rowUpdated, false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean updatesAreDetected(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type can detect + * a visible delete by calling the rowUpdated method. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type can detect visible deletes + * using rowUpdated, false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean deletesAreDetected(int type) throws SQLException; + + /** + * This method tests whether or not the specified result set type can detect + * a visible insert by calling the rowUpdated method. + * + * @param type The desired result type, which is one of the constants + * defined in ResultSet. + * @return true if the result set type can detect visible inserts + * using rowUpdated, false otherwise. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + boolean insertsAreDetected(int type) throws SQLException; + + /** + * This method tests whether or not the database supports batch updates. + * + * @return true if batch updates are supported, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean supportsBatchUpdates() throws SQLException; + + /** + * This method returns the list of user defined data types in use. These + * are returned as a ResultSet with the following columns: + * + *
          + *
        1. TYPE_CAT - The catalog name, which may be null.
        2. + *
        3. TYPE_SCEHM - The schema name, which may be null.
        4. + *
        5. TYPE_NAME - The user defined data type name.
        6. + *
        7. CLASS_NAME - The Java class name this type maps to.
        8. + *
        9. DATA_TYPE - A type identifier from Types for this type. + * This will be one of JAVA_OBJECT, STRUCT, or + * DISTINCT.
        10. + *
        11. REMARKS - Comments about this data type.
        12. + *
        + * + * @param catalog The catalog to retrieve information from, or the empty string + * to return entities not associated with a catalog, or null + * to return information from all catalogs. + * @param schemaPattern The schema to retrieve information from, or the + * empty string to return entities not associated with a schema. + * @param typePattern The type name pattern to match. + * @param types The type identifier patterns (from Types) to + * match. + * @return A ResultSet with the requested type information + * @exception SQLException If an error occurs. + */ + ResultSet getUDTs(String catalog, String schemaPattern, String + typePattern, int[] types) throws SQLException; + + /** + * This method returns the Connection object that was used + * to generate the metadata in this object. + * + * @return The connection for this object. + * @exception SQLException If an error occurs. + */ + Connection getConnection() throws SQLException; + + /** + * This method tests whether the databse supports savepoints. + * + * @return true if the database supports savepoints, + * false if it does not. + * @exception SQLException If an error occurs. + * @see Savepoint + * @since 1.4 + */ + boolean supportsSavepoints() throws SQLException; + + /** + * This method tests whether the database supports named parameters. + * + * @return true if the database supports named parameters, + * false if it does not. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + boolean supportsNamedParameters() throws SQLException; + + /** + * This method tests whether the database supports returning multiple + * ResultSetS from a CallableStatement at once. + * + * @return true if the database supports returnig multiple + * results at once, false if it does not. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + boolean supportsMultipleOpenResults() throws SQLException; + + /** + * @since 1.4 + */ + boolean supportsGetGeneratedKeys() throws SQLException; + + /** + * @since 1.4 + */ + ResultSet getSuperTypes(String catalog, String schemaPattern, + String typePattern) throws SQLException; + + /** + * @since 1.4 + */ + ResultSet getSuperTables(String catalog, String schemaPattern, + String tablePattern) throws SQLException; + + /** + * @since 1.4 + */ + ResultSet getAttributes(String catalog, String schemaPattern, String + typePattern, String attributePattern) throws SQLException; + + /** + * This method tests if the database supports the specified holdability type. + * Valid values for this parameter are specified in the + * ResultSet class. + * + * @param holdability The holdability type to test. + * @return true if the database supports the holdability type, + * false if it does not. + * @exception SQLException If an error occurs. + * @see ResultSet + * @since 1.4 + */ + boolean supportsResultSetHoldability(int holdability) + throws SQLException; + + /** + * This method returns the default holdability type of ResultSetS + * retrieved from this database. The possible values are specified in the + * ResultSet class. + * + * @return The default holdability type. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int getResultSetHoldability() throws SQLException; + + /** + * This method returns the major version number of the database. + * + * @return The major version number of the database. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int getDatabaseMajorVersion() throws SQLException; + + /** + * This method returns the minor version number of the database. + * + * @return The minor version number of the database. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int getDatabaseMinorVersion() throws SQLException; + + /** + * This method returns the major version number of the JDBC driver. + * + * @return The major version number of the JDBC driver. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int getJDBCMajorVersion() throws SQLException; + + /** + * This method returns the minor version number of the JDBC driver. + * + * @return The minor version number of the database. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + int getJDBCMinorVersion() throws SQLException; + + /** + * @since 1.4 + */ + int getSQLStateType() throws SQLException; + + /** + * @since 1.4 + */ + boolean locatorsUpdateCopy() throws SQLException; + + /** + * @since 1.4 + */ + boolean supportsStatementPooling() throws SQLException; +} diff --git a/libjava/classpath/java/sql/Date.java b/libjava/classpath/java/sql/Date.java new file mode 100644 index 000000000..f5eea1a2d --- /dev/null +++ b/libjava/classpath/java/sql/Date.java @@ -0,0 +1,184 @@ +/* Date.java -- Wrapper around java.util.Date + Copyright (C) 1999, 2000, 2003, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +/** + * This class is a wrapper around java.util.Date to allow the JDBC + * driver to identify the value as a SQL Date. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Date extends java.util.Date +{ + static final long serialVersionUID = 1511598038487230103L; + + /** + * Used for parsing and formatting this date. + */ + private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + + /** + * This method initializes a new instance of this class with the + * specified year, month, and day. + * + * @param year The year of this date minue 1900. + * @param month The month of this date (0-11). + * @param day The day of this date (1-31). + * + * @deprecated + */ + public Date(int year, int month, int day) + { + super(year, month, day); + } + + /** + * This method initializes a new instance of this class with the + * specified time value representing the number of milliseconds since + * Jan 1, 1970 at 12:00 midnight GMT. + * + * @param date The time value to intialize this date to. + */ + public Date(long date) + { + super(date); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getHours() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getMinutes() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getSeconds() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setHours(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setMinutes(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setSeconds(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method returns a new instance of this class by parsing a + * date in JDBC format into a Java date. + * + * @param str The string to parse. + * @return The resulting java.sql.Date value. + */ + public static Date valueOf (String str) + { + try + { + java.util.Date d = (java.util.Date) sdf.parseObject(str); + + if (d == null) + throw new IllegalArgumentException(str); + else + return new Date(d.getTime()); + } + catch (ParseException e) + { + throw new IllegalArgumentException(str); + } + } + + /** + * This method returns this date in JDBC format. + * + * @return This date as a string. + */ + public String toString() + { + return sdf.format(this); + } +} diff --git a/libjava/classpath/java/sql/Driver.java b/libjava/classpath/java/sql/Driver.java new file mode 100644 index 000000000..d7b2e8a17 --- /dev/null +++ b/libjava/classpath/java/sql/Driver.java @@ -0,0 +1,123 @@ +/* Driver.java -- A JDBC driver + Copyright (C) 1999, 2000, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.util.Properties; + +/** + * This interface specifies a mechanism for accessing a JDBC database + * driver. When the class implementing this method is loaded, it should + * register an instance of itself with the DriverManager in + * a static initializer. + *

        + * Because the DriverManager might attempt to use several + * drivers to find one that can connect to the requested database, + * this driver should not cause large numbers of classes and code to + * be loaded. If another driver is the one that ends up performing the + * request, any loading done by this driver would be wasted. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Driver +{ + /** + * This method connects to the specified database using the connection + * properties supplied. If the driver does not understand the database + * URL, it should return null instead of throwing an + * exception since the DriverManager will probe a driver + * in this manner. + * + * @param url The URL string for this connection. + * @param properties The list of database connection properties. + * @return A Connection object for the newly established + * connection, or null if the URL is not understood. + * @exception SQLException If an error occurs. + */ + Connection connect(String url, Properties properties) throws SQLException; + + /** + * This method tests whether or not the driver believes it can connect to + * the specified database. The driver should only test whether it + * understands and accepts the URL. It should not necessarily attempt to + * probe the database for a connection. + * + * @param url The database URL string. + * @return true if the drivers can connect to the database, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean acceptsURL(String url) throws SQLException; + + /** + * This method returns an array of possible properties that could be + * used to connect to the specified database. + * + * @param url The URL string of the database to connect to. + * @param properties The list of properties the caller is planning to use + * to connect to the database. + * @return A list of possible additional properties for a connection to this + * database. This list may be empty. + * @exception SQLException If an error occurs. + */ + DriverPropertyInfo[] getPropertyInfo(String url, Properties properties) + throws SQLException; + + /** + * This method returns the major version number of the driver. + * + * @return The major version number of the driver. + */ + int getMajorVersion(); + + /** + * This method returns the minor version number of the driver. + * + * @return The minor version number of the driver. + */ + int getMinorVersion(); + + /** + * This method tests whether or not the driver is JDBC compliant. This + * method should only return true if the driver has been + * certified as JDBC compliant. + * + * @return true if the driver has been certified JDBC compliant, + * false otherwise. + */ + boolean jdbcCompliant(); +} diff --git a/libjava/classpath/java/sql/DriverManager.java b/libjava/classpath/java/sql/DriverManager.java new file mode 100644 index 000000000..86ed02930 --- /dev/null +++ b/libjava/classpath/java/sql/DriverManager.java @@ -0,0 +1,345 @@ +/* DriverManager.java -- Manage JDBC drivers + Copyright (C) 1999, 2000, 2001, 2003, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * This class manages the JDBC drivers in the system. It maintains a + * registry of drivers and locates the appropriate driver to handle a + * JDBC database URL. + *

        + * On startup, DriverManager loads all the managers specified + * by the system property jdbc.drivers. The value of this + * property should be a colon separated list of fully qualified driver + * class names. Additional drivers can be loaded at any time by + * simply loading the driver class with class.forName(String). + * The driver should automatically register itself in a static + * initializer. + *

        + * The methods in this class are all static. This class + * cannot be instantiated. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DriverManager +{ + /** + * This is the log stream for JDBC drivers. + */ + private static PrintStream log_stream; + + /** + * This is the log writer for JDBC drivers. + */ + private static PrintWriter log_writer; + + /** + * This is the login timeout used by JDBC drivers. + */ + private static int login_timeout; + + /** + * This is the list of JDBC drivers that are loaded. + */ + private static Vector drivers; + // Hmm, seems like we might want to do a Hashtable and lookup by something, + // but what would it be? + + // Load all drivers on startup + static + { + drivers = new Vector(); + + String driver_string = System.getProperty("jdbc.drivers"); + if (driver_string != null) + { + StringTokenizer st = new StringTokenizer(driver_string); + while (st.hasMoreTokens()) + { + String driver_classname = st.nextToken(); + + try + { + Class.forName(driver_classname); // The driver registers itself + } + catch (Exception e) + { + // Ignore not founds + } + } + } + + } + + /** Can't be instantiated. */ + private DriverManager() + { + } + + /** + * This method returns the log writer being used by all JDBC drivers. + * This method should be used in place of the deprecated + * getLogStream method. + * + * @return The log writer in use by JDBC drivers. + */ + public static PrintWriter getLogWriter() + { + return log_writer; + } + + /** + * This method sets the log writer being used by JDBC drivers. This is a + * system-wide parameter that affects all drivers. Note that since there + * is no way to retrieve a PrintStream from a + * PrintWriter, this method cannot set the log stream in + * use by JDBC. Thus any older drivers may not see this setting. + * + * @param out The new log writer for JDBC. + */ + public static void setLogWriter(PrintWriter out) + { + DriverManager.log_writer = out; + } + +/** + * This method attempts to return a connection to the specified + * JDBC URL string using the specified connection properties. + * + * @param url The JDBC URL string to connect to. + * @param properties The connection properties. + * + * @return A Connection to that URL. + * + * @exception SQLException If an error occurs. + */ + public static Connection getConnection(String url, Properties properties) + throws SQLException + { + Driver d = getDriver(url); + if (d == null) + throw new SQLException("Driver not found for URL: " + url); + + return d.connect(url, properties); + } + + + /** + * This method attempts to return a connection to the specified + * JDBC URL string using the specified username and password. + * + * @param url The JDBC URL string to connect to. + * @param user The username to connect with. + * @param password The password to connect with. + * @return A Connection to that URL. + * @exception SQLException If an error occurs. + */ + public static Connection getConnection(String url, String user, + String password) throws SQLException + { + Properties p = new Properties(); + + if (user != null) + p.setProperty("user", user); + if (password != null) + p.setProperty("password", password); + + return getConnection(url, p); + } + + /** + * This method attempts to return a connection to the specified + * JDBC URL string. + * + * @param url The JDBC URL string to connect to. + * + * @return A Connection to that URL. + * + * @exception SQLException If an error occurs. + */ + public static Connection getConnection(String url) throws SQLException + { + return getConnection(url, new Properties()); + } + + /** + * This method returns a driver that can connect to the specified + * JDBC URL string. This will be selected from among drivers loaded + * at initialization time and those drivers manually loaded by the + * same class loader as the caller. + * + * @param url The JDBC URL string to find a driver for. + * + * @return A Driver that can connect to the specified + * URL. + * + * @exception SQLException If an error occurs, or no suitable driver can be found. + */ + public static Driver getDriver(String url) throws SQLException + { + // FIXME: Limit driver search to the appropriate subset of loaded drivers. + Enumeration e = drivers.elements(); + while(e.hasMoreElements()) + { + Driver d = (Driver)e.nextElement(); + if (d.acceptsURL(url)) + return d; + } + + throw new SQLException("No driver found for " + url); + } + + /** + * This method registers a new driver with the manager. This is normally + * called by the driver itself in a static initializer. + * + * @param driver The new Driver to add. + * + * @exception SQLException If an error occurs. + */ + public static void registerDriver(Driver driver) throws SQLException + { + if (! drivers.contains(driver)) + drivers.addElement(driver); + } + +/** + * This method de-registers a driver from the manager. + * + * @param driver The Driver to unregister. + * + * @exception SQLException If an error occurs. + */ + public static void deregisterDriver(Driver driver) throws SQLException + { + if (drivers.contains(driver)) + drivers.removeElement(driver); + } + + /** + * This method returns a list of all the currently registered JDBC drivers + * that were loaded by the current ClassLoader. + * + * @return An Enumeration of all currently loaded JDBC drivers. + */ + public static Enumeration getDrivers() + { + Vector v = new Vector(); + Enumeration e = drivers.elements(); + + // Is this right? + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + + while(e.hasMoreElements()) + { + Object obj = e.nextElement(); + + ClassLoader loader = obj.getClass().getClassLoader(); + + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + if (! loader.equals(cl)) + continue; + + v.addElement(obj); + } + + return v.elements(); + } + + /** + * This method set the login timeout used by JDBC drivers. This is a + * system-wide parameter that applies to all drivers. + * + * @param seconds The new login timeout value. + */ + public static void setLoginTimeout(int seconds) + { + DriverManager.login_timeout = seconds; + } + + /** + * This method returns the login timeout in use by JDBC drivers systemwide. + * + * @return The login timeout. + */ + public static int getLoginTimeout() + { + return login_timeout; + } + + /** + * This method sets the log stream in use by JDBC. + * + * @param stream The log stream in use by JDBC. + * @deprecated Use setLogWriter instead. + */ + public static void setLogStream(PrintStream stream) + { + DriverManager.log_stream = stream; + } + + /** + * This method returns the log stream in use by JDBC. + * + * @return The log stream in use by JDBC. + * @deprecated Use getLogWriter() instead. + */ + public static PrintStream getLogStream() + { + return log_stream; + } + + /** + * This method prints the specified line to the log stream. + * + * @param message The string to write to the log stream. + */ + public static void println(String message) + { + if (log_stream != null) // Watch for user not using logging + log_stream.println(message); + } +} diff --git a/libjava/classpath/java/sql/DriverPropertyInfo.java b/libjava/classpath/java/sql/DriverPropertyInfo.java new file mode 100644 index 000000000..c191fa5ef --- /dev/null +++ b/libjava/classpath/java/sql/DriverPropertyInfo.java @@ -0,0 +1,88 @@ +/* DriverPropertyInfo.java -- Property information about drivers. + Copyright (C) 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This class holds a driver property that can be used for querying or + * setting driver configuration parameters. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DriverPropertyInfo +{ + /** + * The name of the property. + */ + public String name; + + /** + * A description of the property, possibly null. + */ + public String description; + + /** + * A flag indicating whether or not a value for this property is required + * in order to connect to the database. + */ + public boolean required; + + /** + * This is the value of the property. + */ + public String value; + + /** + * If values are restricted to certain choices, this is the list of valid + * ones. Otherwise it is null. + */ + public String[] choices; + + /** + * This method initializes a new instance of DriverPropertyInfo + * with the specified name and value. All other fields are defaulted. + * + * @param name The name of the property. + * @param value The value to assign to the property. + */ + public DriverPropertyInfo(String name, String value) + { + this.name = name; + this.value = value; + } +} diff --git a/libjava/classpath/java/sql/ParameterMetaData.java b/libjava/classpath/java/sql/ParameterMetaData.java new file mode 100644 index 000000000..722d78bec --- /dev/null +++ b/libjava/classpath/java/sql/ParameterMetaData.java @@ -0,0 +1,103 @@ +/* ParameterMetaData.java + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +/** + * @since 1.4 + */ +public interface ParameterMetaData +{ + int parameterNoNulls = 0; + + int parameterNullable = 1; + + int parameterNullableUnknown = 2; + + int parameterModeUnknown = 0; + + int parameterModeIn = 1; + + int parameterModeInOut = 2; + + int parameterModeOut = 4; + + /** + * @since 1.4 + */ + int getParameterCount() throws SQLException; + + /** + * @since 1.4 + */ + int isNullable(int param) throws SQLException; + + /** + * @since 1.4 + */ + boolean isSigned(int param) throws SQLException; + + /** + * @since 1.4 + */ + int getPrecision(int param) throws SQLException; + + /** + * @since 1.4 + */ + int getScale(int param) throws SQLException; + + /** + * @since 1.4 + */ + int getParameterType(int param) throws SQLException; + + /** + * @since 1.4 + */ + String getParameterTypeName(int param) throws SQLException; + + /** + * @since 1.4 + */ + String getParameterClassName(int param) throws SQLException; + + /** + * @since 1.4 + */ + int getParameterMode(int param) throws SQLException; +} diff --git a/libjava/classpath/java/sql/PreparedStatement.java b/libjava/classpath/java/sql/PreparedStatement.java new file mode 100644 index 000000000..4490b5bab --- /dev/null +++ b/libjava/classpath/java/sql/PreparedStatement.java @@ -0,0 +1,458 @@ +/* PreparedStatement.java -- Interface for pre-compiled statements. + Copyright (C) 1999, 2000, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.util.Calendar; + +/** + * This interface provides a mechanism for executing pre-compiled + * statements. This provides greater efficiency when calling the same + * statement multiple times. Parameters are allowed in a statement, + * providings for maximum reusability. + * + *

        Note that in this class parameter indices start at 1, not 0.

        + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface PreparedStatement extends Statement +{ + /** + * This method executes a prepared SQL query and returns its ResultSet. + * + * @return The ResultSet of the SQL statement. + * @exception SQLException If an error occurs. + */ + ResultSet executeQuery() throws SQLException; + + /** + * This method executes an SQL INSERT, UPDATE or DELETE statement. SQL + * statements that return nothing such as SQL DDL statements can be executed. + * + * @return The result is either the row count for INSERT, UPDATE or DELETE + * statements; or 0 for SQL statements that return nothing. + * @exception SQLException If an error occurs. + */ + int executeUpdate() throws SQLException; + + /** + * This method populates the specified parameter with a SQL NULL value + * for the specified type. + * + * @param index The index of the parameter to set. + * @param sqlType The SQL type identifier of the parameter from + * Types + * + * @exception SQLException If an error occurs. + */ + void setNull(int index, int sqlType) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * boolean value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setBoolean(int index, boolean value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * byte value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setByte(int index, byte value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * short value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setShort(int index, short value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * int value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setInt(int index, int value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * long value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setLong(int index, long value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * float value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setFloat(int index, float value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * double value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setDouble(int index, double value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.math.BigDecimal value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setBigDecimal(int index, BigDecimal value) throws + SQLException; + + /** + * This method sets the specified parameter from the given Java + * String value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setString(int index, String value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * byte array value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setBytes(int index, byte[] value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Date value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setDate(int index, Date value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Time value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setTime(int index, Time value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Timestamp value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setTimestamp(int index, Timestamp value) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * ASCII InputStream value. + * + * @param index The index of the parameter value to set. + * @param stream The stream from which the parameter value is read. + * @param count The number of bytes in the stream. + * @exception SQLException If an error occurs. + */ + void setAsciiStream(int index, InputStream stream, int count) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Unicode UTF-8 InputStream value. + * + * @param index The index of the parameter value to set. + * @param stream The stream from which the parameter value is read. + * @param count The number of bytes in the stream. + * @exception SQLException If an error occurs. + * @deprecated + */ + void setUnicodeStream(int index, InputStream stream, int count) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * binary InputStream value. + * + * @param index The index of the parameter value to set. + * @param stream The stream from which the parameter value is read. + * @param count The number of bytes in the stream. + * @exception SQLException If an error occurs. + */ + void setBinaryStream(int index, InputStream stream, int count) + throws SQLException; + + /** + * This method clears all of the input parameter that have been + * set on this statement. + * + * @exception SQLException If an error occurs. + */ + void clearParameters() throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Object value. The specified SQL object type will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param sqlType The SQL type to use for the parameter, from + * Types + * @param scale The scale of the value, for numeric values only. + * @exception SQLException If an error occurs. + * @see Types + */ + void setObject(int index, Object value, int sqlType, int scale) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Object value. The specified SQL object type will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param sqlType The SQL type to use for the parameter, from + * Types + * @exception SQLException If an error occurs. + * @see Types + */ + void setObject(int index, Object value, int sqlType) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Object value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setObject(int index, Object value) throws SQLException; + + /** + * This method executes a prepared SQL query. + * Some prepared statements return multiple results; the execute method + * handles these complex statements as well as the simpler form of + * statements handled by executeQuery and executeUpdate. + * + * @return The result of the SQL statement. + * @exception SQLException If an error occurs. + */ + boolean execute() throws SQLException; + + /** + * This method adds a set of parameters to the batch for JDBC 2.0. + * @exception SQLException If an error occurs. + */ + void addBatch() throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * character Reader value. + * + * @param index The index of the parameter value to set. + * @param reader The reader from which the parameter value is read. + * @param count The number of characters in the stream. + * @exception SQLException If an error occurs. + */ + void setCharacterStream(int index, Reader reader, int count) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Ref value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The Ref used to set the value of the parameter. + * @exception SQLException If an error occurs. + */ + void setRef(int index, Ref value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Blob value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The Blob used to set the + * value of the parameter. + * @exception SQLException If an error occurs. + */ + void setBlob(int index, Blob value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Clob value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The Clob used to set the + * value of the parameter. + * @exception SQLException If an error occurs. + */ + void setClob(int index, Clob value) throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * Array value. The default object type to SQL type mapping + * will be used. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + */ + void setArray(int index, Array value) throws SQLException; + + /** + * This method returns meta data for the result set from this statement. + * + * @return Meta data for the result set from this statement. + * @exception SQLException If an error occurs. + */ + ResultSetMetaData getMetaData() throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Date value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param cal The Calendar to use for timezone and locale. + * @exception SQLException If an error occurs. + */ + void setDate(int index, Date value, Calendar cal) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Time value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param cal The Calendar to use for timezone and locale. + * @exception SQLException If an error occurs. + */ + void setTime(int index, Time value, Calendar cal) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.sql.Timestamp value. + * + * @param index The index of the parameter value to set. + * @param value The value of the parameter. + * @param cal The Calendar to use for timezone and locale. + * @exception SQLException If an error occurs. + */ + void setTimestamp(int index, Timestamp value, Calendar cal) + throws SQLException; + + /** + * This method populates the specified parameter with a SQL NULL value + * for the specified type. + * + * @param index The index of the parameter to set. + * @param sqlType The SQL type identifier of the parameter from + * Types + * @param typeName The name of the data type, for user defined types. + * @exception SQLException If an error occurs. + */ + void setNull(int index, int sqlType, String typeName) + throws SQLException; + + /** + * This method sets the specified parameter from the given Java + * java.net.URL value. + * + * @param index The index of the parameter to set. + * @param value The value of the parameter. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void setURL(int index, URL value) throws SQLException; + + /** + * Returns information about the parameters set on this + * PreparedStatement (see {@link ParameterMetaData} for a + * detailed description of the provided information). + * + * @return Meta data for the parameters of this statement. + * @see ParameterMetaData + * @since 1.4 + */ + ParameterMetaData getParameterMetaData() throws SQLException; +} diff --git a/libjava/classpath/java/sql/Ref.java b/libjava/classpath/java/sql/Ref.java new file mode 100644 index 000000000..f37c8fe4d --- /dev/null +++ b/libjava/classpath/java/sql/Ref.java @@ -0,0 +1,75 @@ +/* Ref.java -- Reference to a SQL structured type. + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.sql; + +import java.util.Map; + +/** + * This interface provides a mechanism for obtaining information about + * a SQL structured type + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + */ +public interface Ref +{ + /** + * This method returns the fully qualified name of the SQL structured + * type of the referenced item. + * + * @return The fully qualified name of the SQL structured type. + * @exception SQLException If an error occurs. + * @since 1.2 + */ + String getBaseTypeName() throws SQLException; + + /** + * @since 1.4 + */ + Object getObject(Map> map) throws SQLException; + + /** + * @since 1.4 + */ + Object getObject() throws SQLException; + + /** + * @since 1.4 + */ + void setObject(Object value) throws SQLException; +} diff --git a/libjava/classpath/java/sql/ResultSet.java b/libjava/classpath/java/sql/ResultSet.java new file mode 100644 index 000000000..3b49a6a0d --- /dev/null +++ b/libjava/classpath/java/sql/ResultSet.java @@ -0,0 +1,1612 @@ +/* ResultSet.java -- A SQL statement result set. + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.util.Calendar; +import java.util.Map; + +/** + * This interface provides access to the data set returned by a SQL + * statement. An instance of this interface is returned by the various + * execution methods in the Statement. + * + *

        This class models a cursor, which can be stepped through one row at a + * time. Methods are provided for accessing columns by column name or by + * index.

        + * + *

        Note that a result set is invalidated if the statement that returned + * it is closed.

        + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface ResultSet +{ + /** + * The rows will be processed in order from first to last. + */ + int FETCH_FORWARD = 1000; + + /** + * The rows will be processed in order from last to first. + */ + int FETCH_REVERSE = 1001; + + /** + * The rows will be processed in an unknown order + */ + int FETCH_UNKNOWN = 1002; + + /** + * This type of result set may only step forward through the rows returned. + */ + int TYPE_FORWARD_ONLY = 1003; + + /** + * This type of result set is scrollable and is not sensitive to changes + * made by other statements. + */ + int TYPE_SCROLL_INSENSITIVE = 1004; + + /** + * This type of result set is scrollable and is also sensitive to changes + * made by other statements. + */ + int TYPE_SCROLL_SENSITIVE = 1005; + + /** + * The concurrency mode of for the result set may not be modified. + */ + int CONCUR_READ_ONLY = 1007; + + /** + * The concurrency mode of for the result set may be modified. + */ + int CONCUR_UPDATABLE = 1008; + + int HOLD_CURSORS_OVER_COMMIT = 1; + + int CLOSE_CURSORS_AT_COMMIT = 2; + + /** + * This method advances to the next row in the result set. Any streams + * open on the current row are closed automatically. + * + * @return true if the next row exists, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean next() throws SQLException; + + /** + * This method closes the result set and frees any associated resources. + * + * @exception SQLException If an error occurs. + */ + void close() throws SQLException; + + /** + * This method tests whether the value of the last column that was fetched + * was actually a SQL NULL value. + * + * @return true if the last column fetched was a NULL, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean wasNull() throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * String. + * + * @param columnIndex The index of the column to return. + * @return The column value as a String. + * @exception SQLException If an error occurs. + */ + String getString(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * boolean. + * + * @param columnIndex The index of the column to return. + * @return The column value as a boolean. + * @exception SQLException If an error occurs. + */ + boolean getBoolean(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * byte. + * + * @param columnIndex The index of the column to return. + * @return The column value as a byte. + * @exception SQLException If an error occurs. + */ + byte getByte(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * short. + * + * @param columnIndex The index of the column to return. + * @return The column value as a short. + * @exception SQLException If an error occurs. + */ + short getShort(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * int. + * + * @param columnIndex The index of the column to return. + * @return The column value as a int. + * @exception SQLException If an error occurs. + */ + int getInt(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * long. + * + * @param columnIndex The index of the column to return. + * @return The column value as a long. + * @exception SQLException If an error occurs. + */ + long getLong(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * float. + * + * @param columnIndex The index of the column to return. + * @return The column value as a float. + * @exception SQLException If an error occurs. + */ + float getFloat(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * double. + * + * @param columnIndex The index of the column to return. + * @return The column value as a double. + * @exception SQLException If an error occurs. + */ + double getDouble(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * BigDecimal. + * + * @param columnIndex The index of the column to return. + * @param scale The number of digits to the right of the decimal to return. + * @return The column value as a BigDecimal. + * @exception SQLException If an error occurs. + * @deprecated + */ + BigDecimal getBigDecimal(int columnIndex, int scale) + throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * byte array. + * + * @param columnIndex The index of the column to return. + * @return The column value as a byte array + * @exception SQLException If an error occurs. + */ + byte[] getBytes(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * java.sql.Date. + * + * @param columnIndex The index of the column to return. + * @return The column value as a java.sql.Date. + * @exception SQLException If an error occurs. + */ + Date getDate(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * java.sql.Time. + * + * @param columnIndex The index of the column to return. + * @return The column value as a java.sql.Time. + * @exception SQLException If an error occurs. + */ + Time getTime(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * java.sql.Timestamp. + * + * @param columnIndex The index of the column to return. + * @return The column value as a java.sql.Timestamp. + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as an ASCII + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling next() or close() on this result set + * will close this stream as well. + * + * @param columnIndex The index of the column to return. + * @return The column value as an ASCII InputStream. + * @exception SQLException If an error occurs. + */ + InputStream getAsciiStream(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Unicode UTF-8 + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling next() or close() on this result set + * will close this stream as well. + * + * @param columnIndex The index of the column to return. + * @return The column value as a Unicode UTF-8 InputStream. + * @exception SQLException If an error occurs. + * @deprecated Use getCharacterStream instead. + */ + InputStream getUnicodeStream(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a raw byte + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling next() or close() on this result set + * will close this stream as well. + * + * @param columnIndex The index of the column to return. + * @return The column value as a raw byte InputStream. + * @exception SQLException If an error occurs. + */ + InputStream getBinaryStream(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * String. + * + * @param columnName The name of the column to return. + * @return The column value as a String. + * @exception SQLException If an error occurs. + */ + String getString(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * boolean. + * + * @param columnName The name of the column to return. + * @return The column value as a boolean. + * @exception SQLException If an error occurs. + */ + boolean getBoolean(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * byte. + * + * @param columnName The name of the column to return. + * @return The column value as a byte. + * @exception SQLException If an error occurs. + */ + byte getByte(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * short. + * + * @param columnName The name of the column to return. + * @return The column value as a short. + * @exception SQLException If an error occurs. + */ + short getShort(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * int. + * + * @param columnName The name of the column to return. + * @return The column value as a int. + * @exception SQLException If an error occurs. + */ + int getInt(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * long. + * + * @param columnName The name of the column to return. + * @return The column value as a long. + * @exception SQLException If an error occurs. + */ + long getLong(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * float. + * + * @param columnName The name of the column to return. + * @return The column value as a float. + * @exception SQLException If an error occurs. + */ + float getFloat(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * double. + * + * @param columnName The name of the column to return. + * @return The column value as a double. + * @exception SQLException If an error occurs. + */ + double getDouble(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * BigDecimal. + * + * @param columnName The name of the column to return. + * @return The column value as a BigDecimal. + * @exception SQLException If an error occurs. + * @deprecated + */ + BigDecimal getBigDecimal(String columnName, int scale) + throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * byte array. + * + * @param columnName The name of the column to return. + * @return The column value as a byte array + * @exception SQLException If an error occurs. + */ + byte[] getBytes(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * java.sql.Date. + * + * @param columnName The name of the column to return. + * @return The column value as a java.sql.Date. + * @exception SQLException If an error occurs. + */ + Date getDate(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * java.sql.Time. + * + * @param columnName The name of the column to return. + * @return The column value as a java.sql.Time. + * @exception SQLException If an error occurs. + */ + Time getTime(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * java.sql.Timestamp. + * + * @param columnName The name of the column to return. + * @return The column value as a java.sql.Timestamp. + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as an ASCII + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling next() or close() on this result set + * will close this stream as well. + * + * @param columnName The name of the column to return. + * @return The column value as an ASCII InputStream. + * @exception SQLException If an error occurs. + */ + InputStream getAsciiStream(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Unicode UTF-8 + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling next() or close() on this result set + * will close this stream as well. + * + * @param columnName The name of the column to return. + * @return The column value as a Unicode UTF-8 InputStream. + * @exception SQLException If an error occurs. + * @deprecated Use getCharacterStream instead. + */ + InputStream getUnicodeStream(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a raw byte + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling next() or close() on this result set + * will close this stream as well. + * + * @param columnName The name of the column to return. + * @return The column value as a raw byte InputStream. + * @exception SQLException If an error occurs. + */ + InputStream getBinaryStream(String columnName) throws SQLException; + + /** + * This method returns the first SQL warning associated with this result + * set. Any additional warnings will be chained to this one. + * + * @return The first SQLWarning for this result set, or null if + * there are no warnings. + * @exception SQLException If an error occurs. + */ + SQLWarning getWarnings() throws SQLException; + + /** + * This method clears all warnings associated with this result set. + * + * @exception SQLException If an error occurs. + */ + void clearWarnings() throws SQLException; + + /** + * This method returns the name of the database cursor used by this + * result set. + * + * @return The name of the database cursor used by this result set. + * @exception SQLException If an error occurs. + */ + String getCursorName() throws SQLException; + + /** + * This method returns data about the columns returned as part of the + * result set as a ResultSetMetaData instance. + * + * @return The ResultSetMetaData instance for this result set. + * @exception SQLException If an error occurs. + */ + ResultSetMetaData getMetaData() throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * Object. + * + * @param columnIndex The index of the column to return. + * @return The column value as an Object. + * @exception SQLException If an error occurs. + */ + Object getObject(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * Object. + * + * @param columnName The name of the column to return. + * @return The column value as an Object. + * @exception SQLException If an error occurs. + */ + Object getObject(String columnName) throws SQLException; + + /** + * This method returns the column index of the specified named column. + * + * @param columnName The name of the column. + * @return The index of the column. + * @exception SQLException If an error occurs. + */ + int findColumn(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a character + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling next() or close() on this result set + * will close this stream as well. + * + * @param columnIndex The index of the column to return. + * @return The column value as an character Reader. + * @exception SQLException If an error occurs. + */ + Reader getCharacterStream(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a character + * stream. Note that all the data from this stream must be read before + * fetching the value of any other column. Please also be aware that + * calling next() or close() on this result set + * will close this stream as well. + * + * @param columnName The name of the column to return. + * @return The column value as an character Reader. + * @exception SQLException If an error occurs. + */ + Reader getCharacterStream(String columnName) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * BigDecimal. + * + * @param columnIndex The index of the column to return. + * @return The column value as a BigDecimal. + * @exception SQLException If an error occurs. + */ + BigDecimal getBigDecimal(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * BigDecimal. + * + * @param columnName The name of the column to return. + * @return The column value as a BigDecimal. + * @exception SQLException If an error occurs. + */ + BigDecimal getBigDecimal(String columnName) throws SQLException; + + /** + * This method tests whether or not the cursor is before the first row + * in the result set. + * + * @return true if the cursor is positioned before the first + * row, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isBeforeFirst() throws SQLException; + + /** + * This method tests whether or not the cursor is after the last row + * in the result set. + * + * @return true if the cursor is positioned after the last + * row, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isAfterLast() throws SQLException; + + /** + * This method tests whether or not the cursor is positioned on the first + * row in the result set. + * + * @return true if the cursor is positioned on the first + * row, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isFirst() throws SQLException; + + /** + * This method tests whether or not the cursor is on the last row + * in the result set. + * + * @return true if the cursor is positioned on the last + * row, false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isLast() throws SQLException; + + /** + * This method repositions the cursor to before the first row in the + * result set. + * + * @exception SQLException If an error occurs. + */ + void beforeFirst() throws SQLException; + + /** + * This method repositions the cursor to after the last row in the result + * set. + * + * @exception SQLException If an error occurs. + */ + void afterLast() throws SQLException; + + /** + * This method repositions the cursor on the first row in the + * result set. + * + * @return true if the cursor is on a valid row; + * false if there are no rows in the result set. + * @exception SQLException If an error occurs. + */ + boolean first() throws SQLException; + + /** + * This method repositions the cursor on the last row in the result + * set. + * + * @return true if the cursor is on a valid row; + * false if there are no rows in the result set. + * @exception SQLException If an error occurs. + */ + boolean last() throws SQLException; + + /** + * This method returns the current row number in the cursor. Numbering + * begins at index 1. + * + * @return The current row number, or 0 if there is not current row. + * @exception SQLException If an error occurs. + */ + int getRow() throws SQLException; + + /** + * This method positions the result set to the specified absolute row. + * Positive numbers are row offsets from the beginning of the result + * set (numbering starts from row 1) and negative numbers are row offsets + * from the end of the result set (numbering starts from -1). + * + * @param row The row to position the result set to. + * + * @return true if the current position was changed, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean absolute(int row) throws SQLException; + + /** + * This method moves the result set position relative to the current row. + * The offset can be positive or negative. + * + * @param rows The number of row positions to move. + * @return true if the current position was changed, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean relative(int rows) throws SQLException; + + /** + * This method moves the current position to the previous row in the + * result set. + * + * @return true if the previous row exists, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean previous() throws SQLException; + + /** + * This method provides a hint to the driver about which direction the + * result set will be processed in. + * + * @param direction The direction in which rows will be processed. The + * allowed values are the FETCH_* constants + * defined in this interface. + * @exception SQLException If an error occurs. + */ + void setFetchDirection(int direction) throws SQLException; + + /** + * This method returns the current fetch direction for this result set. + * + * @return The fetch direction for this result set. + * @exception SQLException If an error occurs. + */ + int getFetchDirection() throws SQLException; + + /** + * This method provides a hint to the driver about how many rows at a + * time it should fetch from the database. + * + * @param rows The number of rows the driver should fetch per call. + * @exception SQLException If an error occurs. + */ + void setFetchSize(int rows) throws SQLException; + + /** + * This method returns the current number of rows that will be fetched + * from the database at a time. + * + * @return The current fetch size for this result set. + * @exception SQLException If an error occurs. + */ + int getFetchSize() throws SQLException; + + /** + * This method returns the result set type of this result set. This will + * be one of the TYPE_* constants defined in this interface. + * + * @return The result set type. + * @exception SQLException If an error occurs. + */ + int getType() throws SQLException; + + /** + * This method returns the concurrency type of this result set. This will + * be one of the CONCUR_* constants defined in this interface. + * + * @return The result set concurrency type. + * @exception SQLException If an error occurs. + */ + int getConcurrency() throws SQLException; + + /** + * This method tests whether or not the current row in the result set + * has been updated. Updates must be visible in order of this method to + * detect the update. + * + * @return true if the row has been updated, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean rowUpdated() throws SQLException; + + /** + * This method tests whether or not the current row in the result set + * has been inserted. Inserts must be visible in order of this method to + * detect the insert. + * + * @return true if the row has been inserted, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean rowInserted() throws SQLException; + + /** + * This method tests whether or not the current row in the result set + * has been deleted. Deletes must be visible in order of this method to + * detect the deletion. + * + * @return true if the row has been deleted, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean rowDeleted() throws SQLException; + + /** + * This method updates the specified column to have a NULL value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @exception SQLException If an error occurs. + */ + void updateNull(int columnIndex) throws SQLException; + + /** + * This method updates the specified column to have a boolean value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBoolean(int columnIndex, boolean value) throws SQLException; + + /** + * This method updates the specified column to have a byte value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateByte(int columnIndex, byte value) throws SQLException; + + /** + * This method updates the specified column to have a short value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateShort(int columnIndex, short value) throws SQLException; + + /** + * This method updates the specified column to have an int value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateInt(int columnIndex, int value) throws SQLException; + + /** + * This method updates the specified column to have a long value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateLong(int columnIndex, long value) throws SQLException; + + /** + * This method updates the specified column to have a float value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateFloat(int columnIndex, float value) throws SQLException; + + /** + * This method updates the specified column to have a double value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateDouble(int columnIndex, double value) throws SQLException; + + /** + * This method updates the specified column to have a BigDecimal value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBigDecimal(int columnIndex, BigDecimal value) + throws SQLException; + + /** + * This method updates the specified column to have a String value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateString(int columnIndex, String value) throws SQLException; + + /** + * This method updates the specified column to have a byte array value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBytes(int columnIndex, byte[] value) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Date value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateDate(int columnIndex, Date value) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Time value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateTime(int columnIndex, Time value) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Timestamp value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateTimestamp(int columnIndex, Timestamp value) + throws SQLException; + + /** + * This method updates the specified column from an ASCII text stream. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param stream The stream from which the column value is updated. + * @param count The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateAsciiStream(int columnIndex, InputStream stream, int count) + throws SQLException; + + /** + * This method updates the specified column from a binary stream. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param stream The stream from which the column value is updated. + * @param count The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateBinaryStream(int columnIndex, InputStream stream, int count) + throws SQLException; + + /** + * This method updates the specified column from a character stream. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param reader The reader from which the column value is updated. + * @param count The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateCharacterStream(int columnIndex, Reader reader, int count) + throws SQLException; + + /** + * This method updates the specified column to have an Object value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @param scale The scale of the object in question, which is used only + * for numeric type objects. + * @exception SQLException If an error occurs. + */ + void updateObject(int columnIndex, Object value, int scale) + throws SQLException; + + /** + * This method updates the specified column to have an Object value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnIndex The index of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateObject(int columnIndex, Object value) throws SQLException; + + /** + * This method updates the specified column to have a NULL value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @exception SQLException If an error occurs. + */ + void updateNull(String columnName) throws SQLException; + + /** + * This method updates the specified column to have a boolean value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBoolean(String columnName, boolean value) throws SQLException; + + /** + * This method updates the specified column to have a byte value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateByte(String columnName, byte value) throws SQLException; + + /** + * This method updates the specified column to have a short value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateShort(String columnName, short value) throws SQLException; + + /** + * This method updates the specified column to have an int value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateInt(String columnName, int value) throws SQLException; + + /** + * This method updates the specified column to have a long value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateLong(String columnName, long value) throws SQLException; + + /** + * This method updates the specified column to have a float value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateFloat(String columnName, float value) throws SQLException; + + /** + * This method updates the specified column to have a double value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateDouble(String columnName, double value) throws SQLException; + + /** + * This method updates the specified column to have a BigDecimal value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBigDecimal(String columnName, BigDecimal value) + throws SQLException; + + /** + * This method updates the specified column to have a String value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateString(String columnName, String value) throws SQLException; + + /** + * This method updates the specified column to have a byte array value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateBytes(String columnName, byte[] value) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Date value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateDate(String columnName, Date value) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Time value. This + * does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateTime(String columnName, Time value) throws SQLException; + + /** + * This method updates the specified column to have a java.sql.Timestamp value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateTimestamp(String columnName, Timestamp value) + throws SQLException; + + /** + * This method updates the specified column from an ASCII text stream. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param stream The stream from which the column value is updated. + * @param count The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateAsciiStream(String columnName, InputStream stream, int count) + throws SQLException; + + /** + * This method updates the specified column from a binary stream. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param stream The stream from which the column value is updated. + * @param count The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateBinaryStream(String columnName, InputStream stream, int count) + throws SQLException; + + /** + * This method updates the specified column from a character stream. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param reader The reader from which the column value is updated. + * @param count The length of the stream. + * @exception SQLException If an error occurs. + */ + void updateCharacterStream(String columnName, Reader reader, int count) + throws SQLException; + + /** + * This method updates the specified column to have an Object value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @param scale The scale of the object in question, which is used only + * for numeric type objects. + * @exception SQLException If an error occurs. + */ + void updateObject(String columnName, Object value, int scale) + throws SQLException; + + /** + * This method updates the specified column to have an Object value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @param columnName The name of the column to update. + * @param value The new value of the column. + * @exception SQLException If an error occurs. + */ + void updateObject(String columnName, Object value) throws SQLException; + + /** + * This method inserts the current row into the database. The result set + * must be positioned on the insert row in order to call this method + * successfully. + * + * @exception SQLException If an error occurs. + */ + void insertRow() throws SQLException; + + /** + * This method updates the current row in the database. + * + * @exception SQLException If an error occurs. + */ + void updateRow() throws SQLException; + + /** + * This method deletes the current row in the database. + * + * @exception SQLException If an error occurs. + */ + void deleteRow() throws SQLException; + + /** + * This method refreshes the contents of the current row from the database. + * + * @exception SQLException If an error occurs. + */ + void refreshRow() throws SQLException; + + /** + * This method cancels any changes that have been made to a row. If + * the rowUpdate method has been called, then the changes + * cannot be undone. + * + * @exception SQLException If an error occurs. + */ + void cancelRowUpdates() throws SQLException; + + /** + * This method positions the result set to the "insert row", which allows + * a new row to be inserted into the database from the result set. + * + * @exception SQLException If an error occurs. + */ + void moveToInsertRow() throws SQLException; + + /** + * This method moves the result set position from the insert row back to + * the current row that was selected prior to moving to the insert row. + * + * @exception SQLException If an error occurs. + */ + void moveToCurrentRow() throws SQLException; + + /** + * This method returns a the Statement that was used to + * produce this result set. + * + * @return The Statement used to produce this result set. + * + * @exception SQLException If an error occurs. + */ + Statement getStatement() throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * Object using the specified SQL type to Java type map. + * + * @param columnIndex The index of the column to return. + * @param map The SQL type to Java type map to use. + * @return The value of the column as an Object. + * @exception SQLException If an error occurs. + */ + Object getObject(int columnIndex, Map> map) + throws SQLException; + + /** + * This method returns a Ref for the specified column which + * represents the structured type for the column. + * + * @param columnIndex The index of the column to return. + * @return A Ref object for the column + * @exception SQLException If an error occurs. + */ + Ref getRef(int columnIndex) throws SQLException; + + /** + * This method returns the specified column value as a BLOB. + * + * @param columnIndex The index of the column value to return. + * @return The value of the column as a BLOB. + * @exception SQLException If an error occurs. + */ + Blob getBlob(int columnIndex) throws SQLException; + + /** + * This method returns the specified column value as a CLOB. + * + * @param columnIndex The index of the column value to return. + * @return The value of the column as a CLOB. + * @exception SQLException If an error occurs. + */ + Clob getClob(int columnIndex) throws SQLException; + + /** + * This method returns the specified column value as an Array. + * + * @param columnIndex The index of the column value to return. + * @return The value of the column as an Array. + * @exception SQLException If an error occurs. + */ + Array getArray(int columnIndex) throws SQLException; + + /** + * This method returns the value of the specified column as a Java + * Object using the specified SQL type to Java type map. + * + * @param columnName The name of the column to return. + * @param map The SQL type to Java type map to use. + * @return The value of the column as an Object. + * @exception SQLException If an error occurs. + */ + Object getObject(String columnName, Map> map) + throws SQLException; + + /** + * This method returns a Ref for the specified column which + * represents the structured type for the column. + * + * @param columnName The name of the column to return. + * @return A Ref object for the column + * @exception SQLException If an error occurs. + */ + Ref getRef(String columnName) throws SQLException; + + /** + * This method returns the specified column value as a BLOB. + * + * @param columnName The name of the column value to return. + * @return The value of the column as a BLOB. + * @exception SQLException If an error occurs. + */ + Blob getBlob(String columnName) throws SQLException; + + /** + * This method returns the specified column value as a CLOB. + * + * @param columnName The name of the column value to return. + * @return The value of the column as a CLOB. + * @exception SQLException If an error occurs. + */ + Clob getClob(String columnName) throws SQLException; + + /** + * This method returns the specified column value as an Array. + * + * @param columnName The name of the column value to return. + * @return The value of the column as an Array. + * @exception SQLException If an error occurs. + */ + Array getArray(String columnName) throws SQLException; + + /** + * This method returns the specified column value as a + * java.sql.Date. The specified Calendar is used + * to generate a value for the date if the database does not support + * timezones. + * + * @param columnIndex The index of the column value to return. + * @param cal The Calendar to use for calculating timezones. + * @return The value of the column as a java.sql.Date. + * @exception SQLException If an error occurs. + */ + Date getDate(int columnIndex, Calendar cal) throws SQLException; + + /** + * This method returns the specified column value as a + * java.sql.Date. The specified Calendar is used + * to generate a value for the date if the database does not support + * timezones. + * + * @param columnName The name of the column value to return. + * @param cal The Calendar to use for calculating timezones. + * @return The value of the column as a java.sql.Date. + * @exception SQLException If an error occurs. + */ + Date getDate(String columnName, Calendar cal) throws SQLException; + + /** + * This method returns the specified column value as a + * java.sql.Time. The specified Calendar is used + * to generate a value for the time if the database does not support + * timezones. + * + * @param columnIndex The index of the column value to return. + * @param cal The Calendar to use for calculating timezones. + * @return The value of the column as a java.sql.Time. + * @exception SQLException If an error occurs. + */ + Time getTime(int columnIndex, Calendar cal) throws SQLException; + + /** + * This method returns the specified column value as a + * java.sql.Time. The specified Calendar is used + * to generate a value for the time if the database does not support + * timezones. + * + * @param columnName The name of the column value to return. + * @param cal The Calendar to use for calculating timezones. + * @return The value of the column as a java.sql.Time. + * @exception SQLException If an error occurs. + */ + Time getTime(String columnName, Calendar cal) throws SQLException; + + /** + * This method returns the specified column value as a + * java.sql.Timestamp. The specified Calendar is used + * to generate a value for the timestamp if the database does not support + * timezones. + * + * @param columnIndex The index of the column value to return. + * @param cal The Calendar to use for calculating timezones. + * @return The value of the column as a java.sql.Timestamp. + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(int columnIndex, Calendar cal) + throws SQLException; + + /** + * This method returns the specified column value as a + * java.sql.Timestamp. The specified Calendar is used + * to generate a value for the timestamp if the database does not support + * timezones. + * + * @param columnName The name of the column value to return. + * @param cal The Calendar to use for calculating timezones. + * + * @return The value of the column as a java.sql.Timestamp. + * + * @exception SQLException If an error occurs. + */ + Timestamp getTimestamp(String columnName, Calendar cal) + throws SQLException; + + /** + * This method returns the specified column value as a + * java.net.URL. + * + * @param columnIndex The index of the column value to return. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + URL getURL(int columnIndex) throws SQLException; + + /** + * This method returns the specified column value as a + * java.net.URL. + * + * @param columnName The name of the column value to return. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + URL getURL(String columnName) throws SQLException; + + /** + * This method updates the specified column to have a + * java.sql.Ref value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @parm columnIndex The index of the column value to update. + * @parm value The java.sql.Ref used to set the new value + * of the column. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void updateRef(int columnIndex, Ref value) throws SQLException; + + /** + * This method updates the specified column to have a + * java.sql.Ref value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @parm columnName The name of the column value to update. + * @parm value The java.sql.Ref used to set the new value + * of the column. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void updateRef(String columnName, Ref value) throws SQLException; + + /** + * This method updates the specified column to have a + * java.sql.Blob value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @parm columnIndex The index of the column value to update. + * @parm value The java.sql.Blob used to set the new value + * of the column. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void updateBlob(int columnIndex, Blob value) throws SQLException; + + /** + * This method updates the specified column to have a + * java.sql.Blob value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @parm columnName The name of the column value to update. + * @parm value The java.sql.Blob used to set the new value + * of the column. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void updateBlob(String columnName, Blob value) throws SQLException; + + /** + * This method updates the specified column to have a + * java.sql.Clob value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @parm columnIndex The index of the column value to update. + * @parm value The java.sql.Clob used to set the new value + * of the column. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void updateClob(int columnIndex, Clob value) throws SQLException; + + /** + * This method updates the specified column to have a + * java.sql.Clob value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @parm columnName The name of the column value to update. + * @parm value The java.sql.Clob used to set the new value + * of the column. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void updateClob(String columnName, Clob value) throws SQLException; + + /** + * This method updates the specified column to have a + * java.sqlArray value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @parm columnIndex The index of the column value to update. + * @parm value The new value of the column. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void updateArray(int columnIndex, Array value) throws SQLException; + + /** + * This method updates the specified column to have a + * java.sqlArray value. + * This does not update the actual database. updateRow must be + * called in order to do that. + * + * @parm columnName The name of the column value to update. + * @parm value The new value of the column. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void updateArray(String columnName, Array value) throws SQLException; +} diff --git a/libjava/classpath/java/sql/ResultSetMetaData.java b/libjava/classpath/java/sql/ResultSetMetaData.java new file mode 100644 index 000000000..98485e3d3 --- /dev/null +++ b/libjava/classpath/java/sql/ResultSetMetaData.java @@ -0,0 +1,281 @@ +/* ResultSetMetaData.java -- Returns information about the ResultSet + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This interface provides a mechanism for obtaining information about + * the columns that are present in a ResultSet. + * + *

        Note that in this class column indices start at 1, not 0.

        + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface ResultSetMetaData +{ + /** + * The column does not allow NULL's. + */ + int columnNoNulls = 0; + + /** + * The column allows NULL's. + */ + int columnNullable = 1; + + /** + * It is unknown whether or not the column allows NULL's. + */ + int columnNullableUnknown = 2; + + /** + * This method returns the number of columns in the result set. + * + * @return The number of columns in the result set. + * @exception SQLException If an error occurs. + */ + int getColumnCount() throws SQLException; + + /** + * This method test whether or not the column is an auto-increment column. + * Auto-increment columns are read-only. + * + * @param columnIndex The index of the column to test. + * @return true if the column is auto-increment, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isAutoIncrement(int columnIndex) throws SQLException; + + /** + * This method tests whether or not a column is case sensitive in its values. + * + * @param columnIndex The index of the column to test. + * @return true if the column value is case sensitive, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isCaseSensitive(int columnIndex) throws SQLException; + + /** + * This method tests whether not the specified column can be used in + * a WHERE clause. + * + * @param columnIndex The index of the column to test. + * @return true if the column may be used in a WHERE clause, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isSearchable(int columnIndex) throws SQLException; + + /** + * This method tests whether or not the column stores a monetary value. + * + * @param columnIndex The index of the column to test. + * @return true if the column contains a monetary value, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isCurrency(int columnIndex) throws SQLException; + + /** + * This method returns a value indicating whether or not the specified + * column may contain a NULL value. + * + * @param columnIndex The index of the column to test. + * @return A constant indicating whether or not the column can contain NULL, + * which will be one of columnNoNulls, + * columnNullable, or columnNullableUnknown. + * @exception SQLException If an error occurs. + */ + int isNullable(int columnIndex) throws SQLException; + + /** + * This method tests whether or not the value of the specified column + * is signed or unsigned. + * + * @param columnIndex The index of the column to test. + * @return true if the column value is signed, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isSigned(int columnIndex) throws SQLException; + + /** + * This method returns the maximum number of characters that can be used + * to display a value in this column. + * + * @param columnIndex The index of the column to check. + * @return The maximum number of characters that can be used to display a + * value for this column. + * @exception SQLException If an error occurs. + */ + int getColumnDisplaySize(int columnIndex) throws SQLException; + + /** + * This method returns a string that should be used as a caption for this + * column for user display purposes. + * + * @param columnIndex The index of the column to check. + * @return A display string for the column. + * @exception SQLException If an error occurs. + */ + String getColumnLabel(int columnIndex) throws SQLException; + + /** + * This method returns the name of the specified column. + * + * @param columnIndex The index of the column to return the name of. + * @return The name of the column. + * @exception SQLException If an error occurs. + */ + String getColumnName(int columnIndex) throws SQLException; + + /** + * This method returns the name of the schema that contains the specified + * column. + * + * @param columnIndex The index of the column to check the schema name for. + * @return The name of the schema that contains the column. + * @exception SQLException If an error occurs. + */ + String getSchemaName(int columnIndex) throws SQLException; + + /** + * This method returns the precision of the specified column, which is the + * number of decimal digits it contains. + * + * @param columnIndex The index of the column to check the precision on. + * @return The precision of the specified column. + * @exception SQLException If an error occurs. + */ + int getPrecision(int columnIndex) throws SQLException; + + /** + * This method returns the scale of the specified column, which is the + * number of digits to the right of the decimal point. + * + * @param columnIndex The index column to check the scale of. + * @return The scale of the column. + * @exception SQLException If an error occurs. + */ + int getScale(int columnIndex) throws SQLException; + + /** + * This method returns the name of the table containing the specified + * column. + * + * @param columnIndex The index of the column to check the table name for. + * @return The name of the table containing the column. + * @exception SQLException If an error occurs. + */ + String getTableName(int columnIndex) throws SQLException; + + /** + * This method returns the name of the catalog containing the specified + * column. + * + * @param columnIndex The index of the column to check the catalog name for. + * @return The name of the catalog containing the column. + * @exception SQLException If an error occurs. + */ + String getCatalogName(int columnIndex) throws SQLException; + + /** + * This method returns the SQL type of the specified column. This will + * be one of the constants from Types. + * + * @param columnIndex The index of the column to check the SQL type of. + * @return The SQL type for this column. + * @exception SQLException If an error occurs. + * @see Types + */ + int getColumnType(int columnIndex) throws SQLException; + + /** + * This method returns the name of the SQL type for this column. + * + * @param columnIndex The index of the column to check the SQL type name for. + * @return The name of the SQL type for this column. + * @exception SQLException If an error occurs. + */ + String getColumnTypeName(int columnIndex) throws SQLException; + + /** + * This method tests whether or not the specified column is read only. + * + * @param columnIndex The index of the column to check. + * @return true if the column is read only, false + * otherwise. + * @exception SQLException If an error occurs. + */ + boolean isReadOnly(int columnIndex) throws SQLException; + + /** + * This method tests whether or not the column may be writable. This + * does not guarantee that a write will be successful. + * + * @param columnIndex The index of the column to check for writability. + * @return true if the column may be writable, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isWritable(int columnIndex) throws SQLException; + + /** + * This method tests whether or not the column is writable. This + * does guarantee that a write will be successful. + * + * @param columnIndex The index of the column to check for writability. + * @return true if the column is writable, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean isDefinitelyWritable(int columnIndex) throws SQLException; + + /** + * This method returns the name of the Java class which will be used to + * create objects representing the data in this column. + * + * @param columnIndex The index of the column to check. + * @return The name of the Java class that will be used for values in + * this column. + * @exception SQLException If an error occurs. + */ + String getColumnClassName(int columnIndex) throws SQLException; +} diff --git a/libjava/classpath/java/sql/SQLData.java b/libjava/classpath/java/sql/SQLData.java new file mode 100644 index 000000000..97a42a443 --- /dev/null +++ b/libjava/classpath/java/sql/SQLData.java @@ -0,0 +1,72 @@ +/* SQLData.java -- Custom mapping for a user defined datatype + Copyright (C) 1999, 2000, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This interface is used for mapping SQL data to user defined datatypes. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface SQLData +{ + /** + * This method returns the user defined datatype name for this object. + * + * @return The user defined data type name for this object. + * @exception SQLException If an error occurs. + */ + String getSQLTypeName() throws SQLException; + + /** + * This method populates the data in the object from the specified stream. + * + * @param stream The stream to read the data from. + * @param typeName The data type name of the data on the stream. + * @exception SQLException If an error occurs. + */ + void readSQL(SQLInput stream, String typeName) throws SQLException; + + /** + * This method writes the data in this object to the specified stream. + * + * @param stream The stream to write the data to. + * @exception SQLException If an error occurs. + */ + void writeSQL(SQLOutput stream) throws SQLException; +} diff --git a/libjava/classpath/java/sql/SQLException.java b/libjava/classpath/java/sql/SQLException.java new file mode 100644 index 000000000..d8d845c6b --- /dev/null +++ b/libjava/classpath/java/sql/SQLException.java @@ -0,0 +1,167 @@ +/* SQLException.java -- General SQL exception + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This exception is thrown when a database error occurs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class SQLException extends Exception +{ + static final long serialVersionUID = 2135244094396331484L; + + /** + * This is the next exception in the chain + */ + private SQLException next; + + /** + * This is the state of the SQL statement at the time of the error. + */ + private String SQLState; + + /** + * The vendor error code for this error + */ + private int vendorCode; + + /** + * This method initializes a nwe instance of SQLException + * with the specified descriptive error message, SQL state string, and + * vendor code. + * + * @param message A string describing the nature of the error. + * @param SQLState A string containing the SQL state of the error. + * @param vendorCode The vendor error code associated with this error. + */ + public SQLException(String message, String SQLState, int vendorCode) + { + super(message); + this.SQLState = SQLState; + this.vendorCode = vendorCode; + } + + /** + * This method initializes a new instance of SQLException + * with the specified descriptive error message and SQL state string. + * The vendor error code of this instance will be 0. + * + * @param message A string describing the nature of the error. + * @param SQLState A string containing the SQL state of the error. + */ + public SQLException(String message, String SQLState) + { + this(message, SQLState, 0); + } + + /** + * This method initializes a new instance of SQLException + * with the specified descriptive error message. The SQL state of this + * instance will be null and the vendor error code will be 0. + * + * @param message A string describing the nature of the error. + */ + public SQLException(String message) + { + this(message, null, 0); + } + + /** + * This method initializes a new instance of SQLException + * that does not have a descriptive messages and SQL state, and which + * has a vendor error code of 0. + */ + public SQLException() + { + this(null, null, 0); + } + + /** + * This method returns the SQLState information associated with this + * error. The value returned is a String which is formatted + * using the XOPEN SQL state conventions. + * + * @return The SQL state, which may be null. + */ + public String getSQLState() + { + return SQLState; + } + + /** + * This method returns the vendor specific error code associated with + * this error. + * + * @return The vendor specific error code associated with this error. + */ + public int getErrorCode() + { + return vendorCode; + } + + /** + * This method returns the exception that is chained to this object. + * + * @return The exception chained to this object, which may be + * null. + */ + public SQLException getNextException() + { + return next; + } + + /** + * This method adds a new exception to the end of the chain of exceptions + * that are chained to this object. + * + * @param e The exception to add to the end of the chain. + */ + public void setNextException(SQLException e) + { + if (e == null) + return; + + SQLException list_entry = this; + while (list_entry.getNextException() != null) + list_entry = list_entry.getNextException(); + + list_entry.next = e; + } +} diff --git a/libjava/classpath/java/sql/SQLInput.java b/libjava/classpath/java/sql/SQLInput.java new file mode 100644 index 000000000..e277a5020 --- /dev/null +++ b/libjava/classpath/java/sql/SQLInput.java @@ -0,0 +1,258 @@ +/* SQLInput.java -- Read SQL values from a stream + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; + +/** + * This interface provides methods for reading values from a stream + * that is connected to a SQL structured or distinct type. It is used + * for custom mapping of user defined data types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface SQLInput +{ + /** + * This method reads the next item from the stream a Java + * String. + * + * @return The value read from the stream as a String. + * @exception SQLException If an error occurs. + */ + String readString() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * boolean. + * + * @return The value read from the stream as a boolean. + * @exception SQLException If an error occurs. + */ + boolean readBoolean() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * byte. + * + * @return The value read from the stream as a byte. + * @exception SQLException If an error occurs. + */ + byte readByte() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * short. + * + * @return The value read from the stream as a short. + * @exception SQLException If an error occurs. + */ + short readShort() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * int. + * + * @return The value read from the stream as an int. + * @exception SQLException If an error occurs. + */ + int readInt() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * long. + * + * @return The value read from the stream as a long. + * @exception SQLException If an error occurs. + */ + long readLong() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * float. + * + * @return The value read from the stream as a float. + * @exception SQLException If an error occurs. + */ + float readFloat() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * double. + * + * @return The value read from the stream as a double. + * @exception SQLException If an error occurs. + */ + double readDouble() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * BigDecimal. + * + * @return The value read from the stream as a BigDecimal. + * @exception SQLException If an error occurs. + */ + BigDecimal readBigDecimal() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * byte array + * + * @return The value read from the stream as a byte array. + * @exception SQLException If an error occurs. + */ + byte[] readBytes() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * java.sql.Date. + * + * @return The value read from the stream as a java.sql.Date. + * @exception SQLException If an error occurs. + */ + Date readDate() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * java.sql.Time. + * + * @return The value read from the stream as a java.sql.Time. + * @exception SQLException If an error occurs. + */ + Time readTime() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * java.sql.Timestamp. + * + * @return The value read from the stream as a java.sql.Timestamp. + * @exception SQLException If an error occurs. + */ + Timestamp readTimestamp() throws SQLException; + + /** + * This method reads the next item from the stream a character + * Reader. + * + * @return The value read from the stream as a Reader. + * @exception SQLException If an error occurs. + */ + Reader readCharacterStream() throws SQLException; + + /** + * This method reads the next item from the stream a ASCII text + * InputStream. + * + * @return The value read from the stream as an InputStream. + * @exception SQLException If an error occurs. + */ + InputStream readAsciiStream() throws SQLException; + + /** + * This method reads the next item from the stream a binary + * InputStream. + * + * @return The value read from the stream as an InputStream. + * @exception SQLException If an error occurs. + */ + InputStream readBinaryStream() throws SQLException; + + /** + * This method reads the next item from the stream a Java + * Object. + * + * @return The value read from the stream as an Object. + * @exception SQLException If an error occurs. + */ + Object readObject() throws SQLException; + + /** + * This method reads the next item from the stream a Java SQL + * Ref. + * + * @return The value read from the stream as an Ref. + * @exception SQLException If an error occurs. + */ + Ref readRef() throws SQLException; + + /** + * This method reads the next item from the stream a Java SQL + * Blob. + * + * @return The value read from the stream as a Blob. + * @exception SQLException If an error occurs. + */ + Blob readBlob() throws SQLException; + + /** + * This method reads the next item from the stream a Java SQL + * Clob. + * + * @return The value read from the stream as a Clob. + * @exception SQLException If an error occurs. + */ + Clob readClob() throws SQLException; + + /** + * This method reads the next item from the stream a Java SQL + * Array. + * + * @return The value read from the stream as an Array. + * @exception SQLException If an error occurs. + */ + Array readArray() throws SQLException; + + /** + * This method tests whether or not the last value read was a SQL + * NULL value. + * + * @return true if the last value read was a NULL, + * false otherwise. + * @exception SQLException If an error occurs. + */ + boolean wasNull() throws SQLException; + + /** + * @since 1.4 + */ + URL readURL() throws SQLException; +} diff --git a/libjava/classpath/java/sql/SQLOutput.java b/libjava/classpath/java/sql/SQLOutput.java new file mode 100644 index 000000000..cd075f1d2 --- /dev/null +++ b/libjava/classpath/java/sql/SQLOutput.java @@ -0,0 +1,267 @@ +/* SQLOutput.java -- Write SQL values to a stream + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; + +/** + * This interface provides methods for writing Java types to a SQL stream. + * It is used for implemented custom type mappings for user defined data + * types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface SQLOutput +{ + /** + * This method writes the specified Java String + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeString(String value) throws SQLException; + + /** + * This method writes the specified Java boolean + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeBoolean(boolean value) throws SQLException; + + /** + * This method writes the specified Java byte + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeByte(byte value) throws SQLException; + + /** + * This method writes the specified Java short + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeShort(short value) throws SQLException; + + /** + * This method writes the specified Java int + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeInt(int value) throws SQLException; + + /** + * This method writes the specified Java long + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeLong(long value) throws SQLException; + + /** + * This method writes the specified Java float + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeFloat(float value) throws SQLException; + + /** + * This method writes the specified Java double + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeDouble(double value) throws SQLException; + + /** + * This method writes the specified Java BigDecimal + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeBigDecimal(BigDecimal value) throws SQLException; + + /** + * This method writes the specified Java byte array + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeBytes(byte[] value) throws SQLException; + + /** + * This method writes the specified Java java.sql.Date + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeDate(Date value) throws SQLException; + + /** + * This method writes the specified Java java.sql.Time + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeTime(Time value) throws SQLException; + + /** + * This method writes the specified Java java.sql.Timestamp + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeTimestamp(Timestamp value) throws SQLException; + + /** + * This method writes the specified Java character stream + * to the SQL stream. + * + * @param stream The stream that holds the character data to write. + * @exception SQLException If an error occurs. + */ + void writeCharacterStream(Reader stream) throws SQLException; + + /** + * This method writes the specified ASCII text stream + * to the SQL stream. + * + * @param stream The stream that holds the ASCII data to write. + * @exception SQLException If an error occurs. + */ + void writeAsciiStream(InputStream stream) throws SQLException; + + /** + * This method writes the specified uninterpreted binary byte stream + * to the SQL stream. + * + * @param stream The stream that holds the binary data to write. + * @exception SQLException If an error occurs. + */ + void writeBinaryStream(InputStream stream) throws SQLException; + + /** + * This method writes the specified Java SQLData object + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeObject(SQLData value) throws SQLException; + + /** + * This method writes the specified Java SQL Ref object + * to the SQL stream. + * + * @param value The Ref object to write to the stream. + * @exception SQLException If an error occurs. + * @see Ref + */ + void writeRef(Ref value) throws SQLException; + + + /** + * This method writes the specified Java SQL Blob object + * to the SQL stream. + * + * @param value The Blob object to write to the stream. + * @exception SQLException If an error occurs. + * @see Blob + */ + void writeBlob(Blob value) throws SQLException; + + /** + * This method writes the specified Java SQL Clob object + * to the SQL stream. + * + * @param value The Clob object to write to the stream. + * @exception SQLException If an error occurs. + * @see Clob + */ + void writeClob(Clob value) throws SQLException; + + /** + * This method writes the specified Java SQL Struct object + * to the SQL stream. + * + * @param value The Struct object to write to the stream. + * @exception SQLException If an error occurs. + * @see Struct + */ + void writeStruct(Struct value) throws SQLException; + + /** + * This method writes the specified Java SQL Array object + * to the SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + */ + void writeArray(Array value) throws SQLException; + + /** + * This method writes the specified java.net.URL object to the + * SQL stream. + * + * @param value The value to write to the stream. + * @exception SQLException If an error occurs. + * @since 1.4 + */ + void writeURL(URL value) throws SQLException; +} diff --git a/libjava/classpath/java/sql/SQLPermission.java b/libjava/classpath/java/sql/SQLPermission.java new file mode 100644 index 000000000..ed3dd7f9e --- /dev/null +++ b/libjava/classpath/java/sql/SQLPermission.java @@ -0,0 +1,57 @@ +/* SQLPermission.java + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.security.BasicPermission; + +/** + * @since 1.3 + */ +public final class SQLPermission extends BasicPermission +{ + public SQLPermission(String name) + { + super(name); + } + + public SQLPermission(String name, String actions) + { + super(name, actions); + } +} diff --git a/libjava/classpath/java/sql/SQLWarning.java b/libjava/classpath/java/sql/SQLWarning.java new file mode 100644 index 000000000..c2cee0ad5 --- /dev/null +++ b/libjava/classpath/java/sql/SQLWarning.java @@ -0,0 +1,120 @@ +/* SQLWarning.java -- Database access warnings. + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This exception is thrown when a database warning occurs. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class SQLWarning extends SQLException +{ + static final long serialVersionUID = 3917336774604784856L; + + /** + * This method initializes a nwe instance of SQLWarning + * with the specified descriptive error message, SQL state string, and + * vendor code. + * + * @param message A string describing the nature of the error. + * @param SQLState A string containing the SQL state of the error. + * @param vendorCode The vendor error code associated with this error. + */ + public SQLWarning(String message, String SQLState, int vendorCode) + { + super(message, SQLState, vendorCode); + } + + /** + * This method initializes a new instance of SQLWarning + * with the specified descriptive error message and SQL state string. + * The vendor error code of this instance will be 0. + * + * @param message A string describing the nature of the error. + * @param SQLState A string containing the SQL state of the error. + */ + public SQLWarning(String message, String SQLState) + { + super(message, SQLState); + } + + /** + * This method initializes a new instance of SQLWarning + * with the specified descriptive error message. The SQL state of this + * instance will be null and the vendor error code will be 0. + * + * @param message A string describing the nature of the error. + */ + public SQLWarning(String message) + { + super(message); + } + + /** + * This method initializes a new instance of SQLWarning + * that does not have a descriptive messages and SQL state, and which + * has a vendor error code of 0. + */ + public SQLWarning() + { + super(); + } + + /** + * This method returns the exception that is chained to this object. + * + * @return The exception chained to this object, which may be + * null. + */ + public SQLWarning getNextWarning() + { + return (SQLWarning) super.getNextException(); + } + + /** + * This method adds a new exception to the end of the chain of exceptions + * that are chained to this object. + * + * @param w The exception to add to the end of the chain. + */ + public void setNextWarning(SQLWarning w) + { + super.setNextException(w); + } +} diff --git a/libjava/classpath/java/sql/Savepoint.java b/libjava/classpath/java/sql/Savepoint.java new file mode 100644 index 000000000..6526dfb44 --- /dev/null +++ b/libjava/classpath/java/sql/Savepoint.java @@ -0,0 +1,55 @@ +/* SavePoint.java -- Returns information about the ResultSet + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * @since 1.4 + */ +public interface Savepoint +{ + /** + * @since 1.4 + */ + int getSavepointId() throws SQLException; + + /** + * @since 1.4 + */ + String getSavepointName() throws SQLException; +} diff --git a/libjava/classpath/java/sql/Statement.java b/libjava/classpath/java/sql/Statement.java new file mode 100644 index 000000000..1b57fb3ce --- /dev/null +++ b/libjava/classpath/java/sql/Statement.java @@ -0,0 +1,375 @@ +/* Statement.java -- Interface for executing SQL statements. + Copyright (C) 1999, 2000, 2002, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This interface provides a mechanism for executing SQL statements. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Statement +{ + int CLOSE_CURRENT_RESULT = 1; + int KEEP_CURRENT_RESULT = 2; + int CLOSE_ALL_RESULTS = 3; + int SUCCESS_NO_INFO = -2; + int EXECUTE_FAILED = -3; + int RETURN_GENERATED_KEYS = 1; + int NO_GENERATED_KEYS = 2; + + /** + * This method executes the specified SQL SELECT statement and returns a + * (possibly empty) ResultSet with the results of the query. + * + * @param sql The SQL statement to execute. + * @return The result set of the SQL statement. + * @exception SQLException If an error occurs. + */ + ResultSet executeQuery(String sql) throws SQLException; + + /** + * This method executes the specified SQL INSERT, UPDATE, or DELETE statement + * and returns the number of rows affected, which may be 0. + * + * @param sql The SQL statement to execute. + * @return The number of rows affected by the SQL statement. + * @exception SQLException If an error occurs. + */ + int executeUpdate(String sql) throws SQLException; + + /** + * This method closes the statement and frees any associated resources. + * + * @exception SQLException If an error occurs. + */ + void close() throws SQLException; + + /** + * This method returns the maximum length of any column value in bytes. + * + * @return The maximum length of any column value in bytes. + * @exception SQLException If an error occurs. + */ + int getMaxFieldSize() throws SQLException; + + /** + * This method sets the limit for the maximum length of any column in bytes. + * + * @param maxSize The new maximum length of any column in bytes. + * @exception SQLException If an error occurs. + */ + void setMaxFieldSize(int maxSize) throws SQLException; + + /** + * This method returns the maximum possible number of rows in a result set. + * + * @return The maximum possible number of rows in a result set. + * @exception SQLException If an error occurs. + */ + int getMaxRows() throws SQLException; + + /** + * This method sets the maximum number of rows that can be present in a + * result set. + * + * @param maxRows The maximum possible number of rows in a result set. + * @exception SQLException If an error occurs. + */ + void setMaxRows(int maxRows) throws SQLException; + + /** + * This method sets the local escape processing mode on or off. The + * default value is on. + * + * @param escape true to enable local escape processing, + * false to disable it. + * @exception SQLException If an error occurs. + */ + void setEscapeProcessing(boolean escape) throws SQLException; + + /** + * The method returns the number of seconds a statement may be in process + * before timing out. A value of 0 means there is no timeout. + * + * @return The SQL statement timeout in seconds. + * @exception SQLException If an error occurs. + */ + int getQueryTimeout() throws SQLException; + + /** + * This method sets the number of seconds a statement may be in process + * before timing out. A value of 0 means there is no timeout. + * + * @param seconds The new SQL statement timeout value. + * @exception SQLException If an error occurs. + */ + void setQueryTimeout(int seconds) throws SQLException; + + /** + * This method cancels an outstanding statement, if the database supports + * that operation. + * + * @exception SQLException If an error occurs. + */ + void cancel() throws SQLException; + + /** + * This method returns the first SQL warning attached to this statement. + * Subsequent warnings will be chained to this one. + * + * @return The first SQL warning for this statement. + * @exception SQLException If an error occurs. + */ + SQLWarning getWarnings() throws SQLException; + + /** + * This method clears any SQL warnings that have been attached to this + * statement. + * + * @exception SQLException If an error occurs. + */ + void clearWarnings() throws SQLException; + + /** + * This method sets the cursor name that will be used by the result set. + * + * @param name The cursor name to use for this statement. + * @exception SQLException If an error occurs. + */ + void setCursorName(String name) throws SQLException; + + /** + * This method executes an arbitrary SQL statement of any time. The + * methods getResultSet, getMoreResults and + * getUpdateCount retrieve the results. + * + * @return true if a result set was returned, false + * if an update count was returned. + * @exception SQLException If an error occurs. + */ + boolean execute(String sql) throws SQLException; + + /** + * This method returns the result set of the SQL statement that was + * executed. This should be called only once per result set returned. + * + * @return The result set of the query, or null if there was + * no result set (for example, if the statement was an UPDATE). + * @exception SQLException If an error occurs. + * @see #execute(String) + * @see #execute(String, int) + * @see #execute(String, int[]) + * @see #execute(String, String[]) + */ + ResultSet getResultSet() throws SQLException; + + /** + * This method returns the update count of the SQL statement that was + * executed. This should be called only once per executed SQL statement. + * + * @return The update count of the query, or -1 if there was no update + * count (for example, if the statement was a SELECT). + * @exception SQLException If an error occurs. + * @see #execute(String) + * @see #execute(String, int) + * @see #execute(String, int[]) + * @see #execute(String, String[]) + */ + int getUpdateCount() throws SQLException; + + /** + * This method advances the result set pointer to the next result set, + * which can then be retrieved using getResultSet + * + * @return true if there is another result set, + * false otherwise (for example, the next result is an + * update count). + * @exception SQLException If an error occurs. + * @see #execute(String) + * @see #execute(String, int) + * @see #execute(String, int[]) + * @see #execute(String, String[]) + */ + boolean getMoreResults() throws SQLException; + + /** + * This method informs the driver which direction the result set will + * be accessed in. + * + * @param direction The direction the result set will be accessed in (?????) + * @exception SQLException If an error occurs. + */ + void setFetchDirection(int direction) throws SQLException; + + /** + * This method returns the current direction that the driver thinks the + * result set will be accessed int. + * + * @return The direction the result set will be accessed in (????) + * @exception SQLException If an error occurs. + */ + int getFetchDirection() throws SQLException; + + /** + * This method informs the driver how many rows it should fetch from the + * database at a time. + * + * @param numRows The number of rows the driver should fetch at a time + * to populate the result set. + * @exception SQLException If an error occurs. + */ + void setFetchSize(int numRows) throws SQLException; + + /** + * This method returns the number of rows the driver believes should be + * fetched from the database at a time. + * + * @return The number of rows that will be fetched from the database at a time. + * @exception SQLException If an error occurs. + */ + int getFetchSize() throws SQLException; + + /** + * This method returns the concurrency type of the result set for this + * statement. This will be one of the concurrency types defined in + * ResultSet. + * + * @return The concurrency type of the result set for this statement. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + int getResultSetConcurrency() throws SQLException; + + /** + * This method returns the result set type for this statement. This will + * be one of the result set types defined in ResultSet. + * + * @return The result set type for this statement. + * @exception SQLException If an error occurs. + * @see ResultSet + */ + int getResultSetType() throws SQLException; + + /** + * This method adds a SQL statement to a SQL batch. A driver is not + * required to implement this method. + * + * @param sql The sql statement to add to the batch. + * @exception SQLException If an error occurs. + */ + void addBatch(String sql) throws SQLException; + + /** + * This method clears out any SQL statements that have been populated in + * the current batch. A driver is not required to implement this method. + * + * @exception SQLException If an error occurs. + */ + void clearBatch() throws SQLException; + + /** + * This method executes the SQL batch and returns an array of update + * counts - one for each SQL statement in the batch - ordered in the same + * order the statements were added to the batch. A driver is not required + * to implement this method. + * + * @return An array of update counts for this batch. + * @exception SQLException If an error occurs. + */ + int[] executeBatch() throws SQLException; + + /** + * This method returns the Connection instance that was + * used to create this object. + * + * @return The connection used to create this object. + * @exception SQLException If an error occurs. + */ + Connection getConnection() throws SQLException; + + /** + * @since 1.4 + */ + boolean getMoreResults(int current) throws SQLException; + + /** + * @since 1.4 + */ + ResultSet getGeneratedKeys() throws SQLException; + + /** + * @since 1.4 + */ + int executeUpdate(String sql, int autoGeneratedKeys) + throws SQLException; + + /** + * @since 1.4 + */ + int executeUpdate(String sql, int[] columnIndexes) + throws SQLException; + + /** + * @since 1.4 + */ + int executeUpdate(String sql, String[] columnNames) + throws SQLException; + + /** + * @since 1.4 + */ + boolean execute(String sql, int autoGeneratedKeys) + throws SQLException; + + /** + * @since 1.4 + */ + boolean execute(String sql, int[] columnIndexes) throws SQLException; + + /** + * @since 1.4 + */ + boolean execute(String sql, String[] columnNames) + throws SQLException; + + /** + * @since 1.4 + */ + int getResultSetHoldability() throws SQLException; +} diff --git a/libjava/classpath/java/sql/Struct.java b/libjava/classpath/java/sql/Struct.java new file mode 100644 index 000000000..897344b98 --- /dev/null +++ b/libjava/classpath/java/sql/Struct.java @@ -0,0 +1,77 @@ +/* Struct.java -- Mapping for a SQL structured type. + Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.util.Map; + +/** + * This interface implements the standard type mapping for a SQL + * structured type. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Struct +{ + /** + * This method returns the name of the SQL structured type for this + * object. + * + * @return The SQL structured type name. + * @exception SQLException If an error occurs. + */ + String getSQLTypeName() throws SQLException; + + /** + * This method returns the attributes of this SQL structured type. + * + * @return The attributes of this structure type. + * @exception SQLException If an error occurs. + */ + Object[] getAttributes() throws SQLException; + + /** + * This method returns the attributes of this SQL structured type. + * The specified map of type mappings overrides the default mappings. + * + * @param map The map of SQL type mappings. + * @return The attributes of this structure type. + * @exception SQLException If a error occurs. + */ + Object[] getAttributes(Map> map) throws SQLException; +} diff --git a/libjava/classpath/java/sql/Time.java b/libjava/classpath/java/sql/Time.java new file mode 100644 index 000000000..8de163683 --- /dev/null +++ b/libjava/classpath/java/sql/Time.java @@ -0,0 +1,201 @@ +/* Time.java -- Wrapper around java.util.Date + Copyright (C) 1999, 2000, 2002, 2003, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +/** + * This class is a wrapper around java.util.Date to allow the JDBC + * driver to identify the value as a SQL Time. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Time extends java.util.Date +{ + static final long serialVersionUID = 8397324403548013681L; + + /** + * Used for parsing and formatting this date. + */ + private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getDate() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getDay() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getMonth() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public int getYear() throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setDate(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setMonth(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method always throws an IllegalArgumentException. + * + * @throws IllegalArgumentException when it's called. + * @deprecated + */ + public void setYear(int newValue) throws IllegalArgumentException + { + throw new IllegalArgumentException(); + } + + /** + * This method returns a new instance of this class by parsing a + * date in JDBC format into a Java date. + * + * @param str The string to parse. + * @return The resulting java.sql.Time value. + */ + public static Time valueOf (String str) + { + try + { + java.util.Date d = (java.util.Date) sdf.parseObject(str); + + if (d == null) + throw new IllegalArgumentException(str); + else + return new Time(d.getTime()); + } + catch (ParseException e) + { + throw new IllegalArgumentException(str); + } + } + + /** + * This method initializes a new instance of this class with the + * specified year, month, and day. + * + * @param hour The hour for this Time (0-23) + * @param minute The minute for this time (0-59) + * @param second The second for this time (0-59) + * @deprecated + */ + public Time(int hour, int minute, int second) + { + super(System.currentTimeMillis()); + + setHours(hour); + setMinutes(minute); + setSeconds(second); + } + + /** + * This method initializes a new instance of this class with the + * specified time value representing the number of milliseconds since + * Jan 1, 1970 at 12:00 midnight GMT. + * + * @param date The time value to intialize this Time to. + */ + public Time(long date) + { + super(date); + } + + /** + * This method returns this date in JDBC format. + * + * @return This date as a string. + */ + public String toString () + { + return sdf.format (this); + } + +} diff --git a/libjava/classpath/java/sql/Timestamp.java b/libjava/classpath/java/sql/Timestamp.java new file mode 100644 index 000000000..9488f8e2d --- /dev/null +++ b/libjava/classpath/java/sql/Timestamp.java @@ -0,0 +1,319 @@ +/* Time.java -- Wrapper around java.util.Date + Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; + +/** + * This class is a wrapper around java.util.Date to allow the JDBC + * driver to identify the value as a SQL Timestamp. Note that this + * class also adds an additional field for nano-seconds, and so + * is not completely identical to java.util.Date as + * the java.sql.Date and java.sql.Time + * classes are. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Timestamp extends java.util.Date +{ + static final long serialVersionUID = 2745179027874758501L; + + /** + * Used for parsing and formatting this date. + */ + private static SimpleDateFormat dateFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static DecimalFormat decimalFormat = new DecimalFormat("000000000"); + private static StringBuffer sbuf = new StringBuffer(29); + + /** + * The nanosecond value for this object + */ + private int nanos; + + /** + * This method returns a new instance of this class by parsing a + * date in JDBC format into a Java date. + * + * @param str The string to parse. + * @return The resulting java.sql.Timestamp value. + */ + public static Timestamp valueOf(String str) + { + int nanos = 0; + int dot = str.indexOf('.'); + if (dot != -1) + { + if (str.lastIndexOf('.') != dot) + throw new IllegalArgumentException(str); + + int len = str.length() - dot - 1; + if (len < 1 || len > 9) + throw new IllegalArgumentException(str); + + nanos = Integer.parseInt(str.substring(dot + 1)); + for (int i = len; i < 9; i++) + nanos *= 10; + + str = str.substring(0, dot); + + } + + try + { + java.util.Date d; + synchronized (dateFormat) + { + d = (java.util.Date) dateFormat.parseObject(str); + } + + if (d == null) + throw new IllegalArgumentException(str); + + Timestamp ts = new Timestamp(d.getTime() + nanos / 1000000); + ts.nanos = nanos; + return ts; + } + catch (ParseException e) + { + throw new IllegalArgumentException(str); + } + } + + /** + * This method initializes a new instance of this class with the + * specified year, month, and day. + * + * @param year The year for this Timestamp (year - 1900) + * @param month The month for this Timestamp (0-11) + * @param day The day for this Timestamp (1-31) + * @param hour The hour for this Timestamp (0-23) + * @param minute The minute for this Timestamp (0-59) + * @param second The second for this Timestamp (0-59) + * @param nanos The nanosecond value for this Timestamp (0 to 999,999,9999) + * @deprecated + */ + public Timestamp(int year, int month, int day, int hour, int minute, + int second, int nanos) + { + super(year, month, day, hour, minute, second); + this.nanos = nanos; + } + + /** + * This method initializes a new instance of this class with the + * specified time value representing the number of milliseconds since + * Jan 1, 1970 at 12:00 midnight GMT. + * + * @param date The time value to intialize this Time to. + */ + public Timestamp(long date) + { + super(date - (date % 1000)); + nanos = (int) (date % 1000) * 1000000; + } + + /** + * Return the value of this Timestamp as the number of milliseconds + * since Jan 1, 1970 at 12:00 midnight GMT. + */ + public long getTime() + { + return super.getTime() + (nanos / 1000000); + } + + /** + * This method returns this date in JDBC format. + * + * @return This date as a string. + */ + public String toString() + { + synchronized (dateFormat) + { + sbuf.setLength(0); + dateFormat.format(this, sbuf, null); + sbuf.append('.'); + decimalFormat.format(nanos, sbuf, null); + int end = sbuf.length() - 1; + while (end > 20 && sbuf.charAt(end) == '0') + end--; + return sbuf.substring(0, end + 1); + } + } + + /** + * This method returns the nanosecond value for this object. + * @return The nanosecond value for this object. + */ + public int getNanos() + { + return nanos; + } + + /** + * This method sets the nanosecond value for this object. + * + * @param nanos The nanosecond value for this object. + */ + public void setNanos(int nanos) + { + this.nanos = nanos; + } + + /** + * This methods tests whether this object is earlier than the specified + * object. + * + * @param ts The other Timestamp to test against. + * @return true if this object is earlier than the other object, + * false otherwise. + */ + public boolean before(Timestamp ts) + { + long time1 = getTime(); + long time2 = ts.getTime(); + if (time1 < time2 || (time1 == time2 && getNanos() < ts.getNanos())) + return true; + return false; + } + + /** + * This methods tests whether this object is later than the specified + * object. + * + * @param ts The other Timestamp to test against. + * + * @return true if this object is later than the other object, + * false otherwise. + */ + public boolean after(Timestamp ts) + { + long time1 = getTime(); + long time2 = ts.getTime(); + if (time1 > time2 || (time1 == time2 && getNanos() > ts.getNanos())) + return true; + return false; + } + + /** + * This method these the specified Object for equality + * against this object. This will be true if an only if the specified + * object is an instance of Timestamp and has the same + * time value fields. + * + * @param obj The object to test against for equality. + * + * @return true if the specified object is equal to this + * object, false otherwise. + */ + public boolean equals(Object obj) + { + if (!(obj instanceof Timestamp)) + return false; + + return equals((Timestamp) obj); + } + + /** + * This method tests the specified timestamp for equality against this + * object. This will be true if and only if the specified object is + * not null and contains all the same time value fields + * as this object. + * + * @param ts The Timestamp to test against for equality. + * + * @return true if the specified object is equal to this + * object, false otherwise. + */ + public boolean equals(Timestamp ts) + { + if (ts == null) + return false; + + if (ts.getTime() != getTime()) + return false; + + if (ts.getNanos() != getNanos()) + return false; + + return true; + } + + /** + * Compares this Timestamp to another one. + * + * @param ts The other Timestamp. + * @return 0, if both Timestamp's represent exactly + * the same date, a negative value if this Timestamp is + * before the specified Timestamp and a positive value + * otherwise. + * @since 1.2 + */ + public int compareTo(Timestamp ts) + { + int s = super.compareTo((java.util.Date) ts); + if (s != 0) + return s; + // If Date components were equal, then we check the nanoseconds. + return nanos - ts.nanos; + } + + /** + * Compares this Timestamp to another one. This behaves like + * compareTo(Timestamp), but it may throw a + * ClassCastException, if the specified object is not of type + * Timestamp. + * + * @param obj The object to compare with. + * @return 0, if both Timestamp's represent exactly + * the same date, a negative value if this Timestamp is + * before the specified Timestamp and a positive value + * otherwise. + * @exception ClassCastException if obj is not of type Timestamp. + * @see #compareTo(Timestamp) + * @since 1.2 + */ + public int compareTo(java.util.Date obj) + { + return compareTo((Timestamp) obj); + } +} diff --git a/libjava/classpath/java/sql/Types.java b/libjava/classpath/java/sql/Types.java new file mode 100644 index 000000000..c07ef1353 --- /dev/null +++ b/libjava/classpath/java/sql/Types.java @@ -0,0 +1,85 @@ +/* Types.java -- SQL type constants + Copyright (C) 1999, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.sql; + +/** + * This class contains constants that are used to identify SQL data types. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Types +{ + // These should be self explanatory. People need a SQL book, not + // Javadoc comments for these. + public static final int BIT = -7; + public static final int TINYINT = -6; + public static final int SMALLINT = 5; + public static final int INTEGER = 4; + public static final int BIGINT = -5; + public static final int FLOAT = 6; + public static final int REAL = 7; + public static final int DOUBLE = 8; + public static final int NUMERIC = 2; + public static final int DECIMAL = 3; + public static final int CHAR = 1; + public static final int VARCHAR = 12; + public static final int LONGVARCHAR = -1; + public static final int DATE = 91; + public static final int TIME = 92; + public static final int TIMESTAMP = 93; + public static final int BINARY = -2; + public static final int VARBINARY = -3; + public static final int LONGVARBINARY = -4; + public static final int NULL = 0; + public static final int OTHER = 1111; + public static final int JAVA_OBJECT = 2000; + public static final int DISTINCT = 2001; + public static final int STRUCT = 2002; + public static final int ARRAY = 2003; + public static final int BLOB = 2004; + public static final int CLOB = 2005; + public static final int REF = 2006; + public static final int DATALINK = 70; + public static final int BOOLEAN = 16; + + // This class can't be instantiated. + private Types() + { + } +} diff --git a/libjava/classpath/java/sql/package.html b/libjava/classpath/java/sql/package.html new file mode 100644 index 000000000..e8982f529 --- /dev/null +++ b/libjava/classpath/java/sql/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.sql + + +

        Interfaces and classes to connect to a database and wrappers for data +in the database and result queries.

        + + + diff --git a/libjava/classpath/java/text/Annotation.java b/libjava/classpath/java/text/Annotation.java new file mode 100644 index 000000000..7c4d001f6 --- /dev/null +++ b/libjava/classpath/java/text/Annotation.java @@ -0,0 +1,112 @@ +/* Annotation.java -- Wrapper for a text attribute object + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This class is used as a wrapper for a text attribute object. Annotation + * objects are associated with a specific range of text. Changing either + * the text range or the underlying text invalidates the object. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Annotation +{ + +/* + * Instance Variables + */ + +/** + * This is the attribute object being wrappered + */ +private Object attrib; + +/*************************************************************************/ + +/** + * Constructors + */ + +/** + * This method initializes a new instance of Annotation to + * wrapper the specified text attribute object. + * + * @param attrib The text attribute Object to wrapper. + */ +public +Annotation(Object attrib) +{ + this.attrib = attrib; +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This method returns the text attribute object this Annotation + * instance is wrappering. + * + * @return The text attribute object for this Annotation. + */ +public Object +getValue() +{ + return(attrib); +} + +/*************************************************************************/ + +/** + * This method returns a String representation of this + * object. + * + * @return This object as a String. + */ +public String +toString() +{ + return(getClass().getName() + "[value=" + attrib.toString() + "]"); +} + +} // class Annotation diff --git a/libjava/classpath/java/text/AttributedCharacterIterator.java b/libjava/classpath/java/text/AttributedCharacterIterator.java new file mode 100644 index 000000000..72c0ebbfe --- /dev/null +++ b/libjava/classpath/java/text/AttributedCharacterIterator.java @@ -0,0 +1,277 @@ +/* AttributedCharacterIterator.java -- Iterate over attributes + Copyright (C) 1998, 1999, 2004, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.io.InvalidObjectException; +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +/** + * This interface extends the CharacterIterator interface + * in order to support iteration over character attributes as well as + * over the characters themselves. + *

        + * In addition to attributes of specific characters, this interface + * supports the concept of the "attribute run", which is an attribute + * that is defined for a particular value across an entire range of + * characters or which is undefined over a range of characters. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + */ +public interface AttributedCharacterIterator extends CharacterIterator +{ + /** + * Defines attribute keys that are used as text attributes. + */ + public static class Attribute implements Serializable + { + private static final long serialVersionUID = -9142742483513960612L; + + /** + * This is the attribute for the language of the text. The value of + * attributes of this key type are instances of Locale. + */ + public static final Attribute LANGUAGE = new Attribute("language"); + + /** + * This is the attribute for the reading form of text. This is used + * for storing pronunciation along with the written text for languages + * which need it. The value of attributes of this key type are + * instances of Annotation which wrappers a + * String. + */ + public static final Attribute READING = new Attribute("reading"); + + /** + * This is the attribute for input method segments. The value of attributes + * of this key type are instances of Annotation which wrapper + * a String. + */ + public static final Attribute INPUT_METHOD_SEGMENT = + new Attribute("input_method_segment"); + + /** + * The name of the attribute key + * @serial + */ + private String name; + + /** + * Initializes a new instance of this class with the specified name. + * + * @param name The name of this attribute key. + */ + protected Attribute(String name) + { + this.name = name; + } + + /** + * Returns the name of this attribute. + * + * @return The attribute name + */ + protected String getName() + { + return name; + } + + /** + * Resolves an instance of + * AttributedCharacterIterator.Attribute + * that is being deserialized to one of the three pre-defined attribute + * constants. It does this by comparing the names of the attributes. The + * constant that the deserialized object resolves to is returned. + * + * @return The resolved contant value + * + * @exception InvalidObjectException If the object being deserialized + * cannot be resolved. + */ + protected Object readResolve() throws InvalidObjectException + { + if (getName().equals(READING.getName())) + return READING; + + if (getName().equals(LANGUAGE.getName())) + return LANGUAGE; + + if (getName().equals(INPUT_METHOD_SEGMENT.getName())) + return INPUT_METHOD_SEGMENT; + + throw new InvalidObjectException ("Can't resolve Attribute: " + + getName()); + } + + /** + * Tests this object for equality against the specified object. + * The two objects will be considered equal if and only if: + *

          + *
        • The specified object is not null. + *
        • The specified object is an instance of + * AttributedCharacterIterator.Attribute. + *
        • The specified object has the same attribute name as this object. + *
        + * + * @param obj the Object to test for equality against this + * object. + * + * @return true if the specified object is equal to this one, + * false otherwise. + */ + public final boolean equals(Object obj) + { + if (obj == this) + return true; + else + return false; + } + + /** + * Returns a hash value for this object. + * + * @return A hash value for this object. + */ + public final int hashCode() + { + return super.hashCode(); + } + + /** + * Returns a String representation of this object. + * + * @return A String representation of this object. + */ + public String toString() + { + return getClass().getName() + "(" + getName() + ")"; + } + + } // Inner class Attribute + + /** + * Returns a list of all keys that are defined for the + * text range. This can be an empty list if no attributes are defined. + * + * @return A list of keys + */ + Set getAllAttributeKeys(); + + /** + * Returns a Map of the attributes defined for the current + * character. + * + * @return A Map of the attributes for the current character. + */ + Map getAttributes(); + + /** + * Returns the value of the specified attribute for the + * current character. If the attribute is not defined for the current + * character, null is returned. + * + * @param attrib The attribute to retrieve the value of. + * + * @return The value of the specified attribute + */ + Object getAttribute(AttributedCharacterIterator.Attribute attrib); + + /** + * Returns the index of the first character in the run that + * contains all attributes defined for the current character. + * + * @return The start index of the run + */ + int getRunStart(); + + /** + * Returns the index of the first character in the run that + * contains all attributes in the specified Set defined for + * the current character. + * + * @param attribs The Set of attributes. + * + * @return The start index of the run. + */ + int getRunStart(Set attribs); + + /** + * Returns the index of the first character in the run that + * contains the specified attribute defined for the current character. + * + * @param attrib The attribute. + * + * @return The start index of the run. + */ + int getRunStart(AttributedCharacterIterator.Attribute attrib); + + /** + * Returns the index of the character after the end of the run + * that contains all attributes defined for the current character. + * + * @return The end index of the run. + */ + int getRunLimit(); + + /** + * Returns the index of the character after the end of the run + * that contains all attributes in the specified Set defined + * for the current character. + * + * @param attribs The Set of attributes. + * + * @return The end index of the run. + */ + int getRunLimit(Set attribs); + + /** + * Returns the index of the character after the end of the run + * that contains the specified attribute defined for the current character. + * + * @param attrib The attribute. + * + * @return The end index of the run. + */ + int getRunLimit(AttributedCharacterIterator.Attribute attrib); + +} // interface AttributedCharacterIterator diff --git a/libjava/classpath/java/text/AttributedString.java b/libjava/classpath/java/text/AttributedString.java new file mode 100644 index 000000000..7ffb3d4c0 --- /dev/null +++ b/libjava/classpath/java/text/AttributedString.java @@ -0,0 +1,378 @@ +/* AttributedString.java -- Models text with attributes + Copyright (C) 1998, 1999, 2004, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.lang.CPStringBuilder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * This class models a String with attributes over various + * subranges of the string. It allows applications to access this + * information via the AttributedCharacterIterator interface. + * + * @since 1.2 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @since 1.2 + */ +public class AttributedString +{ + + /** + * The attributes and ranges of text over which those attributes apply. + */ + final class AttributeRange + { + + /** A Map of the attributes */ + Map attribs; + + /** The beginning index of the attributes */ + int beginIndex; + + /** The ending index of the attributes */ + int endIndex; + + /** + * Creates a new attribute range. + * + * @param attribs the attributes. + * @param beginIndex the start index. + * @param endIndex the end index. + */ + AttributeRange(Map attribs, int beginIndex, int endIndex) + { + this.attribs = attribs; + this.beginIndex = beginIndex; + this.endIndex = endIndex; + } + + } // Inner class AttributeRange + + /** The string we are representing. */ + private StringCharacterIterator sci; + + /** The attribute information */ + private AttributeRange[] attribs; + + /** + * Creates a new instance of AttributedString + * that represents the specified String with no attributes. + * + * @param str The String to be attributed (null not + * permitted). + * + * @throws NullPointerException if str is null. + */ + public AttributedString(String str) + { + sci = new StringCharacterIterator(str); + attribs = new AttributeRange[0]; + } + + /** + * Creates a new instance of AttributedString + * that represents that specified String with the specified + * attributes over the entire length of the String. + * + * @param str The String to be attributed. + * @param attributes The attribute list. + */ + public AttributedString(String str, + Map attributes) + { + this(str); + + attribs = new AttributeRange[1]; + attribs[0] = new AttributeRange(attributes, 0, str.length()); + } + + /** + * Initializes a new instance of AttributedString + * that will use the text and attribute information from the specified + * AttributedCharacterIterator. + * + * @param aci The AttributedCharacterIterator containing the + * text and attribute information (null not + * permitted). + * + * @throws NullPointerException if aci is null. + */ + public AttributedString(AttributedCharacterIterator aci) + { + this(aci, aci.getBeginIndex(), aci.getEndIndex(), null); + } + + /** + * Initializes a new instance of AttributedString + * that will use the text and attribute information from the specified + * subrange of the specified AttributedCharacterIterator. + * + * @param aci The AttributedCharacterIterator containing the + * text and attribute information. + * @param beginIndex The beginning index of the text subrange. + * @param endIndex The ending index of the text subrange. + */ + public AttributedString(AttributedCharacterIterator aci, int beginIndex, + int endIndex) + { + this(aci, beginIndex, endIndex, null); + } + + /** + * Initializes a new instance of AttributedString + * that will use the text and attribute information from the specified + * subrange of the specified AttributedCharacterIterator. + * Only attributes from the source iterator that are present in the + * specified array of attributes will be included in the attribute list + * for this object. + * + * @param aci The AttributedCharacterIterator containing the + * text and attribute information. + * @param begin The beginning index of the text subrange. + * @param end The ending index of the text subrange. + * @param attributes A list of attributes to include from the iterator, or + * null to include all attributes. + */ + public AttributedString(AttributedCharacterIterator aci, int begin, int end, + AttributedCharacterIterator.Attribute[] attributes) + { + // Validate some arguments + if ((begin < 0) || (end < begin) || end > aci.getEndIndex()) + throw new IllegalArgumentException("Bad index values"); + + CPStringBuilder sb = new CPStringBuilder(""); + + // Get the valid attribute list + Set allAttribs = aci.getAllAttributeKeys(); + if (attributes != null) + allAttribs.retainAll(Arrays.asList(attributes)); + + // Loop through and extract the attributes + char c = aci.setIndex(begin); + + ArrayList accum = new ArrayList(); + do + { + sb.append(c); + + Iterator iter = allAttribs.iterator(); + while(iter.hasNext()) + { + Object obj = iter.next(); + + // What should we do if this is not true? + if (!(obj instanceof AttributedCharacterIterator.Attribute)) + continue; + + AttributedCharacterIterator.Attribute attrib = + (AttributedCharacterIterator.Attribute)obj; + + // Make sure the attribute is defined. + Object attribObj = aci.getAttribute(attrib); + if (attribObj == null) + continue; + int rl = aci.getRunLimit(attrib); + if (rl > end) + rl = end; + rl -= begin; + + // Check to see if we already processed this one + int rs = aci.getRunStart(attrib); + if ((rs < aci.getIndex()) && (aci.getIndex() != begin)) + continue; + + // If the attribute run starts before the beginning index, we + // need to junk it if it is an Annotation. + rs -= begin; + if (rs < 0) + { + if (attribObj instanceof Annotation) + continue; + + rs = 0; + } + + // Create a map object. Yes this will only contain one attribute + Map newMap = new Hashtable(); + newMap.put(attrib, attribObj); + + // Add it to the attribute list. + accum.add(new AttributeRange(newMap, rs, rl)); + } + + c = aci.next(); + } + while( aci.getIndex() < end ); + + attribs = new AttributeRange[accum.size()]; + attribs = (AttributeRange[]) accum.toArray(attribs); + + sci = new StringCharacterIterator(sb.toString()); + } + + /** + * Adds a new attribute that will cover the entire string. + * + * @param attrib The attribute to add. + * @param value The value of the attribute. + */ + public void addAttribute(AttributedCharacterIterator.Attribute attrib, + Object value) + { + addAttribute(attrib, value, 0, sci.getEndIndex()); + } + + /** + * Adds a new attribute that will cover the specified subrange + * of the string. + * + * @param attrib The attribute to add. + * @param value The value of the attribute, which may be null. + * @param begin The beginning index of the subrange. + * @param end The ending index of the subrange. + * + * @exception IllegalArgumentException If attribute is null or + * the subrange is not valid. + */ + public void addAttribute(AttributedCharacterIterator.Attribute attrib, + Object value, int begin, int end) + { + if (attrib == null) + throw new IllegalArgumentException("null attribute"); + if (end <= begin) + throw new IllegalArgumentException("Requires end > begin"); + HashMap hm = new HashMap(); + hm.put(attrib, value); + + addAttributes(hm, begin, end); + } + + /** + * Adds all of the attributes in the specified list to the + * specified subrange of the string. + * + * @param attributes The list of attributes. + * @param beginIndex The beginning index. + * @param endIndex The ending index + * + * @throws NullPointerException if attributes is + * null. + * @throws IllegalArgumentException if the subrange is not valid. + */ + public void addAttributes(Map attributes, + int beginIndex, int endIndex) + { + if (attributes == null) + throw new NullPointerException("null attribute"); + + if ((beginIndex < 0) || (endIndex > sci.getEndIndex()) || + (endIndex <= beginIndex)) + throw new IllegalArgumentException("bad range"); + + AttributeRange[] new_list = new AttributeRange[attribs.length + 1]; + System.arraycopy(attribs, 0, new_list, 0, attribs.length); + attribs = new_list; + attribs[attribs.length - 1] = new AttributeRange(attributes, beginIndex, + endIndex); + } + + /** + * Returns an AttributedCharacterIterator that + * will iterate over the entire string. + * + * @return An AttributedCharacterIterator for the entire string. + */ + public AttributedCharacterIterator getIterator() + { + return(new AttributedStringIterator(sci, attribs, 0, sci.getEndIndex(), + null)); + } + + /** + * Returns an AttributedCharacterIterator that + * will iterate over the entire string. This iterator will return information + * about the list of attributes in the specified array. Attributes not in + * the array may or may not be returned by the iterator. If the specified + * array is null, all attributes will be returned. + * + * @param attributes A list of attributes to include in the returned iterator. + * + * @return An AttributedCharacterIterator for this string. + */ + public AttributedCharacterIterator getIterator( + AttributedCharacterIterator.Attribute[] attributes) + { + return(getIterator(attributes, 0, sci.getEndIndex())); + } + + /** + * Returns an AttributedCharacterIterator that + * will iterate over the specified subrange. This iterator will return + * information about the list of attributes in the specified array. + * Attributes not in the array may or may not be returned by the iterator. + * If the specified array is null, all attributes will be + * returned. + * + * @param attributes A list of attributes to include in the returned iterator. + * @param beginIndex The beginning index of the subrange. + * @param endIndex The ending index of the subrange. + * + * @return An AttributedCharacterIterator for this string. + */ + public AttributedCharacterIterator getIterator( + AttributedCharacterIterator.Attribute[] attributes, + int beginIndex, int endIndex) + { + if ((beginIndex < 0) || (endIndex > sci.getEndIndex()) || + (endIndex < beginIndex)) + throw new IllegalArgumentException("bad range"); + + return(new AttributedStringIterator(sci, attribs, beginIndex, endIndex, + attributes)); + } + +} // class AttributedString diff --git a/libjava/classpath/java/text/AttributedStringIterator.java b/libjava/classpath/java/text/AttributedStringIterator.java new file mode 100644 index 000000000..429bd7063 --- /dev/null +++ b/libjava/classpath/java/text/AttributedStringIterator.java @@ -0,0 +1,389 @@ +/* AttributedStringIterator.java -- Class to iterate over AttributedString + Copyright (C) 1998, 1999, 2004, 2005, 2006, Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * This class implements the AttributedCharacterIterator interface. It + * is used by AttributedString.getIterator(). + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class AttributedStringIterator implements AttributedCharacterIterator +{ + + /*************************************************************************/ + + /** The character iterator containing the text */ + private CharacterIterator ci; + + /** The list of attributes and ranges */ + private AttributedString.AttributeRange[] attribs; + + /** + * The list of attributes that the user is interested in. We may, + * at our option, not return any other attributes. + */ + private AttributedCharacterIterator.Attribute[] restricts; + + /*************************************************************************/ + + /** + * Creates a new instance. + * + * @param sci an iterator for the string content. + * @param attribs the attribute ranges. + * @param beginIndex the start index. + * @param endIndex the end index. + * @param restricts the attributes that the user is interested in. + */ + AttributedStringIterator(StringCharacterIterator sci, + AttributedString.AttributeRange[] attribs, + int beginIndex, int endIndex, + AttributedCharacterIterator.Attribute[] restricts) + { + this.ci = new StringCharacterIterator(sci, beginIndex, endIndex); + this.attribs = attribs; + this.restricts = restricts; + } + + /*************************************************************************/ + + // First we have a bunch of stupid redirects. If StringCharacterIterator + // weren't final, I just would have extended that for this class. Alas, no. + + public Object clone() + { + return(ci.clone()); + } + + public char current() + { + return(ci.current()); + } + + public char next() + { + return(ci.next()); + } + + public char previous() + { + return(ci.previous()); + } + + public char first() + { + return(ci.first()); + } + + public char last() + { + return(ci.last()); + } + + public int getIndex() + { + return(ci.getIndex()); + } + + public char setIndex(int index) + { + return(ci.setIndex(index)); + } + + public int getBeginIndex() + { + return(ci.getBeginIndex()); + } + + public int getEndIndex() + { + return(ci.getEndIndex()); + } + + /* + * Here is where the AttributedCharacterIterator methods start. + */ + + /*************************************************************************/ + + /** + * Returns a list of all the attribute keys that are defined anywhere + * on this string. + */ + public Set getAllAttributeKeys() + { + HashSet s = new HashSet(); + if (attribs == null) + return(s); + + for (int i = 0; i < attribs.length; i++) + { + if (attribs[i].beginIndex > getEndIndex() + || attribs[i].endIndex <= getBeginIndex()) + continue; + + Set key_set = attribs[i].attribs.keySet(); + Iterator iter = key_set.iterator(); + while (iter.hasNext()) + { + s.add(iter.next()); + } + } + + return(s); + } + + /*************************************************************************/ + + /** + * Various methods that determine how far the run extends for various + * attribute combinations. + */ + + public int getRunLimit() + { + return getRunLimit(getAllAttributeKeys()); + } + + public int getRunLimit(AttributedCharacterIterator.Attribute attrib) + { + HashSet s = new HashSet(); + s.add(attrib); + return(getRunLimit(s)); + } + + public synchronized int getRunLimit(Set attributeSet) + { + if (attributeSet == null) + return ci.getEndIndex(); + + int current = ci.getIndex(); + int end = ci.getEndIndex(); + int limit = current; + if (current == end) + return end; + Map runValues = getAttributes(); + while (limit < end) + { + Iterator iterator = attributeSet.iterator(); + while (iterator.hasNext()) + { + Attribute attributeKey = (Attribute) iterator.next(); + Object v1 = runValues.get(attributeKey); + Object v2 = getAttribute(attributeKey, limit + 1); + boolean changed = false; + // check for equal or both null, if NO return start + if (v1 != null) + { + changed = !v1.equals(v2); + } + else + { + changed = (v2 != null); + } + if (changed) + return limit + 1; + } + // no differences, so increment limit and next and loop again + limit++; + } + return end; + } + + /*************************************************************************/ + + /** + * Various methods that determine where the run begins for various + * attribute combinations. + */ + + /** + * Returns the index of the first character in the run containing the current + * character and defined by all the attributes defined for that character + * position. + * + * @return The run start index. + */ + public int getRunStart() + { + return(getRunStart(getAttributes().keySet())); + } + + /** + * Returns the index of the first character in the run, defined by the + * specified attribute, that contains the current character. + * + * @param attrib the attribute (null permitted). + * + * return The index of the first character in the run. + */ + public int getRunStart(AttributedCharacterIterator.Attribute attrib) + { + if (attrib == null) + return ci.getBeginIndex(); + HashSet s = new HashSet(); + s.add(attrib); + return(getRunStart(s)); + } + + /** + * Returns the index of the first character in the run, defined by the + * specified attribute set, that contains the current character. + * + * @param attributeSet the attribute set (null permitted). + * + * return The index of the first character in the run. + */ + public int getRunStart(Set attributeSet) + { + if (attributeSet == null) + return ci.getBeginIndex(); + + int current = ci.getIndex(); + int begin = ci.getBeginIndex(); + int start = current; + if (start == begin) + return begin; + Map runValues = getAttributes(); + int prev = start - 1; + while (start > begin) + { + Iterator iterator = attributeSet.iterator(); + while (iterator.hasNext()) + { + Attribute attributeKey = (Attribute) iterator.next(); + Object v1 = runValues.get(attributeKey); + Object v2 = getAttribute(attributeKey, prev); + boolean changed = false; + // check for equal or both null, if NO return start + if (v1 != null) + { + changed = !v1.equals(v2); + } + else + { + changed = (v2 != null); + } + if (changed) + return start; + } + // no differences, so decrement start and prev and loop again + start--; + prev--; + } + return start; + } + + /*************************************************************************/ + + /** + * Returns the value for an attribute at the specified position. If the + * attribute key (key) is null, the method returns + * null. + * + * @param key the key (null permitted). + * @param pos the character position. + * + * @return The attribute value (possibly null). + */ + private Object getAttribute(AttributedCharacterIterator.Attribute key, + int pos) + { + if (attribs == null) + return null; + for (int i = attribs.length - 1; i >= 0; i--) + { + if (pos >= attribs[i].beginIndex && pos < attribs[i].endIndex) + { + Set keys = attribs[i].attribs.keySet(); + if (keys.contains(key)) + { + return attribs[i].attribs.get(key); + } + } + } + return null; + } + + /** + * Returns the value for an attribute at the current position. If the + * attribute key (key) is null, the method returns + * null. + * + * @param key the key (null permitted). + * + * @return The attribute value (possibly null). + */ + public Object getAttribute(AttributedCharacterIterator.Attribute key) + { + return getAttribute(key, ci.getIndex()); + } + + /*************************************************************************/ + + /** + * Return a list of all the attributes and values defined for this + * character + */ + public Map getAttributes() + { + HashMap m = new HashMap(); + if (attribs == null) + return(m); + + for (int i = 0; i < attribs.length; i++) + { + if ((ci.getIndex() >= attribs[i].beginIndex) && + (ci.getIndex() < attribs[i].endIndex)) + m.putAll(attribs[i].attribs); + } + + return(m); + } + +} // class AttributedStringIterator diff --git a/libjava/classpath/java/text/Bidi.java b/libjava/classpath/java/text/Bidi.java new file mode 100644 index 000000000..6a7bd0750 --- /dev/null +++ b/libjava/classpath/java/text/Bidi.java @@ -0,0 +1,1001 @@ +/* Bidi.java -- Bidirectional Algorithm implementation + Copyright (C) 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.awt.font.NumericShaper; +import java.awt.font.TextAttribute; +import java.util.ArrayList; + + +/** + * Bidirectional Algorithm implementation. + * + * The full algorithm is + * Unicode Standard + * Annex #9: The Bidirectional Algorithm. + * + * @since 1.4 + */ +public final class Bidi +{ + /** + * This indicates that a strongly directional character in the text should + * set the initial direction, but if no such character is found, then the + * initial direction will be left-to-right. + */ + public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2; + + /** + * This indicates that a strongly directional character in the text should + * set the initial direction, but if no such character is found, then the + * initial direction will be right-to-left. + */ + public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1; + + /** + * This indicates that the initial direction should be left-to-right. + */ + public static final int DIRECTION_LEFT_TO_RIGHT = 0; + + /** + * This indicates that the initial direction should be right-to-left. + */ + public static final int DIRECTION_RIGHT_TO_LEFT = 1; + + // Flags used when computing the result. + private static final int LTOR = 1 << DIRECTION_LEFT_TO_RIGHT; + private static final int RTOL = 1 << DIRECTION_RIGHT_TO_LEFT; + + // The text we are examining, and the starting offset. + // If we had a better way to handle createLineBidi, we wouldn't + // need this at all -- which for the String case would be an + // efficiency win. + private char[] text; + private int textOffset; + // The embeddings corresponding to the text, and the starting offset. + private byte[] embeddings; + private int embeddingOffset; + // The length of the text (and embeddings) to use. + private int length; + // The flags. + private int flags; + + // All instance fields following this point are initialized + // during analysis. Fields before this must be set by the constructor. + + // The initial embedding level. + private int baseEmbedding; + // The type of each character in the text. + private byte[] types; + // The levels we compute. + private byte[] levels; + + // A list of indices where a formatting code was found. These + // are indicies into the original text -- not into the text after + // the codes have been removed. + private ArrayList formatterIndices; + + // Indices of the starts of runs in the text. + private int[] runs; + + // A convenience field where we keep track of what kinds of runs + // we've seen. + private int resultFlags; + + /** + * Create a new Bidi object given an attributed character iterator. + * This constructor will examine various attributes of the text: + *
          + *
        • {@link TextAttribute#RUN_DIRECTION} is used to determine the + * paragraph's base embedding level. This constructor will recognize + * either {@link TextAttribute#RUN_DIRECTION_LTR} or + * {@link TextAttribute#RUN_DIRECTION_RTL}. If neither is given, + * {@link #DIRECTION_DEFAULT_LEFT_TO_RIGHT} is assumed. + *
        • + * + *
        • If {@link TextAttribute#NUMERIC_SHAPING} is seen, then numeric + * shaping will be done before the Bidi algorithm is run. + *
        • + * + *
        • If {@link TextAttribute#BIDI_EMBEDDING} is seen on a given + * character, then the value of this attribute will be used as an + * embedding level override. + *
        • + *
        + * @param iter the attributed character iterator to use + */ + public Bidi(AttributedCharacterIterator iter) + { + // If set, this attribute should be set on all characters. + // We don't check this (should we?) but we do assume that we + // can simply examine the first character. + Object val = iter.getAttribute(TextAttribute.RUN_DIRECTION); + if (val == TextAttribute.RUN_DIRECTION_LTR) + this.flags = DIRECTION_LEFT_TO_RIGHT; + else if (val == TextAttribute.RUN_DIRECTION_RTL) + this.flags = DIRECTION_RIGHT_TO_LEFT; + else + this.flags = DIRECTION_DEFAULT_LEFT_TO_RIGHT; + + // Likewise this attribute should be specified on the whole text. + // We read it here and then, if it is set, we apply the numeric shaper + // to the text before processing it. + NumericShaper shaper = null; + val = iter.getAttribute(TextAttribute.NUMERIC_SHAPING); + if (val instanceof NumericShaper) + shaper = (NumericShaper) val; + + char[] text = new char[iter.getEndIndex() - iter.getBeginIndex()]; + this.embeddings = new byte[this.text.length]; + this.embeddingOffset = 0; + this.length = text.length; + for (int i = 0; i < this.text.length; ++i) + { + this.text[i] = iter.current(); + + val = iter.getAttribute(TextAttribute.BIDI_EMBEDDING); + if (val instanceof Integer) + { + int ival = ((Integer) val).intValue(); + byte bval; + if (ival < -62 || ival > 62) + bval = 0; + else + bval = (byte) ival; + this.embeddings[i] = bval; + } + } + + // Invoke the numeric shaper, if specified. + if (shaper != null) + shaper.shape(this.text, 0, this.length); + + runBidi(); + } + + /** + * Create a new Bidi object with the indicated text and, possibly, explicit + * embedding settings. + * + * If the embeddings array is null, it is ignored. Otherwise it is taken to + * be explicit embedding settings corresponding to the text. Positive values + * from 1 to 61 are embedding levels, and negative values from -1 to -61 are + * embedding overrides. (FIXME: not at all clear what this really means.) + * + * @param text the text to use + * @param offset the offset of the first character of the text + * @param embeddings the explicit embeddings, or null if there are none + * @param embedOffset the offset of the first embedding value to use + * @param length the length of both the text and the embeddings + * @param flags a flag indicating the base embedding direction + */ + public Bidi(char[] text, int offset, byte[] embeddings, int embedOffset, + int length, int flags) + { + if (flags != DIRECTION_DEFAULT_LEFT_TO_RIGHT + && flags != DIRECTION_DEFAULT_RIGHT_TO_LEFT + && flags != DIRECTION_LEFT_TO_RIGHT + && flags != DIRECTION_RIGHT_TO_LEFT) + throw new IllegalArgumentException("unrecognized 'flags' argument: " + + flags); + this.text = text; + this.textOffset = offset; + this.embeddings = embeddings; + this.embeddingOffset = embedOffset; + this.length = length; + this.flags = flags; + + runBidi(); + } + + /** + * Create a new Bidi object using the contents of the given String + * as the text. + * @param text the text to use + * @param flags a flag indicating the base embedding direction + */ + public Bidi(String text, int flags) + { + if (flags != DIRECTION_DEFAULT_LEFT_TO_RIGHT + && flags != DIRECTION_DEFAULT_RIGHT_TO_LEFT + && flags != DIRECTION_LEFT_TO_RIGHT + && flags != DIRECTION_RIGHT_TO_LEFT) + throw new IllegalArgumentException("unrecognized 'flags' argument: " + + flags); + + // This is inefficient, but it isn't clear whether it matters. + // If it does we can change our implementation a bit to allow either + // a String or a char[]. + this.text = text.toCharArray(); + this.textOffset = 0; + this.embeddings = null; + this.embeddingOffset = 0; + this.length = text.length(); + this.flags = flags; + + runBidi(); + } + + /** + * Implementation function which computes the initial type of + * each character in the input. + */ + private void computeTypes() + { + types = new byte[length]; + for (int i = 0; i < length; ++i) + types[i] = Character.getDirectionality(text[textOffset + i]); + } + + /** + * An internal function which implements rules P2 and P3. + * This computes the base embedding level. + * @return the paragraph's base embedding level + */ + private int computeParagraphEmbeddingLevel() + { + // First check to see if the user supplied a directionality override. + if (flags == DIRECTION_LEFT_TO_RIGHT + || flags == DIRECTION_RIGHT_TO_LEFT) + return flags; + + // This implements rules P2 and P3. + // (Note that we don't need P1, as the user supplies + // a paragraph.) + for (int i = 0; i < length; ++i) + { + int dir = types[i]; + if (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT) + return DIRECTION_LEFT_TO_RIGHT; + if (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT + || dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT) + return DIRECTION_RIGHT_TO_LEFT; + } + return (flags == DIRECTION_DEFAULT_LEFT_TO_RIGHT + ? DIRECTION_LEFT_TO_RIGHT + : DIRECTION_RIGHT_TO_LEFT); + } + + /** + * An internal function which implements rules X1 through X9. + * This computes the initial levels for the text, handling + * explicit overrides and embeddings. + */ + private void computeExplicitLevels() + { + levels = new byte[length]; + byte currentEmbedding = (byte) baseEmbedding; + // The directional override is a Character directionality + // constant. -1 means there is no override. + byte directionalOverride = -1; + // The stack of pushed embeddings, and the stack pointer. + // Note that because the direction is inherent in the depth, + // and because we have a bit left over in a byte, we can encode + // the override, if any, directly in this value on the stack. + final int MAX_DEPTH = 62; + byte[] embeddingStack = new byte[MAX_DEPTH]; + int sp = 0; + + for (int i = 0; i < length; ++i) + { + // If we see an explicit embedding, we use that, even if + // the current character is itself a directional override. + if (embeddings != null && embeddings[embeddingOffset + i] != 0) + { + // It isn't at all clear what we're supposed to do here. + // What does a negative value really mean? + // Should we push on the embedding stack here? + currentEmbedding = embeddings[embeddingOffset + i]; + if (currentEmbedding < 0) + { + currentEmbedding = (byte) -currentEmbedding; + directionalOverride + = (((currentEmbedding % 2) == 0) + ? Character.DIRECTIONALITY_LEFT_TO_RIGHT + : Character.DIRECTIONALITY_RIGHT_TO_LEFT); + } + else + directionalOverride = -1; + continue; + } + // No explicit embedding. + boolean isLtoR = false; + boolean isSpecial = true; + switch (types[i]) + { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + isLtoR = true; + // Fall through. + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + { + byte newEmbedding; + if (isLtoR) + { + // Least greater even. + newEmbedding = (byte) ((currentEmbedding & ~1) + 2); + } + else + { + // Least greater odd. + newEmbedding = (byte) ((currentEmbedding + 1) | 1); + } + // FIXME: we don't properly handle invalid pushes. + if (newEmbedding < MAX_DEPTH) + { + // The new level is valid. Push the old value. + // See above for a comment on the encoding here. + if (directionalOverride != -1) + currentEmbedding |= Byte.MIN_VALUE; + embeddingStack[sp++] = currentEmbedding; + currentEmbedding = newEmbedding; + if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE) + directionalOverride = Character.DIRECTIONALITY_LEFT_TO_RIGHT; + else if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE) + directionalOverride = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + else + directionalOverride = -1; + } + } + break; + case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT: + { + // FIXME: we don't properly handle a pop with a corresponding + // invalid push. + if (sp == 0) + { + // We saw a pop without a push. Just ignore it. + break; + } + byte newEmbedding = embeddingStack[--sp]; + currentEmbedding = (byte) (newEmbedding & 0x7f); + if (newEmbedding < 0) + directionalOverride + = (((newEmbedding & 1) == 0) + ? Character.DIRECTIONALITY_LEFT_TO_RIGHT + : Character.DIRECTIONALITY_RIGHT_TO_LEFT); + else + directionalOverride = -1; + } + break; + default: + isSpecial = false; + break; + } + levels[i] = currentEmbedding; + if (isSpecial) + { + // Mark this character for removal. + if (formatterIndices == null) + formatterIndices = new ArrayList(); + formatterIndices.add(Integer.valueOf(i)); + } + else if (directionalOverride != -1) + types[i] = directionalOverride; + } + + // Remove the formatting codes and update both the arrays + // and 'length'. It would be more efficient not to remove + // these codes, but it is also more complicated. Also, the + // Unicode algorithm reference does not properly describe + // how this is to be done -- from what I can tell, their suggestions + // in this area will not yield the correct results. + if (formatterIndices == null) + return; + int output = 0, input = 0; + final int size = formatterIndices.size(); + for (int i = 0; i <= size; ++i) + { + int nextFmt; + if (i == size) + nextFmt = length; + else + nextFmt = ((Integer) formatterIndices.get(i)).intValue(); + // Non-formatter codes are from 'input' to 'nextFmt'. + int len = nextFmt - input; + System.arraycopy(levels, input, levels, output, len); + System.arraycopy(types, input, types, output, len); + output += len; + input = nextFmt + 1; + } + length -= formatterIndices.size(); + } + + /** + * An internal function to compute the boundaries of runs + * in the text. It isn't strictly necessary to do this, but + * it lets us write some following passes in a less complicated + * way. Also it lets us efficiently implement some of the public + * methods. A run is simply a sequence of characters at the + * same level. + */ + private void computeRuns() + { + int runCount = 0; + int currentEmbedding = baseEmbedding; + for (int i = 0; i < length; ++i) + { + if (levels[i] != currentEmbedding) + { + currentEmbedding = levels[i]; + ++runCount; + } + } + + // This may be called multiple times. If so, and if + // the number of runs has not changed, then don't bother + // allocating a new array. + if (runs == null || runs.length != runCount + 1) + runs = new int[runCount + 1]; + int where = 0; + int lastRunStart = 0; + currentEmbedding = baseEmbedding; + for (int i = 0; i < length; ++i) + { + if (levels[i] != currentEmbedding) + { + runs[where++] = lastRunStart; + lastRunStart = i; + currentEmbedding = levels[i]; + } + } + runs[where++] = lastRunStart; + } + + /** + * An internal method to resolve weak types. This implements + * rules W1 through W7. + */ + private void resolveWeakTypes() + { + final int runCount = getRunCount(); + + int previousLevel = baseEmbedding; + for (int run = 0; run < runCount; ++run) + { + int start = getRunStart(run); + int end = getRunLimit(run); + int level = getRunLevel(run); + + // These are the names used in the Bidi algorithm. + byte sor = (((Math.max(previousLevel, level) % 2) == 0) + ? Character.DIRECTIONALITY_LEFT_TO_RIGHT + : Character.DIRECTIONALITY_RIGHT_TO_LEFT); + int nextLevel; + if (run == runCount - 1) + nextLevel = baseEmbedding; + else + nextLevel = getRunLevel(run + 1); + byte eor = (((Math.max(level, nextLevel) % 2) == 0) + ? Character.DIRECTIONALITY_LEFT_TO_RIGHT + : Character.DIRECTIONALITY_RIGHT_TO_LEFT); + + byte prevType = sor; + byte prevStrongType = sor; + for (int i = start; i < end; ++i) + { + final byte nextType = (i == end - 1) ? eor : types[i + 1]; + + // Rule W1: change NSM to the prevailing direction. + if (types[i] == Character.DIRECTIONALITY_NONSPACING_MARK) + types[i] = prevType; + else + prevType = types[i]; + + // Rule W2: change EN to AN in some cases. + if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + { + if (prevStrongType == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + types[i] = Character.DIRECTIONALITY_ARABIC_NUMBER; + } + else if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT + || types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT + || types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + prevStrongType = types[i]; + + // Rule W3: change AL to R. + if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + types[i] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + + // Rule W4: handle separators between two numbers. + if (prevType == Character.DIRECTIONALITY_EUROPEAN_NUMBER + && nextType == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + { + if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR + || types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) + types[i] = nextType; + } + else if (prevType == Character.DIRECTIONALITY_ARABIC_NUMBER + && nextType == Character.DIRECTIONALITY_ARABIC_NUMBER + && types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) + types[i] = nextType; + + // Rule W5: change a sequence of european terminators to + // european numbers, if they are adjacent to european numbers. + // We also include BN characters in this. + if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR + || types[i] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL) + { + if (prevType == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + types[i] = prevType; + else + { + // Look ahead to see if there is an EN terminating this + // sequence of ETs. + int j = i + 1; + while (j < end + && (types[j] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR + || types[j] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL)) + ++j; + if (j < end + && types[j] == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + { + // Change them all to EN now. + for (int k = i; k < j; ++k) + types[k] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + } + } + } + + // Rule W6: separators and terminators change to ON. + // Again we include BN. + if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR + || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR + || types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR + || types[i] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL) + types[i] = Character.DIRECTIONALITY_OTHER_NEUTRALS; + + // Rule W7: change european number types. + if (prevStrongType == Character.DIRECTIONALITY_LEFT_TO_RIGHT + && types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + types[i] = prevStrongType; + } + + previousLevel = level; + } + } + + /** + * An internal method to resolve neutral types. This implements + * rules N1 and N2. + */ + private void resolveNeutralTypes() + { + // This implements rules N1 and N2. + final int runCount = getRunCount(); + + int previousLevel = baseEmbedding; + for (int run = 0; run < runCount; ++run) + { + int start = getRunStart(run); + int end = getRunLimit(run); + int level = getRunLevel(run); + + byte embeddingDirection + = (((level % 2) == 0) ? Character.DIRECTIONALITY_LEFT_TO_RIGHT + : Character.DIRECTIONALITY_RIGHT_TO_LEFT); + // These are the names used in the Bidi algorithm. + byte sor = (((Math.max(previousLevel, level) % 2) == 0) + ? Character.DIRECTIONALITY_LEFT_TO_RIGHT + : Character.DIRECTIONALITY_RIGHT_TO_LEFT); + int nextLevel; + if (run == runCount - 1) + nextLevel = baseEmbedding; + else + nextLevel = getRunLevel(run + 1); + byte eor = (((Math.max(level, nextLevel) % 2) == 0) + ? Character.DIRECTIONALITY_LEFT_TO_RIGHT + : Character.DIRECTIONALITY_RIGHT_TO_LEFT); + + byte prevStrong = sor; + int neutralStart = -1; + for (int i = start; i <= end; ++i) + { + byte newStrong = -1; + byte thisType = i == end ? eor : types[i]; + switch (thisType) + { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + newStrong = Character.DIRECTIONALITY_LEFT_TO_RIGHT; + break; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_ARABIC_NUMBER: + case Character.DIRECTIONALITY_EUROPEAN_NUMBER: + newStrong = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + break; + case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL: + case Character.DIRECTIONALITY_OTHER_NEUTRALS: + case Character.DIRECTIONALITY_SEGMENT_SEPARATOR: + case Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR: + case Character.DIRECTIONALITY_WHITESPACE: + if (neutralStart == -1) + neutralStart = i; + break; + } + // If we see a strong character, update all the neutrals. + if (newStrong != -1) + { + if (neutralStart != -1) + { + byte override = (prevStrong == newStrong + ? prevStrong + : embeddingDirection); + for (int j = neutralStart; j < i; ++j) + types[j] = override; + } + prevStrong = newStrong; + neutralStart = -1; + } + } + + previousLevel = level; + } + } + + /** + * An internal method to resolve implicit levels. + * This implements rules I1 and I2. + */ + private void resolveImplicitLevels() + { + // This implements rules I1 and I2. + for (int i = 0; i < length; ++i) + { + if ((levels[i] & 1) == 0) + { + if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT) + ++levels[i]; + else if (types[i] == Character.DIRECTIONALITY_ARABIC_NUMBER + || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + levels[i] += 2; + } + else + { + if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT + || types[i] == Character.DIRECTIONALITY_ARABIC_NUMBER + || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + ++levels[i]; + } + + // Update the result flags. + resultFlags |= 1 << (levels[i] & 1); + } + // One final update of the result flags, using the base level. + resultFlags |= 1 << baseEmbedding; + } + + /** + * This reinserts the formatting codes that we removed early on. + * Actually it does not insert formatting codes per se, but rather + * simply inserts new levels at the appropriate locations in the + * 'levels' array. + */ + private void reinsertFormattingCodes() + { + if (formatterIndices == null) + return; + int input = length; + int output = levels.length; + // Process from the end as we are copying the array over itself here. + for (int index = formatterIndices.size() - 1; index >= 0; --index) + { + int nextFmt = ((Integer) formatterIndices.get(index)).intValue(); + + // nextFmt points to a location in the original array. So, + // nextFmt+1 is the target of our copying. output is the location + // to which we last copied, thus we can derive the length of the + // copy from it. + int len = output - nextFmt - 1; + output = nextFmt; + input -= len; + // Note that we no longer need 'types' at this point, so we + // only edit 'levels'. + if (nextFmt + 1 < levels.length) + System.arraycopy(levels, input, levels, nextFmt + 1, len); + + // Now set the level at the reinsertion point. + int rightLevel; + if (output == levels.length - 1) + rightLevel = baseEmbedding; + else + rightLevel = levels[output + 1]; + int leftLevel; + if (input == 0) + leftLevel = baseEmbedding; + else + leftLevel = levels[input]; + levels[output] = (byte) Math.max(leftLevel, rightLevel); + } + length = levels.length; + } + + /** + * This is the main internal entry point. After a constructor + * has initialized the appropriate local state, it will call + * this method to do all the work. + */ + private void runBidi() + { + computeTypes(); + baseEmbedding = computeParagraphEmbeddingLevel(); + computeExplicitLevels(); + computeRuns(); + resolveWeakTypes(); + resolveNeutralTypes(); + resolveImplicitLevels(); + // We're done with the types. Let the GC clean up. + types = null; + reinsertFormattingCodes(); + // After resolving the implicit levels, the number + // of runs may have changed. + computeRuns(); + } + + /** + * Return true if the paragraph base embedding is left-to-right, + * false otherwise. + */ + public boolean baseIsLeftToRight() + { + return baseEmbedding == DIRECTION_LEFT_TO_RIGHT; + } + + /** + * Create a new Bidi object for a single line of text, taken + * from the text used when creating the current Bidi object. + * @param start the index of the first character of the line + * @param end the index of the final character of the line + * @return a new Bidi object for the indicated line of text + */ + public Bidi createLineBidi(int start, int end) + { + // This isn't the most efficient implementation possible. + // This probably does not matter, so we choose simplicity instead. + int level = getLevelAt(start); + int flag = (((level % 2) == 0) + ? DIRECTION_LEFT_TO_RIGHT + : DIRECTION_RIGHT_TO_LEFT); + return new Bidi(text, textOffset + start, + embeddings, embeddingOffset + start, + end - start, flag); + } + + /** + * Return the base embedding level of the paragraph. + */ + public int getBaseLevel() + { + return baseEmbedding; + } + + /** + * Return the length of the paragraph, in characters. + */ + public int getLength() + { + return length; + } + + /** + * Return the level at the indicated character. If the + * supplied index is less than zero or greater than the length + * of the text, then the paragraph's base embedding level will + * be returned. + * @param offset the character to examine + * @return the level of that character + */ + public int getLevelAt(int offset) + { + if (offset < 0 || offset >= length) + return getBaseLevel(); + return levels[offset]; + } + + /** + * Return the number of runs in the result. A run is + * a sequence of characters at the same embedding level. + */ + public int getRunCount() + { + return runs.length; + } + + /** + * Return the level of the indicated run. + * @param which the run to examine + * @return the level of that run + */ + public int getRunLevel(int which) + { + return levels[runs[which]]; + } + + /** + * Return the index of the character just following the end + * of the indicated run. + * @param which the run to examine + * @return the index of the character after the final character + * of the run + */ + public int getRunLimit(int which) + { + if (which == runs.length - 1) + return length; + return runs[which + 1]; + } + + /** + * Return the index of the first character in the indicated run. + * @param which the run to examine + * @return the index of the first character of the run + */ + public int getRunStart(int which) + { + return runs[which]; + } + + /** + * Return true if the text is entirely left-to-right, and the + * base embedding is also left-to-right. + */ + public boolean isLeftToRight() + { + return resultFlags == LTOR; + } + + /** + * Return true if the text consists of mixed left-to-right and + * right-to-left runs, or if the text consists of one kind of run + * which differs from the base embedding direction. + */ + public boolean isMixed() + { + return resultFlags == (LTOR | RTOL); + } + + /** + * Return true if the text is entirely right-to-left, and the + * base embedding is also right-to-left. + */ + public boolean isRightToLeft() + { + return resultFlags == RTOL; + } + + /** + * Return a String describing the internal state of this object. + * This is only useful for debugging. + */ + public String toString() + { + return "Bidi Bidi Bidi I like you, Buck!"; + } + + /** + * Reorder objects according to the levels passed in. This implements + * reordering as defined by the Unicode bidirectional layout specification. + * The levels are integers from 0 to 62; even numbers represent left-to-right + * runs, and odd numbers represent right-to-left runs. + * + * @param levels the levels associated with each object + * @param levelOffset the index of the first level to use + * @param objs the objects to reorder according to the levels + * @param objOffset the index of the first object to use + * @param count the number of objects (and levels) to manipulate + */ + public static void reorderVisually(byte[] levels, int levelOffset, + Object[] objs, int objOffset, int count) + { + // We need a copy of the 'levels' array, as we are going to modify it. + // This is unfortunate but difficult to avoid. + byte[] levelCopy = new byte[count]; + // Do this explicitly so we can also find the maximum depth at the + // same time. + int max = 0; + int lowestOdd = 63; + for (int i = 0; i < count; ++i) + { + levelCopy[i] = levels[levelOffset + i]; + max = Math.max(levelCopy[i], max); + if (levelCopy[i] % 2 != 0) + lowestOdd = Math.min(lowestOdd, levelCopy[i]); + } + + // Reverse the runs starting with the deepest. + for (int depth = max; depth >= lowestOdd; --depth) + { + int start = 0; + while (start < count) + { + // Find the start of a run >= DEPTH. + while (start < count && levelCopy[start] < depth) + ++start; + if (start == count) + break; + // Find the end of the run. + int end = start + 1; + while (end < count && levelCopy[end] >= depth) + ++end; + + // Reverse this run. + for (int i = 0; i < (end - start) / 2; ++i) + { + byte tmpb = levelCopy[end - i - 1]; + levelCopy[end - i - 1] = levelCopy[start + i]; + levelCopy[start + i] = tmpb; + Object tmpo = objs[objOffset + end - i - 1]; + objs[objOffset + end - i - 1] = objs[objOffset + start + i]; + objs[objOffset + start + i] = tmpo; + } + + // Handle the next run. + start = end + 1; + } + } + } + + /** + * Returns false if all characters in the text between start and end + * are all left-to-right text. This implementation is just calls + * Character.getDirectionality(char) on all characters + * and makes sure all characters are either explicitly left-to-right + * or neutral in directionality (character types L, EN, ES, ET, AN, + * CS, S and WS). + */ + public static boolean requiresBidi(char[] text, int start, int end) + { + for (int i = start; i < end; i++) + { + byte dir = Character.getDirectionality(text[i]); + if (dir != Character.DIRECTIONALITY_LEFT_TO_RIGHT + && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER + && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR + && dir != Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR + && dir != Character.DIRECTIONALITY_ARABIC_NUMBER + && dir != Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR + && dir != Character.DIRECTIONALITY_SEGMENT_SEPARATOR + && dir != Character.DIRECTIONALITY_WHITESPACE + && dir != Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR) + return true; + } + + return false; + } +} diff --git a/libjava/classpath/java/text/BreakIterator.java b/libjava/classpath/java/text/BreakIterator.java new file mode 100644 index 000000000..628cb7235 --- /dev/null +++ b/libjava/classpath/java/text/BreakIterator.java @@ -0,0 +1,444 @@ +/* BreakIterator.java -- Breaks text into elements + Copyright (C) 1998, 1999, 2001, 2004, 2005, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.locale.LocaleHelper; + +import gnu.java.text.CharacterBreakIterator; +import gnu.java.text.LineBreakIterator; +import gnu.java.text.SentenceBreakIterator; +import gnu.java.text.WordBreakIterator; + +import java.text.spi.BreakIteratorProvider; + +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.ServiceLoader; + +/** + * This class iterates over text elements such as words, lines, sentences, + * and characters. It can only iterate over one of these text elements at + * a time. An instance of this class configured for the desired iteration + * type is created by calling one of the static factory methods, not + * by directly calling a constructor. + * + * The standard iterators created by the factory methods in this + * class will be valid upon creation. That is, their methods will + * not cause exceptions if called before you call setText(). + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date March 19, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct to 1.1. + */ +public abstract class BreakIterator implements Cloneable +{ + /** + * This value is returned by the next() and + * previous in order to indicate that the end of the + * text has been reached. + */ + // The value was discovered by writing a test program. + public static final int DONE = -1; + + /** + * This method initializes a new instance of BreakIterator. + * This protected constructor is available to subclasses as a default + * no-arg superclass constructor. + */ + protected BreakIterator () + { + } + + /** + * Create a clone of this object. + */ + public Object clone () + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException e) + { + return null; + } + } + + /** + * This method returns the index of the current text element boundary. + * + * @return The current text boundary. + */ + public abstract int current (); + + /** + * This method returns the first text element boundary in the text being + * iterated over. + * + * @return The first text boundary. + */ + public abstract int first (); + + /** + * This methdod returns the offset of the text element boundary following + * the specified offset. + * + * @param pos The text index from which to find the next text boundary. + * + * @return The next text boundary following the specified index. + */ + public abstract int following (int pos); + + /** + * This method returns a list of locales for which instances of + * BreakIterator are available. + * + * @return A list of available locales + */ + public static synchronized Locale[] getAvailableLocales () + { + Locale[] l = new Locale[1]; + l[0] = Locale.US; + return l; + } + + private static BreakIterator getInstance (String type, Locale loc) + { + String className; + try + { + ResourceBundle res + = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + className = res.getString(type); + } + catch (MissingResourceException x) + { + return null; + } + try + { + Class k = Class.forName(className); + return (BreakIterator) k.newInstance(); + } + catch (ClassNotFoundException x1) + { + return null; + } + catch (InstantiationException x2) + { + return null; + } + catch (IllegalAccessException x3) + { + return null; + } + } + + /** + * This method returns an instance of BreakIterator that will + * iterate over characters as defined in the default locale. + * + * @return A BreakIterator instance for the default locale. + */ + public static BreakIterator getCharacterInstance () + { + return getCharacterInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of BreakIterator that will + * iterate over characters as defined in the specified locale. + * + * @param locale The desired locale. + * + * @return A BreakIterator instance for the specified locale. + */ + public static BreakIterator getCharacterInstance (Locale locale) + { + BreakIterator r = getInstance("CharacterIterator", locale); + if (r != null) + return r; + for (BreakIteratorProvider p : + ServiceLoader.load(BreakIteratorProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + BreakIterator bi = p.getCharacterInstance(locale); + if (bi != null) + return bi; + break; + } + } + } + if (locale.equals(Locale.ROOT)) + return new CharacterBreakIterator(); + return getCharacterInstance(LocaleHelper.getFallbackLocale(locale)); + } + + /** + * This method returns an instance of BreakIterator that will + * iterate over line breaks as defined in the default locale. + * + * @return A BreakIterator instance for the default locale. + */ + public static BreakIterator getLineInstance () + { + return getLineInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of BreakIterator that will + * iterate over line breaks as defined in the specified locale. + * + * @param locale The desired locale. + * + * @return A BreakIterator instance for the default locale. + */ + public static BreakIterator getLineInstance (Locale locale) + { + BreakIterator r = getInstance ("LineIterator", locale); + if (r != null) + return r; + for (BreakIteratorProvider p : + ServiceLoader.load(BreakIteratorProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + BreakIterator bi = p.getLineInstance(locale); + if (bi != null) + return bi; + break; + } + } + } + if (locale.equals(Locale.ROOT)) + return new LineBreakIterator(); + return getLineInstance(LocaleHelper.getFallbackLocale(locale)); + } + + /** + * This method returns an instance of BreakIterator that will + * iterate over sentences as defined in the default locale. + * + * @return A BreakIterator instance for the default locale. + */ + public static BreakIterator getSentenceInstance () + { + return getSentenceInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of BreakIterator that will + * iterate over sentences as defined in the specified locale. + * + * @param locale The desired locale. + * + * @return A BreakIterator instance for the default locale. + */ + public static BreakIterator getSentenceInstance (Locale locale) + { + BreakIterator r = getInstance ("SentenceIterator", locale); + if (r != null) + return r; + for (BreakIteratorProvider p : + ServiceLoader.load(BreakIteratorProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + BreakIterator bi = p.getSentenceInstance(locale); + if (bi != null) + return bi; + break; + } + } + } + if (locale.equals(Locale.ROOT)) + return new SentenceBreakIterator(); + return getSentenceInstance(LocaleHelper.getFallbackLocale(locale)); + } + + /** + * This method returns the text this object is iterating over as a + * CharacterIterator. + * + * @return The text being iterated over. + */ + public abstract CharacterIterator getText (); + + /** + * This method returns an instance of BreakIterator that will + * iterate over words as defined in the default locale. + * + * @return A BreakIterator instance for the default locale. + */ + public static BreakIterator getWordInstance () + { + return getWordInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of BreakIterator that will + * iterate over words as defined in the specified locale. + * + * @param locale The desired locale. + * + * @return A BreakIterator instance for the default locale. + */ + public static BreakIterator getWordInstance (Locale locale) + { + BreakIterator r = getInstance ("WordIterator", locale); + if (r != null) + return r; + for (BreakIteratorProvider p : + ServiceLoader.load(BreakIteratorProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + BreakIterator bi = p.getWordInstance(locale); + if (bi != null) + return bi; + break; + } + } + } + if (locale.equals(Locale.ROOT)) + return new WordBreakIterator(); + return getWordInstance(LocaleHelper.getFallbackLocale(locale)); + } + + /** + * This method tests whether or not the specified position is a text + * element boundary. + * + * @param pos The text position to test. + * + * @return true if the position is a boundary, + * false otherwise. + */ + public boolean isBoundary (int pos) + { + if (pos == 0) + return true; + return following (pos - 1) == pos; + } + + /** + * This method returns the last text element boundary in the text being + * iterated over. + * + * @return The last text boundary. + */ + public abstract int last (); + + /** + * This method returns the text element boundary following the current + * text position. + * + * @return The next text boundary. + */ + public abstract int next (); + + /** + * This method returns the n'th text element boundary following the current + * text position. + * + * @param n The number of text element boundaries to skip. + * + * @return The next text boundary. + */ + public abstract int next (int n); + + /** + * This methdod returns the offset of the text element boundary preceding + * the specified offset. + * + * @param pos The text index from which to find the preceding text boundary. + * + * @returns The next text boundary preceding the specified index. + */ + public int preceding (int pos) + { + if (following (pos) == DONE) + last (); + while (previous () >= pos) + ; + return current (); + } + + /** + * This method returns the text element boundary preceding the current + * text position. + * + * @return The previous text boundary. + */ + public abstract int previous (); + + /** + * This method sets the text string to iterate over. + * + * @param newText The String to iterate over. + */ + public void setText (String newText) + { + setText (new StringCharacterIterator (newText)); + } + + /** + * This method sets the text to iterate over from the specified + * CharacterIterator. + * + * @param newText The desired CharacterIterator. + */ + public abstract void setText (CharacterIterator newText); +} diff --git a/libjava/classpath/java/text/CharacterIterator.java b/libjava/classpath/java/text/CharacterIterator.java new file mode 100644 index 000000000..e202d91df --- /dev/null +++ b/libjava/classpath/java/text/CharacterIterator.java @@ -0,0 +1,148 @@ +/* CharacterIterator.java -- Iterate over a character range + Copyright (C) 1998, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This interface defines a mechanism for iterating over a range of + * characters. For a given range of text, a beginning and ending index, + * as well as a current index are defined. These values can be queried + * by the methods in this interface. Additionally, various methods allow + * the index to be set. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface CharacterIterator extends Cloneable +{ + /** + * This is a special constant value that is returned when the beginning or + * end of the character range has been reached. + */ + char DONE = '\uFFFF'; + + /** + * This method returns the character at the current index position + * + * @return The character at the current index position. + */ + char current(); + + /** + * This method increments the current index and then returns the character + * at the new index value. If the index is already at + * getEndIndex() - 1, it will not be incremented. + * + * @return The character at the position of the incremented index value, + * or {@link #DONE} if the index has reached getEndIndex() - 1 + */ + char next(); + + /** + * This method decrements the current index and then returns the character + * at the new index value. If the index value is already at the beginning + * index, it will not be decremented. + * + * @return The character at the position of the decremented index value, + * or {@link #DONE} if index was already equal to the beginning index + * value. + */ + char previous(); + + /** + * This method sets the index value to the beginning of the range and returns + * the character there. + * + * @return The character at the beginning of the range, or {@link #DONE} if + * the range is empty. + */ + char first(); + + /** + * This method sets the index value to getEndIndex() - 1 and + * returns the character there. If the range is empty, then the index value + * will be set equal to the beginning index. + * + * @return The character at the end of the range, or {@link #DONE} if the + * range is empty. + */ + char last(); + + /** + * This method returns the current value of the index. + * + * @return The current index value + */ + int getIndex(); + + /** + * This method sets the value of the index to the specified value, then + * returns the character at that position. + * + * @param index The new index value. + * + * @return The character at the new index value or {@link #DONE} if the index + * value is equal to {@link #getEndIndex()}. + */ + char setIndex (int index) throws IllegalArgumentException; + + /** + * This method returns the character position of the first character in the + * range. + * + * @return The index of the first character in the range. + */ + int getBeginIndex(); + + /** + * This method returns the character position of the end of the text range. + * This will actually be the index of the first character following the + * end of the range. In the event the text range is empty, this will be + * equal to the first character in the range. + * + * @return The index of the end of the range. + */ + int getEndIndex(); + + /** + * This method creates a copy of this CharacterIterator. + * + * @return A copy of this CharacterIterator. + */ + Object clone(); + +} // interface CharacterIterator diff --git a/libjava/classpath/java/text/ChoiceFormat.java b/libjava/classpath/java/text/ChoiceFormat.java new file mode 100644 index 000000000..4842f491d --- /dev/null +++ b/libjava/classpath/java/text/ChoiceFormat.java @@ -0,0 +1,506 @@ +/* ChoiceFormat.java -- Format over a range of numbers + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.lang.CPStringBuilder; + +import java.util.Vector; + +/** + * This class allows a format to be specified based on a range of numbers. + * To use this class, first specify two lists of formats and range terminators. + * These lists must be arrays of equal length. The format of index + * i will be selected for value X if + * terminator[i] <= X < limit[i + 1]. If the value X is not + * included in any range, then either the first or last format will be + * used depending on whether the value X falls outside the range. + *

        + * This sounds complicated, but that is because I did a poor job of + * explaining it. Consider the following example: + *

        + * +

        terminators = { 1, ChoiceFormat.nextDouble(1) }
        +formats = { "file", "files" }
        + * + *

        + * In this case if the actual number tested is one or less, then the word + * "file" is used as the format value. If the number tested is greater than + * one, then "files" is used. This allows plurals to be handled + * gracefully. Note the use of the method nextDouble. This + * method selects the next highest double number than its argument. This + * effectively makes any double greater than 1.0 cause the "files" string + * to be selected. (Note that all terminator values are specified as + * doubles. + *

        + * Note that in order for this class to work properly, the range terminator + * array must be sorted in ascending order and the format string array + * must be the same length as the terminator array. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date March 9, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.1. + */ +public class ChoiceFormat extends NumberFormat +{ + /** + * This method sets new range terminators and format strings for this + * object based on the specified pattern. This pattern is of the form + * "term#string|term#string...". For example "1#Sunday|2#Monday|#Tuesday". + * + * @param newPattern The pattern of terminators and format strings. + * + * @exception IllegalArgumentException If the pattern is not valid + */ + public void applyPattern (String newPattern) + { + // Note: we assume the same kind of quoting rules apply here. + // This isn't explicitly documented. But for instance we accept + // '#' as a literal hash in a format string. + int index = 0, max = newPattern.length(); + Vector stringVec = new Vector (); + Vector limitVec = new Vector (); + final CPStringBuilder buf = new CPStringBuilder (); + + while (true) + { + // Find end of double. + int dstart = index; + while (index < max) + { + char c = newPattern.charAt(index); + if (c == '#' || c == '\u2064' || c == '<') + break; + ++index; + } + + if (index == max) + throw new IllegalArgumentException ("unexpected end of text"); + Double d = Double.valueOf (newPattern.substring(dstart, index)); + + if (newPattern.charAt(index) == '<') + d = Double.valueOf (nextDouble (d.doubleValue())); + + limitVec.addElement(d); + + // Scan text. + ++index; + buf.setLength(0); + while (index < max) + { + char c = newPattern.charAt(index); + if (c == '\'' && index < max + 1 + && newPattern.charAt(index + 1) == '\'') + { + buf.append(c); + ++index; + } + else if (c == '\'' && index < max + 2) + { + buf.append(newPattern.charAt(index + 1)); + index += 2; + } + else if (c == '|') + break; + else + buf.append(c); + ++index; + } + + stringVec.addElement(buf.toString()); + if (index == max) + break; + ++index; + } + + choiceFormats = new String[stringVec.size()]; + stringVec.copyInto(choiceFormats); + + choiceLimits = new double[limitVec.size()]; + for (int i = 0; i < choiceLimits.length; ++i) + { + Double d = (Double) limitVec.elementAt(i); + choiceLimits[i] = d.doubleValue(); + } + } + + /** + * This method initializes a new instance of ChoiceFormat that + * generates its range terminator and format string arrays from the + * specified pattern. This pattern is of the form + * "term#string|term#string...". For example "1#Sunday|2#Monday|#Tuesday". + * This is the same pattern type used by the applyPattern + * method. + * + * @param newPattern The pattern of terminators and format strings. + * + * @exception IllegalArgumentException If the pattern is not valid + */ + public ChoiceFormat (String newPattern) + { + super (); + applyPattern (newPattern); + } + + /** + * This method initializes a new instance of ChoiceFormat that + * will use the specified range terminators and format strings. + * + * @param choiceLimits The array of range terminators + * @param choiceFormats The array of format strings + */ + public ChoiceFormat (double[] choiceLimits, String[] choiceFormats) + { + super (); + setChoices (choiceLimits, choiceFormats); + } + + /** + * This method tests this object for equality with the specified + * object. This will be true if and only if: + *

          + *
        • The specified object is not null.
        • + *
        • The specified object is an instance of ChoiceFormat.
        • + *
        • The termination ranges and format strings are identical to + * this object's.
        • + *
        + * + * @param obj The object to test for equality against. + * + * @return true if the specified object is equal to + * this one, false otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof ChoiceFormat)) + return false; + ChoiceFormat cf = (ChoiceFormat) obj; + if (choiceLimits.length != cf.choiceLimits.length) + return false; + for (int i = choiceLimits.length - 1; i >= 0; --i) + { + if (choiceLimits[i] != cf.choiceLimits[i] + || !choiceFormats[i].equals(cf.choiceFormats[i])) + return false; + } + return true; + } + + /** + * This method appends the appropriate format string to the specified + * StringBuffer based on the supplied long + * argument. + * + * @param num The number used for determine (based on the range + * terminators) which format string to append. + * @param appendBuf The StringBuffer to append the format string + * to. + * @param pos Unused. + * + * @return The StringBuffer with the format string appended. + */ + public StringBuffer format (long num, StringBuffer appendBuf, + FieldPosition pos) + { + return format ((double) num, appendBuf, pos); + } + + /** + * This method appends the appropriate format string to the specified + * StringBuffer based on the supplied double + * argument. + * + * @param num The number used for determine (based on the range + * terminators) which format string to append. + * @param appendBuf The StringBuffer to append the format string to. + * @param pos Unused. + * + * @return The StringBuffer with the format string appended. + */ + public StringBuffer format (double num, StringBuffer appendBuf, + FieldPosition pos) + { + if (choiceLimits.length == 0) + return appendBuf; + + int index = 0; + if (! Double.isNaN(num) && num >= choiceLimits[0]) + { + for (; index < choiceLimits.length - 1; ++index) + { + if (choiceLimits[index] <= num && num < choiceLimits[index + 1]) + break; + } + } + + return appendBuf.append(choiceFormats[index]); + } + + /** + * This method returns the list of format strings in use. + * + * @return The list of format objects. + */ + public Object[] getFormats () + { + return (Object[]) choiceFormats.clone(); + } + + /** + * This method returns the list of range terminators in use. + * + * @return The list of range terminators. + */ + public double[] getLimits () + { + return (double[]) choiceLimits.clone(); + } + + /** + * This method returns a hash value for this object + * + * @return A hash value for this object. + */ + public int hashCode () + { + int hash = 0; + for (int i = 0; i < choiceLimits.length; ++i) + { + long v = Double.doubleToLongBits(choiceLimits[i]); + hash ^= (v ^ (v >>> 32)); + hash ^= choiceFormats[i].hashCode(); + } + return hash; + } + + /** + * This method returns the lowest possible double greater than the + * specified double. If the specified double value is equal to + * Double.NaN then that is the value returned. + * + * @param d The specified double + * + * @return The lowest double value greater than the specified double. + */ + public static final double nextDouble (double d) + { + return nextDouble (d, true); + } + + /** + * This method returns a double that is either the next highest double + * or next lowest double compared to the specified double depending on the + * value of the passed boolean parameter. If the boolean parameter is + * true, then the lowest possible double greater than the + * specified double will be returned. Otherwise the highest possible + * double less than the specified double will be returned. + * + * @param d The specified double + * @param next true to return the next highest + * double, false otherwise. + * + * @return The next highest or lowest double value. + */ + public static double nextDouble (double d, boolean next) + { + if (Double.isInfinite(d) || Double.isNaN(d)) + return d; + + long bits = Double.doubleToLongBits(d); + + long mantMask = (1L << mantissaBits) - 1; + long mantissa = bits & mantMask; + + long expMask = (1L << exponentBits) - 1; + long exponent = (bits >>> mantissaBits) & expMask; + + if (next ^ (bits < 0)) // Increment magnitude + { + if (mantissa == (1L << mantissaBits) - 1) + { + mantissa = 0L; + exponent++; + + // Check for absolute overflow. + if (exponent >= (1L << mantissaBits)) + return (bits > 0) ? Double.POSITIVE_INFINITY + : Double.NEGATIVE_INFINITY; + } + else + mantissa++; + } + else // Decrement magnitude + { + if (exponent == 0L && mantissa == 0L) + { + // The only case where there is a change of sign + return next ? Double.MIN_VALUE : -Double.MIN_VALUE; + } + else + { + if (mantissa == 0L) + { + mantissa = (1L << mantissaBits) - 1; + exponent--; + } + else + mantissa--; + } + } + + long result = bits < 0 ? 1 : 0; + result = (result << exponentBits) | exponent; + result = (result << mantissaBits) | mantissa; + return Double.longBitsToDouble(result); + } + + /** + * I'm not sure what this method is really supposed to do, as it is + * not documented. + */ + public Number parse (String sourceStr, ParsePosition pos) + { + int index = pos.getIndex(); + for (int i = 0; i < choiceLimits.length; ++i) + { + if (sourceStr.startsWith(choiceFormats[i], index)) + { + pos.setIndex(index + choiceFormats[i].length()); + return Double.valueOf (choiceLimits[i]); + } + } + pos.setErrorIndex(index); + return Double.valueOf (Double.NaN); + } + + /** + * This method returns the highest possible double less than the + * specified double. If the specified double value is equal to + * Double.NaN then that is the value returned. + * + * @param d The specified double + * + * @return The highest double value less than the specified double. + */ + public static final double previousDouble (double d) + { + return nextDouble (d, false); + } + + /** + * This method sets new range terminators and format strings for this + * object. + * + * @param choiceLimits The new range terminators + * @param choiceFormats The new choice formats + */ + public void setChoices (double[] choiceLimits, String[] choiceFormats) + { + if (choiceLimits == null || choiceFormats == null) + throw new NullPointerException (); + if (choiceLimits.length != choiceFormats.length) + throw new IllegalArgumentException (); + this.choiceFormats = (String[]) choiceFormats.clone(); + this.choiceLimits = (double[]) choiceLimits.clone(); + } + + private void quoteString (CPStringBuilder dest, String text) + { + int max = text.length(); + for (int i = 0; i < max; ++i) + { + char c = text.charAt(i); + if (c == '\'') + { + dest.append(c); + dest.append(c); + } + else if (c == '#' || c == '|' || c == '\u2064' || c == '<') + { + dest.append('\''); + dest.append(c); + dest.append('\''); + } + else + dest.append(c); + } + } + + /** + * This method returns the range terminator list and format string list + * as a String suitable for using with the + * applyPattern method. + * + * @return A pattern string for this object + */ + public String toPattern () + { + CPStringBuilder result = new CPStringBuilder (); + for (int i = 0; i < choiceLimits.length; ++i) + { + result.append(choiceLimits[i]); + result.append('#'); + quoteString (result, choiceFormats[i]); + } + return result.toString(); + } + + /** + * This is the list of format strings. Note that this variable is + * specified by the serialization spec of this class. + */ + private String[] choiceFormats; + + /** + * This is the list of range terminator values. Note that this variable is + * specified by the serialization spec of this class. + */ + private double[] choiceLimits; + + // Number of mantissa bits in double. + private static final int mantissaBits = 52; + // Number of exponent bits in a double. + private static final int exponentBits = 11; + + private static final long serialVersionUID = 1795184449645032964L; +} diff --git a/libjava/classpath/java/text/CollationElementIterator.java b/libjava/classpath/java/text/CollationElementIterator.java new file mode 100644 index 000000000..0ca23d074 --- /dev/null +++ b/libjava/classpath/java/text/CollationElementIterator.java @@ -0,0 +1,490 @@ +/* CollationElementIterator.java -- Walks through collation elements + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.lang.CPStringBuilder; + +import java.util.ArrayList; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to JDK 1.1. + */ + +/** + * This class walks through the character collation elements of a + * String as defined by the collation rules in an instance of + * RuleBasedCollator. There is no public constructor for + * this class. An instance is created by calling the + * getCollationElementIterator method on + * RuleBasedCollator. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Guilhem Lavaux (guilhem.lavaux@free.fr) + */ +public final class CollationElementIterator +{ + /** + * This is a constant value that is returned to indicate that the end of + * the string was encountered. + */ + public static final int NULLORDER = -1; + + /** + * This is the RuleBasedCollator this object was created from. + */ + RuleBasedCollator collator; + + /** + * This is the String that is being iterated over. + */ + CharacterIterator text; + + /** + * This is the index into the collation decomposition where we are currently scanning. + */ + int index; + + /** + * This is the index into the String where we are currently scanning. + */ + int textIndex; + + /** + * Array containing the collation decomposition of the + * text given to the constructor. + */ + private RuleBasedCollator.CollationElement[] text_decomposition; + + /** + * Array containing the index of the specified block. + */ + private int[] text_indexes; + + /** + * This method initializes a new instance of CollationElementIterator + * to iterate over the specified String using the rules in the + * specified RuleBasedCollator. + * + * @param collator The RuleBasedCollation used for calculating collation values + * @param text The String to iterate over. + */ + CollationElementIterator(RuleBasedCollator collator, String text) + { + this.collator = collator; + + setText (text); + } + + /** + * This method initializes a new instance of CollationElementIterator + * to iterate over the specified String using the rules in the + * specified RuleBasedCollator. + * + * @param collator The RuleBasedCollation used for calculating collation values + * @param text The character iterator to iterate over. + */ + CollationElementIterator(RuleBasedCollator collator, CharacterIterator text) + { + this.collator = collator; + + setText (text); + } + + RuleBasedCollator.CollationElement nextBlock() + { + if (index >= text_decomposition.length) + return null; + + RuleBasedCollator.CollationElement e = text_decomposition[index]; + + textIndex = text_indexes[index+1]; + + index++; + + return e; + } + + RuleBasedCollator.CollationElement previousBlock() + { + if (index == 0) + return null; + + index--; + RuleBasedCollator.CollationElement e = text_decomposition[index]; + + textIndex = text_indexes[index+1]; + + return e; + } + + /** + * This method returns the collation ordering value of the next character sequence + * in the string (it may be an extended character following collation rules). + * This method will return NULLORDER if the + * end of the string was reached. + * + * @return The collation ordering value. + */ + public int next() + { + RuleBasedCollator.CollationElement e = nextBlock(); + + if (e == null) + return NULLORDER; + + return e.getValue(); + } + + /** + * This method returns the collation ordering value of the previous character + * in the string. This method will return NULLORDER if the + * beginning of the string was reached. + * + * @return The collation ordering value. + */ + public int previous() + { + RuleBasedCollator.CollationElement e = previousBlock(); + + if (e == null) + return NULLORDER; + + return e.getValue(); + } + + /** + * This method returns the primary order value for the given collation + * value. + * + * @param order The collation value returned from next() or + * previous(). + * + * @return The primary order value of the specified collation value. This is + * the high 16 bits. + */ + public static int primaryOrder(int order) + { + // From the JDK 1.2 spec. + return order >>> 16; + } + + /** + * This method resets the internal position pointer to read from the + * beginning of the String again. + */ + public void reset() + { + index = 0; + textIndex = 0; + } + + /** + * This method returns the secondary order value for the given collation + * value. + * + * @param order The collation value returned from next() or + * previous(). + * + * @return The secondary order value of the specified collation value. This + * is the bits 8-15. + */ + public static short secondaryOrder(int order) + { + // From the JDK 1.2 spec. + return (short) ((order >>> 8) & 255); + } + + /** + * This method returns the tertiary order value for the given collation + * value. + * + * @param order The collation value returned from next() or + * previous(). + * + * @return The tertiary order value of the specified collation value. This + * is the low eight bits. + */ + public static short tertiaryOrder(int order) + { + // From the JDK 1.2 spec. + return (short) (order & 255); + } + + /** + * This method sets the String that it is iterating over + * to the specified String. + * + * @param text The new String to iterate over. + * + * @since 1.2 + */ + public void setText(String text) + { + int idx = 0; + int idx_idx = 0; + int alreadyExpanded = 0; + int idxToMove = 0; + + this.text = new StringCharacterIterator(text); + this.index = 0; + + String work_text = text.intern(); + + ArrayList a_element = new ArrayList(); + ArrayList a_idx = new ArrayList(); + + // Build element collection ordered as they come in "text". + while (idx < work_text.length()) + { + String key, key_old; + + Object object = null; + int p = 1; + + // IMPROVE: use a TreeMap with a prefix-ordering rule. + key_old = key = null; + do + { + if (object != null) + key_old = key; + key = work_text.substring (idx, idx+p); + object = collator.prefix_tree.get (key); + if (object != null && idx < alreadyExpanded) + { + RuleBasedCollator.CollationElement prefix = (RuleBasedCollator.CollationElement)object; + if (prefix.expansion != null && + prefix.expansion.startsWith(work_text.substring(0, idx))) + { + object = null; + key = key_old; + } + } + p++; + } + while (idx+p <= work_text.length()); + + if (object == null) + key = key_old; + + RuleBasedCollator.CollationElement prefix = + (RuleBasedCollator.CollationElement) collator.prefix_tree.get (key); + + /* + * First case: There is no such sequence in the database. + * We will have to build one from the context. + */ + if (prefix == null) + { + /* + * We are dealing with sequences in an expansion. They + * are treated as accented characters (tertiary order). + */ + if (alreadyExpanded > 0) + { + RuleBasedCollator.CollationElement e = + collator.getDefaultAccentedElement (work_text.charAt (idx)); + + a_element.add (e); + a_idx.add (new Integer(idx_idx)); + idx++; + alreadyExpanded--; + if (alreadyExpanded == 0) + { + /* There is not any characters left in the expansion set. + * We can increase the pointer in the source string. + */ + idx_idx += idxToMove; + idxToMove = 0; + } + else + idx_idx++; + } + else + { + /* This is a normal character. */ + RuleBasedCollator.CollationElement e = + collator.getDefaultElement (work_text.charAt (idx)); + Integer i_ref = new Integer(idx_idx); + + /* Don't forget to mark it as a special sequence so the + * string can be ordered. + */ + a_element.add (RuleBasedCollator.SPECIAL_UNKNOWN_SEQ); + a_idx.add (i_ref); + a_element.add (e); + a_idx.add (i_ref); + idx_idx++; + idx++; + } + continue; + } + + /* + * Second case: Here we have found a matching sequence. + * Here we have an expansion string prepend it to the "work text" and + * add the corresponding sorting element. We must also mark + */ + if (prefix.expansion != null) + { + work_text = prefix.expansion + + work_text.substring (idx+prefix.key.length()); + idx = 0; + a_element.add (prefix); + a_idx.add (new Integer(idx_idx)); + if (alreadyExpanded == 0) + idxToMove = prefix.key.length(); + alreadyExpanded += prefix.expansion.length()-prefix.key.length(); + } + else + { + /* Third case: the simplest. We have got the prefix and it + * has not to be expanded. + */ + a_element.add (prefix); + a_idx.add (new Integer(idx_idx)); + idx += prefix.key.length(); + /* If the sequence is in an expansion, we must decrease the + * counter. + */ + if (alreadyExpanded > 0) + { + alreadyExpanded -= prefix.key.length(); + if (alreadyExpanded == 0) + { + idx_idx += idxToMove; + idxToMove = 0; + } + } + else + idx_idx += prefix.key.length(); + } + } + + text_decomposition = (RuleBasedCollator.CollationElement[]) + a_element.toArray(new RuleBasedCollator.CollationElement[a_element.size()]); + text_indexes = new int[a_idx.size()+1]; + for (int i = 0; i < a_idx.size(); i++) + { + text_indexes[i] = ((Integer)a_idx.get(i)).intValue(); + } + text_indexes[a_idx.size()] = text.length(); + } + + /** + * This method sets the String that it is iterating over + * to the String represented by the specified + * CharacterIterator. + * + * @param source The CharacterIterator containing the new + * String to iterate over. + */ + public void setText(CharacterIterator source) + { + CPStringBuilder expand = new CPStringBuilder(); + + // For now assume we read from the beginning of the string. + for (char c = source.first(); + c != CharacterIterator.DONE; + c = source.next()) + expand.append(c); + + setText(expand.toString()); + } + + /** + * This method returns the current offset into the String + * that is being iterated over. + * + * @return The iteration index position. + * + * @since 1.2 + */ + public int getOffset() + { + return textIndex; + } + + /** + * This method sets the iteration index position into the current + * String to the specified value. This value must not + * be negative and must not be greater than the last index position + * in the String. + * + * @param offset The new iteration index position. + * + * @exception IllegalArgumentException If the new offset is not valid. + */ + public void setOffset(int offset) + { + if (offset < 0) + throw new IllegalArgumentException("Negative offset: " + offset); + + if (offset > (text.getEndIndex() - 1)) + throw new IllegalArgumentException("Offset too large: " + offset); + + for (index = 0; index < text_decomposition.length; index++) + { + if (offset <= text_indexes[index]) + break; + } + /* + * As text_indexes[0] == 0, we should not have to take care whether index is + * greater than 0. It is always. + */ + if (text_indexes[index] == offset) + textIndex = offset; + else + textIndex = text_indexes[index-1]; + } + + /** + * This method returns the maximum length of any expansion sequence that + * ends with the specified collation order value. (Whatever that means). + * + * @param value The collation order value + * + * @return The maximum length of an expansion sequence. + */ + public int getMaxExpansion(int value) + { + return 1; + } +} diff --git a/libjava/classpath/java/text/CollationKey.java b/libjava/classpath/java/text/CollationKey.java new file mode 100644 index 000000000..ce9db5f0d --- /dev/null +++ b/libjava/classpath/java/text/CollationKey.java @@ -0,0 +1,186 @@ +/* CollationKey.java -- Precomputed collation value + Copyright (C) 1998, 1999, 2000, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import java.util.Arrays; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct. + */ + +/** + * This class represents a pre-computed series of bits representing a + * String for under a particular Collator. This + * value may be compared bitwise against another CollationKey + * representing a different String under the same + * Collator in a manner than is usually more efficient than + * using the raw Collator compare methods. There is overhead + * associated with calculating this value, so it is generally not + * advisable to compute CollationKey's unless multiple + * comparisons against a String will be done. (For example, + * in a sort routine). + *

        + * This class cannot be instantiated directly. Instead, a + * CollationKey is created by calling the + * getCollationKey method on an instance of Collator. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @date March 25, 1999 + */ +public class CollationKey implements Comparable +{ + /** + * This is the Collator this object was created from. + */ + private Collator collator; + + /** + * This is the String this object represents. + */ + private String originalText; + + /** + * This is the bit value for this key. + */ + private byte[] key; + + CollationKey (Collator collator, String originalText, byte[] key) + { + this.collator = collator; + this.originalText = originalText; + this.key = key; + } + + /** + * This method compares the specified object to this one. An integer is + * returned which indicates whether the specified object is less than, + * greater than, or equal to this object. + * + * @param ck The CollationKey to compare against this one. + * + * @return A negative integer if this object is less than the specified object, 0 if it is equal or a positive integer if it is greater than the specified object. + */ + public int compareTo (CollationKey ck) + { + int max = Math.min (key.length, ck.key.length); + + for (int i = 0; i < max; ++i) + { + if (key[i] != ck.key[i]) + return key[i] - ck.key[i]; + } + + return key.length - ck.key.length; + } + + /** + * This method tests the specified Object for equality with + * this object. This will be true if and only if: + *

        + *

          + *
        • The specified object must not be null
        • + *
        • The specified object is an instance of CollationKey.
        • + *
        • The specified object was created from the same Collator + * as this object.
        • + *
        • The specified object has the same source string and bit key as + * this object.
        • + *
        + * + * @param obj The Object to test for equality. + * + * @return true if the specified object is equal to this one, false otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof CollationKey)) + return false; + + CollationKey ck = (CollationKey) obj; + + if (ck.collator != collator) + return false; + + if (!ck.getSourceString ().equals (getSourceString ())) + return false; + + if (! Arrays.equals (ck.toByteArray (), toByteArray ())) + return false; + + return true; + } + + /** + * This method returns the String that this object was created + * from. + * + * @return The source String for this object. + */ + public String getSourceString() + { + return originalText; + } + + /** + * This method returns a hash value for this object. The hash value + * returned will be the hash code of the bit key so that identical bit + * keys will return the same value. + * + * @return A hash value for this object. + */ + public int hashCode() + { + // We just follow BitSet instead of thinking up something new. + long h = originalText.hashCode(); + for (int i = key.length - 1; i >= 0; --i) + h ^= key[i] * (i + 1); + return (int) ((h >> 32) ^ h); + } + + /** + * This method returns the collation bit sequence as a byte array. + * + * @return A byte array containing the collation bit sequence. + */ + public byte[] toByteArray() + { + return key; + } +} diff --git a/libjava/classpath/java/text/Collator.java b/libjava/classpath/java/text/Collator.java new file mode 100644 index 000000000..a9fc55ccf --- /dev/null +++ b/libjava/classpath/java/text/Collator.java @@ -0,0 +1,425 @@ +/* Collator.java -- Perform locale dependent String comparisons. + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.locale.LocaleHelper; + +import java.text.spi.CollatorProvider; + +import java.util.Comparator; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.ServiceLoader; + +/** + * This class is the abstract superclass of classes which perform + * locale dependent String comparisons. A caller requests + * an instance of Collator for a particular locale using + * the getInstance() static method in this class. That method + * will return a locale specific subclass of Collator which + * can be used to perform String comparisons for that locale. + * If a subclass of Collator cannot be located for a particular + * locale, a default instance for the current locale will be returned. + * + * In addition to setting the correct locale, there are two additional + * settings that can be adjusted to affect String comparisons: + * strength and decomposition. The strength value determines the level + * of signficance of character differences required for them to sort + * differently. (For example, whether or not capital letters are considered + * different from lower case letters). The decomposition value affects how + * variants of the same character are treated for sorting purposes. (For + * example, whether or not an accent is signficant or not). These settings + * are described in detail in the documentation for the methods and values + * that are related to them. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date March 18, 1999 + */ +public abstract class Collator implements Comparator, Cloneable +{ + /** + * This constant is a strength value which indicates that only primary + * differences between characters will be considered signficant. As an + * example, two completely different English letters such as 'a' and 'b' + * are considered to have a primary difference. + */ + public static final int PRIMARY = 0; + + /** + * This constant is a strength value which indicates that only secondary + * or primary differences between characters will be considered + * significant. An example of a secondary difference between characters + * are instances of the same letter with different accented forms. + */ + public static final int SECONDARY = 1; + + /** + * This constant is a strength value which indicates that tertiary, + * secondary, and primary differences will be considered during sorting. + * An example of a tertiary difference is capitalization of a given letter. + * This is the default value for the strength setting. + */ + public static final int TERTIARY = 2; + + /** + * This constant is a strength value which indicates that any difference + * at all between character values are considered significant. + */ + public static final int IDENTICAL = 3; + + /** + * This constant indicates that accented characters won't be decomposed + * when performing comparisons. This will yield the fastest results, but + * will only work correctly in call cases for languages which do not + * use accents such as English. + */ + public static final int NO_DECOMPOSITION = 0; + + /** + * This constant indicates that only characters which are canonical variants + * in Unicode 2.0 will be decomposed prior to performing comparisons. This + * will cause accented languages to be sorted correctly. This is the + * default decomposition value. + */ + public static final int CANONICAL_DECOMPOSITION = 1; + + /** + * This constant indicates that both canonical variants and compatibility + * variants in Unicode 2.0 will be decomposed prior to performing + * comparisons. This is the slowest mode, but is required to get the + * correct sorting for certain languages with certain special formats. + */ + public static final int FULL_DECOMPOSITION = 2; + + /** + * This method initializes a new instance of Collator to have + * the default strength (TERTIARY) and decomposition + * (CANONICAL_DECOMPOSITION) settings. This constructor is protected and + * is for use by subclasses only. Non-subclass callers should use the + * static getInstance() methods of this class to instantiate + * Collation objects for the desired locale. + */ + protected Collator () + { + strength = TERTIARY; + decmp = CANONICAL_DECOMPOSITION; + } + + /** + * This method compares the two String's and returns an + * integer indicating whether or not the first argument is less than, + * equal to, or greater than the second argument. The comparison is + * performed according to the rules of the locale for this + * Collator and the strength and decomposition rules in + * effect. + * + * @param source The first object to compare + * @param target The second object to compare + * + * @return A negative integer if str1 < str2, 0 if str1 == str2, or + * a positive integer if str1 > str2. + */ + public abstract int compare (String source, String target); + + /** + * This method compares the two Object's and returns an + * integer indicating whether or not the first argument is less than, + * equal to, or greater than the second argument. These two objects + * must be String's or an exception will be thrown. + * + * @param o1 The first object to compare + * @param o2 The second object to compare + * + * @return A negative integer if obj1 < obj2, 0 if obj1 == obj2, or + * a positive integer if obj1 > obj2. + * + * @exception ClassCastException If the arguments are not instances + * of String. + */ + public int compare (Object o1, Object o2) + { + return compare ((String) o1, (String) o2); + } + + /** + * This method tests the specified object for equality against this + * object. This will be true if and only if the following conditions are + * met: + *
          + *
        • The specified object is not null.
        • + *
        • The specified object is an instance of Collator.
        • + *
        • The specified object has the same strength and decomposition + * settings as this object.
        • + *
        + * + * @param obj The Object to test for equality against + * this object. + * + * @return true if the specified object is equal to + * this one, false otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof Collator)) + return false; + Collator c = (Collator) obj; + return decmp == c.decmp && strength == c.strength; + } + + /** + * This method tests whether the specified String's are equal + * according to the collation rules for the locale of this object and + * the current strength and decomposition settings. + * + * @param source The first String to compare + * @param target The second String to compare + * + * @return true if the two strings are equal, + * false otherwise. + */ + public boolean equals (String source, String target) + { + return compare (source, target) == 0; + } + + /** + * This method returns a copy of this Collator object. + * + * @return A duplicate of this object. + */ + public Object clone () + { + try + { + return super.clone (); + } + catch (CloneNotSupportedException _) + { + return null; + } + } + + /** + * This method returns an array of Locale objects which is + * the list of locales for which Collator objects exist. + * + * @return The list of locales for which Collator's exist. + */ + public static synchronized Locale[] getAvailableLocales () + { + return LocaleHelper.getCollatorLocales(); + } + + /** + * This method transforms the specified String into a + * CollationKey for faster comparisons. This is useful when + * comparisons against a string might be performed multiple times, such + * as during a sort operation. + * + * @param source The String to convert. + * + * @return A CollationKey for the specified String. + */ + public abstract CollationKey getCollationKey (String source); + + /** + * This method returns the current decomposition setting for this + * object. This * will be one of NO_DECOMPOSITION, + * CANONICAL_DECOMPOSITION, or * FULL_DECOMPOSITION. See the + * documentation for those constants for an * explanation of this + * setting. + * + * @return The current decomposition setting. + */ + public synchronized int getDecomposition () + { + return decmp; + } + + /** + * This method returns an instance of Collator for the + * default locale. + * + * @return A Collator for the default locale. + */ + public static Collator getInstance () + { + return getInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of Collator for the + * specified locale. If no Collator exists for the desired + * locale, the fallback procedure described in + * {@link java.util.spi.LocaleServiceProvider} is invoked. + * + * @param loc The desired locale to load a Collator for. + * + * @return A Collator for the requested locale + */ + public static Collator getInstance (Locale loc) + { + String pattern; + try + { + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + return new RuleBasedCollator(res.getString("collation_rules")); + } + catch (MissingResourceException x) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + catch (ParseException x) + { + throw (InternalError)new InternalError().initCause(x); + } + for (CollatorProvider p : ServiceLoader.load(CollatorProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + Collator c = p.getInstance(loc); + if (c != null) + return c; + break; + } + } + } + if (loc.equals(Locale.ROOT)) + { + try + { + return new RuleBasedCollator("<0<1<2<3<4<5<6<7<8<9= allFields.length || calendarField < 0) + throw new IllegalArgumentException("no such calendar field (" + + calendarField + ")"); + + return allFields[calendarField]; + } + + protected Object readResolve() throws InvalidObjectException + { + String s = getName(); + + for (int i=0;iDateFormat. + */ + protected DateFormat () + { + } + + /** + * This method tests this object for equality against the specified object. + * The two objects will be considered equal if an only if the specified + * object: + *

        + *

          + *
        • Is not null.
        • + *
        • Is an instance of DateFormat.
        • + *
        • Has equal numberFormat field as this object.
        • + *
        • Has equal (Calendar) TimeZone rules as this object.
        • + *
        • Has equal (Calendar) isLenient results.
        • + *
        • Has equal Calendar first day of week and minimal days in week + * values.
        • + *
        + * Note that not all properties of the Calendar are relevant for a + * DateFormat. For formatting only the fact whether or not the + * TimeZone has the same rules and whether the calendar is lenient + * and has the same week rules is compared for this implementation + * of equals. Other properties of the Calendar (such as the time) + * are not taken into account. + * + * @param obj The object to test for equality against. + * + * @return true if the specified object is equal to this object, + * false otherwise. + */ + public boolean equals (Object obj) + { + if (!(obj instanceof DateFormat)) + return false; + + DateFormat d = (DateFormat) obj; + TimeZone tz = getTimeZone(); + TimeZone tzd = d.getTimeZone(); + if (tz.hasSameRules(tzd)) + if (isLenient() == d.isLenient()) + { + Calendar c = getCalendar(); + Calendar cd = d.getCalendar(); + if ((c == null && cd == null) + || + (c.getFirstDayOfWeek() == cd.getFirstDayOfWeek() + && + c.getMinimalDaysInFirstWeek() + == cd.getMinimalDaysInFirstWeek())) + return ((numberFormat == null && d.numberFormat == null) + || numberFormat.equals(d.numberFormat)); + } + + return false; + } + + /** + * This method returns a copy of this object. + * + * @return A copy of this object. + */ + public Object clone () + { + // We know the superclass just call's Object's generic cloner. + return super.clone (); + } + + /** + * This method formats the specified Object into a date string + * and appends it to the specified StringBuffer. + * The specified object must be an instance of Number or + * Date or an IllegalArgumentException will be + * thrown. + * + * @param obj The Object to format. + * @param buf The StringBuffer to append the resultant + * String to. + * @param pos Is updated to the start and end index of the + * specified field. + * + * @return The StringBuffer supplied on input, with the + * formatted date/time appended. + */ + public final StringBuffer format (Object obj, + StringBuffer buf, FieldPosition pos) + { + if (obj instanceof Number) + obj = new Date(((Number) obj).longValue()); + else if (! (obj instanceof Date)) + throw new IllegalArgumentException + ("Cannot format given Object as a Date"); + + return format ((Date) obj, buf, pos); + } + + /** + * Formats the date argument according to the pattern specified. + * + * @param date The formatted date. + */ + public final String format (Date date) + { + StringBuffer sb = new StringBuffer (); + format (date, sb, new FieldPosition (MONTH_FIELD)); + return sb.toString(); + } + + /** + * This method formats a Date into a string and appends it + * to the specified StringBuffer. + * + * @param date The Date value to format. + * @param buf The StringBuffer to append the resultant + * String to. + * @param pos Is updated to the start and end index of the + * specified field. + * + * @return The StringBuffer supplied on input, with the + * formatted date/time appended. + */ + public abstract StringBuffer format (Date date, + StringBuffer buf, FieldPosition pos); + + /** + * This method returns a list of available locales supported by this + * class. + */ + public static Locale[] getAvailableLocales() + { + return Locale.getAvailableLocales(); + } + + /** + * This method returns the Calendar object being used by + * this object to parse/format datetimes. + * + * @return The Calendar being used by this object. + * + * @see java.util.Calendar + */ + public Calendar getCalendar () + { + return calendar; + } + + private static DateFormat computeInstance (int style, Locale loc, + boolean use_date, boolean use_time) + { + return computeInstance (style, style, loc, use_date, use_time); + } + + private static DateFormat computeInstance (int dateStyle, int timeStyle, + Locale loc, boolean use_date, + boolean use_time) + throws MissingResourceException + { + if (loc.equals(Locale.ROOT)) + return computeDefault(dateStyle,timeStyle,use_date,use_time); + + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + + String pattern = null; + if (use_date) + { + String name, def; + switch (dateStyle) + { + case FULL: + name = "fullDateFormat"; + def = "EEEE MMMM d, yyyy G"; + break; + case LONG: + name = "longDateFormat"; + def = "MMMM d, yyyy"; + break; + case MEDIUM: + name = "mediumDateFormat"; + def = "d-MMM-yy"; + break; + case SHORT: + name = "shortDateFormat"; + def = "M/d/yy"; + break; + default: + throw new IllegalArgumentException (); + } + try + { + pattern = res == null ? def : res.getString(name); + } + catch (MissingResourceException x) + { + pattern = def; + } + } + + if (use_time) + { + if (pattern == null) + pattern = ""; + else + pattern += " "; + + String name, def; + switch (timeStyle) + { + case FULL: + name = "fullTimeFormat"; + def = "h:mm:ss;S 'o''clock' a z"; + break; + case LONG: + name = "longTimeFormat"; + def = "h:mm:ss a z"; + break; + case MEDIUM: + name = "mediumTimeFormat"; + def = "h:mm:ss a"; + break; + case SHORT: + name = "shortTimeFormat"; + def = "h:mm a"; + break; + default: + throw new IllegalArgumentException (); + } + + String s; + try + { + s = res == null ? def : res.getString(name); + } + catch (MissingResourceException x) + { + s = def; + } + pattern += s; + } + + return new SimpleDateFormat (pattern, loc); + } + + private static DateFormat computeDefault (int dateStyle, int timeStyle, + boolean use_date, boolean use_time) + { + String pattern = null; + if (use_date) + { + switch (dateStyle) + { + case FULL: + pattern = "EEEE MMMM d, yyyy G"; + break; + case LONG: + pattern = "MMMM d, yyyy"; + break; + case MEDIUM: + pattern = "d-MMM-yy"; + break; + case SHORT: + pattern = "M/d/yy"; + default: + throw new IllegalArgumentException (); + } + } + + if (use_time) + { + if (pattern == null) + pattern = ""; + else + pattern += " "; + + switch (timeStyle) + { + case FULL: + pattern += "h:mm:ss;S 'o''clock' a z"; + break; + case LONG: + pattern += "h:mm:ss a z"; + break; + case MEDIUM: + pattern += "h:mm:ss a"; + break; + case SHORT: + pattern += "h:mm a"; + break; + default: + throw new IllegalArgumentException (); + } + } + + return new SimpleDateFormat (pattern, Locale.ROOT); + } + + /** + * This method returns an instance of DateFormat that will + * format using the default formatting style for dates. + * + * @return A new DateFormat instance. + */ + public static final DateFormat getDateInstance () + { + return getDateInstance (DEFAULT, Locale.getDefault()); + } + + /** + * This method returns an instance of DateFormat that will + * format using the specified formatting style for dates. + * + * @param style The type of formatting to perform. + * + * @return A new DateFormat instance. + */ + public static final DateFormat getDateInstance (int style) + { + return getDateInstance (style, Locale.getDefault()); + } + + /** + * This method returns an instance of DateFormat that will + * format using the specified formatting style for dates. The specified + * localed will be used in place of the default. + * + * @param style The type of formatting to perform. + * @param loc The desired locale. + * + * @return A new DateFormat instance. + */ + public static final DateFormat getDateInstance (int style, Locale loc) + { + try + { + return computeInstance (style, loc, true, false); + } + catch (MissingResourceException e) + { + for (DateFormatProvider p : + ServiceLoader.load(DateFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + DateFormat df = p.getDateInstance(style, loc); + if (df != null) + return df; + break; + } + } + } + return getDateInstance(style, + LocaleHelper.getFallbackLocale(loc)); + } + } + + /** + * This method returns a new instance of DateFormat that + * formats both dates and times using the SHORT style. + * + * @return A new DateFormatinstance. + */ + public static final DateFormat getDateTimeInstance () + { + return getDateTimeInstance (DEFAULT, DEFAULT, Locale.getDefault()); + } + + /** + * This method returns a new instance of DateFormat that + * formats both dates and times using the DEFAULT style. + * + * @return A new DateFormatinstance. + */ + public static final DateFormat getDateTimeInstance (int dateStyle, + int timeStyle) + { + return getDateTimeInstance (dateStyle, timeStyle, Locale.getDefault()); + } + + /** + * This method returns a new instance of DateFormat that + * formats both dates and times using the specified styles. + * + * @param dateStyle The desired style for date formatting. + * @param timeStyle The desired style for time formatting + * + * @return A new DateFormatinstance. + */ + public static final DateFormat getDateTimeInstance (int dateStyle, + int timeStyle, + Locale loc) + { + try + { + return computeInstance (dateStyle, timeStyle, loc, true, true); + } + catch (MissingResourceException e) + { + for (DateFormatProvider p : + ServiceLoader.load(DateFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + DateFormat df = p.getDateTimeInstance(dateStyle, + timeStyle, loc); + if (df != null) + return df; + break; + } + } + } + return getDateTimeInstance(dateStyle, timeStyle, + LocaleHelper.getFallbackLocale(loc)); + } + } + + /** + * This method returns a new instance of DateFormat that + * formats both dates and times using the SHORT style. + * + * @return A new DateFormatinstance. + */ + public static final DateFormat getInstance () + { + // JCL book says SHORT. + return getDateTimeInstance (SHORT, SHORT, Locale.getDefault()); + } + + /** + * This method returns the NumberFormat object being used + * by this object to parse/format time values. + * + * @return The NumberFormat in use by this object. + */ + public NumberFormat getNumberFormat () + { + return numberFormat; + } + + /** + * This method returns an instance of DateFormat that will + * format using the default formatting style for times. + * + * @return A new DateFormat instance. + */ + public static final DateFormat getTimeInstance () + { + return getTimeInstance (DEFAULT, Locale.getDefault()); + } + + /** + * This method returns an instance of DateFormat that will + * format using the specified formatting style for times. + * + * @param style The type of formatting to perform. + * + * @return A new DateFormat instance. + */ + public static final DateFormat getTimeInstance (int style) + { + return getTimeInstance (style, Locale.getDefault()); + } + + /** + * This method returns an instance of DateFormat that will + * format using the specified formatting style for times. The specified + * localed will be used in place of the default. + * + * @param style The type of formatting to perform. + * @param loc The desired locale. + * + * @return A new DateFormat instance. + */ + public static final DateFormat getTimeInstance (int style, Locale loc) + { + try + { + return computeInstance (style, loc, false, true); + } + catch (MissingResourceException e) + { + for (DateFormatProvider p : + ServiceLoader.load(DateFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + DateFormat df = p.getTimeInstance(style, loc); + if (df != null) + return df; + break; + } + } + } + return getTimeInstance(style, + LocaleHelper.getFallbackLocale(loc)); + } + } + + /** + * This method returns the TimeZone object being used by + * this instance. + * + * @return The time zone in use. + */ + public TimeZone getTimeZone () + { + return calendar.getTimeZone(); + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode () + { + if (numberFormat != null) + return numberFormat.hashCode(); + else + return 0; + } + + /** + * This method indicates whether or not the parsing of date and time + * values should be done in a lenient value. + * + * @return true if date/time parsing is lenient, + * false otherwise. + */ + public boolean isLenient () + { + return calendar.isLenient(); + } + + /** + * This method parses the specified date/time string. + * + * @param source The string to parse. + * @return The resultant date. + * + * @exception ParseException If the specified string cannot be parsed. + */ + public Date parse (String source) throws ParseException + { + ParsePosition pos = new ParsePosition(0); + Date result = parse (source, pos); + if (result == null) + { + int index = pos.getErrorIndex(); + if (index < 0) + index = pos.getIndex(); + throw new ParseException("invalid Date syntax in \"" + + source + '\"', index); + } + return result; + } + + /** + * This method parses the specified String into a + * Date. The pos argument contains the + * starting parse position on method entry and the ending parse + * position on method exit. + * + * @param source The string to parse. + * @param pos The starting parse position in entry, the ending parse + * position on exit. + * + * @return The parsed date, or null if the string cannot + * be parsed. + */ + public abstract Date parse (String source, ParsePosition pos); + + /** + * This method is identical to parse(String, ParsePosition), + * but returns its result as an Object instead of a + * Date. + * + * @param source The string to parse. + * @param pos The starting parse position in entry, the ending parse + * position on exit. + * + * @return The parsed date, or null if the string cannot + * be parsed. + */ + public Object parseObject (String source, ParsePosition pos) + { + return parse(source, pos); + } + + /** + * This method specified the Calendar that should be used + * by this object to parse/format datetimes. + * + * @param calendar The new Calendar for this object. + * + * @see java.util.Calendar + */ + public void setCalendar (Calendar calendar) + { + this.calendar = calendar; + } + + /** + * This method specifies whether or not this object should be lenient in + * the syntax it accepts while parsing date/time values. + * + * @param lenient true if parsing should be lenient, + * false otherwise. + */ + public void setLenient (boolean lenient) + { + calendar.setLenient(lenient); + } + + /** + * This method specifies the NumberFormat object that should + * be used by this object to parse/format times. + * + * @param numberFormat The NumberFormat in use by this object. + */ + public void setNumberFormat (NumberFormat numberFormat) + { + this.numberFormat = numberFormat; + } + + /** + * This method sets the time zone that should be used by this object. + * + * @param timeZone The new time zone. + */ + public void setTimeZone (TimeZone timeZone) + { + calendar.setTimeZone(timeZone); + } +} diff --git a/libjava/classpath/java/text/DateFormatSymbols.java b/libjava/classpath/java/text/DateFormatSymbols.java new file mode 100644 index 000000000..c22dd38f7 --- /dev/null +++ b/libjava/classpath/java/text/DateFormatSymbols.java @@ -0,0 +1,761 @@ +/* DateFormatSymbols.java -- Format over a range of numbers + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.locale.LocaleHelper; + +import java.io.IOException; + +import java.text.spi.DateFormatSymbolsProvider; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.ServiceLoader; +import java.util.TimeZone; + +import java.util.spi.TimeZoneNameProvider; + +/** + * This class acts as container for locale specific date/time formatting + * information such as the days of the week and the months of the year. + * + * @author Per Bothner (bothner@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @date October 24, 1998. + */ +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3. + * Status: Believed complete and correct. + */ +public class DateFormatSymbols implements java.io.Serializable, Cloneable +{ + String[] ampms; + String[] eras; + private String localPatternChars; + String[] months; + String[] shortMonths; + String[] shortWeekdays; + String[] weekdays; + + /** + * The set of properties for obtaining the metazone data. + */ + private static transient final Properties properties; + + /** + * Reads in the properties. + */ + static + { + properties = new Properties(); + try + { + properties.load(DateFormatSymbols.class.getResourceAsStream("metazones.properties")); + } + catch (IOException exception) + { + System.out.println("Failed to load weeks resource: " + exception); + } + } + + /** + * The timezone strings supplied by the runtime. + */ + private String[][] runtimeZoneStrings; + + /** + * Custom timezone strings supplied by {@link #setZoneStrings()}. + */ + private String[][] zoneStrings; + + private static final long serialVersionUID = -5987973545549424702L; + + // The order of these prefixes must be the same as in DateFormat + private static final String[] formatPrefixes = + { + "full", "long", "medium", "short" + }; + + // These are each arrays with a value for SHORT, MEDIUM, LONG, FULL, + // and DEFAULT (constants defined in java.text.DateFormat). While + // not part of the official spec, we need a way to get at locale-specific + // default formatting patterns. They are declared package scope so + // as to be easily accessible where needed (DateFormat, SimpleDateFormat). + transient String[] dateFormats; + transient String[] timeFormats; + + private static String[] getStringArray(ResourceBundle res, String name) + { + return res.getString(name).split("\u00ae"); + } + + private String[][] getZoneStrings(ResourceBundle res, Locale locale) + { + List allZones = new ArrayList(); + try + { + Map systemZones = new HashMap(); + while (true) + { + int index = 0; + String country = locale.getCountry(); + String data = res.getString("zoneStrings"); + String[] zones = data.split("\u00a9"); + for (int a = 0; a < zones.length; ++a) + { + String[] strings = zones[a].split("\u00ae"); + String type = properties.getProperty(strings[0] + "." + country); + if (type == null) + type = properties.getProperty(strings[0] + ".DEFAULT"); + if (type != null) + strings[0] = type; + if (strings.length < 5) + { + String[] newStrings = new String[5]; + System.arraycopy(strings, 0, newStrings, 0, strings.length); + for (int b = strings.length; b < newStrings.length; ++b) + newStrings[b] = ""; + strings = newStrings; + } + String[] existing = systemZones.get(strings[0]); + if (existing != null && existing.length > 1) + { + for (int b = 1; b < existing.length; ++b) + if (!existing[b].equals("")) + strings[b] = existing[b]; + } + systemZones.put(strings[0], strings); + } + if (res.getLocale() == Locale.ROOT) + break; + else + res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + LocaleHelper.getFallbackLocale(res.getLocale()), + ClassLoader.getSystemClassLoader()); + } + /* Final sanity check for missing values */ + for (String[] zstrings : systemZones.values()) + { + if (zstrings[1].equals("") && zstrings[2].equals("")) + { + for (Map.Entry entry : properties.entrySet()) + { + String val = (String) entry.getValue(); + if (val.equals(zstrings[0])) + { + String key = (String) entry.getKey(); + String metazone = key.substring(0, key.indexOf(".")); + String type = properties.getProperty(metazone + "." + locale.getCountry()); + if (type == null) + type = properties.getProperty(metazone + ".DEFAULT"); + if (type != null) + { + String[] ostrings = systemZones.get(type); + zstrings[1] = ostrings[1]; + zstrings[2] = ostrings[2]; + } + } + } + } + } + allZones.addAll(systemZones.values()); + } + catch (MissingResourceException e) + { + /* This means runtime support for the locale + * is not available, so we just include providers. */ + } + for (TimeZoneNameProvider p : + ServiceLoader.load(TimeZoneNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + for (String id : TimeZone.getAvailableIDs()) + { + String[] z = new String[5]; + z[0] = id; + z[1] = p.getDisplayName(id, false, + TimeZone.LONG, + locale); + z[2] = p.getDisplayName(id, false, + TimeZone.SHORT, + locale); + z[3] = p.getDisplayName(id, true, + TimeZone.LONG, + locale); + z[4] = p.getDisplayName(id, true, + TimeZone.SHORT, + locale); + allZones.add(z); + } + break; + } + } + } + return allZones.toArray(new String[allZones.size()][]); + } + + private String[] formatsForKey(ResourceBundle res, String key) + { + String[] values = new String[formatPrefixes.length]; + + for (int i = 0; i < formatPrefixes.length; i++) + values[i] = res.getString(formatPrefixes[i] + key); + + return values; + } + + /** + * This method initializes a new instance of DateFormatSymbols + * by loading the date format information for the specified locale. + * This constructor only obtains instances using the runtime's resources; + * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances, + * call {@link #getInstance(java.util.Locale)} instead. + * + * @param locale The locale for which date formatting symbols should + * be loaded. + * @throws MissingResourceException if the resources for the specified + * locale could not be found or loaded. + * @see #getInstance(java.util.Locale) + */ + public DateFormatSymbols (Locale locale) + throws MissingResourceException + { + ResourceBundle res + = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", locale, + ClassLoader.getSystemClassLoader()); + + ampms = getStringArray(res, "ampms"); + eras = getStringArray(res, "eras"); + localPatternChars = res.getString("localPatternChars"); + months = getStringArray(res, "months"); + shortMonths = getStringArray(res, "shortMonths"); + shortWeekdays = getStringArray(res, "shortWeekdays"); + weekdays = getStringArray(res, "weekdays"); + dateFormats = formatsForKey(res, "DateFormat"); + timeFormats = formatsForKey(res, "TimeFormat"); + runtimeZoneStrings = getZoneStrings(res, locale); + } + + /** + * This method loads the format symbol information for the default + * locale. This constructor only obtains instances using the runtime's resources; + * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances, + * call {@link #getInstance()} instead. + * + * @throws MissingResourceException if the resources for the default + * locale could not be found or loaded. + * @see #getInstance() + */ + public DateFormatSymbols() + throws MissingResourceException + { + this (Locale.getDefault()); + } + + /** + * This method returns the list of strings used for displaying AM or PM. + * This is a two element String array indexed by + * Calendar.AM and Calendar.PM + * + * @return The list of AM/PM display strings. + */ + public String[] getAmPmStrings() + { + return ampms; + } + + /** + * This method returns the list of strings used for displaying eras + * (e.g., "BC" and "AD"). This is a two element String + * array indexed by Calendar.BC and Calendar.AD. + * + * @return The list of era disply strings. + */ + public String[] getEras() + { + return eras; + } + + /** + * This method returns the pattern character information for this + * object. This is an 18 character string that contains the characters + * that are used in creating the date formatting strings in + * SimpleDateFormat. The following are the character + * positions in the string and which format character they correspond + * to (the character in parentheses is the default value in the US English + * locale): + *

        + *

          + *
        • 0 - era (G)
        • + *
        • 1 - year (y)
        • + *
        • 2 - month (M)
        • + *
        • 3 - day of month (d)
        • + *
        • 4 - hour out of 12, from 1-12 (h)
        • + *
        • 5 - hour out of 24, from 0-23 (H)
        • + *
        • 6 - minute (m)
        • + *
        • 7 - second (s)
        • + *
        • 8 - millisecond (S)
        • + *
        • 9 - date of week (E)
        • + *
        • 10 - date of year (D)
        • + *
        • 11 - day of week in month, eg. "4th Thur in Nov" (F)
        • + *
        • 12 - week in year (w)
        • + *
        • 13 - week in month (W)
        • + *
        • 14 - am/pm (a)
        • + *
        • 15 - hour out of 24, from 1-24 (k)
        • + *
        • 16 - hour out of 12, from 0-11 (K)
        • + *
        • 17 - time zone (z)
        • + *
        + * + * @return The format patter characters + */ + public String getLocalPatternChars() + { + return localPatternChars; + } + + /** + * This method returns the list of strings used for displaying month + * names (e.g., "January" and "February"). This is a thirteen element + * string array indexed by Calendar.JANUARY through + * Calendar.UNDECEMBER. Note that there are thirteen + * elements because some calendars have thriteen months. + * + * @return The list of month display strings. + */ + public String[] getMonths () + { + return months; + } + + /** + * This method returns the list of strings used for displaying abbreviated + * month names (e.g., "Jan" and "Feb"). This is a thirteen element + * String array indexed by Calendar.JANUARY + * through Calendar.UNDECEMBER. Note that there are thirteen + * elements because some calendars have thirteen months. + * + * @return The list of abbreviated month display strings. + */ + public String[] getShortMonths () + { + return shortMonths; + } + + /** + * This method returns the list of strings used for displaying abbreviated + * weekday names (e.g., "Sun" and "Mon"). This is an eight element + * String array indexed by Calendar.SUNDAY + * through Calendar.SATURDAY. Note that the first element + * of this array is ignored. + * + * @return This list of abbreviated weekday display strings. + */ + public String[] getShortWeekdays () + { + return shortWeekdays; + } + + /** + * This method returns the list of strings used for displaying weekday + * names (e.g., "Sunday" and "Monday"). This is an eight element + * String array indexed by Calendar.SUNDAY + * through Calendar.SATURDAY. Note that the first element + * of this array is ignored. + * + * @return This list of weekday display strings. + */ + public String[] getWeekdays () + { + return weekdays; + } + + /** + * This method returns this list of localized timezone display strings. + * This is a two dimensional String array where each row in + * the array contains five values: + *

        + *

          + *
        • 0 - The non-localized time zone id string.
        • + *
        • 1 - The long name of the time zone (standard time).
        • + *
        • 2 - The short name of the time zone (standard time).
        • + *
        • 3 - The long name of the time zone (daylight savings time).
        • + *
        • 4 - the short name of the time zone (daylight savings time).
        • + *
        + *

        + * If {@link #setZoneStrings(String[][])} has been called, then the value + * passed to this will be returned. Otherwise the returned array contains + * zone names provided by the runtime environment and any + * {@link java.util.spi.TimeZoneProvider} instances. + *

        + * + * @return The list of time zone display strings. + * @see #setZoneStrings(String[][]) + */ + public String[][] getZoneStrings() + { + if (zoneStrings != null) + return zoneStrings; + return runtimeZoneStrings; + } + + /** + * This method sets the list of strings used to display AM/PM values to + * the specified list. + * This is a two element String array indexed by + * Calendar.AM and Calendar.PM + * + * @param value The new list of AM/PM display strings. + */ + public void setAmPmStrings (String[] value) + { + if(value==null) + throw new NullPointerException(); + ampms = value; + } + + /** + * This method sets the list of strings used to display time eras to + * to the specified list. + * This is a two element String + * array indexed by Calendar.BC and Calendar.AD. + * + * @param labels The new list of era display strings. + */ + public void setEras (String[] labels) + { + if(labels==null) + throw new NullPointerException(); + eras = labels; + } + + /** + * This method sets the list of characters used to specific date/time + * formatting strings. + * This is an 18 character string that contains the characters + * that are used in creating the date formatting strings in + * SimpleDateFormat. The following are the character + * positions in the string and which format character they correspond + * to (the character in parentheses is the default value in the US English + * locale): + *

        + *

          + *
        • 0 - era (G)
        • + *
        • 1 - year (y)
        • + *
        • 2 - month (M)
        • + *
        • 3 - day of month (d)
        • + *
        • 4 - hour out of 12, from 1-12 (h)
        • + *
        • 5 - hour out of 24, from 0-23 (H)
        • + *
        • 6 - minute (m)
        • + *
        • 7 - second (s)
        • + *
        • 8 - millisecond (S)
        • + *
        • 9 - date of week (E)
        • + *
        • 10 - date of year (D)
        • + *
        • 11 - day of week in month, eg. "4th Thur in Nov" (F)
        • + *
        • 12 - week in year (w)
        • + *
        • 13 - week in month (W)
        • + *
        • 14 - am/pm (a)
        • + *
        • 15 - hour out of 24, from 1-24 (k)
        • + *
        • 16 - hour out of 12, from 0-11 (K)
        • + *
        • 17 - time zone (z)
        • + *
        + * + * @param chars The new format pattern characters + */ + public void setLocalPatternChars (String chars) + { + if(chars==null) + throw new NullPointerException(); + localPatternChars = chars; + } + + /** + * This method sets the list of strings used to display month names. + * This is a thirteen element + * string array indexed by Calendar.JANUARY through + * Calendar.UNDECEMBER. Note that there are thirteen + * elements because some calendars have thriteen months. + * + * @param labels The list of month display strings. + */ + public void setMonths (String[] labels) + { + if(labels==null) + throw new NullPointerException(); + months = labels; + } + + /** + * This method sets the list of strings used to display abbreviated month + * names. + * This is a thirteen element + * String array indexed by Calendar.JANUARY + * through Calendar.UNDECEMBER. Note that there are thirteen + * elements because some calendars have thirteen months. + * + * @param labels The new list of abbreviated month display strings. + */ + public void setShortMonths (String[] labels) + { + if(labels==null) + throw new NullPointerException(); + shortMonths = labels; + } + + /** + * This method sets the list of strings used to display abbreviated + * weekday names. + * This is an eight element + * String array indexed by Calendar.SUNDAY + * through Calendar.SATURDAY. Note that the first element + * of this array is ignored. + * + * @param labels This list of abbreviated weekday display strings. + */ + public void setShortWeekdays (String[] labels) + { + if(labels==null) + throw new NullPointerException(); + shortWeekdays = labels; + } + + /** + * This method sets the list of strings used to display weekday names. + * This is an eight element + * String array indexed by Calendar.SUNDAY + * through Calendar.SATURDAY. Note that the first element + * of this array is ignored. + * + * @param labels This list of weekday display strings. + */ + public void setWeekdays (String[] labels) + { + if(labels==null) + throw new NullPointerException(); + weekdays = labels; + } + + /** + * This method sets the list of display strings for time zones. + * This is a two dimensional String array where each row in + * the array contains five values: + *

        + *

          + *
        • 0 - The non-localized time zone id string.
        • + *
        • 1 - The long name of the time zone (standard time).
        • + *
        • 2 - The short name of the time zone (standard time).
        • + *
        • 3 - The long name of the time zone (daylight savings time).
        • + *
        • 4 - the short name of the time zone (daylight savings time).
        • + *
        + * + * @params zones The list of time zone display strings. + */ + public void setZoneStrings (String[][] zones) + { + if(zones==null) + throw new NullPointerException(); + zoneStrings = zones; + } + + /* Does a "deep" equality test - recurses into arrays. */ + private static boolean equals (Object x, Object y) + { + if (x == y) + return true; + if (x == null || y == null) + return false; + if (! (x instanceof Object[]) || ! (y instanceof Object[])) + return x.equals(y); + Object[] xa = (Object[]) x; + Object[] ya = (Object[]) y; + if (xa.length != ya.length) + return false; + for (int i = xa.length; --i >= 0; ) + { + if (! equals(xa[i], ya[i])) + return false; + } + return true; + } + + private static int hashCode (Object x) + { + if (x == null) + return 0; + if (! (x instanceof Object[])) + return x.hashCode(); + Object[] xa = (Object[]) x; + int hash = 0; + for (int i = 0; i < xa.length; i++) + hash = 37 * hashCode(xa[i]); + return hash; + } + + /** + * This method tests a specified object for equality against this object. + * This will be true if and only if the specified object: + *

        + *

          + *
        • Is not null.
        • + *
        • Is an instance of DateFormatSymbols.
        • + *
        • Contains identical formatting symbols to this object.
        • + *
        + * + * @param obj The Object to test for equality against. + * + * @return true if the specified object is equal to this one, + * false otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof DateFormatSymbols)) + return false; + DateFormatSymbols other = (DateFormatSymbols) obj; + return (equals(ampms, other.ampms) + && equals(eras, other.eras) + && equals(localPatternChars, other.localPatternChars) + && equals(months, other.months) + && equals(shortMonths, other.shortMonths) + && equals(shortWeekdays, other.shortWeekdays) + && equals(weekdays, other.weekdays) + && equals(zoneStrings, other.zoneStrings)); + } + + /** + * Returns a new copy of this object. + * + * @return A copy of this object + */ + public Object clone () + { + try + { + return super.clone (); + } + catch (CloneNotSupportedException e) + { + return null; + } + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode () + { + return (hashCode(ampms) + ^ hashCode(eras) + ^ hashCode(localPatternChars) + ^ hashCode(months) + ^ hashCode(shortMonths) + ^ hashCode(shortWeekdays) + ^ hashCode(weekdays) + ^ hashCode(zoneStrings)); + } + + /** + * Returns a {@link DateFormatSymbols} instance for the + * default locale obtained from either the runtime itself + * or one of the installed + * {@link java.text.spi.DateFormatSymbolsProvider} instances. + * This is equivalent to calling + * getInstance(Locale.getDefault()). + * + * @return a {@link DateFormatSymbols} instance for the default + * locale. + * @since 1.6 + */ + public static final DateFormatSymbols getInstance() + { + return getInstance(Locale.getDefault()); + } + + /** + * Returns a {@link DateFormatSymbols} instance for the + * specified locale obtained from either the runtime itself + * or one of the installed + * {@link java.text.spi.DateFormatSymbolsProvider} instances. + * + * @param locale the locale for which an instance should be + * returned. + * @return a {@link DateFormatSymbols} instance for the specified + * locale. + * @throws NullPointerException if locale is + * null. + * @since 1.6 + */ + public static final DateFormatSymbols getInstance(Locale locale) + { + try + { + DateFormatSymbols syms = new DateFormatSymbols(locale); + return syms; + } + catch (MissingResourceException e) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (DateFormatSymbolsProvider p : + ServiceLoader.load(DateFormatSymbolsProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + DateFormatSymbols syms = p.getInstance(locale); + if (syms != null) + return syms; + break; + } + } + } + return getInstance(LocaleHelper.getFallbackLocale(locale)); + } + +} diff --git a/libjava/classpath/java/text/DecimalFormat.java b/libjava/classpath/java/text/DecimalFormat.java new file mode 100644 index 000000000..9f02bb8d4 --- /dev/null +++ b/libjava/classpath/java/text/DecimalFormat.java @@ -0,0 +1,2278 @@ +/* DecimalFormat.java -- Formats and parses numbers + Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +/* + * This class contains few bits from ICU4J (http://icu.sourceforge.net/), + * Copyright by IBM and others and distributed under the + * distributed under MIT/X. + */ + +package java.text; + +import gnu.java.lang.CPStringBuilder; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import java.util.ArrayList; +import java.util.Currency; +import java.util.Locale; + +/* + * This note is here for historical reasons and because I had not the courage + * to remove it :) + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @date March 4, 1999 + * + * Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2. + * Note however that the docs are very unclear about how format parsing + * should work. No doubt there are problems here. + */ + +/** + * This class is a concrete implementation of NumberFormat used to format + * decimal numbers. The class can format numbers given a specific locale. + * Generally, to get an instance of DecimalFormat you should call the factory + * methods in the NumberFormat base class. + * + * @author Mario Torre (neugens@limasoftware.net) + * @author Tom Tromey (tromey@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class DecimalFormat extends NumberFormat +{ + /** serialVersionUID for serializartion. */ + private static final long serialVersionUID = 864413376551465018L; + + /** Defines the default number of digits allowed while formatting integers. */ + private static final int DEFAULT_INTEGER_DIGITS = 309; + + /** + * Defines the default number of digits allowed while formatting + * fractions. + */ + private static final int DEFAULT_FRACTION_DIGITS = 340; + + /** + * Locale-independent pattern symbols. + */ + // Happen to be the same as the US symbols. + private static final DecimalFormatSymbols nonLocalizedSymbols + = new DecimalFormatSymbols (Locale.US); + + /** + * Defines if parse should return a BigDecimal or not. + */ + private boolean parseBigDecimal; + + /** + * Defines if we have to use the monetary decimal separator or + * the decimal separator while formatting numbers. + */ + private boolean useCurrencySeparator; + + /** Defines if the decimal separator is always shown or not. */ + private boolean decimalSeparatorAlwaysShown; + + /** + * Defines if the decimal separator has to be shown. + * + * This is different then decimalSeparatorAlwaysShown, + * as it defines if the format string contains a decimal separator or no. + */ + private boolean showDecimalSeparator; + + /** + * This field is used to determine if the grouping + * separator is included in the format string or not. + * This is only needed to match the behaviour of the RI. + */ + private boolean groupingSeparatorInPattern; + + /** Defines the size of grouping groups when grouping is used. */ + private byte groupingSize; + + /** + * This is an internal parameter used to keep track of the number + * of digits the form the exponent, when exponential notation is used. + * It is used with exponentRound + */ + private byte minExponentDigits; + + /** This field is used to set the exponent in the engineering notation. */ + private int exponentRound; + + /** Multiplier used in percent style formats. */ + private int multiplier; + + /** Multiplier used in percent style formats. */ + private int negativePatternMultiplier; + + /** The negative prefix. */ + private String negativePrefix; + + /** The negative suffix. */ + private String negativeSuffix; + + /** The positive prefix. */ + private String positivePrefix; + + /** The positive suffix. */ + private String positiveSuffix; + + /** Decimal Format Symbols for the given locale. */ + private DecimalFormatSymbols symbols; + + /** Determine if we have to use exponential notation or not. */ + private boolean useExponentialNotation; + + /** + * Defines the maximum number of integer digits to show when we use + * the exponential notation. + */ + private int maxIntegerDigitsExponent; + + /** Defines if the format string has a negative prefix or not. */ + private boolean hasNegativePrefix; + + /** Defines if the format string has a fractional pattern or not. */ + private boolean hasFractionalPattern; + + /** Stores a list of attributes for use by formatToCharacterIterator. */ + private ArrayList attributes = new ArrayList(); + + /** + * Constructs a DecimalFormat which uses the default + * pattern and symbols. + */ + public DecimalFormat() + { + this ("#,##0.###"); + } + + /** + * Constructs a DecimalFormat which uses the given + * pattern and the default symbols for formatting and parsing. + * + * @param pattern the non-localized pattern to use. + * @throws NullPointerException if any argument is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public DecimalFormat(String pattern) + { + this (pattern, new DecimalFormatSymbols()); + } + + /** + * Constructs a DecimalFormat using the given pattern + * and formatting symbols. This construction method is used to give + * complete control over the formatting process. + * + * @param pattern the non-localized pattern to use. + * @param symbols the set of symbols used for parsing and formatting. + * @throws NullPointerException if any argument is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public DecimalFormat(String pattern, DecimalFormatSymbols symbols) + { + this.symbols = (DecimalFormatSymbols) symbols.clone(); + applyPatternWithSymbols(pattern, nonLocalizedSymbols); + } + + /** + * Apply the given localized patern to the current DecimalFormat object. + * + * @param pattern The localized pattern to apply. + * @throws IllegalArgumentException if the given pattern is invalid. + * @throws NullPointerException if the input pattern is null. + */ + public void applyLocalizedPattern (String pattern) + { + applyPatternWithSymbols(pattern, this.symbols); + } + + /** + * Apply the given localized pattern to the current DecimalFormat object. + * + * @param pattern The localized pattern to apply. + * @throws IllegalArgumentException if the given pattern is invalid. + * @throws NullPointerException if the input pattern is null. + */ + public void applyPattern(String pattern) + { + applyPatternWithSymbols(pattern, nonLocalizedSymbols); + } + + public Object clone() + { + DecimalFormat c = (DecimalFormat) super.clone(); + c.symbols = (DecimalFormatSymbols) symbols.clone(); + return c; + } + + /** + * Tests this instance for equality with an arbitrary object. This method + * returns true if: + *
          + *
        • obj is not null;
        • + *
        • obj is an instance of DecimalFormat;
        • + *
        • this instance and obj have the same attributes;
        • + *
        + * + * @param obj the object (null permitted). + * + * @return A boolean. + */ + public boolean equals(Object obj) + { + if (! (obj instanceof DecimalFormat)) + return false; + DecimalFormat dup = (DecimalFormat) obj; + return (decimalSeparatorAlwaysShown == dup.decimalSeparatorAlwaysShown + && groupingUsed == dup.groupingUsed + && groupingSeparatorInPattern == dup.groupingSeparatorInPattern + && groupingSize == dup.groupingSize + && multiplier == dup.multiplier + && useExponentialNotation == dup.useExponentialNotation + && minExponentDigits == dup.minExponentDigits + && minimumIntegerDigits == dup.minimumIntegerDigits + && maximumIntegerDigits == dup.maximumIntegerDigits + && minimumFractionDigits == dup.minimumFractionDigits + && maximumFractionDigits == dup.maximumFractionDigits + && parseBigDecimal == dup.parseBigDecimal + && useCurrencySeparator == dup.useCurrencySeparator + && showDecimalSeparator == dup.showDecimalSeparator + && exponentRound == dup.exponentRound + && negativePatternMultiplier == dup.negativePatternMultiplier + && maxIntegerDigitsExponent == dup.maxIntegerDigitsExponent + // XXX: causes equivalent patterns to fail + // && hasNegativePrefix == dup.hasNegativePrefix + && equals(negativePrefix, dup.negativePrefix) + && equals(negativeSuffix, dup.negativeSuffix) + && equals(positivePrefix, dup.positivePrefix) + && equals(positiveSuffix, dup.positiveSuffix) + && symbols.equals(dup.symbols)); + } + + /** + * Returns a hash code for this object. + * + * @return A hash code. + */ + public int hashCode() + { + return toPattern().hashCode(); + } + + /** + * Produce a formatted {@link String} representation of this object. + * The passed object must be of type number. + * + * @param obj The {@link Number} to format. + * @param sbuf The destination String; text will be appended to this String. + * @param pos If used on input can be used to define an alignment + * field. If used on output defines the offsets of the alignment field. + * @return The String representation of this long. + */ + public final StringBuffer format(Object obj, StringBuffer sbuf, FieldPosition pos) + { + if (obj instanceof BigInteger) + { + BigDecimal decimal = new BigDecimal((BigInteger) obj); + formatInternal(decimal, true, sbuf, pos); + return sbuf; + } + else if (obj instanceof BigDecimal) + { + formatInternal((BigDecimal) obj, true, sbuf, pos); + return sbuf; + } + + return super.format(obj, sbuf, pos); + } + + /** + * Produce a formatted {@link String} representation of this double. + * + * @param number The double to format. + * @param dest The destination String; text will be appended to this String. + * @param fieldPos If used on input can be used to define an alignment + * field. If used on output defines the offsets of the alignment field. + * @return The String representation of this long. + * @throws NullPointerException if dest or fieldPos are null + */ + public StringBuffer format(double number, StringBuffer dest, + FieldPosition fieldPos) + { + // special cases for double: NaN and negative or positive infinity + if (Double.isNaN(number)) + { + // 1. NaN + String nan = symbols.getNaN(); + dest.append(nan); + + // update field position if required + if ((fieldPos.getField() == INTEGER_FIELD || + fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER)) + { + int index = dest.length(); + fieldPos.setBeginIndex(index - nan.length()); + fieldPos.setEndIndex(index); + } + } + else if (Double.isInfinite(number)) + { + // 2. Infinity + if (number < 0) + dest.append(this.negativePrefix); + else + dest.append(this.positivePrefix); + + dest.append(symbols.getInfinity()); + + if (number < 0) + dest.append(this.negativeSuffix); + else + dest.append(this.positiveSuffix); + + if ((fieldPos.getField() == INTEGER_FIELD || + fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER)) + { + fieldPos.setBeginIndex(dest.length()); + fieldPos.setEndIndex(0); + } + } + else + { + // get the number as a BigDecimal + BigDecimal bigDecimal = new BigDecimal(String.valueOf(number)); + formatInternal(bigDecimal, false, dest, fieldPos); + } + + return dest; + } + + /** + * Produce a formatted {@link String} representation of this long. + * + * @param number The long to format. + * @param dest The destination String; text will be appended to this String. + * @param fieldPos If used on input can be used to define an alignment + * field. If used on output defines the offsets of the alignment field. + * @return The String representation of this long. + */ + public StringBuffer format(long number, StringBuffer dest, + FieldPosition fieldPos) + { + BigDecimal bigDecimal = new BigDecimal(String.valueOf(number)); + formatInternal(bigDecimal, true, dest, fieldPos); + return dest; + } + + /** + * Return an AttributedCharacterIterator as a result of + * the formatting of the passed {@link Object}. + * + * @return An {@link AttributedCharacterIterator}. + * @throws NullPointerException if value is null. + * @throws IllegalArgumentException if value is not an instance of + * {@link Number}. + */ + public AttributedCharacterIterator formatToCharacterIterator(Object value) + { + /* + * This method implementation derives directly from the + * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X. + */ + + if (value == null) + throw new NullPointerException("Passed Object is null"); + + if (!(value instanceof Number)) throw new + IllegalArgumentException("Cannot format given Object as a Number"); + + StringBuffer text = new StringBuffer(); + attributes.clear(); + super.format(value, text, new FieldPosition(0)); + + AttributedString as = new AttributedString(text.toString()); + + // add NumberFormat field attributes to the AttributedString + for (int i = 0; i < attributes.size(); i++) + { + FieldPosition pos = (FieldPosition) attributes.get(i); + Format.Field attribute = pos.getFieldAttribute(); + + as.addAttribute(attribute, attribute, pos.getBeginIndex(), + pos.getEndIndex()); + } + + // return the CharacterIterator from AttributedString + return as.getIterator(); + } + + /** + * Returns the currency corresponding to the currency symbol stored + * in the instance of DecimalFormatSymbols used by this + * DecimalFormat. + * + * @return A new instance of Currency if + * the currency code matches a known one, null otherwise. + */ + public Currency getCurrency() + { + return symbols.getCurrency(); + } + + /** + * Returns a copy of the symbols used by this instance. + * + * @return A copy of the symbols. + */ + public DecimalFormatSymbols getDecimalFormatSymbols() + { + return (DecimalFormatSymbols) symbols.clone(); + } + + /** + * Gets the interval used between a grouping separator and the next. + * For example, a grouping size of 3 means that the number 1234 is + * formatted as 1,234. + * + * The actual character used as grouping separator depends on the + * locale and is defined by {@link DecimalFormatSymbols#getDecimalSeparator()} + * + * @return The interval used between a grouping separator and the next. + */ + public int getGroupingSize() + { + return groupingSize; + } + + /** + * Gets the multiplier used in percent and similar formats. + * + * @return The multiplier used in percent and similar formats. + */ + public int getMultiplier() + { + return multiplier; + } + + /** + * Gets the negative prefix. + * + * @return The negative prefix. + */ + public String getNegativePrefix() + { + return negativePrefix; + } + + /** + * Gets the negative suffix. + * + * @return The negative suffix. + */ + public String getNegativeSuffix() + { + return negativeSuffix; + } + + /** + * Gets the positive prefix. + * + * @return The positive prefix. + */ + public String getPositivePrefix() + { + return positivePrefix; + } + + /** + * Gets the positive suffix. + * + * @return The positive suffix. + */ + public String getPositiveSuffix() + { + return positiveSuffix; + } + + public boolean isDecimalSeparatorAlwaysShown() + { + return decimalSeparatorAlwaysShown; + } + + /** + * Define if parse(java.lang.String, java.text.ParsePosition) + * should return a {@link BigDecimal} or not. + * + * @param newValue + */ + public void setParseBigDecimal(boolean newValue) + { + this.parseBigDecimal = newValue; + } + + /** + * Returns true if + * parse(java.lang.String, java.text.ParsePosition) returns + * a BigDecimal, false otherwise. + * The default return value for this method is false. + * + * @return true if the parse method returns a {@link BigDecimal}, + * false otherwise. + * @since 1.5 + * @see #setParseBigDecimal(boolean) + */ + public boolean isParseBigDecimal() + { + return this.parseBigDecimal; + } + + /** + * This method parses the specified string into a Number. + * + * The parsing starts at pos, which is updated as the parser + * consume characters in the passed string. + * On error, the Position object index is not updated, while + * error position is set appropriately, an null is returned. + * + * @param str The string to parse. + * @param pos The desired ParsePosition. + * + * @return The parsed Number + */ + public Number parse(String str, ParsePosition pos) + { + // a special values before anything else + // NaN + if (str.contains(this.symbols.getNaN())) + return Double.valueOf(Double.NaN); + + // this will be our final number + CPStringBuilder number = new CPStringBuilder(); + + // special character + char minus = symbols.getMinusSign(); + + // starting parsing position + int start = pos.getIndex(); + + // validate the string, it have to be in the + // same form as the format string or parsing will fail + String _negativePrefix = (this.negativePrefix.compareTo("") == 0 + ? minus + positivePrefix + : this.negativePrefix); + + // we check both prefixes, because one might be empty. + // We want to pick the longest prefix that matches. + int positiveLen = positivePrefix.length(); + int negativeLen = _negativePrefix.length(); + + boolean isNegative = str.startsWith(_negativePrefix); + boolean isPositive = str.startsWith(positivePrefix); + + if (isPositive && isNegative) + { + // By checking this way, we preserve ambiguity in the case + // where the negative format differs only in suffix. + if (negativeLen > positiveLen) + { + start += _negativePrefix.length(); + isNegative = true; + } + else + { + start += positivePrefix.length(); + isPositive = true; + if (negativeLen < positiveLen) + isNegative = false; + } + } + else if (isNegative) + { + start += _negativePrefix.length(); + isPositive = false; + } + else if (isPositive) + { + start += positivePrefix.length(); + isNegative = false; + } + else + { + pos.setErrorIndex(start); + return null; + } + + // other special characters used by the parser + char decimalSeparator = symbols.getDecimalSeparator(); + char zero = symbols.getZeroDigit(); + char exponent = symbols.getExponential(); + + // stop parsing position in the string + int stop = start + this.maximumIntegerDigits + maximumFractionDigits + 2; + + if (useExponentialNotation) + stop += minExponentDigits + 1; + + boolean inExponent = false; + + // correct the size of the end parsing flag + int len = str.length(); + if (len < stop) stop = len; + char groupingSeparator = symbols.getGroupingSeparator(); + + int i = start; + while (i < stop) + { + char ch = str.charAt(i); + i++; + + if (ch >= zero && ch <= (zero + 9)) + { + number.append(ch); + } + else if (this.parseIntegerOnly) + { + i--; + break; + } + else if (ch == decimalSeparator) + { + number.append('.'); + } + else if (ch == exponent) + { + number.append(ch); + inExponent = !inExponent; + } + else if ((ch == '+' || ch == '-' || ch == minus)) + { + if (inExponent) + number.append(ch); + else + { + i--; + break; + } + } + else + { + if (!groupingUsed || ch != groupingSeparator) + { + i--; + break; + } + } + } + + // 2nd special case: infinity + // XXX: need to be tested + if (str.contains(symbols.getInfinity())) + { + int inf = str.indexOf(symbols.getInfinity()); + pos.setIndex(inf); + + // FIXME: ouch, this is really ugly and lazy code... + if (this.parseBigDecimal) + { + if (isNegative) + return BigDecimal.valueOf(Double.NEGATIVE_INFINITY); + + return BigDecimal.valueOf(Double.POSITIVE_INFINITY); + } + + if (isNegative) + return Double.valueOf(Double.NEGATIVE_INFINITY); + + return Double.valueOf(Double.POSITIVE_INFINITY); + } + + // no number... + if (i == start || number.length() == 0) + { + pos.setErrorIndex(i); + return null; + } + + // now we have to check the suffix, done here after number parsing + // or the index will not be updated correctly... + boolean hasNegativeSuffix = str.endsWith(this.negativeSuffix); + boolean hasPositiveSuffix = str.endsWith(this.positiveSuffix); + boolean positiveEqualsNegative = negativeSuffix.equals(positiveSuffix); + + positiveLen = positiveSuffix.length(); + negativeLen = negativeSuffix.length(); + + if (isNegative && !hasNegativeSuffix) + { + pos.setErrorIndex(i); + return null; + } + else if (hasNegativeSuffix && + !positiveEqualsNegative && + (negativeLen > positiveLen)) + { + isNegative = true; + } + else if (!hasPositiveSuffix) + { + pos.setErrorIndex(i); + return null; + } + + if (isNegative) number.insert(0, '-'); + + pos.setIndex(i); + + // now we handle the return type + BigDecimal bigDecimal = new BigDecimal(number.toString()); + if (this.parseBigDecimal) + return bigDecimal; + + // want integer? + if (this.parseIntegerOnly) + return Long.valueOf(bigDecimal.longValue()); + + // 3th special case -0.0 + if (isNegative && (bigDecimal.compareTo(BigDecimal.ZERO) == 0)) + return Double.valueOf(-0.0); + + try + { + BigDecimal integer + = bigDecimal.setScale(0, BigDecimal.ROUND_UNNECESSARY); + return Long.valueOf(integer.longValue()); + } + catch (ArithmeticException e) + { + return Double.valueOf(bigDecimal.doubleValue()); + } + } + + /** + * Sets the Currency on the + * DecimalFormatSymbols used, which also sets the + * currency symbols on those symbols. + * + * @param currency The new Currency on the + * DecimalFormatSymbols. + */ + public void setCurrency(Currency currency) + { + Currency current = symbols.getCurrency(); + if (current != currency) + { + String oldSymbol = symbols.getCurrencySymbol(); + int len = oldSymbol.length(); + symbols.setCurrency(currency); + String newSymbol = symbols.getCurrencySymbol(); + int posPre = positivePrefix.indexOf(oldSymbol); + if (posPre != -1) + positivePrefix = positivePrefix.substring(0, posPre) + + newSymbol + positivePrefix.substring(posPre+len); + int negPre = negativePrefix.indexOf(oldSymbol); + if (negPre != -1) + negativePrefix = negativePrefix.substring(0, negPre) + + newSymbol + negativePrefix.substring(negPre+len); + int posSuf = positiveSuffix.indexOf(oldSymbol); + if (posSuf != -1) + positiveSuffix = positiveSuffix.substring(0, posSuf) + + newSymbol + positiveSuffix.substring(posSuf+len); + int negSuf = negativeSuffix.indexOf(oldSymbol); + if (negSuf != -1) + negativeSuffix = negativeSuffix.substring(0, negSuf) + + newSymbol + negativeSuffix.substring(negSuf+len); + } + } + + /** + * Sets the symbols used by this instance. This method makes a copy of + * the supplied symbols. + * + * @param newSymbols the symbols (null not permitted). + */ + public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) + { + symbols = (DecimalFormatSymbols) newSymbols.clone(); + } + + /** + * Define if the decimal separator should be always visible or only + * visible when needed. This method as effect only on integer values. + * Pass true if you want the decimal separator to be + * always shown, false otherwise. + * + * @param newValue true if you want the decimal separator to be + * always shown, false otherwise. + */ + public void setDecimalSeparatorAlwaysShown(boolean newValue) + { + decimalSeparatorAlwaysShown = newValue; + } + + /** + * Sets the number of digits used to group portions of the integer part of + * the number. For example, the number 123456, with a grouping + * size of 3, is rendered 123,456. + * + * @param groupSize The number of digits used while grouping portions + * of the integer part of a number. + */ + public void setGroupingSize(int groupSize) + { + groupingSize = (byte) groupSize; + } + + /** + * Sets the maximum number of digits allowed in the integer + * portion of a number to the specified value. + * The new value will be the choosen as the minimum between + * newvalue and 309. Any value below zero will be + * replaced by zero. + * + * @param newValue The new maximum integer digits value. + */ + public void setMaximumIntegerDigits(int newValue) + { + newValue = (newValue > 0) ? newValue : 0; + super.setMaximumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS)); + } + + /** + * Sets the minimum number of digits allowed in the integer + * portion of a number to the specified value. + * The new value will be the choosen as the minimum between + * newvalue and 309. Any value below zero will be + * replaced by zero. + * + * @param newValue The new minimum integer digits value. + */ + public void setMinimumIntegerDigits(int newValue) + { + newValue = (newValue > 0) ? newValue : 0; + super.setMinimumIntegerDigits(Math.min(newValue, DEFAULT_INTEGER_DIGITS)); + } + + /** + * Sets the maximum number of digits allowed in the fraction + * portion of a number to the specified value. + * The new value will be the choosen as the minimum between + * newvalue and 309. Any value below zero will be + * replaced by zero. + * + * @param newValue The new maximum fraction digits value. + */ + public void setMaximumFractionDigits(int newValue) + { + newValue = (newValue > 0) ? newValue : 0; + super.setMaximumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS)); + } + + /** + * Sets the minimum number of digits allowed in the fraction + * portion of a number to the specified value. + * The new value will be the choosen as the minimum between + * newvalue and 309. Any value below zero will be + * replaced by zero. + * + * @param newValue The new minimum fraction digits value. + */ + public void setMinimumFractionDigits(int newValue) + { + newValue = (newValue > 0) ? newValue : 0; + super.setMinimumFractionDigits(Math.min(newValue, DEFAULT_FRACTION_DIGITS)); + } + + /** + * Sets the multiplier for use in percent and similar formats. + * For example, for percent set the multiplier to 100, for permille, set the + * miltiplier to 1000. + * + * @param newValue the new value for multiplier. + */ + public void setMultiplier(int newValue) + { + multiplier = newValue; + } + + /** + * Sets the negative prefix. + * + * @param newValue The new negative prefix. + */ + public void setNegativePrefix(String newValue) + { + negativePrefix = newValue; + } + + /** + * Sets the negative suffix. + * + * @param newValue The new negative suffix. + */ + public void setNegativeSuffix(String newValue) + { + negativeSuffix = newValue; + } + + /** + * Sets the positive prefix. + * + * @param newValue The new positive prefix. + */ + public void setPositivePrefix(String newValue) + { + positivePrefix = newValue; + } + + /** + * Sets the new positive suffix. + * + * @param newValue The new positive suffix. + */ + public void setPositiveSuffix(String newValue) + { + positiveSuffix = newValue; + } + + /** + * This method returns a string with the formatting pattern being used + * by this object. The string is localized. + * + * @return A localized String with the formatting pattern. + * @see #toPattern() + */ + public String toLocalizedPattern() + { + return computePattern(this.symbols); + } + + /** + * This method returns a string with the formatting pattern being used + * by this object. The string is not localized. + * + * @return A String with the formatting pattern. + * @see #toLocalizedPattern() + */ + public String toPattern() + { + return computePattern(nonLocalizedSymbols); + } + + /* ***** private methods ***** */ + + /** + * This is an shortcut helper method used to test if two given strings are + * equals. + * + * @param s1 The first string to test for equality. + * @param s2 The second string to test for equality. + * @return true if the strings are both null or + * equals. + */ + private boolean equals(String s1, String s2) + { + if (s1 == null || s2 == null) + return s1 == s2; + return s1.equals(s2); + } + + + /* ****** PATTERN ****** */ + + /** + * This helper function creates a string consisting of all the + * characters which can appear in a pattern and must be quoted. + */ + private String patternChars (DecimalFormatSymbols syms) + { + CPStringBuilder buf = new CPStringBuilder (); + + buf.append(syms.getDecimalSeparator()); + buf.append(syms.getDigit()); + buf.append(syms.getExponential()); + buf.append(syms.getGroupingSeparator()); + buf.append(syms.getMinusSign()); + buf.append(syms.getPatternSeparator()); + buf.append(syms.getPercent()); + buf.append(syms.getPerMill()); + buf.append(syms.getZeroDigit()); + buf.append('\''); + buf.append('\u00a4'); + + return buf.toString(); + } + + /** + * Quote special characters as defined by patChars in the + * input string. + * + * @param text + * @param patChars + * @return A StringBuffer with special characters quoted. + */ + private CPStringBuilder quoteFix(String text, String patChars) + { + CPStringBuilder buf = new CPStringBuilder(); + + int len = text.length(); + char ch; + for (int index = 0; index < len; ++index) + { + ch = text.charAt(index); + if (patChars.indexOf(ch) != -1) + { + buf.append('\''); + buf.append(ch); + if (ch != '\'') buf.append('\''); + } + else + { + buf.append(ch); + } + } + + return buf; + } + + /** + * Returns the format pattern, localized to follow the given + * symbols. + */ + private String computePattern(DecimalFormatSymbols symbols) + { + StringBuilder mainPattern = new StringBuilder(); + + // We have to at least emit a zero for the minimum number of + // digits. Past that we need hash marks up to the grouping + // separator (and one beyond). + int _groupingSize = groupingUsed ? groupingSize + 1: groupingSize; + int totalDigits = Math.max(minimumIntegerDigits, _groupingSize); + + // if it is not in exponential notiation, + // we always have a # prebended + if (!useExponentialNotation) mainPattern.append(symbols.getDigit()); + + for (int i = 1; i < totalDigits - minimumIntegerDigits; i++) + mainPattern.append(symbols.getDigit()); + + for (int i = totalDigits - minimumIntegerDigits; i < totalDigits; i++) + mainPattern.append(symbols.getZeroDigit()); + + if (groupingUsed) + { + mainPattern.insert(mainPattern.length() - groupingSize, + symbols.getGroupingSeparator()); + } + + // See if we need decimal info. + if (minimumFractionDigits > 0 || maximumFractionDigits > 0 || + decimalSeparatorAlwaysShown) + { + mainPattern.append(symbols.getDecimalSeparator()); + } + + for (int i = 0; i < minimumFractionDigits; ++i) + mainPattern.append(symbols.getZeroDigit()); + + for (int i = minimumFractionDigits; i < maximumFractionDigits; ++i) + mainPattern.append(symbols.getDigit()); + + if (useExponentialNotation) + { + mainPattern.append(symbols.getExponential()); + + for (int i = 0; i < minExponentDigits; ++i) + mainPattern.append(symbols.getZeroDigit()); + + if (minExponentDigits == 0) + mainPattern.append(symbols.getDigit()); + } + + // save the pattern + String pattern = mainPattern.toString(); + + // so far we have the pattern itself, now we need to add + // the positive and the optional negative prefixes and suffixes + String patternChars = patternChars(symbols); + mainPattern.insert(0, quoteFix(positivePrefix, patternChars)); + mainPattern.append(quoteFix(positiveSuffix, patternChars)); + + if (hasNegativePrefix) + { + mainPattern.append(symbols.getPatternSeparator()); + mainPattern.append(quoteFix(negativePrefix, patternChars)); + mainPattern.append(pattern); + mainPattern.append(quoteFix(negativeSuffix, patternChars)); + } + + // finally, return the pattern string + return mainPattern.toString(); + } + + /* ****** FORMAT PARSING ****** */ + + /** + * Scan the input string and define a pattern suitable for use + * with this decimal format. + * + * @param pattern + * @param symbols + */ + private void applyPatternWithSymbols(String pattern, + DecimalFormatSymbols symbols) + { + // The pattern string is described by a BNF diagram. + // we could use a recursive parser to read and prepare + // the string, but this would be too slow and resource + // intensive, while this code is quite critical as it is + // called always when the class is instantiated and every + // time a new pattern is given. + // Our strategy is to divide the string into section as given by + // the BNF diagram, iterating through the string and setting up + // the parameters we need for formatting (which is basicly what + // a descendent recursive parser would do - but without recursion). + // I'm sure that there are smarter methods to do this. + + // Restore default values. Most of these will be overwritten + // but we want to be sure that nothing is left out. + setDefaultValues(); + + int len = pattern.length(); + if (len == 0) + { + // this is another special case... + this.minimumIntegerDigits = 1; + this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS; + this.minimumFractionDigits = 0; + this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS; + + // FIXME: ...and these values may not be valid in all locales + this.minExponentDigits = 0; + this.showDecimalSeparator = true; + this.groupingUsed = true; + this.groupingSize = 3; + + return; + } + + int start = scanFix(pattern, symbols, 0, true); + if (start < len) start = scanNumberInteger(pattern, symbols, start); + if (start < len) + { + start = scanFractionalPortion(pattern, symbols, start); + } + else + { + // special case, pattern that ends here does not have a fractional + // portion + this.minimumFractionDigits = 0; + this.maximumFractionDigits = 0; + //this.decimalSeparatorAlwaysShown = false; + //this.showDecimalSeparator = false; + } + + // XXX: this fixes a compatibility test with the RI. + // If new uses cases fail, try removing this line first. + //if (!this.hasIntegerPattern && !this.hasFractionalPattern) + // throw new IllegalArgumentException("No valid pattern found!"); + + if (start < len) start = scanExponent(pattern, symbols, start); + if (start < len) start = scanFix(pattern, symbols, start, false); + if (start < len) scanNegativePattern(pattern, symbols, start); + + if (useExponentialNotation && + (maxIntegerDigitsExponent > minimumIntegerDigits) && + (maxIntegerDigitsExponent > 1)) + { + minimumIntegerDigits = 1; + exponentRound = maxIntegerDigitsExponent; + } + + if (useExponentialNotation) + maximumIntegerDigits = maxIntegerDigitsExponent; + + if (!this.hasFractionalPattern && this.showDecimalSeparator == true) + { + this.decimalSeparatorAlwaysShown = true; + } + } + + /** + * Scans for the prefix or suffix portion of the pattern string. + * This method handles the positive subpattern of the pattern string. + * + * @param pattern The pattern string to parse. + * @return The position in the pattern string where parsing ended. + */ + private int scanFix(String pattern, DecimalFormatSymbols sourceSymbols, + int start, boolean prefix) + { + CPStringBuilder buffer = new CPStringBuilder(); + + // the number portion is always delimited by one of those + // characters + char decimalSeparator = sourceSymbols.getDecimalSeparator(); + char patternSeparator = sourceSymbols.getPatternSeparator(); + char groupingSeparator = sourceSymbols.getGroupingSeparator(); + char digit = sourceSymbols.getDigit(); + char zero = sourceSymbols.getZeroDigit(); + char minus = sourceSymbols.getMinusSign(); + + // other special characters, cached here to avoid method calls later + char percent = sourceSymbols.getPercent(); + char permille = sourceSymbols.getPerMill(); + + String currencySymbol = this.symbols.getCurrencySymbol(); + + boolean quote = false; + + char ch = pattern.charAt(start); + if (ch == patternSeparator) + { + // negative subpattern + this.hasNegativePrefix = true; + ++start; + return start; + } + + int len = pattern.length(); + int i; + for (i = start; i < len; i++) + { + ch = pattern.charAt(i); + + // we are entering into the negative subpattern + if (!quote && ch == patternSeparator) + { + if (this.hasNegativePrefix) + { + throw new IllegalArgumentException("Invalid pattern found: " + + start); + } + + this.hasNegativePrefix = true; + ++i; + break; + } + + // this means we are inside the number portion + if (!quote && + (ch == minus || ch == digit || ch == zero || + ch == groupingSeparator)) + break; + + if (!quote && ch == decimalSeparator) + { + this.showDecimalSeparator = true; + break; + } + else if (quote && ch != '\'') + { + buffer.append(ch); + continue; + } + + if (ch == '\u00A4') + { + // CURRENCY + currencySymbol = this.symbols.getCurrencySymbol(); + + // if \u00A4 is doubled, we use the international currency symbol + if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4') + { + currencySymbol = this.symbols.getInternationalCurrencySymbol(); + i++; + } + + this.useCurrencySeparator = true; + buffer.append(currencySymbol); + } + else if (ch == percent) + { + // PERCENT + this.multiplier = 100; + buffer.append(this.symbols.getPercent()); + } + else if (ch == permille) + { + // PERMILLE + this.multiplier = 1000; + buffer.append(this.symbols.getPerMill()); + } + else if (ch == '\'') + { + // QUOTE + if ((i + 1) < len && pattern.charAt(i + 1) == '\'') + { + // we need to add ' to the buffer + buffer.append(ch); + i++; + } + else + { + quote = !quote; + continue; + } + } + else + { + buffer.append(ch); + } + } + + if (prefix) + { + this.positivePrefix = buffer.toString(); + this.negativePrefix = minus + "" + positivePrefix; + } + else + { + this.positiveSuffix = buffer.toString(); + } + + return i; + } + + /** + * Scan the given string for number patterns, starting + * from start. + * This method searches the integer part of the pattern only. + * + * @param pattern The pattern string to parse. + * @param start The starting parse position in the string. + * @return The position in the pattern string where parsing ended, + * counted from the beginning of the string (that is, 0). + */ + private int scanNumberInteger(String pattern, DecimalFormatSymbols symbols, + int start) + { + char digit = symbols.getDigit(); + char zero = symbols.getZeroDigit(); + char groupingSeparator = symbols.getGroupingSeparator(); + char decimalSeparator = symbols.getDecimalSeparator(); + char exponent = symbols.getExponential(); + char patternSeparator = symbols.getPatternSeparator(); + + // count the number of zeroes in the pattern + // this number defines the minum digits in the integer portion + int zeros = 0; + + // count the number of digits used in grouping + int _groupingSize = 0; + + this.maxIntegerDigitsExponent = 0; + + boolean intPartTouched = false; + + char ch; + int len = pattern.length(); + int i; + for (i = start; i < len; i++) + { + ch = pattern.charAt(i); + + // break on decimal separator or exponent or pattern separator + if (ch == decimalSeparator || ch == exponent) + break; + + if (this.hasNegativePrefix && ch == patternSeparator) + throw new IllegalArgumentException("Invalid pattern found: " + + start); + + if (ch == digit) + { + // in our implementation we could relax this strict + // requirement, but this is used to keep compatibility with + // the RI + if (zeros > 0) throw new + IllegalArgumentException("digit mark following zero in " + + "positive subpattern, not allowed. Position: " + i); + + _groupingSize++; + intPartTouched = true; + this.maxIntegerDigitsExponent++; + } + else if (ch == zero) + { + zeros++; + _groupingSize++; + this.maxIntegerDigitsExponent++; + } + else if (ch == groupingSeparator) + { + this.groupingSeparatorInPattern = true; + this.groupingUsed = true; + _groupingSize = 0; + } + else + { + // any other character not listed above + // means we are in the suffix portion + break; + } + } + + if (groupingSeparatorInPattern) this.groupingSize = (byte) _groupingSize; + this.minimumIntegerDigits = zeros; + + // XXX: compatibility code with the RI: the number of minimum integer + // digits is at least one when maximumIntegerDigits is more than zero + if (intPartTouched && this.maximumIntegerDigits > 0 && + this.minimumIntegerDigits == 0) + this.minimumIntegerDigits = 1; + + return i; + } + + /** + * Scan the given string for number patterns, starting + * from start. + * This method searches the fractional part of the pattern only. + * + * @param pattern The pattern string to parse. + * @param start The starting parse position in the string. + * @return The position in the pattern string where parsing ended, + * counted from the beginning of the string (that is, 0). + */ + private int scanFractionalPortion(String pattern, + DecimalFormatSymbols symbols, + int start) + { + char digit = symbols.getDigit(); + char zero = symbols.getZeroDigit(); + char groupingSeparator = symbols.getGroupingSeparator(); + char decimalSeparator = symbols.getDecimalSeparator(); + char exponent = symbols.getExponential(); + char patternSeparator = symbols.getPatternSeparator(); + + // first character needs to be '.' otherwise we are not parsing the + // fractional portion + char ch = pattern.charAt(start); + if (ch != decimalSeparator) + { + this.minimumFractionDigits = 0; + this.maximumFractionDigits = 0; + return start; + } + + ++start; + + this.hasFractionalPattern = true; + + this.minimumFractionDigits = 0; + int digits = 0; + + int len = pattern.length(); + int i; + for (i = start; i < len; i++) + { + ch = pattern.charAt(i); + + // we hit the exponential or negative subpattern + if (ch == exponent || ch == patternSeparator) + break; + + // pattern error + if (ch == groupingSeparator || ch == decimalSeparator) throw new + IllegalArgumentException("unexpected character '" + ch + "' " + + "in fractional subpattern. Position: " + i); + + if (ch == digit) + { + digits++; + } + else if (ch == zero) + { + if (digits > 0) throw new + IllegalArgumentException("digit mark following zero in " + + "positive subpattern, not allowed. Position: " + i); + + this.minimumFractionDigits++; + } + else + { + // we are in the suffix section of pattern + break; + } + } + + if (i == start) this.hasFractionalPattern = false; + + this.maximumFractionDigits = this.minimumFractionDigits + digits; + this.showDecimalSeparator = true; + + return i; + } + + /** + * Scan the given string for number patterns, starting + * from start. + * This method searches the expoential part of the pattern only. + * + * @param pattern The pattern string to parse. + * @param start The starting parse position in the string. + * @return The position in the pattern string where parsing ended, + * counted from the beginning of the string (that is, 0). + */ + private int scanExponent(String pattern, DecimalFormatSymbols symbols, + int start) + { + char digit = symbols.getDigit(); + char zero = symbols.getZeroDigit(); + char groupingSeparator = symbols.getGroupingSeparator(); + char decimalSeparator = symbols.getDecimalSeparator(); + char exponent = symbols.getExponential(); + + char ch = pattern.charAt(start); + + if (ch == decimalSeparator) + { + // ignore dots + ++start; + } + + if (ch != exponent) + { + this.useExponentialNotation = false; + return start; + } + + ++start; + + this.minExponentDigits = 0; + + int len = pattern.length(); + int i; + for (i = start; i < len; i++) + { + ch = pattern.charAt(i); + + if (ch == groupingSeparator || ch == decimalSeparator || + ch == digit || ch == exponent) throw new + IllegalArgumentException("unexpected character '" + ch + "' " + + "in exponential subpattern. Position: " + i); + + if (ch == zero) + { + this.minExponentDigits++; + } + else + { + // any character other than zero is an exit point + break; + } + } + + this.useExponentialNotation = true; + + return i; + } + + /** + * Scan the given string for number patterns, starting + * from start. + * This method searches the negative part of the pattern only and scan + * throught the end of the string. + * + * @param pattern The pattern string to parse. + * @param start The starting parse position in the string. + */ + private void scanNegativePattern(String pattern, + DecimalFormatSymbols sourceSymbols, + int start) + { + StringBuilder buffer = new StringBuilder(); + + // the number portion is always delimited by one of those + // characters + char decimalSeparator = sourceSymbols.getDecimalSeparator(); + char patternSeparator = sourceSymbols.getPatternSeparator(); + char groupingSeparator = sourceSymbols.getGroupingSeparator(); + char digit = sourceSymbols.getDigit(); + char zero = sourceSymbols.getZeroDigit(); + char minus = sourceSymbols.getMinusSign(); + + // other special charcaters, cached here to avoid method calls later + char percent = sourceSymbols.getPercent(); + char permille = sourceSymbols.getPerMill(); + + String CURRENCY_SYMBOL = this.symbols.getCurrencySymbol(); + String currencySymbol = CURRENCY_SYMBOL; + + boolean quote = false; + boolean prefixDone = false; + + int len = pattern.length(); + if (len > 0) this.hasNegativePrefix = true; + + char ch = pattern.charAt(start); + if (ch == patternSeparator) + { + // no pattern separator in the negative pattern + if ((start + 1) > len) throw new + IllegalArgumentException("unexpected character '" + ch + "' " + + "in negative subpattern."); + start++; + } + + int i; + for (i = start; i < len; i++) + { + ch = pattern.charAt(i); + + // this means we are inside the number portion + if (!quote && + (ch == digit || ch == zero || ch == decimalSeparator || + ch == patternSeparator || ch == groupingSeparator)) + { + if (!prefixDone) + { + this.negativePrefix = buffer.toString(); + buffer.delete(0, buffer.length()); + prefixDone = true; + } + } + else if (ch == minus) + { + buffer.append(this.symbols.getMinusSign()); + } + else if (quote && ch != '\'') + { + buffer.append(ch); + } + else if (ch == '\u00A4') + { + // CURRENCY + currencySymbol = CURRENCY_SYMBOL; + + // if \u00A4 is doubled, we use the international currency symbol + if ((i + 1) < len && pattern.charAt(i + 1) == '\u00A4') + { + currencySymbol = this.symbols.getInternationalCurrencySymbol(); + i = i + 2; + } + + // FIXME: not sure about this, the specs says that we only have to + // change prefix and suffix, so leave it as commented + // unless in case of bug report/errors + //this.useCurrencySeparator = true; + + buffer.append(currencySymbol); + } + else if (ch == percent) + { + // PERCENT + this.negativePatternMultiplier = 100; + buffer.append(this.symbols.getPercent()); + } + else if (ch == permille) + { + // PERMILLE + this.negativePatternMultiplier = 1000; + buffer.append(this.symbols.getPerMill()); + } + else if (ch == '\'') + { + // QUOTE + if ((i + 1) < len && pattern.charAt(i + 1) == '\'') + { + // we need to add ' to the buffer + buffer.append(ch); + i++; + } + else + { + quote = !quote; + } + } + else if (ch == patternSeparator) + { + // no pattern separator in the negative pattern + throw new IllegalArgumentException("unexpected character '" + ch + + "' in negative subpattern."); + } + else + { + buffer.append(ch); + } + } + + if (prefixDone) + this.negativeSuffix = buffer.toString(); + else + this.negativePrefix = buffer.toString(); + } + + /* ****** FORMATTING ****** */ + + /** + * Handles the real formatting. + * + * We use a BigDecimal to format the number without precision loss. + * All the rounding is done by methods in BigDecimal. + * The isLong parameter is used to determine if we are + * formatting a long or BigInteger. In this case, we avoid to format + * the fractional part of the number (unless specified otherwise in the + * format string) that would consist only of a 0 digit. + * + * @param number A BigDecimal representation fo the input number. + * @param dest The destination buffer. + * @param isLong A boolean that indicates if this BigDecimal is a real + * decimal or an integer. + * @param fieldPos Use to keep track of the formatting position. + */ + private void formatInternal(BigDecimal number, boolean isLong, + StringBuffer dest, FieldPosition fieldPos) + { + // The specs says that fieldPos should not be null, and that we + // should throw a NPE, but it seems that in few classes that + // reference this one, fieldPos is set to null. + // This is even defined in the javadoc, see for example MessageFormat. + // I think the best here is to check for fieldPos and build one if it is + // null. If it cause harms or regressions, just remove this line and + // fix the classes in the point of call, insted. + if (fieldPos == null) fieldPos = new FieldPosition(0); + + int _multiplier = this.multiplier; + + // used to track attribute starting position for each attribute + int attributeStart = -1; + + // now get the sign this will be used by the special case Inifinity + // and by the normal cases. + boolean isNegative = (number.signum() < 0) ? true : false; + if (isNegative) + { + attributeStart = dest.length(); + + // append the negative prefix to the string + dest.append(negativePrefix); + + // once got the negative prefix, we can use + // the absolute value. + number = number.abs(); + + _multiplier = negativePatternMultiplier; + + addAttribute(Field.SIGN, attributeStart, dest.length()); + } + else + { + // not negative, use the positive prefix + dest.append(positivePrefix); + } + + // these are used ot update the field position + int beginIndexInt = dest.length(); + int endIndexInt = 0; + int beginIndexFract = 0; + int endIndexFract = 0; + + // compute the multiplier to use with percent and similar + number = number.multiply(BigDecimal.valueOf(_multiplier)); + + // XXX: special case, not sure if it belongs here or if it is + // correct at all. There may be other special cases as well + // these should be handled in the format string parser. + if (this.maximumIntegerDigits == 0 && this.maximumFractionDigits == 0) + { + number = BigDecimal.ZERO; + this.maximumIntegerDigits = 1; + this.minimumIntegerDigits = 1; + } + + // get the absolute number + number = number.abs(); + + // the scaling to use while formatting this number + int scale = this.maximumFractionDigits; + + // this is the actual number we will use + // it is corrected later on to handle exponential + // notation, if needed + long exponent = 0; + + // are we using exponential notation? + if (this.useExponentialNotation) + { + exponent = getExponent(number); + number = number.movePointLeft((int) exponent); + + // FIXME: this makes the test ##.###E0 to pass, + // but all all the other tests to fail... + // this should be really something like + // min + max - what is already shown... + //scale = this.minimumIntegerDigits + this.maximumFractionDigits; + } + + // round the number to the nearest neighbor + number = number.setScale(scale, BigDecimal.ROUND_HALF_EVEN); + + // now get the integer and fractional part of the string + // that will be processed later + String plain = number.toPlainString(); + + String intPart = null; + String fractPart = null; + + // remove - from the integer part, this is needed as + // the Narrowing Primitive Conversions algorithm used may loose + // information about the sign + int minusIndex = plain.lastIndexOf('-', 0); + if (minusIndex > -1) plain = plain.substring(minusIndex + 1); + + // strip the decimal portion + int dot = plain.indexOf('.'); + if (dot > -1) + { + intPart = plain.substring(0, dot); + dot++; + + if (useExponentialNotation) + fractPart = plain.substring(dot, dot + scale); + else + fractPart = plain.substring(dot); + } + else + { + intPart = plain; + } + + // used in various places later on + int intPartLen = intPart.length(); + endIndexInt = intPartLen; + + // if the number of digits in our intPart is not greater than the + // minimum we have to display, we append zero to the destination + // buffer before adding the integer portion of the number. + int zeroes = minimumIntegerDigits - intPartLen; + if (zeroes > 0) + { + attributeStart = Math.max(dest.length() - 1, 0); + appendZero(dest, zeroes, minimumIntegerDigits); + } + + if (this.useExponentialNotation) + { + // For exponential numbers, the significant in mantissa are + // the sum of the minimum integer and maximum fraction + // digits, and does not take into account the maximun integer + // digits to display. + + if (attributeStart < 0) + attributeStart = Math.max(dest.length() - 1, 0); + appendDigit(intPart, dest, this.groupingUsed); + } + else + { + // non exponential notation + intPartLen = intPart.length(); + int canary = Math.min(intPartLen, this.maximumIntegerDigits); + + // remove from the string the number in excess + // use only latest digits + intPart = intPart.substring(intPartLen - canary); + endIndexInt = intPart.length() + 1; + + // append it + if (maximumIntegerDigits > 0 && + !(this.minimumIntegerDigits == 0 && + intPart.compareTo(String.valueOf(symbols.getZeroDigit())) == 0)) + { + if (attributeStart < 0) + attributeStart = Math.max(dest.length() - 1, 0); + appendDigit(intPart, dest, this.groupingUsed); + } + } + + // add the INTEGER attribute + addAttribute(Field.INTEGER, attributeStart, dest.length()); + + // ...update field position, if needed, and return... + if ((fieldPos.getField() == INTEGER_FIELD || + fieldPos.getFieldAttribute() == NumberFormat.Field.INTEGER)) + { + fieldPos.setBeginIndex(beginIndexInt); + fieldPos.setEndIndex(endIndexInt); + } + + handleFractionalPart(dest, fractPart, fieldPos, isLong); + + // and the exponent + if (this.useExponentialNotation) + { + attributeStart = dest.length(); + + dest.append(symbols.getExponential()); + + addAttribute(Field.EXPONENT_SYMBOL, attributeStart, dest.length()); + attributeStart = dest.length(); + + if (exponent < 0) + { + dest.append(symbols.getMinusSign()); + exponent = -exponent; + + addAttribute(Field.EXPONENT_SIGN, attributeStart, dest.length()); + } + + attributeStart = dest.length(); + + String exponentString = String.valueOf(exponent); + int exponentLength = exponentString.length(); + + for (int i = 0; i < minExponentDigits - exponentLength; i++) + dest.append(symbols.getZeroDigit()); + + for (int i = 0; i < exponentLength; ++i) + dest.append(exponentString.charAt(i)); + + addAttribute(Field.EXPONENT, attributeStart, dest.length()); + } + + // now include the suffixes... + if (isNegative) + { + dest.append(negativeSuffix); + } + else + { + dest.append(positiveSuffix); + } + } + + /** + * Add to the input buffer the result of formatting the fractional + * portion of the number. + * + * @param dest + * @param fractPart + * @param fieldPos + * @param isLong + */ + private void handleFractionalPart(StringBuffer dest, String fractPart, + FieldPosition fieldPos, boolean isLong) + { + int dotStart = 0; + int dotEnd = 0; + boolean addDecimal = false; + + if (this.decimalSeparatorAlwaysShown || + ((!isLong || this.useExponentialNotation) && + this.showDecimalSeparator && this.maximumFractionDigits > 0) || + this.minimumFractionDigits > 0) + { + dotStart = dest.length(); + + if (this.useCurrencySeparator) + dest.append(symbols.getMonetaryDecimalSeparator()); + else + dest.append(symbols.getDecimalSeparator()); + + dotEnd = dest.length(); + addDecimal = true; + } + + // now handle the fraction portion of the number + int fractStart = 0; + int fractEnd = 0; + boolean addFractional = false; + + if ((!isLong || this.useExponentialNotation) + && this.maximumFractionDigits > 0 + || this.minimumFractionDigits > 0) + { + fractStart = dest.length(); + fractEnd = fractStart; + + int digits = this.minimumFractionDigits; + + if (this.useExponentialNotation) + { + digits = (this.minimumIntegerDigits + this.minimumFractionDigits) + - dest.length(); + if (digits < 0) digits = 0; + } + + fractPart = adjustTrailingZeros(fractPart, digits); + + // FIXME: this code must be improved + // now check if the factional part is just 0, in this case + // we need to remove the '.' unless requested + boolean allZeros = true; + char fracts[] = fractPart.toCharArray(); + for (int i = 0; i < fracts.length; i++) + { + if (fracts[i] != '0') + allZeros = false; + } + + if (!allZeros || (minimumFractionDigits > 0)) + { + appendDigit(fractPart, dest, false); + fractEnd = dest.length(); + + addDecimal = true; + addFractional = true; + } + else if (!this.decimalSeparatorAlwaysShown) + { + dest.deleteCharAt(dest.length() - 1); + addDecimal = false; + } + else + { + fractEnd = dest.length(); + addFractional = true; + } + } + + if (addDecimal) + addAttribute(Field.DECIMAL_SEPARATOR, dotStart, dotEnd); + + if (addFractional) + addAttribute(Field.FRACTION, fractStart, fractEnd); + + if ((fieldPos.getField() == FRACTION_FIELD || + fieldPos.getFieldAttribute() == NumberFormat.Field.FRACTION)) + { + fieldPos.setBeginIndex(fractStart); + fieldPos.setEndIndex(fractEnd); + } + } + + /** + * Append to destthe give number of zeros. + * Grouping is added if needed. + * The integer totalDigitCount defines the total number of digits + * of the number to which we are appending zeroes. + */ + private void appendZero(StringBuffer dest, int zeroes, int totalDigitCount) + { + char ch = symbols.getZeroDigit(); + char gSeparator = symbols.getGroupingSeparator(); + + int i = 0; + int gPos = totalDigitCount; + for (i = 0; i < zeroes; i++, gPos--) + { + if (this.groupingSeparatorInPattern && + (this.groupingUsed && this.groupingSize != 0) && + (gPos % groupingSize == 0 && i > 0)) + dest.append(gSeparator); + + dest.append(ch); + } + + // special case, that requires adding an additional separator + if (this.groupingSeparatorInPattern && + (this.groupingUsed && this.groupingSize != 0) && + (gPos % groupingSize == 0)) + dest.append(gSeparator); + } + + /** + * Append src to dest. + * + * Grouping is added if groupingUsed is set + * to true. + */ + private void appendDigit(String src, StringBuffer dest, + boolean groupingUsed) + { + int zero = symbols.getZeroDigit() - '0'; + + int ch; + char gSeparator = symbols.getGroupingSeparator(); + + int len = src.length(); + for (int i = 0, gPos = len; i < len; i++, gPos--) + { + ch = src.charAt(i); + if (groupingUsed && this.groupingSize != 0 && + gPos % groupingSize == 0 && i > 0) + dest.append(gSeparator); + + dest.append((char) (zero + ch)); + } + } + + /** + * Calculate the exponent to use if eponential notation is used. + * The exponent is calculated as a power of ten. + * number should be positive, if is zero, or less than zero, + * zero is returned. + */ + private long getExponent(BigDecimal number) + { + long exponent = 0; + + if (number.signum() > 0) + { + double _number = number.doubleValue(); + exponent = (long) Math.floor (Math.log10(_number)); + + // get the right value for the exponent + exponent = exponent - (exponent % this.exponentRound); + + // if the minimumIntegerDigits is more than zero + // we display minimumIntegerDigits of digits. + // so, for example, if minimumIntegerDigits == 2 + // and the actual number is 0.123 it will be + // formatted as 12.3E-2 + // this means that the exponent have to be shifted + // to the correct value. + if (minimumIntegerDigits > 0) + exponent -= minimumIntegerDigits - 1; + } + + return exponent; + } + + /** + * Remove contiguos zeros from the end of the src string, + * if src contains more than minimumDigits digits. + * if src contains less that minimumDigits, + * then append zeros to the string. + * + * Only the first block of zero digits is removed from the string + * and only if they fall in the src.length - minimumDigits + * portion of the string. + * + * @param src The string with the correct number of zeros. + */ + private String adjustTrailingZeros(String src, int minimumDigits) + { + int len = src.length(); + String result; + + // remove all trailing zero + if (len > minimumDigits) + { + int zeros = 0; + for (int i = len - 1; i > minimumDigits; i--) + { + if (src.charAt(i) == '0') + ++zeros; + else + break; + } + result = src.substring(0, len - zeros); + } + else + { + char zero = symbols.getZeroDigit(); + CPStringBuilder _result = new CPStringBuilder(src); + for (int i = len; i < minimumDigits; i++) + { + _result.append(zero); + } + result = _result.toString(); + } + + return result; + } + + /** + * Adds an attribute to the attributes list. + * + * @param field + * @param begin + * @param end + */ + private void addAttribute(Field field, int begin, int end) + { + /* + * This method and its implementation derives directly from the + * ICU4J (http://icu.sourceforge.net/) library, distributed under MIT/X. + */ + + FieldPosition pos = new FieldPosition(field); + pos.setBeginIndex(begin); + pos.setEndIndex(end); + attributes.add(pos); + } + + /** + * Sets the default values for the various properties in this DecimaFormat. + */ + private void setDefaultValues() + { + // Maybe we should add these values to the message bundle and take + // the most appropriate for them for any locale. + // Anyway, these seem to be good values for a default in most languages. + // Note that most of these will change based on the format string. + + this.negativePrefix = String.valueOf(symbols.getMinusSign()); + this.negativeSuffix = ""; + this.positivePrefix = ""; + this.positiveSuffix = ""; + + this.multiplier = 1; + this.negativePatternMultiplier = 1; + this.exponentRound = 1; + + this.hasNegativePrefix = false; + + this.minimumIntegerDigits = 1; + this.maximumIntegerDigits = DEFAULT_INTEGER_DIGITS; + this.minimumFractionDigits = 0; + this.maximumFractionDigits = DEFAULT_FRACTION_DIGITS; + this.minExponentDigits = 0; + + this.groupingSize = 0; + + this.decimalSeparatorAlwaysShown = false; + this.showDecimalSeparator = false; + this.useExponentialNotation = false; + this.groupingUsed = false; + this.groupingSeparatorInPattern = false; + + this.useCurrencySeparator = false; + + this.hasFractionalPattern = false; + } +} diff --git a/libjava/classpath/java/text/DecimalFormatSymbols.java b/libjava/classpath/java/text/DecimalFormatSymbols.java new file mode 100644 index 000000000..fa33f34e4 --- /dev/null +++ b/libjava/classpath/java/text/DecimalFormatSymbols.java @@ -0,0 +1,766 @@ +/* DecimalFormatSymbols.java -- Format symbols used by DecimalFormat + Copyright (C) 1999, 2000, 2001, 2004, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.locale.LocaleHelper; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +import java.text.spi.DecimalFormatSymbolsProvider; + +import java.util.Currency; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.ServiceLoader; + +/** + * This class is a container for the symbols used by + * DecimalFormat to format numbers and currency + * for a particular locale. These are + * normally handled automatically, but an application can override + * values as desired using this class. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @date February 24, 1999 + * @see java.text.DecimalFormat + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2. + */ +public class DecimalFormatSymbols implements Cloneable, Serializable +{ + public Object clone () + { + try + { + return super.clone(); + } + catch(CloneNotSupportedException e) + { + return null; + } + } + + /** + * This method initializes a new instance of + * DecimalFormatSymbols for the default locale. + * This constructor only obtains instances using the runtime's resources; + * to also include {@link java.text.spi.DateFormatSymbolsProvider} instances, + * call {@link #getInstance()} instead. + * + * @see #getInstance() + */ + public DecimalFormatSymbols () + { + this (Locale.getDefault()); + } + + /** + * Retrieves a valid string, either using the supplied resource + * bundle or the default value. + * + * @param bundle the resource bundle to use to find the string. + * @param name key for the string in the resource bundle. + * @param def default value for the string. + */ + private String safeGetString(ResourceBundle bundle, + String name, String def) + { + if (bundle != null) + { + try + { + return bundle.getString(name); + } + catch (MissingResourceException x) + { + } + } + return def; + } + + private char safeGetChar(ResourceBundle bundle, + String name, char def) + { + String r = null; + if (bundle != null) + { + try + { + r = bundle.getString(name); + } + catch (MissingResourceException x) + { + } + } + if (r == null || r.length() < 1) + return def; + return r.charAt(0); + } + + /** + * This method initializes a new instance of + * DecimalFormatSymbols for the specified locale. + * Note: if the locale does not have an associated + * Currency instance, the currency symbol and + * international currency symbol will be set to the strings "?" + * and "XXX" respectively. This generally happens with language + * locales (those with no specified country), such as + * Locale.ENGLISH. This constructor only obtains + * instances using the runtime's resources; to also include + * {@link java.text.spi.DecimalFormatSymbolsProvider} instances, + * call {@link #getInstance(java.util.Locale)} instead. + * + * @param loc The local to load symbols for. + * @throws NullPointerException if the locale is null. + * @see #getInstance(java.util.Locale) + */ + public DecimalFormatSymbols (Locale loc) + { + ResourceBundle res; + + try + { + res = ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + } + catch (MissingResourceException x) + { + res = null; + } + locale = loc; + currency = Currency.getInstance("XXX"); + currencySymbol = "?"; + intlCurrencySymbol = "XXX"; + try + { + Currency localeCurrency = Currency.getInstance(loc); + if (localeCurrency != null) + { + setCurrency(localeCurrency); + } + } + catch(IllegalArgumentException exception) + { + /* Locale has an invalid currency */ + } + decimalSeparator = safeGetChar (res, "decimalSeparator", '.'); + digit = safeGetChar (res, "digit", '#'); + exponential = safeGetChar (res, "exponential", 'E'); + groupingSeparator = safeGetChar (res, "groupingSeparator", ','); + infinity = safeGetString (res, "infinity", "\u221e"); + try + { + monetarySeparator = safeGetChar (res, "monetarySeparator", '.'); + } + catch (MissingResourceException x) + { + monetarySeparator = decimalSeparator; + } + minusSign = safeGetChar (res, "minusSign", '-'); + NaN = safeGetString (res, "NaN", "\ufffd"); + patternSeparator = safeGetChar (res, "patternSeparator", ';'); + percent = safeGetChar (res, "percent", '%'); + perMill = safeGetChar (res, "perMill", '\u2030'); + zeroDigit = safeGetChar (res, "zeroDigit", '0'); + } + + /** + * This method this this object for equality against the specified object. + * This will be true if and only if the following criteria are met with + * regard to the specified object: + *

        + *

          + *
        • It is not null.
        • + *
        • It is an instance of DecimalFormatSymbols.
        • + *
        • All of its symbols are identical to the symbols in this object.
        • + *
        + * + * @return true if the specified object is equal to this + * object, false otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof DecimalFormatSymbols)) + return false; + DecimalFormatSymbols dfs = (DecimalFormatSymbols) obj; + return (currencySymbol.equals(dfs.currencySymbol) + && decimalSeparator == dfs.decimalSeparator + && digit == dfs.digit + && exponential == dfs.exponential + && groupingSeparator == dfs.groupingSeparator + && infinity.equals(dfs.infinity) + && intlCurrencySymbol.equals(dfs.intlCurrencySymbol) + && minusSign == dfs.minusSign + && monetarySeparator == dfs.monetarySeparator + && NaN.equals(dfs.NaN) + && patternSeparator == dfs.patternSeparator + && percent == dfs.percent + && perMill == dfs.perMill + && zeroDigit == dfs.zeroDigit); + } + + /** + * Returns the currency corresponding to the currency symbol stored + * in this instance of DecimalFormatSymbols. + * + * @return An instance of Currency which matches + * the currency used, or null if there is no corresponding + * instance. + */ + public Currency getCurrency () + { + return currency; + } + + /** + * This method returns the currency symbol in local format. For example, + * "$" for Canadian dollars. + * + * @return The currency symbol in local format. + */ + public String getCurrencySymbol () + { + return currencySymbol; + } + + /** + * This method returns the character used as the decimal point. + * + * @return The character used as the decimal point. + */ + public char getDecimalSeparator () + { + return decimalSeparator; + } + + /** + * This method returns the character used to represent a digit in a + * format pattern string. + * + * @return The character used to represent a digit in a format + * pattern string. + */ + public char getDigit () + { + return digit; + } + + /** + * This method returns the character used to represent the exponential + * format. This is a GNU Classpath extension. + * + * @return the character used to represent an exponential in a format + * pattern string. + */ + char getExponential () + { + return exponential; + } + + /** + * This method sets the character used to separate groups of digits. For + * example, the United States uses a comma (,) to separate thousands in + * a number. + * + * @return The character used to separate groups of digits. + */ + public char getGroupingSeparator () + { + return groupingSeparator; + } + + /** + * This method returns the character used to represent infinity. + * + * @return The character used to represent infinity. + */ + public String getInfinity () + { + return infinity; + } + + /** + * This method returns the ISO 4217 currency code for + * the currency used. + * + * @return the ISO 4217 currency code. + */ + public String getInternationalCurrencySymbol () + { + return intlCurrencySymbol; + } + + /** + * This method returns the character used to represent the minus sign. + * + * @return The character used to represent the minus sign. + */ + public char getMinusSign () + { + return minusSign; + } + + /** + * This method returns the character used to represent the decimal + * point for currency values. + * + * @return The decimal point character used in currency values. + */ + public char getMonetaryDecimalSeparator () + { + return monetarySeparator; + } + + /** + * This method returns the string used to represent the NaN (not a number) + * value. + * + * @return The string used to represent NaN + */ + public String getNaN () + { + return NaN; + } + + /** + * This method returns the character used to separate positive and negative + * subpatterns in a format pattern. + * + * @return The character used to separate positive and negative subpatterns + * in a format pattern. + */ + public char getPatternSeparator () + { + return patternSeparator; + } + + /** + * This method returns the character used as the percent sign. + * + * @return The character used as the percent sign. + */ + public char getPercent () + { + return percent; + } + + /** + * This method returns the character used as the per mille character. + * + * @return The per mille character. + */ + public char getPerMill () + { + return perMill; + } + + /** + * This method returns the character used to represent the digit zero. + * + * @return The character used to represent the digit zero. + */ + public char getZeroDigit () + { + return zeroDigit; + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode () + { + // Compute based on zero digit, grouping separator, and decimal + // separator -- JCL book. This probably isn't a very good hash + // code. + return zeroDigit << 16 + groupingSeparator << 8 + decimalSeparator; + } + + /** + * This method sets the currency symbol and ISO 4217 currency + * code to the values obtained from the supplied currency. + * + * @param currency the currency from which to obtain the values. + * @throws NullPointerException if the currency is null. + */ + public void setCurrency (Currency currency) + { + intlCurrencySymbol = currency.getCurrencyCode(); + currencySymbol = currency.getSymbol(locale); + this.currency = currency; + } + + /** + * This method sets the currency symbol to the specified value. + * + * @param currency The new currency symbol + */ + public void setCurrencySymbol (String currency) + { + currencySymbol = currency; + } + + /** + * This method sets the decimal point character to the specified value. + * + * @param decimalSep The new decimal point character + */ + public void setDecimalSeparator (char decimalSep) + { + decimalSeparator = decimalSep; + } + + /** + * This method sets the character used to represents a digit in a format + * string to the specified value. + * + * @param digit The character used to represent a digit in a format pattern. + */ + public void setDigit (char digit) + { + this.digit = digit; + } + + /** + * This method sets the exponential character used in the format string to + * the specified value. This is a GNU Classpath extension. + * + * @param exp the character used for the exponential in a format pattern. + */ + void setExponential (char exp) + { + exponential = exp; + } + + /** + * This method sets the character used to separate groups of digits. + * + * @param groupSep The character used to separate groups of digits. + */ + public void setGroupingSeparator (char groupSep) + { + groupingSeparator = groupSep; + } + + /** + * This method sets the string used to represents infinity. + * + * @param infinity The string used to represent infinity. + */ + public void setInfinity (String infinity) + { + this.infinity = infinity; + } + + /** + * This method sets the international currency symbol to the + * specified value. If a valid Currency instance + * exists for the international currency code, then this is + * used for the currency attribute, and the currency symbol + * is set to the corresponding value from this instance. + * Otherwise, the currency attribute is set to null and the + * symbol is left unmodified. + * + * @param currencyCode The new international currency symbol. + */ + public void setInternationalCurrencySymbol (String currencyCode) + { + intlCurrencySymbol = currencyCode; + try + { + currency = Currency.getInstance(currencyCode); + } + catch (IllegalArgumentException exception) + { + currency = null; + } + if (currency != null) + { + setCurrencySymbol(currency.getSymbol(locale)); + } + } + + /** + * This method sets the character used to represent the minus sign. + * + * @param minusSign The character used to represent the minus sign. + */ + public void setMinusSign (char minusSign) + { + this.minusSign = minusSign; + } + + /** + * This method sets the character used for the decimal point in currency + * values. + * + * @param decimalSep The decimal point character used in currency values. + */ + public void setMonetaryDecimalSeparator (char decimalSep) + { + monetarySeparator = decimalSep; + } + + /** + * This method sets the string used to represent the NaN (not a + * number) value. + * + * @param nan The string used to represent NaN + */ + public void setNaN (String nan) + { + NaN = nan; + } + + /** + * This method sets the character used to separate positive and negative + * subpatterns in a format pattern. + * + * @param patternSep The character used to separate positive and + * negative subpatterns in a format pattern. + */ + public void setPatternSeparator (char patternSep) + { + patternSeparator = patternSep; + } + + /** + * This method sets the character used as the percent sign. + * + * @param percent The character used as the percent sign. + */ + public void setPercent (char percent) + { + this.percent = percent; + } + + /** + * This method sets the character used as the per mille character. + * + * @param perMill The per mille character. + */ + public void setPerMill (char perMill) + { + this.perMill = perMill; + } + + /** + * This method sets the character used to represent the digit zero. + * + * @param zeroDigit The character used to represent the digit zero. + */ + public void setZeroDigit (char zeroDigit) + { + this.zeroDigit = zeroDigit; + } + + /** + * @serial A string used for the local currency + */ + private String currencySymbol; + /** + * @serial The char used to separate decimals in a number. + */ + private char decimalSeparator; + /** + * @serial This is the char used to represent a digit in + * a format specification. + */ + private char digit; + /** + * @serial This is the char used to represent the exponent + * separator in exponential notation. + */ + private char exponential; + /** + * @serial This separates groups of thousands in numbers. + */ + private char groupingSeparator; + /** + * @serial This string represents infinity. + */ + private String infinity; + /** + * @serial This string represents the local currency in an international + * context, eg, "C$" for Canadian dollars. + */ + private String intlCurrencySymbol; + /** + * @serial This is the character used to represent the minus sign. + */ + private char minusSign; + /** + * @serial This character is used to separate decimals when formatting + * currency values. + */ + private char monetarySeparator; + /** + * @serial This string is used the represent the Java NaN value for + * "not a number". + */ + private String NaN; + /** + * @serial This is the character used to separate positive and negative + * subpatterns in a format pattern. + */ + private char patternSeparator; + /** + * @serial This is the percent symbols + */ + private char percent; + /** + * @serial This character is used for the mille percent sign. + */ + private char perMill; + /** + * @serial This value represents the type of object being de-serialized. + * 0 indicates a pre-Java 1.1.6 version, 1 indicates 1.1.6 or later. + * 0 indicates a pre-Java 1.1.6 version, 1 indicates 1.1.6 or later, + * 2 indicates 1.4 or later + */ + private int serialVersionOnStream = 2; + /** + * @serial This is the character used to represent 0. + */ + private char zeroDigit; + + /** + * @serial The locale of these currency symbols. + */ + private Locale locale; + + /** + * The currency used for the symbols in this instance. + * This is stored temporarily for efficiency reasons, + * as well as to ensure that the correct instance + * is restored from the currency code. + * + * @serial Ignored. + */ + private transient Currency currency; + + private static final long serialVersionUID = 5772796243397350300L; + + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (serialVersionOnStream < 1) + { + monetarySeparator = decimalSeparator; + exponential = 'E'; + } + if (serialVersionOnStream < 2) + locale = Locale.getDefault(); + + serialVersionOnStream = 2; + } + + /** + * Returns a {@link DecimalFormatSymbols} instance for the + * default locale obtained from either the runtime itself + * or one of the installed + * {@link java.text.spi.DecimalFormatSymbolsProvider} instances. + * This is equivalent to calling + * getInstance(Locale.getDefault()). + * + * @return a {@link DecimalFormatSymbols} instance for the default + * locale. + * @since 1.6 + */ + public static final DecimalFormatSymbols getInstance() + { + return getInstance(Locale.getDefault()); + } + + /** + * Returns a {@link DecimalFormatSymbols} instance for the + * specified locale obtained from either the runtime itself + * or one of the installed + * {@link java.text.spi.DecimalFormatSymbolsProvider} instances. + * + * @param locale the locale for which an instance should be + * returned. + * @return a {@link DecimalFormatSymbols} instance for the specified + * locale. + * @throws NullPointerException if locale is + * null. + * @since 1.6 + */ + public static final DecimalFormatSymbols getInstance(Locale locale) + { + try + { + if (!locale.equals(Locale.ROOT)) + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + locale, + ClassLoader.getSystemClassLoader()); + return new DecimalFormatSymbols(locale); + } + catch (MissingResourceException x) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (DecimalFormatSymbolsProvider p : + ServiceLoader.load(DecimalFormatSymbolsProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + DecimalFormatSymbols syms = p.getInstance(locale); + if (syms != null) + return syms; + break; + } + } + } + return getInstance(LocaleHelper.getFallbackLocale(locale)); + } + +} diff --git a/libjava/classpath/java/text/FieldPosition.java b/libjava/classpath/java/text/FieldPosition.java new file mode 100644 index 000000000..3a92a6778 --- /dev/null +++ b/libjava/classpath/java/text/FieldPosition.java @@ -0,0 +1,232 @@ +/* FieldPosition.java -- Keeps track of field positions while formatting + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This class is used by the java.text formatting classes to track + * field positions. A field position is defined by an identifier value + * and begin and end index positions. The formatting classes in java.text + * typically define constant values for the field identifiers. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class FieldPosition +{ + /** + * This is the field identifier value. + */ + private int field_id; + + /** + * This is the beginning index of the field. + */ + private int begin; + + /** + * This is the ending index of the field. + */ + private int end; + + /** + * This is the field attribute value. + */ + private Format.Field field_attribute; + + /** + * This method initializes a new instance of FieldPosition + * to have the specified field attribute. The attribute will be used as + * an id. It is formally equivalent to calling FieldPosition(field, -1). + * + * @param field The field format attribute. + */ + public FieldPosition (Format.Field field) + { + this(field, -1); + } + + /** + * This method initializes a new instance of FieldPosition + * to have the specified field attribute. The attribute will be used as + * an id is non null. The integer field id is only used if the Format.Field + * attribute is not used by the formatter. + * + * @param field The field format attribute. + * @param field_id The field identifier value. + */ + public FieldPosition (Format.Field field, int field_id) + { + this.field_attribute = field; + this.field_id = field_id; + } + + /** + * This method initializes a new instance of FieldPosition to + * have the specified field id. + * + * @param field_id The field identifier value. + */ + public FieldPosition (int field_id) + { + this.field_id = field_id; + } + + /** + * This method returns the field identifier value for this object. + * + * @return The field identifier. + */ + public int getField () + { + return field_id; + } + + public Format.Field getFieldAttribute () + { + return field_attribute; + } + + /** + * This method returns the beginning index for this field. + * + * @return The beginning index. + */ + public int getBeginIndex () + { + return begin; + } + + /** + * This method sets the beginning index of this field to the specified value. + * + * @param begin The new beginning index. + */ + public void setBeginIndex (int begin) + { + this.begin = begin; + } + + /** + * This method returns the ending index for the field. + * + * @return The ending index. + */ + public int getEndIndex () + { + return end; + } + + /** + * This method sets the ending index of this field to the specified value. + * + * @param end The new ending index. + */ + public void setEndIndex (int end) + { + this.end = end; + } + + /** + * This method tests this object for equality against the specified object. + * The objects will be considered equal if and only if: + *

        + *

          + *
        • The specified object is not null. + *
        • The specified object has the same class as this object. + *
        • The specified object has the same field identifier, field attribute + * and beginning and ending index as this object. + *
        + * + * @param obj The object to test for equality to this object. + * + * @return true if the specified object is equal to + * this object, false otherwise. + */ + public boolean equals (Object obj) + { + if (this == obj) + return true; + + if (obj == null || obj.getClass() != this.getClass()) + return false; + + FieldPosition fp = (FieldPosition) obj; + return (field_id == fp.field_id + && (field_attribute == fp.field_attribute + || (field_attribute != null + && field_attribute.equals(fp.field_attribute))) + && begin == fp.begin + && end == fp.end); + } + + + /** + * This method returns a hash value for this object + * + * @return A hash value for this object. + */ + public int hashCode () + { + int hash = 5; + + hash = 31 * hash + field_id; + hash = 31 * hash + begin; + hash = 31 * hash + end; + hash = 31 * hash + + (null == field_attribute ? 0 : field_attribute.hashCode()); + + return hash; + } + + /** + * This method returns a String representation of this + * object. + * + * @return A String representation of this object. + */ + public String toString () + { + return (getClass ().getName () + + "[field=" + getField () + + ",attribute=" + getFieldAttribute () + + ",beginIndex=" + getBeginIndex () + + ",endIndex=" + getEndIndex () + + "]"); + } +} diff --git a/libjava/classpath/java/text/Format.java b/libjava/classpath/java/text/Format.java new file mode 100644 index 000000000..29edf0552 --- /dev/null +++ b/libjava/classpath/java/text/Format.java @@ -0,0 +1,181 @@ +/* Format.java -- Abstract superclass for formatting/parsing strings. + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.text.FormatCharacterIterator; + +import java.io.Serializable; + +/** + * This class is the abstract superclass of classes that format and parse + * data to/from Strings. It is guaranteed that any + * String produced by a concrete subclass of Format + * will be parseable by that same subclass. + *

        + * In addition to implementing the abstract methods in this class, subclasses + * should provide static factory methods of the form + * getInstance() and getInstance(Locale) if the + * subclass loads different formatting/parsing schemes based on locale. + * These subclasses should also implement a static method called + * getAvailableLocales() which returns an array of + * available locales in the current runtime environment. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public abstract class Format implements Serializable, Cloneable +{ + /** + * For compatability with Sun's JDK 1.4.2 rev. 5 + */ + static final long serialVersionUID = -299282585814624189L; + + public static class Field extends AttributedCharacterIterator.Attribute + { + static final long serialVersionUID = 276966692217360283L; + + protected Field(String name) + { + super(name); + } + } + + /** + * This method initializes a new instance of Format. + * It performs no actions, but acts as a default constructor for + * subclasses. + */ + protected Format () + { + } + + /** + * This method formats an Object into a String. + * + * @param obj The Object to format. + * + * @return The formatted String. + * + * @exception IllegalArgumentException If the Object + * cannot be formatted. + */ + public final String format(Object obj) throws IllegalArgumentException + { + StringBuffer sb = new StringBuffer (); + format (obj, sb, new FieldPosition (0)); + return sb.toString (); + } + + /** + * This method formats an Object into a String and + * appends the String to a StringBuffer. + * + * @param obj The Object to format. + * @param sb The StringBuffer to append to. + * @param pos The desired FieldPosition, which is also + * updated by this call. + * + * @return The updated StringBuffer. + * + * @exception IllegalArgumentException If the Object + * cannot be formatted. + */ + public abstract StringBuffer format (Object obj, StringBuffer sb, + FieldPosition pos) + throws IllegalArgumentException; + + /** + * This method parses a String and converts the parsed + * contents into an Object. + * + * @param str The String to parse. + * + * @return The resulting Object. + * + * @exception ParseException If the String cannot be parsed. + */ + public Object parseObject (String str) throws ParseException + { + ParsePosition pos = new ParsePosition(0); + Object result = parseObject (str, pos); + if (result == null) + { + int index = pos.getErrorIndex(); + if (index < 0) + index = pos.getIndex(); + throw new ParseException("parseObject failed", index); + } + return result; + } + + /** + * This method parses a String and converts the parsed + * contents into an Object. + * + * @param str The String to parse. + * @param pos The starting parse index on input, the ending parse + * index on output. + * + * @return The parsed Object, or null in + * case of error. + */ + public abstract Object parseObject (String str, ParsePosition pos); + + public AttributedCharacterIterator formatToCharacterIterator(Object obj) + { + return new FormatCharacterIterator(format(obj), null, null); + } + + /** + * Creates a copy of this object. + * + * @return The copied Object. + */ + public Object clone () + { + try + { + return super.clone (); + } + catch (CloneNotSupportedException e) + { + return null; + } + } +} diff --git a/libjava/classpath/java/text/MessageFormat.java b/libjava/classpath/java/text/MessageFormat.java new file mode 100644 index 000000000..ba5805aa0 --- /dev/null +++ b/libjava/classpath/java/text/MessageFormat.java @@ -0,0 +1,836 @@ +/* MessageFormat.java - Localized message formatting. + Copyright (C) 1999, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.text.FormatCharacterIterator; + +import java.io.InvalidObjectException; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; + +public class MessageFormat extends Format +{ + /** + * @author Tom Tromey (tromey@cygnus.com) + * @author Jorge Aliss (jaliss@hotmail.com) + * @date March 3, 1999 + */ + /* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2, except serialization. + * and parsing. + */ + private static final class MessageFormatElement + { + // Argument number. + int argNumber; + // Formatter to be used. This is the format set by setFormat. + Format setFormat; + // Formatter to be used based on the type. + Format format; + + // Argument will be checked to make sure it is an instance of this + // class. + Class formatClass; + + // Formatter type. + String type; + // Formatter style. + String style; + + // Text to follow this element. + String trailer; + + // Recompute the locale-based formatter. + void setLocale (Locale loc) + { + if (type != null) + { + if (type.equals("number")) + { + formatClass = java.lang.Number.class; + + if (style == null) + format = NumberFormat.getInstance(loc); + else if (style.equals("currency")) + format = NumberFormat.getCurrencyInstance(loc); + else if (style.equals("percent")) + format = NumberFormat.getPercentInstance(loc); + else if (style.equals("integer")) + format = NumberFormat.getIntegerInstance(loc); + else + { + format = NumberFormat.getNumberInstance(loc); + DecimalFormat df = (DecimalFormat) format; + df.applyPattern(style); + } + } + else if (type.equals("time") || type.equals("date")) + { + formatClass = java.util.Date.class; + + int val = DateFormat.DEFAULT; + boolean styleIsPattern = false; + if (style != null) + { + if (style.equals("short")) + val = DateFormat.SHORT; + else if (style.equals("medium")) + val = DateFormat.MEDIUM; + else if (style.equals("long")) + val = DateFormat.LONG; + else if (style.equals("full")) + val = DateFormat.FULL; + else + styleIsPattern = true; + } + + if (type.equals("time")) + format = DateFormat.getTimeInstance(val, loc); + else + format = DateFormat.getDateInstance(val, loc); + + if (styleIsPattern) + { + SimpleDateFormat sdf = (SimpleDateFormat) format; + sdf.applyPattern(style); + } + } + else if (type.equals("choice")) + { + formatClass = java.lang.Number.class; + + if (style == null) + throw new + IllegalArgumentException ("style required for choice format"); + format = new ChoiceFormat (style); + } + } + } + } + + private static final long serialVersionUID = 6479157306784022952L; + + public static class Field extends Format.Field + { + static final long serialVersionUID = 7899943957617360810L; + + /** + * This is the attribute set for all characters produced + * by MessageFormat during a formatting. + */ + public static final MessageFormat.Field ARGUMENT = new MessageFormat.Field("argument"); + + // For deserialization + @SuppressWarnings("unused") + private Field() + { + super(""); + } + + protected Field(String s) + { + super(s); + } + + /** + * invoked to resolve the true static constant by + * comparing the deserialized object to know name. + * + * @return object constant + */ + protected Object readResolve() throws InvalidObjectException + { + if (getName().equals(ARGUMENT.getName())) + return ARGUMENT; + + throw new InvalidObjectException("no such MessageFormat field called " + getName()); + } + + } + + // Helper that returns the text up to the next format opener. The + // text is put into BUFFER. Returns index of character after end of + // string. Throws IllegalArgumentException on error. + private static int scanString(String pat, int index, CPStringBuilder buffer) + { + int max = pat.length(); + buffer.setLength(0); + boolean quoted = false; + for (; index < max; ++index) + { + char c = pat.charAt(index); + if (quoted) + { + // In a quoted context, a single quote ends the quoting. + if (c == '\'') + quoted = false; + else + buffer.append(c); + } + // Check for '', which is a single quote. + else if (c == '\'' && index + 1 < max && pat.charAt(index + 1) == '\'') + { + buffer.append(c); + ++index; + } + else if (c == '\'') + { + // Start quoting. + quoted = true; + } + else if (c == '{') + break; + else + buffer.append(c); + } + // Note that we explicitly allow an unterminated quote. This is + // done for compatibility. + return index; + } + + // This helper retrieves a single part of a format element. Returns + // the index of the terminating character. + private static int scanFormatElement(String pat, int index, + CPStringBuilder buffer, char term) + { + int max = pat.length(); + buffer.setLength(0); + int brace_depth = 1; + boolean quoted = false; + + for (; index < max; ++index) + { + char c = pat.charAt(index); + // First see if we should turn off quoting. + if (quoted) + { + if (c == '\'') + quoted = false; + // In both cases we fall through to inserting the + // character here. + } + // See if we have just a plain quote to insert. + else if (c == '\'' && index + 1 < max + && pat.charAt(index + 1) == '\'') + { + buffer.append(c); + ++index; + } + // See if quoting should turn on. + else if (c == '\'') + quoted = true; + else if (c == '{') + ++brace_depth; + else if (c == '}') + { + if (--brace_depth == 0) + break; + } + // Check for TERM after braces, because TERM might be `}'. + else if (c == term) + break; + // All characters, including opening and closing quotes, are + // inserted here. + buffer.append(c); + } + return index; + } + + // This is used to parse a format element and whatever non-format + // text might trail it. + private static int scanFormat(String pat, int index, CPStringBuilder buffer, + List elts, Locale locale) + { + MessageFormatElement mfe = new MessageFormatElement (); + elts.add(mfe); + + int max = pat.length(); + + // Skip the opening `{'. + ++index; + + // Fetch the argument number. + index = scanFormatElement (pat, index, buffer, ','); + try + { + mfe.argNumber = Integer.parseInt(buffer.toString()); + } + catch (NumberFormatException nfx) + { + IllegalArgumentException iae = new IllegalArgumentException(pat); + iae.initCause(nfx); + throw iae; + } + + // Extract the element format. + if (index < max && pat.charAt(index) == ',') + { + index = scanFormatElement (pat, index + 1, buffer, ','); + mfe.type = buffer.toString(); + + // Extract the style. + if (index < max && pat.charAt(index) == ',') + { + index = scanFormatElement (pat, index + 1, buffer, '}'); + mfe.style = buffer.toString (); + } + } + + // Advance past the last terminator. + if (index >= max || pat.charAt(index) != '}') + throw new IllegalArgumentException("Missing '}' at end of message format"); + ++index; + + // Now fetch trailing string. + index = scanString (pat, index, buffer); + mfe.trailer = buffer.toString (); + + mfe.setLocale(locale); + + return index; + } + + /** + * Applies the specified pattern to this MessageFormat. + * + * @param newPattern The Pattern + */ + public void applyPattern (String newPattern) + { + pattern = newPattern; + + CPStringBuilder tempBuffer = new CPStringBuilder (); + + int index = scanString (newPattern, 0, tempBuffer); + leader = tempBuffer.toString(); + + List elts = new ArrayList(); + while (index < newPattern.length()) + index = scanFormat (newPattern, index, tempBuffer, elts, locale); + + elements = elts.toArray(new MessageFormatElement[elts.size()]); + } + + /** + * Overrides Format.clone() + */ + public Object clone () + { + MessageFormat c = (MessageFormat) super.clone (); + c.elements = (MessageFormatElement[]) elements.clone (); + return c; + } + + /** + * Overrides Format.equals(Object obj) + */ + public boolean equals (Object obj) + { + if (! (obj instanceof MessageFormat)) + return false; + MessageFormat mf = (MessageFormat) obj; + return (pattern.equals(mf.pattern) + && locale.equals(mf.locale)); + } + + /** + * A convinience method to format patterns. + * + * @param arguments The array containing the objects to be formatted. + */ + public AttributedCharacterIterator formatToCharacterIterator (Object arguments) + { + Object[] arguments_array = (Object[])arguments; + FormatCharacterIterator iterator = new FormatCharacterIterator(); + + formatInternal(arguments_array, new StringBuffer(), null, iterator); + + return iterator; + } + + /** + * A convinience method to format patterns. + * + * @param pattern The pattern used when formatting. + * @param arguments The array containing the objects to be formatted. + */ + public static String format (String pattern, Object... arguments) + { + MessageFormat mf = new MessageFormat (pattern); + StringBuffer sb = new StringBuffer (); + FieldPosition fp = new FieldPosition (NumberFormat.INTEGER_FIELD); + return mf.formatInternal(arguments, sb, fp, null).toString(); + } + + /** + * Returns the pattern with the formatted objects. + * + * @param arguments The array containing the objects to be formatted. + * @param appendBuf The StringBuffer where the text is appened. + * @param fp A FieldPosition object (it is ignored). + */ + public final StringBuffer format (Object arguments[], StringBuffer appendBuf, + FieldPosition fp) + { + return formatInternal(arguments, appendBuf, fp, null); + } + + private StringBuffer formatInternal (Object arguments[], + StringBuffer appendBuf, + FieldPosition fp, + FormatCharacterIterator output_iterator) + { + appendBuf.append(leader); + if (output_iterator != null) + output_iterator.append(leader); + + for (int i = 0; i < elements.length; ++i) + { + Object thisArg = null; + boolean unavailable = false; + if (arguments == null || elements[i].argNumber >= arguments.length) + unavailable = true; + else + thisArg = arguments[elements[i].argNumber]; + + AttributedCharacterIterator iterator = null; + + Format formatter = null; + + if (fp != null && i == fp.getField() && fp.getFieldAttribute() == Field.ARGUMENT) + fp.setBeginIndex(appendBuf.length()); + + if (unavailable) + appendBuf.append("{" + elements[i].argNumber + "}"); + else + { + if (elements[i].setFormat != null) + formatter = elements[i].setFormat; + else if (elements[i].format != null) + { + if (elements[i].formatClass != null + && ! elements[i].formatClass.isInstance(thisArg)) + throw new IllegalArgumentException("Wrong format class"); + + formatter = elements[i].format; + } + else if (thisArg instanceof Number) + formatter = NumberFormat.getInstance(locale); + else if (thisArg instanceof Date) + formatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale); + else + appendBuf.append(thisArg); + } + + if (fp != null && fp.getField() == i && fp.getFieldAttribute() == Field.ARGUMENT) + fp.setEndIndex(appendBuf.length()); + + if (formatter != null) + { + // Special-case ChoiceFormat. + if (formatter instanceof ChoiceFormat) + { + StringBuffer buf = new StringBuffer (); + formatter.format(thisArg, buf, fp); + MessageFormat mf = new MessageFormat (); + mf.setLocale(locale); + mf.applyPattern(buf.toString()); + mf.format(arguments, appendBuf, fp); + } + else + { + if (output_iterator != null) + iterator = formatter.formatToCharacterIterator(thisArg); + else + formatter.format(thisArg, appendBuf, fp); + } + + elements[i].format = formatter; + } + + if (output_iterator != null) + { + HashMap hash_argument = + new HashMap(); + int position = output_iterator.getEndIndex(); + + hash_argument.put (MessageFormat.Field.ARGUMENT, + Integer.valueOf(elements[i].argNumber)); + + + if (iterator != null) + { + output_iterator.append(iterator); + output_iterator.addAttributes(hash_argument, position, + output_iterator.getEndIndex()); + } + else + output_iterator.append(thisArg.toString(), hash_argument); + + output_iterator.append(elements[i].trailer); + } + + appendBuf.append(elements[i].trailer); + } + + return appendBuf; + } + + /** + * Returns the pattern with the formatted objects. The first argument + * must be a array of Objects. + * This is equivalent to format((Object[]) objectArray, appendBuf, fpos) + * + * @param objectArray The object array to be formatted. + * @param appendBuf The StringBuffer where the text is appened. + * @param fpos A FieldPosition object (it is ignored). + */ + public final StringBuffer format (Object objectArray, StringBuffer appendBuf, + FieldPosition fpos) + { + return format ((Object[])objectArray, appendBuf, fpos); + } + + /** + * Returns an array with the Formats for + * the arguments. + */ + public Format[] getFormats () + { + Format[] f = new Format[elements.length]; + for (int i = elements.length - 1; i >= 0; --i) + f[i] = elements[i].setFormat; + return f; + } + + /** + * Returns the locale. + */ + public Locale getLocale () + { + return locale; + } + + /** + * Overrides Format.hashCode() + */ + public int hashCode () + { + // FIXME: not a very good hash. + return pattern.hashCode() + locale.hashCode(); + } + + private MessageFormat () + { + } + + /** + * Creates a new MessageFormat object with + * the specified pattern + * + * @param pattern The Pattern + */ + public MessageFormat(String pattern) + { + this(pattern, Locale.getDefault()); + } + + /** + * Creates a new MessageFormat object with + * the specified pattern + * + * @param pattern The Pattern + * @param locale The Locale to use + * + * @since 1.4 + */ + public MessageFormat(String pattern, Locale locale) + { + this.locale = locale; + applyPattern (pattern); + } + + /** + * Parse a string sourceStr against the pattern specified + * to the MessageFormat constructor. + * + * @param sourceStr the string to be parsed. + * @param pos the current parse position (and eventually the error position). + * @return the array of parsed objects sorted according to their argument number + * in the pattern. + */ + public Object[] parse (String sourceStr, ParsePosition pos) + { + // Check initial text. + int index = pos.getIndex(); + if (! sourceStr.startsWith(leader, index)) + { + pos.setErrorIndex(index); + return null; + } + index += leader.length(); + + ArrayList results = new ArrayList(elements.length); + // Now check each format. + for (int i = 0; i < elements.length; ++i) + { + Format formatter = null; + if (elements[i].setFormat != null) + formatter = elements[i].setFormat; + else if (elements[i].format != null) + formatter = elements[i].format; + + Object value = null; + if (formatter instanceof ChoiceFormat) + { + // We must special-case a ChoiceFormat because it might + // have recursive formatting. + ChoiceFormat cf = (ChoiceFormat) formatter; + String[] formats = (String[]) cf.getFormats(); + double[] limits = cf.getLimits(); + MessageFormat subfmt = new MessageFormat (); + subfmt.setLocale(locale); + ParsePosition subpos = new ParsePosition (index); + + int j; + for (j = 0; value == null && j < limits.length; ++j) + { + subfmt.applyPattern(formats[j]); + subpos.setIndex(index); + value = subfmt.parse(sourceStr, subpos); + } + if (value != null) + { + index = subpos.getIndex(); + value = new Double (limits[j]); + } + } + else if (formatter != null) + { + pos.setIndex(index); + value = formatter.parseObject(sourceStr, pos); + if (value != null) + index = pos.getIndex(); + } + else + { + // We have a String format. This can lose in a number + // of ways, but we give it a shot. + int next_index; + if (elements[i].trailer.length() > 0) + next_index = sourceStr.indexOf(elements[i].trailer, index); + else + next_index = sourceStr.length(); + if (next_index == -1) + { + pos.setErrorIndex(index); + return null; + } + value = sourceStr.substring(index, next_index); + index = next_index; + } + + if (value == null + || ! sourceStr.startsWith(elements[i].trailer, index)) + { + pos.setErrorIndex(index); + return null; + } + + if (elements[i].argNumber >= results.size()) + { + // Emulate padding behaviour of Vector.setSize() with ArrayList + results.ensureCapacity(elements[i].argNumber + 1); + for (int a = results.size(); a <= elements[i].argNumber; ++a) + results.add(a, null); + } + results.set(elements[i].argNumber, value); + + index += elements[i].trailer.length(); + } + + return results.toArray(new Object[results.size()]); + } + + public Object[] parse (String sourceStr) throws ParseException + { + ParsePosition pp = new ParsePosition (0); + Object[] r = parse (sourceStr, pp); + if (r == null) + throw new ParseException ("couldn't parse string", pp.getErrorIndex()); + return r; + } + + public Object parseObject (String sourceStr, ParsePosition pos) + { + return parse (sourceStr, pos); + } + + /** + * Sets the format for the argument at an specified + * index. + * + * @param variableNum The index. + * @param newFormat The Format object. + */ + public void setFormat (int variableNum, Format newFormat) + { + elements[variableNum].setFormat = newFormat; + } + + /** + * Sets the formats for the arguments. + * + * @param newFormats An array of Format objects. + */ + public void setFormats (Format[] newFormats) + { + if (newFormats.length < elements.length) + throw new IllegalArgumentException("Not enough format objects"); + + int len = Math.min(newFormats.length, elements.length); + for (int i = 0; i < len; ++i) + elements[i].setFormat = newFormats[i]; + } + + /** + * Sets the locale. + * + * @param loc A Locale + */ + public void setLocale (Locale loc) + { + locale = loc; + if (elements != null) + { + for (int i = 0; i < elements.length; ++i) + elements[i].setLocale(loc); + } + } + + /** + * Returns the pattern. + */ + public String toPattern () + { + return pattern; + } + + /** + * Return the formatters used sorted by argument index. It uses the + * internal table to fill in this array: if a format has been + * set using setFormat or setFormatByArgumentIndex + * then it returns it at the right index. If not it uses the detected + * formatters during a format call. If nothing is known + * about that argument index it just puts null at that position. + * To get useful informations you may have to call format + * at least once. + * + * @return an array of formatters sorted by argument index. + */ + public Format[] getFormatsByArgumentIndex() + { + int argNumMax = 0; + // First, find the greatest argument number. + for (int i=0;i argNumMax) + argNumMax = elements[i].argNumber; + + Format[] formats = new Format[argNumMax]; + for (int i=0;i + * To create an instance of a concrete subclass of NumberFormat, + * do not call a class constructor directly. Instead, use one of the + * static factory methods in this class such as + * getCurrencyInstance. + * + * @author Tom Tromey (tromey@cygnus.com) + * @author Aaron M. Renn (arenn@urbanophile.com) + * @date March 4, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2, except getAvailableLocales. + */ +public abstract class NumberFormat extends Format implements Cloneable +{ + /** + * This is a constant used to create a FieldPosition object + * that will return the integer portion of a formatted number. + */ + public static final int INTEGER_FIELD = 0; + + /** + * This is a constant used to create a FieldPosition object + * that will return the fractional portion of a formatted number. + */ + public static final int FRACTION_FIELD = 1; + + public static class Field extends Format.Field + { + static final long serialVersionUID = 7494728892700160890L; + + /** + * Attribute set to all characters containing digits of the integer + * part. + */ + public static final NumberFormat.Field INTEGER + = new Field("integer"); + + /** + * Attribute set to all characters containing digits of the fractional + * part. + */ + public static final NumberFormat.Field FRACTION + = new Field("fraction"); + + /** + * Attribute set to all characters containing digits of the exponential + * part. + */ + public static final NumberFormat.Field EXPONENT + = new Field("exponent"); + + /** + * Attribute set to all characters containing a decimal separator. + */ + public static final NumberFormat.Field DECIMAL_SEPARATOR + = new Field("decimal separator"); + + /** + * Attribute set to all characters containing a sign (plus or minus). + */ + public static final NumberFormat.Field SIGN + = new Field("sign"); + + /** + * Attribute set to all characters containing a grouping separator (e.g. + * a comma, a white space,...). + */ + public static final NumberFormat.Field GROUPING_SEPARATOR + = new Field("grouping separator"); + + /** + * Attribute set to all characters containing an exponential symbol (e.g. + * 'E') + */ + public static final NumberFormat.Field EXPONENT_SYMBOL + = new Field("exponent symbol"); + + /** + * Attribute set to all characters containing a percent symbol (e.g. '%') + */ + public static final NumberFormat.Field PERCENT + = new Field("percent"); + + /** + * Attribute set to all characters containing a permille symbol. + */ + public static final NumberFormat.Field PERMILLE + = new Field("permille"); + + /** + * Attribute set to all characters containing the currency unit. + */ + public static final NumberFormat.Field CURRENCY + = new Field("currency"); + + /** + * Attribute set to all characters containing the exponent sign. + */ + public static final NumberFormat.Field EXPONENT_SIGN + = new Field("exponent sign"); + + /** + * Private fields to register all fields contained in this descriptor. + */ + private static final NumberFormat.Field[] allFields = + { + INTEGER, FRACTION, EXPONENT, DECIMAL_SEPARATOR, SIGN, + GROUPING_SEPARATOR, EXPONENT_SYMBOL, PERCENT, + PERMILLE, CURRENCY, EXPONENT_SIGN + }; + + /** + * This constructor is only used by the deserializer. Without it, + * it would fail to construct a valid object. + */ + @SuppressWarnings("unused") + private Field() + { + super(""); + } + + /** + * Create a Field instance with the specified field name. + * + * @param field_name Field name for the new Field instance. + */ + protected Field(String field_name) + { + super (field_name); + } + + /** + * This function is used by the deserializer to know which object + * to use when it encounters an encoded NumberFormat.Field in a + * serialization stream. If the stream is valid it should return + * one of the above field. In the other case we throw an exception. + * + * @return a valid official NumberFormat.Field instance. + * + * @throws InvalidObjectException if the field name is invalid. + */ + protected Object readResolve() throws InvalidObjectException + { + String s = getName(); + for (int i = 0; i < allFields.length; i++) + if (s.equals(allFields[i].getName())) + return allFields[i]; + + throw new InvalidObjectException("no such NumberFormat field called " + + s); + } + } + + /** + * This method is a specialization of the format method that performs + * a simple formatting of the specified long number. + * + * @param number The long to format. + * + * @return The formatted number + */ + public final String format (long number) + { + StringBuffer sbuf = new StringBuffer(50); + format (number, sbuf, new FieldPosition(0)); + return sbuf.toString(); + } + + /** + * @specnote this method was final in releases before 1.5 + */ + public StringBuffer format (Object obj, StringBuffer sbuf, + FieldPosition pos) + { + if (obj instanceof Number) + return format(((Number) obj).doubleValue(), sbuf, pos); + + throw new + IllegalArgumentException("Cannot format given Object as a Number"); + } + + /** + * This method formats the specified double and appends it to + * a StringBuffer. + * + * @param number The double to format. + * @param sbuf The StringBuffer to append the formatted number + * to. + * @param pos The desired FieldPosition. + * + * @return The StringBuffer with the appended number. + */ + public abstract StringBuffer format (double number, + StringBuffer sbuf, FieldPosition pos); + + /** + * This method formats the specified long and appends it to + * a StringBuffer. + * + * @param number The long to format. + * @param sbuf The StringBuffer to append the formatted number + * to. + * @param pos The desired FieldPosition. + * + * @return The StringBuffer with the appended number. + */ + public abstract StringBuffer format (long number, + StringBuffer sbuf, FieldPosition pos); + + /** + * This method tests the specified object for equality against this object. + * This will be true if the following conditions are met: + *

        + *

          + *
        • The specified object is not null. + *
        • The specified object is an instance of NumberFormat. + *
        + *

        + * Since this method does not test much, it is highly advised that + * concrete subclasses override this method. + * + * @param obj The Object to test against equality with + * this object. + * + * @return true if the specified object is equal to + * this object, false otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof NumberFormat)) + return false; + NumberFormat nf = (NumberFormat) obj; + return (groupingUsed == nf.groupingUsed + && maximumFractionDigits == nf.maximumFractionDigits + && maximumIntegerDigits == nf.maximumIntegerDigits + && minimumFractionDigits == nf.minimumFractionDigits + && minimumIntegerDigits == nf.minimumIntegerDigits + && parseIntegerOnly == nf.parseIntegerOnly); + } + + /** + * This method returns a list of locales for which concrete instances + * of NumberFormat subclasses may be created. + * + * @return The list of available locales. + */ + public static Locale[] getAvailableLocales () + { + Locale[] list = new Locale[1]; + list[0] = Locale.US; + return list; + } + + private static NumberFormat computeInstance(Locale loc, String resource, + String def) + throws MissingResourceException + { + if (loc.equals(Locale.ROOT)) + return new DecimalFormat(def, DecimalFormatSymbols.getInstance(loc)); + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + loc, ClassLoader.getSystemClassLoader()); + String fmt; + try + { + fmt = res == null ? def : res.getString(resource); + } + catch (MissingResourceException x) + { + fmt = def; + } + DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(loc); + return new DecimalFormat (fmt, dfs); + } + + /** + * This method returns an instance of NumberFormat suitable + * for formatting and parsing currency values in the default locale. + * + * @return An instance of NumberFormat for handling currencies. + */ + public static final NumberFormat getCurrencyInstance () + { + return getCurrencyInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of NumberFormat suitable + * for formatting and parsing currency values in the specified locale. + * + * @return An instance of NumberFormat for handling currencies. + */ + public static NumberFormat getCurrencyInstance (Locale loc) + { + try + { + NumberFormat format; + + format = computeInstance (loc, "currencyFormat", + "\u00A4#,##0.00;(\u00A4#,##0.00)"); + format.setMaximumFractionDigits(format.getCurrency().getDefaultFractionDigits()); + return format; + } + catch (MissingResourceException e) + { + for (NumberFormatProvider p : + ServiceLoader.load(NumberFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + NumberFormat nf = p.getCurrencyInstance(loc); + if (nf != null) + return nf; + break; + } + } + } + return getCurrencyInstance(LocaleHelper.getFallbackLocale(loc)); + } + } + + /** + * This method returns a default instance for the default locale. This + * will be a concrete subclass of NumberFormat, but the + * actual class returned is dependent on the locale. + * + * @return An instance of the default NumberFormat class. + */ + public static final NumberFormat getInstance () + { + return getInstance (Locale.getDefault()); + } + + /** + * This method returns a default instance for the specified locale. This + * will be a concrete subclass of NumberFormat, but the + * actual class returned is dependent on the locale. + * + * @param loc The desired locale. + * + * @return An instance of the default NumberFormat class. + */ + public static NumberFormat getInstance (Locale loc) + { + // For now always return a number instance. + return getNumberInstance (loc); + } + + /** + * This method returns the maximum number of digits allowed in the fraction + * portion of a number. + * + * @return The maximum number of digits allowed in the fraction + * portion of a number. + */ + public int getMaximumFractionDigits () + { + return maximumFractionDigits; + } + + /** + * This method returns the maximum number of digits allowed in the integer + * portion of a number. + * + * @return The maximum number of digits allowed in the integer + * portion of a number. + */ + public int getMaximumIntegerDigits () + { + return maximumIntegerDigits; + } + + /** + * This method returns the minimum number of digits allowed in the fraction + * portion of a number. + * + * @return The minimum number of digits allowed in the fraction + * portion of a number. + */ + public int getMinimumFractionDigits () + { + return minimumFractionDigits; + } + + /** + * This method returns the minimum number of digits allowed in the integer + * portion of a number. + * + * @return The minimum number of digits allowed in the integer + * portion of a number. + */ + public int getMinimumIntegerDigits () + { + return minimumIntegerDigits; + } + + /** + * This method returns a default instance for the specified locale. This + * will be a concrete subclass of NumberFormat, but the + * actual class returned is dependent on the locale. + * + * @return An instance of the default NumberFormat class. + */ + public static final NumberFormat getNumberInstance () + { + return getNumberInstance (Locale.getDefault()); + } + + /** + * This method returns a general purpose number formatting and parsing + * class for the default locale. This will be a concrete subclass of + * NumberFormat, but the actual class returned is dependent + * on the locale. + * + * @return An instance of a generic number formatter for the default locale. + */ + public static NumberFormat getNumberInstance (Locale loc) + { + try + { + return computeInstance (loc, "numberFormat", "#,##0.###"); + } + catch (MissingResourceException e) + { + for (NumberFormatProvider p : + ServiceLoader.load(NumberFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + NumberFormat nf = p.getNumberInstance(loc); + if (nf != null) + return nf; + break; + } + } + } + return getNumberInstance(LocaleHelper.getFallbackLocale(loc)); + } + } + + /** + * This method returns an integer formatting and parsing class for the + * default locale. This will be a concrete subclass of NumberFormat, + * but the actual class returned is dependent on the locale. + * + * @return An instance of an integer number formatter for the default locale. + * @since 1.4 + */ + public static final NumberFormat getIntegerInstance() + { + return getIntegerInstance (Locale.getDefault()); + } + + /** + * This method returns an integer formatting and parsing class for the + * default locale. This will be a concrete subclass of NumberFormat, + * but the actual class returned is dependent on the locale. + * + * @param locale the desired locale. + * + * @return An instance of an integer number formatter for the desired locale. + * @since 1.4 + */ + public static NumberFormat getIntegerInstance(Locale locale) + { + try + { + NumberFormat format = computeInstance (locale, + "integerFormat", "#,##0"); + format.setMaximumFractionDigits(0); + format.setParseIntegerOnly (true); + return format; + } + catch (MissingResourceException e) + { + for (NumberFormatProvider p : + ServiceLoader.load(NumberFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(locale)) + { + NumberFormat nf = p.getIntegerInstance(locale); + if (nf != null) + return nf; + break; + } + } + } + return getIntegerInstance(LocaleHelper.getFallbackLocale(locale)); + } + } + + /** + * This method returns an instance of NumberFormat suitable + * for formatting and parsing percentage values in the default locale. + * + * @return An instance of NumberFormat for handling percentages. + */ + public static final NumberFormat getPercentInstance () + { + return getPercentInstance (Locale.getDefault()); + } + + /** + * This method returns an instance of NumberFormat suitable + * for formatting and parsing percentage values in the specified locale. + * + * @param loc The desired locale. + * + * @return An instance of NumberFormat for handling percentages. + */ + public static NumberFormat getPercentInstance (Locale loc) + { + try + { + return computeInstance (loc, "percentFormat", "#,##0%"); + } + catch (MissingResourceException e) + { + for (NumberFormatProvider p : + ServiceLoader.load(NumberFormatProvider.class)) + { + for (Locale l : p.getAvailableLocales()) + { + if (l.equals(loc)) + { + NumberFormat nf = p.getPercentInstance(loc); + if (nf != null) + return nf; + break; + } + } + } + return getPercentInstance(LocaleHelper.getFallbackLocale(loc)); + } + } + + /** + * This method returns a hash value for this object. + * + * @return The hash code. + */ + public int hashCode () + { + int hash = super.hashCode(); + hash ^= (maximumFractionDigits + maximumIntegerDigits + + minimumFractionDigits + minimumIntegerDigits); + if (groupingUsed) + hash ^= 0xf0f0; + if (parseIntegerOnly) + hash ^= 0x0f0f; + return hash; + } + + /** + * This method tests whether or not grouping is in use. Grouping is + * a method of marking separations in numbers, such as thousand separators + * in the US English locale. The grouping positions and symbols are all + * locale specific. As an example, with grouping disabled, the number one + * million would appear as "1000000". With grouping enabled, this number + * might appear as "1,000,000". (Both of these assume the US English + * locale). + * + * @return true if grouping is enabled, + * false otherwise. + */ + public boolean isGroupingUsed () + { + return groupingUsed; + } + + /** + * This method tests whether or not only integer values should be parsed. + * If this class is parsing only integers, parsing stops at the decimal + * point. + * + * @return true if only integers are parsed, + * false otherwise. + */ + public boolean isParseIntegerOnly () + { + return parseIntegerOnly; + } + + /** + * This is a default constructor for use by subclasses. + */ + protected NumberFormat () + { + } + + /** + * This method parses the specified string into a Number. This + * will be a Long if possible, otherwise it will be a + * Double. If no number can be parsed, no exception is + * thrown. Instead, the parse position remains at its initial index. + * + * @param sourceStr The string to parse. + * @param pos The desired ParsePosition. + * + * @return The parsed Number + */ + public abstract Number parse (String sourceStr, ParsePosition pos); + + /** + * This method parses the specified string into a Number. This + * will be a Long if possible, otherwise it will be a + * Double. If no number can be parsed, an exception will be + * thrown. + * + * @param sourceStr The string to parse. + * + * @return The parsed Number + * + * @exception ParseException If no number can be parsed. + */ + public Number parse (String sourceStr) throws ParseException + { + ParsePosition pp = new ParsePosition (0); + Number r = parse (sourceStr, pp); + if (r == null) + { + int index = pp.getErrorIndex(); + if (index < 0) + index = pp.getIndex(); + throw new ParseException ("couldn't parse number", index); + } + return r; + } + + /** + * This method parses the specified string into an Object. This + * will be a Long if possible, otherwise it will be a + * Double. If no number can be parsed, no exception is + * thrown. Instead, the parse position remains at its initial index. + * + * @param sourceStr The string to parse. + * @param pos The desired ParsePosition. + * + * @return The parsed Object + */ + public final Object parseObject (String sourceStr, ParsePosition pos) + { + return parse (sourceStr, pos); + } + + /** + * This method sets the grouping behavior of this formatter. Grouping is + * a method of marking separations in numbers, such as thousand separators + * in the US English locale. The grouping positions and symbols are all + * locale specific. As an example, with grouping disabled, the number one + * million would appear as "1000000". With grouping enabled, this number + * might appear as "1,000,000". (Both of these assume the US English + * locale). + * + * @param newValue true to enable grouping, + * false to disable it. + */ + public void setGroupingUsed (boolean newValue) + { + groupingUsed = newValue; + } + + /** + * This method sets the maximum number of digits allowed in the fraction + * portion of a number to the specified value. If this is less than the + * current minimum allowed digits, the minimum allowed digits value will + * be lowered to be equal to the new maximum allowed digits value. + * + * @param digits The new maximum fraction digits value. + */ + public void setMaximumFractionDigits (int digits) + { + maximumFractionDigits = digits; + if (getMinimumFractionDigits () > maximumFractionDigits) + setMinimumFractionDigits (maximumFractionDigits); + } + + /** + * This method sets the maximum number of digits allowed in the integer + * portion of a number to the specified value. If this is less than the + * current minimum allowed digits, the minimum allowed digits value will + * be lowered to be equal to the new maximum allowed digits value. + * + * @param digits The new maximum integer digits value. + */ + public void setMaximumIntegerDigits (int digits) + { + maximumIntegerDigits = digits; + if (getMinimumIntegerDigits () > maximumIntegerDigits) + setMinimumIntegerDigits (maximumIntegerDigits); + } + + /** + * This method sets the minimum number of digits allowed in the fraction + * portion of a number to the specified value. If this is greater than the + * current maximum allowed digits, the maximum allowed digits value will + * be raised to be equal to the new minimum allowed digits value. + * + * @param digits The new minimum fraction digits value. + */ + public void setMinimumFractionDigits (int digits) + { + minimumFractionDigits = digits; + if (getMaximumFractionDigits () < minimumFractionDigits) + setMaximumFractionDigits (minimumFractionDigits); + } + + /** + * This method sets the minimum number of digits allowed in the integer + * portion of a number to the specified value. If this is greater than the + * current maximum allowed digits, the maximum allowed digits value will + * be raised to be equal to the new minimum allowed digits value. + * + * @param digits The new minimum integer digits value. + */ + public void setMinimumIntegerDigits (int digits) + { + minimumIntegerDigits = digits; + if (getMaximumIntegerDigits () < minimumIntegerDigits) + setMaximumIntegerDigits (minimumIntegerDigits); + } + + /** + * This method sets the parsing behavior of this object to parse only + * integers or not. + * + * @param value true to parse only integers, + * false otherwise. + */ + public void setParseIntegerOnly (boolean value) + { + parseIntegerOnly = value; + } + + /** + * This method is a specialization of the format method that performs + * a simple formatting of the specified double number. + * + * @param number The double to format. + * + * @return The formatted number + */ + public final String format (double number) + { + StringBuffer sbuf = new StringBuffer(50); + FieldPosition position = new FieldPosition(0); + + format (number, sbuf, position); + return sbuf.toString(); + } + + // These field names are fixed by the serialization spec. + boolean groupingUsed; + int maximumFractionDigits; + private byte maxFractionDigits; + int maximumIntegerDigits; + private byte maxIntegerDigits; + int minimumFractionDigits; + private byte minFractionDigits; + int minimumIntegerDigits; + private byte minIntegerDigits; + boolean parseIntegerOnly; + private int serialVersionOnStream; + private static final long serialVersionUID = -2308460125733713944L; + + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (serialVersionOnStream < 1) + { + maximumFractionDigits = maxFractionDigits; + maximumIntegerDigits = maxIntegerDigits; + minimumFractionDigits = minFractionDigits; + minimumIntegerDigits = minIntegerDigits; + serialVersionOnStream = 1; + } + } + + private void writeObject(ObjectOutputStream stream) throws IOException + { + maxFractionDigits = maximumFractionDigits < Byte.MAX_VALUE ? + (byte) maximumFractionDigits : Byte.MAX_VALUE; + maxIntegerDigits = maximumIntegerDigits < Byte.MAX_VALUE ? + (byte) maximumIntegerDigits : Byte.MAX_VALUE; + minFractionDigits = minimumFractionDigits < Byte.MAX_VALUE ? + (byte) minimumFractionDigits : Byte.MAX_VALUE; + minIntegerDigits = minimumIntegerDigits < Byte.MAX_VALUE ? + (byte) minimumIntegerDigits : Byte.MAX_VALUE; + serialVersionOnStream = 1; + stream.defaultWriteObject(); + } + + /** + * Returns the currency used by this number format when formatting currency + * values. + * + * The default implementation throws UnsupportedOperationException. + * + * @return The used currency object, or null. + * + * @throws UnsupportedOperationException If the number format class doesn't + * implement currency formatting. + * + * @since 1.4 + */ + public Currency getCurrency() + { + throw new UnsupportedOperationException(); + } + + /** + * Sets the currency used by this number format when formatting currency + * values. + * + * The default implementation throws UnsupportedOperationException. + * + * @param currency The new currency to be used by this number format. + * + * @throws NullPointerException If currenc is null. + * @throws UnsupportedOperationException If the number format class doesn't + * implement currency formatting. + * + * @since 1.4 + */ + public void setCurrency(Currency currency) + { + if (currency == null) + throw new NullPointerException("currency may not be null"); + + throw new UnsupportedOperationException(); + } +} diff --git a/libjava/classpath/java/text/ParseException.java b/libjava/classpath/java/text/ParseException.java new file mode 100644 index 000000000..4c7ad81e7 --- /dev/null +++ b/libjava/classpath/java/text/ParseException.java @@ -0,0 +1,86 @@ +/* ParseException.java -- an error occurred while parsing + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This exception is thrown when an unexpected error occurs during parsing. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + * @see Format + * @see FieldPosition + * @status updated to 1.4 + */ +public class ParseException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 2703218443322787634L; + + /** + * This is the position where the error was encountered. + * + * @serial the zero-based offset in the string where the error occurred + */ + private final int errorOffset; + + /** + * This method initializes a new instance of ParseException + * with a detailed error message and a error position. + * + * @param s the descriptive message describing the error + * @param offset the position where the error was encountered + */ + public ParseException(String s, int offset) + { + super(s); + errorOffset = offset; + } + + /** + * This method returns the position where the error occurred. + * + * @return the position where the error occurred + */ + public int getErrorOffset() + { + return errorOffset; + } +} // class ParseException diff --git a/libjava/classpath/java/text/ParsePosition.java b/libjava/classpath/java/text/ParsePosition.java new file mode 100644 index 000000000..80652161d --- /dev/null +++ b/libjava/classpath/java/text/ParsePosition.java @@ -0,0 +1,160 @@ +/* ParsePosition.java -- Keep track of position while parsing. + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This class is used to keep track of the current position during parsing + * operations. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Per Bothner (bothner@cygnus.com) + */ +public class ParsePosition +{ + /** + * This is the index of the current parse position. + */ + private int index; + + /** + * This is the index of the position where an error occurred during parsing. + */ + private int error_index; + + /** + * This method initializes a new instance of ParsePosition to + * have the specified initial index value. + * + * @param index The initial parsing index. + */ + public ParsePosition (int index) + { + this.index = index; + error_index = -1; + } + + /** + * This method returns the current parsing index. + * + * @return The current parsing index + */ + public int getIndex () + { + return index; + } + + /** + * This method sets the current parsing index to the specified value. + * + * @param index The new parsing index. + */ + public void setIndex (int index) + { + this.index = index; + } + + /** + * This method returns the error index value. This value defaults to -1 + * unless explicitly set to another value. + * + * @return The error index. + */ + public int getErrorIndex () + { + return error_index; + } + + /** + * This method sets the error index to the specified value. + * + * @param error_index The new error index + */ + public void setErrorIndex (int error_index) + { + this.error_index = error_index; + } + + /** + * This method tests the specified object for equality with this + * object. The two objects will be considered equal if and only if + * all of the following conditions are met. + *

        + *

          + *
        • The specified object is not null.
        • + *
        • The specified object is an instance of ParsePosition.
        • + *
        • The specified object has the same index and error index as + * this object.
        • + *
        + * + * @param obj The Object to test for equality against + * this object. + * + * @return true if the specified object is equal to + * this object, false otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof ParsePosition)) + return false; + + ParsePosition other = (ParsePosition) obj; + return index == other.index && error_index == other.error_index; + } + + /** + * Return the hash code for this object. + * @return the hash code + */ + public int hashCode() + { + return index ^ error_index; + } + + /** + * This method returns a String representation of this + * object. + * + * @return A String that represents this object. + */ + public String toString () + { + return (getClass ().getName () + "[index=" + getIndex () + + ",errorIndex=" + getErrorIndex () + "]"); + } +} diff --git a/libjava/classpath/java/text/RuleBasedCollator.java b/libjava/classpath/java/text/RuleBasedCollator.java new file mode 100644 index 000000000..c7fc549fe --- /dev/null +++ b/libjava/classpath/java/text/RuleBasedCollator.java @@ -0,0 +1,1011 @@ +/* RuleBasedCollator.java -- Concrete Collator Class + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.classpath.NotImplementedException; + +import java.util.ArrayList; +import java.util.HashMap; + +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct + */ + +/** + * This class is a concrete subclass of Collator suitable + * for string collation in a wide variety of languages. An instance of + * this class is normally returned by the getInstance method + * of Collator with rules predefined for the requested + * locale. However, an instance of this class can be created manually + * with any desired rules. + *

        + * Rules take the form of a String with the following syntax + *

          + *
        • Modifier: '@'
        • + *
        • Relation: '<' | ';' | ',' | '=' : <text>
        • + *
        • Reset: '&' : <text>
        • + *
        + * The modifier character indicates that accents sort backward as is the + * case with French. The modifier applies to all rules after + * the modifier but before the next primary sequence. If placed at the end + * of the sequence if applies to all unknown accented character. + * The relational operators specify how the text + * argument relates to the previous term. The relation characters have + * the following meanings: + *
          + *
        • '<' - The text argument is greater than the prior term at the primary + * difference level.
        • + *
        • ';' - The text argument is greater than the prior term at the secondary + * difference level.
        • + *
        • ',' - The text argument is greater than the prior term at the tertiary + * difference level.
        • + *
        • '=' - The text argument is equal to the prior term
        • + *
        + *

        + * As for the text argument itself, this is any sequence of Unicode + * characters not in the following ranges: 0x0009-0x000D, 0x0020-0x002F, + * 0x003A-0x0040, 0x005B-0x0060, and 0x007B-0x007E. If these characters are + * desired, they must be enclosed in single quotes. If any whitespace is + * encountered, it is ignored. (For example, "a b" is equal to "ab"). + *

        + * The reset operation inserts the following rule at the point where the + * text argument to it exists in the previously declared rule string. This + * makes it easy to add new rules to an existing string by simply including + * them in a reset sequence at the end. Note that the text argument, or + * at least the first character of it, must be present somewhere in the + * previously declared rules in order to be inserted properly. If this + * is not satisfied, a ParseException will be thrown. + *

        + * This system of configuring RuleBasedCollator is needlessly + * complex and the people at Taligent who developed it (along with the folks + * at Sun who accepted it into the Java standard library) deserve a slow + * and agonizing death. + *

        + * Here are a couple of example of rule strings: + *

        + * "< a < b < c" - This string says that a is greater than b which is + * greater than c, with all differences being primary differences. + *

        + * "< a,A < b,B < c,C" - This string says that 'A' is greater than 'a' with + * a tertiary strength comparison. Both 'b' and 'B' are greater than 'a' and + * 'A' during a primary strength comparison. But 'B' is greater than 'b' + * under a tertiary strength comparison. + *

        + * "< a < c & a < b " - This sequence is identical in function to the + * "< a < b < c" rule string above. The '&' reset symbol indicates that + * the rule "< b" is to be inserted after the text argument "a" in the + * previous rule string segment. + *

        + * "< a < b & y < z" - This is an error. The character 'y' does not appear + * anywhere in the previous rule string segment so the rule following the + * reset rule cannot be inserted. + *

        + * "< a & A @ < e & E < f& F" - This sequence is equivalent to the following + * "< a & A < E & e < f & F". + *

        + * For a description of the various comparison strength types, see the + * documentation for the Collator class. + *

        + * As an additional complication to this already overly complex rule scheme, + * if any characters precede the first rule, these characters are considered + * ignorable. They will be treated as if they did not exist during + * comparisons. For example, "- < a < b ..." would make '-' an ignorable + * character such that the strings "high-tech" and "hightech" would + * be considered identical. + *

        + * A ParseException will be thrown for any of the following + * conditions: + *

          + *
        • Unquoted punctuation characters in a text argument.
        • + *
        • A relational or reset operator not followed by a text argument
        • + *
        • A reset operator where the text argument is not present in + * the previous rule string section.
        • + *
        + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + * @author Guilhem Lavaux (guilhem@kaffe.org) + */ +public class RuleBasedCollator extends Collator +{ + /** + * This class describes what rank has a character (or a sequence of characters) + * in the lexicographic order. Each element in a rule has a collation element. + */ + static final class CollationElement + { + final String key; + final int primary; + final short secondary; + final short tertiary; + final short equality; + final boolean ignore; + final String expansion; + + CollationElement(String key, int primary, short secondary, short tertiary, + short equality, String expansion, boolean ignore) + { + this.key = key; + this.primary = primary; + this.secondary = secondary; + this.tertiary = tertiary; + this.equality = equality; + this.ignore = ignore; + this.expansion = expansion; + } + + int getValue() + { + return (primary << 16) + (secondary << 8) + tertiary; + } + } + + /** + * Basic collation instruction (internal format) to build the series of + * collation elements. It contains an instruction which specifies the new + * state of the generator. The sequence of instruction should not contain + * RESET (it is used by + * {@link #mergeRules(int,java.lang.String,java.util.ArrayList,java.util.ArrayList)}) + * as a temporary state while merging two sets of instructions. + */ + private static final class CollationSorter + { + static final int GREATERP = 0; + static final int GREATERS = 1; + static final int GREATERT = 2; + static final int EQUAL = 3; + static final int RESET = 4; + static final int INVERSE_SECONDARY = 5; + + final int comparisonType; + final String textElement; + final int hashText; + final int offset; + final boolean ignore; + + String expansionOrdering; + + private CollationSorter(final int comparisonType, final String textElement, + final int offset, final boolean ignore) + { + this.comparisonType = comparisonType; + this.textElement = textElement; + this.offset = offset; + this.ignore = ignore; + hashText = textElement.hashCode(); + } + } + + /** + * This is the original rule string. + */ + private String rules; + + /** + * This is the table of collation element values + */ + private CollationElement[] ce_table; + + /** + * Quick-prefix finder. + */ + HashMap prefix_tree; + + /** + * This is the value of the last sequence entered into + * ce_table. It is used to compute the + * ordering value of unspecified character. + */ + private int last_primary_value; + + /** + * This is the value of the last secondary sequence of the + * primary 0, entered into + * ce_table. It is used to compute the + * ordering value of an unspecified accented character. + */ + private int last_tertiary_value; + + /** + * This variable is true if accents need to be sorted + * in the other direction. + */ + private boolean inverseAccentComparison; + + /** + * This collation element is special to unknown sequence. + * The JDK uses it to mark and sort the characters which has + * no collation rules. + */ + static final CollationElement SPECIAL_UNKNOWN_SEQ = + new CollationElement("", (short) 32767, (short) 0, (short) 0, + (short) 0, null, false); + + /** + * This method initializes a new instance of RuleBasedCollator + * with the specified collation rules. Note that an application normally + * obtains an instance of RuleBasedCollator by calling the + * getInstance method of Collator. That method + * automatically loads the proper set of rules for the desired locale. + * + * @param rules The collation rule string. + * + * @exception ParseException If the rule string contains syntax errors. + */ + public RuleBasedCollator(String rules) throws ParseException + { + if (rules.equals("")) + throw new ParseException("empty rule set", 0); + + this.rules = rules; + + buildCollationVector(parseString(rules)); + buildPrefixAccess(); + } + + /** + * This method returns the number of common characters at the beginning + * of the string of the two parameters. + * + * @param prefix A string considered as a prefix to test against + * the other string. + * @param s A string to test the prefix against. + * @return The number of common characters. + */ + static int findPrefixLength(String prefix, String s) + { + int index; + int len = prefix.length(); + + for (index = 0; index < len && index < s.length(); ++index) + { + if (prefix.charAt(index) != s.charAt(index)) + return index; + } + + + return index; + } + + /** + * Here we are merging two sets of sorting instructions: 'patch' into 'main'. This methods + * checks whether it is possible to find an anchor point for the rules to be merged and + * then insert them at that precise point. + * + * @param offset Offset in the string containing rules of the beginning of the rules + * being merged in. + * @param starter Text of the rules being merged. + * @param main Repository of all already parsed rules. + * @param patch Rules to be merged into the repository. + * @throws ParseException if it is impossible to find an anchor point for the new rules. + */ + private void mergeRules(int offset, String starter, ArrayList main, + ArrayList patch) + throws ParseException + { + int insertion_point = -1; + int max_length = 0; + + /* We must check that no rules conflict with another already present. If it + * is the case delete the old rule. + */ + + /* For the moment good old O(N^2) algorithm. + */ + for (int i = 0; i < patch.size(); i++) + { + int j = 0; + + while (j < main.size()) + { + CollationSorter rule1 = patch.get(i); + CollationSorter rule2 = main.get(j); + + if (rule1.textElement.equals(rule2.textElement)) + main.remove(j); + else + j++; + } + } + + // Find the insertion point... O(N) + for (int i = 0; i < main.size(); i++) + { + CollationSorter sorter = main.get(i); + int length = findPrefixLength(starter, sorter.textElement); + + if (length > max_length) + { + max_length = length; + insertion_point = i+1; + } + } + + if (insertion_point < 0) + throw new ParseException("no insertion point found for " + starter, offset); + + if (max_length < starter.length()) + { + /* + * We need to expand the first entry. It must be sorted + * like if it was the reference key itself (like the spec + * said. So the first entry is special: the element is + * replaced by the specified text element for the sorting. + * This text replace the old one for comparisons. However + * to preserve the behaviour we replace the first key (corresponding + * to the found prefix) by a new code rightly ordered in the + * sequence. The rest of the subsequence must be appended + * to the end of the sequence. + */ + CollationSorter sorter = patch.get(0); + + sorter.expansionOrdering = starter.substring(max_length); // Skip the first good prefix element + + main.add(insertion_point, sorter); + + /* + * This is a new set of rules. Append to the list. + */ + patch.remove(0); + insertion_point++; + } + + // Now insert all elements of patch at the insertion point. + for (int i = 0; i < patch.size(); i++) + main.add(i+insertion_point, patch.get(i)); + } + + /** + * This method parses a string and build a set of sorting instructions. The parsing + * may only be partial on the case the rules are to be merged sometime later. + * + * @param stop_on_reset If this parameter is true then the parser stops when it + * encounters a reset instruction. In the other case, it tries to parse the subrules + * and merged it in the same repository. + * @param v Output vector for the set of instructions. + * @param base_offset Offset in the string to begin parsing. + * @param rules Rules to be parsed. + * @return -1 if the parser reached the end of the string, an integer representing the + * offset in the string at which it stopped parsing. + * @throws ParseException if something turned wrong during the parsing. To get details + * decode the message. + */ + private int subParseString(boolean stop_on_reset, ArrayList v, + int base_offset, String rules) + throws ParseException + { + boolean ignoreChars = (base_offset == 0); + int operator = -1; + StringBuilder sb = new StringBuilder(); + boolean doubleQuote = false; + boolean eatingChars = false; + boolean nextIsModifier = false; + boolean isModifier = false; + int i; + +main_parse_loop: + for (i = 0; i < rules.length(); i++) + { + char c = rules.charAt(i); + int type = -1; + + if (!eatingChars && + ((c >= 0x09 && c <= 0x0D) || (c == 0x20))) + continue; + + isModifier = nextIsModifier; + nextIsModifier = false; + + if (eatingChars && c != '\'') + { + doubleQuote = false; + sb.append(c); + continue; + } + if (doubleQuote && eatingChars) + { + sb.append(c); + doubleQuote = false; + continue; + } + + switch (c) + { + case '!': + throw new ParseException + ("Modifier '!' is not yet supported by Classpath", i + base_offset); + case '<': + type = CollationSorter.GREATERP; + break; + case ';': + type = CollationSorter.GREATERS; + break; + case ',': + type = CollationSorter.GREATERT; + break; + case '=': + type = CollationSorter.EQUAL; + break; + case '\'': + eatingChars = !eatingChars; + doubleQuote = true; + break; + case '@': + if (ignoreChars) + throw new ParseException + ("comparison list has not yet been started. You may only use" + + "(<,;=&)", i + base_offset); + // Inverse the order of secondaries from now on. + nextIsModifier = true; + type = CollationSorter.INVERSE_SECONDARY; + break; + case '&': + type = CollationSorter.RESET; + if (stop_on_reset) + break main_parse_loop; + break; + default: + if (operator < 0) + throw new ParseException + ("operator missing at " + (i + base_offset), i + base_offset); + if (! eatingChars + && ((c >= 0x21 && c <= 0x2F) + || (c >= 0x3A && c <= 0x40) + || (c >= 0x5B && c <= 0x60) + || (c >= 0x7B && c <= 0x7E))) + throw new ParseException + ("unquoted punctuation character '" + c + "'", i + base_offset); + + //type = ignoreChars ? CollationSorter.IGNORE : -1; + sb.append(c); + break; + } + + if (type < 0) + continue; + + if (operator < 0) + { + operator = type; + continue; + } + + if (sb.length() == 0 && !isModifier) + throw new ParseException + ("text element empty at " + (i+base_offset), i+base_offset); + + if (operator == CollationSorter.RESET) + { + /* Reposition in the sorting list at the position + * indicated by the text element. + */ + String subrules = rules.substring(i); + ArrayList sorted_rules = new ArrayList(); + int idx; + + // Parse the subrules but do not iterate through all + // sublist. This is the privilege of the first call. + idx = subParseString(true, sorted_rules, base_offset+i, subrules); + + // Merge new parsed rules into the list. + mergeRules(base_offset+i, sb.toString(), v, sorted_rules); + sb.setLength(0); + + // Reset state to none. + operator = -1; + type = -1; + // We have found a new subrule at 'idx' but it has not been parsed. + if (idx >= 0) + { + i += idx-1; + continue main_parse_loop; + } + else + // No more rules. + break main_parse_loop; + } + + String textElement = sb.toString(); + if (operator == CollationSorter.GREATERP) + ignoreChars = false; + CollationSorter sorter = new CollationSorter(operator, textElement, + base_offset + rules.length(), + ignoreChars); + sb.setLength(0); + + v.add(sorter); + operator = type; + } + + if (operator >= 0) + { + int pos = rules.length() + base_offset; + + if ((sb.length() != 0 && nextIsModifier) + || (sb.length() == 0 && !nextIsModifier && !eatingChars)) + throw new ParseException("text element empty at " + pos, pos); + + if (operator == CollationSorter.GREATERP) + ignoreChars = false; + + CollationSorter sorter = new CollationSorter(operator, sb.toString(), + base_offset+pos, ignoreChars); + v.add(sorter); + } + + if (i == rules.length()) + return -1; + else + return i; + } + + /** + * This method creates a copy of this object. + * + * @return A copy of this object. + */ + public Object clone() + { + return super.clone(); + } + + /** + * This method completely parses a string 'rules' containing sorting rules. + * + * @param rules String containing the rules to be parsed. + * @return A set of sorting instructions stored in a Vector. + * @throws ParseException if something turned wrong during the parsing. To get details + * decode the message. + */ + private ArrayList parseString(String rules) + throws ParseException + { + ArrayList v = new ArrayList(); + + // result of the first subParseString is not absolute (may be -1 or a + // positive integer). But we do not care. + subParseString(false, v, 0, rules); + + return v; + } + + /** + * This method uses the sorting instructions built by {@link #parseString} + * to build collation elements which can be directly used to sort strings. + * + * @param parsedElements Parsed instructions stored in a ArrayList. + * @throws ParseException if the order of the instructions are not valid. + */ + private void buildCollationVector(ArrayList parsedElements) + throws ParseException + { + int primary_seq = 0; + int last_tertiary_seq = 0; + short secondary_seq = 0; + short tertiary_seq = 0; + short equality_seq = 0; + boolean inverseComparisons = false; + final boolean DECREASING = false; + final boolean INCREASING = true; + boolean secondaryType = INCREASING; + ArrayList v = new ArrayList(); + + // elts is completely sorted. +element_loop: + for (int i = 0; i < parsedElements.size(); i++) + { + CollationSorter elt = parsedElements.get(i); + + switch (elt.comparisonType) + { + case CollationSorter.GREATERP: + primary_seq++; + if (inverseComparisons) + { + secondary_seq = Short.MAX_VALUE; + secondaryType = DECREASING; + } + else + { + secondary_seq = 0; + secondaryType = INCREASING; + } + tertiary_seq = 0; + equality_seq = 0; + inverseComparisons = false; + break; + case CollationSorter.GREATERS: + if (secondaryType == DECREASING) + secondary_seq--; + else + secondary_seq++; + tertiary_seq = 0; + equality_seq = 0; + break; + case CollationSorter.INVERSE_SECONDARY: + inverseComparisons = true; + continue element_loop; + case CollationSorter.GREATERT: + tertiary_seq++; + if (primary_seq == 0) + last_tertiary_seq = tertiary_seq; + equality_seq = 0; + break; + case CollationSorter.EQUAL: + equality_seq++; + break; + case CollationSorter.RESET: + throw new ParseException + ("Invalid reached state 'RESET'. Internal error", elt.offset); + default: + throw new ParseException + ("Invalid unknown state '" + elt.comparisonType + "'", elt.offset); + } + + v.add(new CollationElement(elt.textElement, primary_seq, + secondary_seq, tertiary_seq, + equality_seq, elt.expansionOrdering, elt.ignore)); + } + + this.inverseAccentComparison = inverseComparisons; + + ce_table = v.toArray(new CollationElement[v.size()]); + + last_primary_value = primary_seq+1; + last_tertiary_value = last_tertiary_seq+1; + } + + /** + * Build a tree where all keys are the texts of collation elements and data is + * the collation element itself. The tree is used when extracting all prefix + * for a given text. + */ + private void buildPrefixAccess() + { + prefix_tree = new HashMap(); + + for (int i = 0; i < ce_table.length; i++) + { + CollationElement e = ce_table[i]; + + prefix_tree.put(e.key, e); + } + } + + /** + * This method returns an integer which indicates whether the first + * specified String is less than, greater than, or equal to + * the second. The value depends not only on the collation rules in + * effect, but also the strength and decomposition settings of this object. + * + * @param source The first String to compare. + * @param target A second String to compare to the first. + * + * @return A negative integer if source < target, a positive integer + * if source > target, or 0 if source == target. + */ + public int compare(String source, String target) + { + CollationElementIterator cs, ct; + CollationElement ord1block = null; + CollationElement ord2block = null; + boolean advance_block_1 = true; + boolean advance_block_2 = true; + + cs = getCollationElementIterator(source); + ct = getCollationElementIterator(target); + + for(;;) + { + int ord1; + int ord2; + + /* + * We have to check whether the characters are ignorable. + * If it is the case then forget them. + */ + if (advance_block_1) + { + ord1block = cs.nextBlock(); + if (ord1block != null && ord1block.ignore) + continue; + } + + if (advance_block_2) + { + ord2block = ct.nextBlock(); + if (ord2block != null && ord2block.ignore) + { + advance_block_1 = false; + continue; + } + } + else + advance_block_2 = true; + + if (!advance_block_1) + advance_block_1 = true; + + if (ord1block != null) + ord1 = ord1block.getValue(); + else + { + if (ord2block == null) + return 0; + return -1; + } + + if (ord2block == null) + return 1; + + ord2 = ord2block.getValue(); + + // We know chars are totally equal, so skip + if (ord1 == ord2) + { + if (getStrength() == IDENTICAL) + if (!ord1block.key.equals(ord2block.key)) + return ord1block.key.compareTo(ord2block.key); + continue; + } + + // Check for primary strength differences + int prim1 = CollationElementIterator.primaryOrder(ord1); + int prim2 = CollationElementIterator.primaryOrder(ord2); + + if (prim1 == 0 && getStrength() < TERTIARY) + { + advance_block_2 = false; + continue; + } + else if (prim2 == 0 && getStrength() < TERTIARY) + { + advance_block_1 = false; + continue; + } + + if (prim1 < prim2) + return -1; + else if (prim1 > prim2) + return 1; + else if (getStrength() == PRIMARY) + continue; + + // Check for secondary strength differences + int sec1 = CollationElementIterator.secondaryOrder(ord1); + int sec2 = CollationElementIterator.secondaryOrder(ord2); + + if (sec1 < sec2) + return -1; + else if (sec1 > sec2) + return 1; + else if (getStrength() == SECONDARY) + continue; + + // Check for tertiary differences + int tert1 = CollationElementIterator.tertiaryOrder(ord1); + int tert2 = CollationElementIterator.tertiaryOrder(ord2); + + if (tert1 < tert2) + return -1; + else if (tert1 > tert2) + return 1; + else if (getStrength() == TERTIARY) + continue; + + // Apparently JDK does this (at least for my test case). + return ord1block.key.compareTo(ord2block.key); + } + } + + /** + * This method tests this object for equality against the specified + * object. This will be true if and only if the specified object is + * another reference to this object. + * + * @param obj The Object to compare against this object. + * + * @return true if the specified object is equal to this object, + * false otherwise. + */ + public boolean equals(Object obj) + { + if (obj == this) + return true; + else + return false; + } + + /** + * This method builds a default collation element without invoking + * the database created from the rules passed to the constructor. + * + * @param c Character which needs a collation element. + * @return A valid brand new CollationElement instance. + */ + CollationElement getDefaultElement(char c) + { + int v; + + // Preliminary support for generic accent sorting inversion (I don't know if all + // characters in the range should be sorted backward). This is the place + // to fix this if needed. + if (inverseAccentComparison && (c >= 0x02B9 && c <= 0x0361)) + v = 0x0361 - ((int) c - 0x02B9); + else + v = (short) c; + return new CollationElement("" + c, last_primary_value + v, + (short) 0, (short) 0, (short) 0, null, false); + } + + /** + * This method builds a default collation element for an accented character + * without invoking the database created from the rules passed to the constructor. + * + * @param c Character which needs a collation element. + * @return A valid brand new CollationElement instance. + */ + CollationElement getDefaultAccentedElement(char c) + { + int v; + + // Preliminary support for generic accent sorting inversion (I don't know if all + // characters in the range should be sorted backward). This is the place + // to fix this if needed. + if (inverseAccentComparison && (c >= 0x02B9 && c <= 0x0361)) + v = 0x0361 - ((int) c - 0x02B9); + else + v = (short) c; + return new CollationElement("" + c, (short) 0, + (short) 0, (short) (last_tertiary_value + v), (short) 0, null, false); + } + + /** + * This method returns an instance for CollationElementIterator + * for the specified String under the collation rules for this + * object. + * + * @param source The String to return the + * CollationElementIterator instance for. + * + * @return A CollationElementIterator for the specified + * String. + */ + public CollationElementIterator getCollationElementIterator(String source) + { + return new CollationElementIterator(this, source); + } + + /** + * This method returns an instance of CollationElementIterator + * for the String represented by the specified + * CharacterIterator. + * + * @param source The CharacterIterator with the desired String. + * + * @return A CollationElementIterator for the specified String. + */ + public CollationElementIterator getCollationElementIterator(CharacterIterator source) + { + return new CollationElementIterator(this, source); + } + + /** + * This method returns an instance of CollationKey for the + * specified String. The object returned will have a + * more efficient mechanism for its comparison function that could + * provide speed benefits if multiple comparisons are performed, such + * as during a sort. + * + * @param source The String to create a CollationKey for. + * + * @return A CollationKey for the specified String. + */ + public CollationKey getCollationKey(String source) + { + CollationElementIterator cei = getCollationElementIterator(source); + ArrayList vect = new ArrayList(); + + int ord = cei.next(); + cei.reset(); //set to start of string + + while (ord != CollationElementIterator.NULLORDER) + { + // If the primary order is null, it means this is an ignorable + // character. + if (CollationElementIterator.primaryOrder(ord) == 0) + { + ord = cei.next(); + continue; + } + switch (getStrength()) + { + case PRIMARY: + ord = CollationElementIterator.primaryOrder(ord); + break; + + case SECONDARY: + ord = CollationElementIterator.primaryOrder(ord) << 8; + ord |= CollationElementIterator.secondaryOrder(ord); + + default: + break; + } + + vect.add(Integer.valueOf(ord)); + ord = cei.next(); //increment to next key + } + + Integer[] objarr = vect.toArray(new Integer[vect.size()]); + byte[] key = new byte[objarr.length * 4]; + + for (int i = 0; i < objarr.length; i++) + { + int j = objarr[i].intValue(); + key [i * 4] = (byte) ((j & 0xFF000000) >> 24); + key [i * 4 + 1] = (byte) ((j & 0x00FF0000) >> 16); + key [i * 4 + 2] = (byte) ((j & 0x0000FF00) >> 8); + key [i * 4 + 3] = (byte) (j & 0x000000FF); + } + + return new CollationKey(this, source, key); + } + + /** + * This method returns a String containing the collation rules + * for this object. + * + * @return The collation rules for this object. + */ + public String getRules() + { + return rules; + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode() + { + return System.identityHashCode(this); + } +} diff --git a/libjava/classpath/java/text/SimpleDateFormat.java b/libjava/classpath/java/text/SimpleDateFormat.java new file mode 100644 index 000000000..05fa4cf15 --- /dev/null +++ b/libjava/classpath/java/text/SimpleDateFormat.java @@ -0,0 +1,1323 @@ +/* SimpleDateFormat.java -- A class for parsing/formating simple + date constructs + Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.text.AttributedFormatBuffer; +import gnu.java.text.FormatBuffer; +import gnu.java.text.FormatCharacterIterator; +import gnu.java.text.StringFormatBuffer; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.Locale; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * SimpleDateFormat provides convenient methods for parsing and formatting + * dates using Gregorian calendars (see java.util.GregorianCalendar). + * This class is not thread-safe; external synchronisation should be applied + * if an instance is to be accessed from multiple threads. + */ +public class SimpleDateFormat extends DateFormat +{ + /** + * This class is used by SimpleDateFormat as a + * compiled representation of a format string. The field + * ID, size, and character used are stored for each sequence + * of pattern characters. + */ + private class CompiledField + { + /** + * The ID of the field within the local pattern characters. + * Package private for use in out class. + */ + int field; + + /** + * The size of the character sequence. + * Package private for use in out class. + */ + int size; + + /** + * The character used. + */ + private char character; + + /** + * Constructs a compiled field using the + * the given field ID, size and character + * values. + * + * @param f the field ID. + * @param s the size of the field. + * @param c the character used. + */ + public CompiledField(int f, int s, char c) + { + field = f; + size = s; + character = c; + } + + /** + * Retrieves the ID of the field relative to + * the local pattern characters. + */ + public int getField() + { + return field; + } + + /** + * Retrieves the size of the character sequence. + */ + public int getSize() + { + return size; + } + + /** + * Retrieves the character used in the sequence. + */ + public char getCharacter() + { + return character; + } + + /** + * Returns a String representation + * of the compiled field, primarily for debugging + * purposes. + * + * @return a String representation. + */ + public String toString() + { + CPStringBuilder builder; + + builder = new CPStringBuilder(getClass().getName()); + builder.append("[field="); + builder.append(field); + builder.append(", size="); + builder.append(size); + builder.append(", character="); + builder.append(character); + builder.append("]"); + + return builder.toString(); + } + } + + /** + * A list of CompiledFields and {@code String}s + * representing the compiled version of the pattern. + * + * @see CompiledField + * @serial Ignored. + */ + private transient ArrayList tokens; + + /** + * The localised data used in formatting, + * such as the day and month names in the local + * language, and the localized pattern characters. + * + * @see DateFormatSymbols + * @serial The localisation data. May not be null. + */ + private DateFormatSymbols formatData; + + /** + * The date representing the start of the century + * used for interpreting two digit years. For + * example, 24/10/2004 would cause two digit + * years to be interpreted as representing + * the years between 2004 and 2104. + * + * @see #get2DigitYearStart() + * @see #set2DigitYearStart(java.util.Date) + * @see Date + * @serial The start date of the century for parsing two digit years. + * May not be null. + */ + private Date defaultCenturyStart; + + /** + * The year at which interpretation of two + * digit years starts. + * + * @see #get2DigitYearStart() + * @see #set2DigitYearStart(java.util.Date) + * @serial Ignored. + */ + private transient int defaultCentury; + + /** + * The non-localized pattern string. This + * only ever contains the pattern characters + * stored in standardChars. Localized patterns + * are translated to this form. + * + * @see #applyPattern(String) + * @see #applyLocalizedPattern(String) + * @see #toPattern() + * @see #toLocalizedPattern() + * @serial The non-localized pattern string. May not be null. + */ + private String pattern; + + /** + * The version of serialized data used by this class. + * Version 0 only includes the pattern and formatting + * data. Version 1 adds the start date for interpreting + * two digit years. + * + * @serial This specifies the version of the data being serialized. + * Version 0 (or no version) specifies just pattern + * and formatData. Version 1 adds + * the defaultCenturyStart. This implementation + * always writes out version 1 data. + */ + private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier + + /** + * For compatability. + */ + private static final long serialVersionUID = 4774881970558875024L; + + // This string is specified in the root of the CLDR. + private static final String standardChars = "GyMdkHmsSEDFwWahKzYeugAZvcL"; + + /** + * Represents the position of the RFC822 timezone pattern character + * in the array of localized pattern characters. In the + * U.S. locale, this is 'Z'. The value is the offset of the current + * time from GMT e.g. -0500 would be five hours prior to GMT. + */ + private static final int RFC822_TIMEZONE_FIELD = 23; + + /** + * Reads the serialized version of this object. + * If the serialized data is only version 0, + * then the date for the start of the century + * for interpreting two digit years is computed. + * The pattern is parsed and compiled following the process + * of reading in the serialized data. + * + * @param stream the object stream to read the data from. + * @throws IOException if an I/O error occurs. + * @throws ClassNotFoundException if the class of the serialized data + * could not be found. + * @throws InvalidObjectException if the pattern is invalid. + */ + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (serialVersionOnStream < 1) + { + computeCenturyStart (); + serialVersionOnStream = 1; + } + else + // Ensure that defaultCentury gets set. + set2DigitYearStart(defaultCenturyStart); + + // Set up items normally taken care of by the constructor. + tokens = new ArrayList(); + try + { + compileFormat(pattern); + } + catch (IllegalArgumentException e) + { + throw new InvalidObjectException("The stream pattern was invalid."); + } + } + + /** + * Compiles the supplied non-localized pattern into a form + * from which formatting and parsing can be performed. + * This also detects errors in the pattern, which will + * be raised on later use of the compiled data. + * + * @param pattern the non-localized pattern to compile. + * @throws IllegalArgumentException if the pattern is invalid. + */ + private void compileFormat(String pattern) + { + // Any alphabetical characters are treated as pattern characters + // unless enclosed in single quotes. + + char thisChar; + int pos; + int field; + CompiledField current = null; + + for (int i = 0; i < pattern.length(); i++) + { + thisChar = pattern.charAt(i); + field = standardChars.indexOf(thisChar); + if (field == -1) + { + current = null; + if ((thisChar >= 'A' && thisChar <= 'Z') + || (thisChar >= 'a' && thisChar <= 'z')) + { + // Not a valid letter + throw new IllegalArgumentException("Invalid letter " + + thisChar + + " encountered at character " + + i + "."); + } + else if (thisChar == '\'') + { + // Quoted text section; skip to next single quote + pos = pattern.indexOf('\'', i + 1); + // First look for '' -- meaning a single quote. + if (pos == i + 1) + tokens.add("'"); + else + { + // Look for the terminating quote. However, if we + // see a '', that represents a literal quote and + // we must iterate. + CPStringBuilder buf = new CPStringBuilder(); + int oldPos = i + 1; + do + { + if (pos == -1) + throw new IllegalArgumentException("Quotes starting at character " + + i + + " not closed."); + buf.append(pattern.substring(oldPos, pos)); + if (pos + 1 >= pattern.length() + || pattern.charAt(pos + 1) != '\'') + break; + buf.append('\''); + oldPos = pos + 2; + pos = pattern.indexOf('\'', pos + 2); + } + while (true); + tokens.add(buf.toString()); + } + i = pos; + } + else + { + // A special character + tokens.add(Character.valueOf(thisChar)); + } + } + else + { + // A valid field + if ((current != null) && (field == current.field)) + current.size++; + else + { + current = new CompiledField(field, 1, thisChar); + tokens.add(current); + } + } + } + } + + /** + * Returns a string representation of this + * class. + * + * @return a string representation of the SimpleDateFormat + * instance. + */ + public String toString() + { + CPStringBuilder output = new CPStringBuilder(getClass().getName()); + output.append("[tokens="); + output.append(tokens); + output.append(", formatData="); + output.append(formatData); + output.append(", defaultCenturyStart="); + output.append(defaultCenturyStart); + output.append(", defaultCentury="); + output.append(defaultCentury); + output.append(", pattern="); + output.append(pattern); + output.append(", serialVersionOnStream="); + output.append(serialVersionOnStream); + output.append(", standardChars="); + output.append(standardChars); + output.append("]"); + return output.toString(); + } + + /** + * Constructs a SimpleDateFormat using the default pattern for + * the default locale. + */ + public SimpleDateFormat() + { + /* + * There does not appear to be a standard API for determining + * what the default pattern for a locale is, so use package-scope + * variables in DateFormatSymbols to encapsulate this. + */ + super(); + Locale locale = Locale.getDefault(); + calendar = new GregorianCalendar(locale); + computeCenturyStart(); + tokens = new ArrayList(); + formatData = new DateFormatSymbols(locale); + pattern = (formatData.dateFormats[DEFAULT] + ' ' + + formatData.timeFormats[DEFAULT]); + compileFormat(pattern); + numberFormat = NumberFormat.getInstance(locale); + numberFormat.setGroupingUsed (false); + numberFormat.setParseIntegerOnly (true); + numberFormat.setMaximumFractionDigits (0); + } + + /** + * Creates a date formatter using the specified non-localized pattern, + * with the default DateFormatSymbols for the default locale. + * + * @param pattern the pattern to use. + * @throws NullPointerException if the pattern is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public SimpleDateFormat(String pattern) + { + this(pattern, Locale.getDefault()); + } + + /** + * Creates a date formatter using the specified non-localized pattern, + * with the default DateFormatSymbols for the given locale. + * + * @param pattern the non-localized pattern to use. + * @param locale the locale to use for the formatting symbols. + * @throws NullPointerException if the pattern is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public SimpleDateFormat(String pattern, Locale locale) + { + super(); + calendar = new GregorianCalendar(locale); + computeCenturyStart(); + tokens = new ArrayList(); + formatData = new DateFormatSymbols(locale); + compileFormat(pattern); + this.pattern = pattern; + numberFormat = NumberFormat.getInstance(locale); + numberFormat.setGroupingUsed (false); + numberFormat.setParseIntegerOnly (true); + numberFormat.setMaximumFractionDigits (0); + } + + /** + * Creates a date formatter using the specified non-localized + * pattern. The specified DateFormatSymbols will be used when + * formatting. + * + * @param pattern the non-localized pattern to use. + * @param formatData the formatting symbols to use. + * @throws NullPointerException if the pattern or formatData is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public SimpleDateFormat(String pattern, DateFormatSymbols formatData) + { + super(); + calendar = new GregorianCalendar(); + computeCenturyStart (); + tokens = new ArrayList(); + if (formatData == null) + throw new NullPointerException("formatData"); + this.formatData = formatData; + compileFormat(pattern); + this.pattern = pattern; + numberFormat = NumberFormat.getInstance(); + numberFormat.setGroupingUsed (false); + numberFormat.setParseIntegerOnly (true); + numberFormat.setMaximumFractionDigits (0); + } + + /** + * This method returns a string with the formatting pattern being used + * by this object. This string is unlocalized. + * + * @return The format string. + */ + public String toPattern() + { + return pattern; + } + + /** + * This method returns a string with the formatting pattern being used + * by this object. This string is localized. + * + * @return The format string. + */ + public String toLocalizedPattern() + { + String localChars = formatData.getLocalPatternChars(); + return translateLocalizedPattern(pattern, standardChars, localChars); + } + + /** + * This method sets the formatting pattern that should be used by this + * object. This string is not localized. + * + * @param pattern The new format pattern. + * @throws NullPointerException if the pattern is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public void applyPattern(String pattern) + { + tokens.clear(); + compileFormat(pattern); + this.pattern = pattern; + } + + /** + * This method sets the formatting pattern that should be used by this + * object. This string is localized. + * + * @param pattern The new format pattern. + * @throws NullPointerException if the pattern is null. + * @throws IllegalArgumentException if the pattern is invalid. + */ + public void applyLocalizedPattern(String pattern) + { + String localChars = formatData.getLocalPatternChars(); + pattern = translateLocalizedPattern(pattern, localChars, standardChars); + applyPattern(pattern); + } + + /** + * Translates either from or to a localized variant of the pattern + * string. For example, in the German locale, 't' (for 'tag') is + * used instead of 'd' (for 'date'). This method translates + * a localized pattern (such as 'ttt') to a non-localized pattern + * (such as 'ddd'), or vice versa. Non-localized patterns use + * a standard set of characters, which match those of the U.S. English + * locale. + * + * @param pattern the pattern to translate. + * @param oldChars the old set of characters (used in the pattern). + * @param newChars the new set of characters (which will be used in the + * pattern). + * @return a version of the pattern using the characters in + * newChars. + */ + private String translateLocalizedPattern(String pattern, + String oldChars, String newChars) + { + int len = pattern.length(); + CPStringBuilder buf = new CPStringBuilder(len); + boolean quoted = false; + for (int i = 0; i < len; i++) + { + char ch = pattern.charAt(i); + if (ch == '\'') + quoted = ! quoted; + if (! quoted) + { + int j = oldChars.indexOf(ch); + if (j >= 0) + ch = newChars.charAt(j); + } + buf.append(ch); + } + return buf.toString(); + } + + /** + * Returns the start of the century used for two digit years. + * + * @return A Date representing the start of the century + * for two digit years. + */ + public Date get2DigitYearStart() + { + return defaultCenturyStart; + } + + /** + * Sets the start of the century used for two digit years. + * + * @param date A Date representing the start of the century for + * two digit years. + */ + public void set2DigitYearStart(Date date) + { + defaultCenturyStart = date; + calendar.clear(); + calendar.setTime(date); + int year = calendar.get(Calendar.YEAR); + defaultCentury = year - (year % 100); + } + + /** + * This method returns a copy of the format symbol information used + * for parsing and formatting dates. + * + * @return a copy of the date format symbols. + */ + public DateFormatSymbols getDateFormatSymbols() + { + return (DateFormatSymbols) formatData.clone(); + } + + /** + * This method sets the format symbols information used for parsing + * and formatting dates. + * + * @param formatData The date format symbols. + * @throws NullPointerException if formatData is null. + */ + public void setDateFormatSymbols(DateFormatSymbols formatData) + { + if (formatData == null) + { + throw new + NullPointerException("The supplied format data was null."); + } + this.formatData = formatData; + } + + /** + * This methods tests whether the specified object is equal to this + * object. This will be true if and only if the specified object: + *

        + *

          + *
        • Is not null.
        • + *
        • Is an instance of SimpleDateFormat.
        • + *
        • Is equal to this object at the superclass (i.e., DateFormat) + * level.
        • + *
        • Has the same formatting pattern.
        • + *
        • Is using the same formatting symbols.
        • + *
        • Is using the same century for two digit years.
        • + *
        + * + * @param o The object to compare for equality against. + * + * @return true if the specified object is equal to this object, + * false otherwise. + */ + public boolean equals(Object o) + { + if (!super.equals(o)) + return false; + + if (!(o instanceof SimpleDateFormat)) + return false; + + SimpleDateFormat sdf = (SimpleDateFormat)o; + + if (defaultCentury != sdf.defaultCentury) + return false; + + if (!toPattern().equals(sdf.toPattern())) + return false; + + if (!getDateFormatSymbols().equals(sdf.getDateFormatSymbols())) + return false; + + return true; + } + + /** + * This method returns a hash value for this object. + * + * @return A hash value for this object. + */ + public int hashCode() + { + return super.hashCode() ^ toPattern().hashCode() ^ defaultCentury ^ + getDateFormatSymbols().hashCode(); + } + + + /** + * Formats the date input according to the format string in use, + * appending to the specified StringBuffer. The input StringBuffer + * is returned as output for convenience. + */ + private void formatWithAttribute(Date date, FormatBuffer buffer, FieldPosition pos) + { + String temp; + calendar.setTime(date); + + // go through vector, filling in fields where applicable, else toString + Iterator iter = tokens.iterator(); + while (iter.hasNext()) + { + Object o = iter.next(); + if (o instanceof CompiledField) + { + CompiledField cf = (CompiledField) o; + int beginIndex = buffer.length(); + + switch (cf.getField()) + { + case ERA_FIELD: + buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA); + break; + case YEAR_FIELD: + // If we have two digits, then we truncate. Otherwise, we + // use the size of the pattern, and zero pad. + buffer.setDefaultAttribute (DateFormat.Field.YEAR); + if (cf.getSize() == 2) + { + temp = "00"+String.valueOf (calendar.get (Calendar.YEAR)); + buffer.append (temp.substring (temp.length() - 2)); + } + else + withLeadingZeros (calendar.get (Calendar.YEAR), cf.getSize(), buffer); + break; + case MONTH_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.MONTH); + if (cf.getSize() < 3) + withLeadingZeros (calendar.get (Calendar.MONTH) + 1, cf.getSize(), buffer); + else if (cf.getSize() < 4) + buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]); + else + buffer.append (formatData.months[calendar.get (Calendar.MONTH)]); + break; + case DATE_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH); + withLeadingZeros (calendar.get (Calendar.DATE), cf.getSize(), buffer); + break; + case HOUR_OF_DAY1_FIELD: // 1-24 + buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1); + withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1, + cf.getSize(), buffer); + break; + case HOUR_OF_DAY0_FIELD: // 0-23 + buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0); + withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), cf.getSize(), buffer); + break; + case MINUTE_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.MINUTE); + withLeadingZeros (calendar.get (Calendar.MINUTE), + cf.getSize(), buffer); + break; + case SECOND_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.SECOND); + withLeadingZeros(calendar.get (Calendar.SECOND), + cf.getSize(), buffer); + break; + case MILLISECOND_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND); + withLeadingZeros (calendar.get (Calendar.MILLISECOND), cf.getSize(), buffer); + break; + case DAY_OF_WEEK_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK); + if (cf.getSize() < 4) + buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]); + else + buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]); + break; + case DAY_OF_YEAR_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR); + withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), cf.getSize(), buffer); + break; + case DAY_OF_WEEK_IN_MONTH_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH); + withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH), + cf.getSize(), buffer); + break; + case WEEK_OF_YEAR_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR); + withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR), + cf.getSize(), buffer); + break; + case WEEK_OF_MONTH_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH); + withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH), + cf.getSize(), buffer); + break; + case AM_PM_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.AM_PM); + buffer.append (formatData.ampms[calendar.get (Calendar.AM_PM)]); + break; + case HOUR1_FIELD: // 1-12 + buffer.setDefaultAttribute (DateFormat.Field.HOUR1); + withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1, + cf.getSize(), buffer); + break; + case HOUR0_FIELD: // 0-11 + buffer.setDefaultAttribute (DateFormat.Field.HOUR0); + withLeadingZeros (calendar.get (Calendar.HOUR), cf.getSize(), buffer); + break; + case TIMEZONE_FIELD: + buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE); + TimeZone zone = calendar.getTimeZone(); + boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0; + // FIXME: XXX: This should be a localized time zone. + String zoneID = zone.getDisplayName + (isDST, cf.getSize() > 3 ? TimeZone.LONG : TimeZone.SHORT); + buffer.append (zoneID); + break; + case RFC822_TIMEZONE_FIELD: + buffer.setDefaultAttribute(DateFormat.Field.TIME_ZONE); + int pureMinutes = (calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET)) / (1000 * 60); + String sign = (pureMinutes < 0) ? "-" : "+"; + pureMinutes = Math.abs(pureMinutes); + int hours = pureMinutes / 60; + int minutes = pureMinutes % 60; + buffer.append(sign); + withLeadingZeros(hours, 2, buffer); + withLeadingZeros(minutes, 2, buffer); + break; + default: + throw new IllegalArgumentException ("Illegal pattern character " + + cf.getCharacter()); + } + if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute() + || cf.getField() == pos.getField())) + { + pos.setBeginIndex(beginIndex); + pos.setEndIndex(buffer.length()); + } + } + else + { + buffer.append(o.toString(), null); + } + } + } + + public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos) + { + formatWithAttribute(date, new StringFormatBuffer (buffer), pos); + + return buffer; + } + + public AttributedCharacterIterator formatToCharacterIterator(Object date) + throws IllegalArgumentException + { + if (date == null) + throw new NullPointerException("null argument"); + if (!(date instanceof Date)) + throw new IllegalArgumentException("argument should be an instance of java.util.Date"); + + AttributedFormatBuffer buf = new AttributedFormatBuffer(); + formatWithAttribute((Date)date, buf, + null); + buf.sync(); + + return new FormatCharacterIterator(buf.getBuffer().toString(), + buf.getRanges(), + buf.getAttributes()); + } + + private void withLeadingZeros(int value, int length, FormatBuffer buffer) + { + String valStr = String.valueOf(value); + for (length -= valStr.length(); length > 0; length--) + buffer.append('0'); + buffer.append(valStr); + } + + private boolean expect(String source, ParsePosition pos, char ch) + { + int x = pos.getIndex(); + boolean r = x < source.length() && source.charAt(x) == ch; + if (r) + pos.setIndex(x + 1); + else + pos.setErrorIndex(x); + return r; + } + + /** + * This method parses the specified string into a date. + * + * @param dateStr The date string to parse. + * @param pos The input and output parse position + * + * @return The parsed date, or null if the string cannot be + * parsed. + */ + public Date parse (String dateStr, ParsePosition pos) + { + int fmt_index = 0; + int fmt_max = pattern.length(); + + calendar.clear(); + boolean saw_timezone = false; + int quote_start = -1; + boolean is2DigitYear = false; + try + { + for (; fmt_index < fmt_max; ++fmt_index) + { + char ch = pattern.charAt(fmt_index); + if (ch == '\'') + { + if (fmt_index < fmt_max - 1 + && pattern.charAt(fmt_index + 1) == '\'') + { + if (! expect (dateStr, pos, ch)) + return null; + ++fmt_index; + } + else + quote_start = quote_start < 0 ? fmt_index : -1; + continue; + } + + if (quote_start != -1 + || ((ch < 'a' || ch > 'z') + && (ch < 'A' || ch > 'Z'))) + { + if (quote_start == -1 && ch == ' ') + { + // A single unquoted space in the pattern may match + // any number of spaces in the input. + int index = pos.getIndex(); + int save = index; + while (index < dateStr.length() + && Character.isWhitespace(dateStr.charAt(index))) + ++index; + if (index > save) + pos.setIndex(index); + else + { + // Didn't see any whitespace. + pos.setErrorIndex(index); + return null; + } + } + else if (! expect (dateStr, pos, ch)) + return null; + continue; + } + + // We've arrived at a potential pattern character in the + // pattern. + int fmt_count = 1; + while (++fmt_index < fmt_max && pattern.charAt(fmt_index) == ch) + { + ++fmt_count; + } + + // We might need to limit the number of digits to parse in + // some cases. We look to the next pattern character to + // decide. + boolean limit_digits = false; + if (fmt_index < fmt_max + && standardChars.indexOf(pattern.charAt(fmt_index)) >= 0) + limit_digits = true; + --fmt_index; + + // We can handle most fields automatically: most either are + // numeric or are looked up in a string vector. In some cases + // we need an offset. When numeric, `offset' is added to the + // resulting value. When doing a string lookup, offset is the + // initial index into the string array. + int calendar_field; + boolean is_numeric = true; + int offset = 0; + boolean maybe2DigitYear = false; + boolean oneBasedHour = false; + boolean oneBasedHourOfDay = false; + Integer simpleOffset; + String[] set1 = null; + String[] set2 = null; + switch (ch) + { + case 'd': + calendar_field = Calendar.DATE; + break; + case 'D': + calendar_field = Calendar.DAY_OF_YEAR; + break; + case 'F': + calendar_field = Calendar.DAY_OF_WEEK_IN_MONTH; + break; + case 'E': + is_numeric = false; + offset = 1; + calendar_field = Calendar.DAY_OF_WEEK; + set1 = formatData.getWeekdays(); + set2 = formatData.getShortWeekdays(); + break; + case 'w': + calendar_field = Calendar.WEEK_OF_YEAR; + break; + case 'W': + calendar_field = Calendar.WEEK_OF_MONTH; + break; + case 'M': + calendar_field = Calendar.MONTH; + if (fmt_count <= 2) + offset = -1; + else + { + is_numeric = false; + set1 = formatData.getMonths(); + set2 = formatData.getShortMonths(); + } + break; + case 'y': + calendar_field = Calendar.YEAR; + if (fmt_count <= 2) + maybe2DigitYear = true; + break; + case 'K': + calendar_field = Calendar.HOUR; + break; + case 'h': + calendar_field = Calendar.HOUR; + oneBasedHour = true; + break; + case 'H': + calendar_field = Calendar.HOUR_OF_DAY; + break; + case 'k': + calendar_field = Calendar.HOUR_OF_DAY; + oneBasedHourOfDay = true; + break; + case 'm': + calendar_field = Calendar.MINUTE; + break; + case 's': + calendar_field = Calendar.SECOND; + break; + case 'S': + calendar_field = Calendar.MILLISECOND; + break; + case 'a': + is_numeric = false; + calendar_field = Calendar.AM_PM; + set1 = formatData.getAmPmStrings(); + break; + case 'z': + case 'Z': + // We need a special case for the timezone, because it + // uses a different data structure than the other cases. + is_numeric = false; + calendar_field = Calendar.ZONE_OFFSET; + String[][] zoneStrings = formatData.getZoneStrings(); + int zoneCount = zoneStrings.length; + int index = pos.getIndex(); + boolean found_zone = false; + simpleOffset = computeOffset(dateStr.substring(index), pos); + if (simpleOffset != null) + { + found_zone = true; + saw_timezone = true; + calendar.set(Calendar.DST_OFFSET, 0); + offset = simpleOffset.intValue(); + } + else + { + for (int j = 0; j < zoneCount; j++) + { + String[] strings = zoneStrings[j]; + int k; + for (k = 0; k < strings.length; ++k) + { + if (dateStr.startsWith(strings[k], index)) + break; + } + if (k != strings.length) + { + found_zone = true; + saw_timezone = true; + TimeZone tz = TimeZone.getTimeZone (strings[0]); + // Check if it's a DST zone or ordinary + if(k == 3 || k == 4) + calendar.set (Calendar.DST_OFFSET, tz.getDSTSavings()); + else + calendar.set (Calendar.DST_OFFSET, 0); + offset = tz.getRawOffset (); + pos.setIndex(index + strings[k].length()); + break; + } + } + } + if (! found_zone) + { + pos.setErrorIndex(pos.getIndex()); + return null; + } + break; + default: + pos.setErrorIndex(pos.getIndex()); + return null; + } + + // Compute the value we should assign to the field. + int value; + int index = -1; + if (is_numeric) + { + numberFormat.setMinimumIntegerDigits(fmt_count); + if (maybe2DigitYear) + index = pos.getIndex(); + Number n = null; + if (limit_digits) + { + // numberFormat.setMaximumIntegerDigits(fmt_count) may + // not work as expected. So we explicitly use substring + // of dateStr. + int origPos = pos.getIndex(); + pos.setIndex(0); + n = numberFormat.parse(dateStr.substring(origPos, origPos + fmt_count), pos); + pos.setIndex(origPos + pos.getIndex()); + } + else + n = numberFormat.parse(dateStr, pos); + if (pos == null || ! (n instanceof Long)) + return null; + value = n.intValue() + offset; + } + else if (set1 != null) + { + index = pos.getIndex(); + int i; + boolean found = false; + for (i = offset; i < set1.length; ++i) + { + if (set1[i] != null) + if (dateStr.toUpperCase().startsWith(set1[i].toUpperCase(), + index)) + { + found = true; + pos.setIndex(index + set1[i].length()); + break; + } + } + if (!found && set2 != null) + { + for (i = offset; i < set2.length; ++i) + { + if (set2[i] != null) + if (dateStr.toUpperCase().startsWith(set2[i].toUpperCase(), + index)) + { + found = true; + pos.setIndex(index + set2[i].length()); + break; + } + } + } + if (!found) + { + pos.setErrorIndex(index); + return null; + } + value = i; + } + else + value = offset; + + if (maybe2DigitYear) + { + // Parse into default century if the numeric year string has + // exactly 2 digits. + int digit_count = pos.getIndex() - index; + if (digit_count == 2) + { + is2DigitYear = true; + value += defaultCentury; + } + } + + // Calendar uses 0-based hours. + // I.e. 00:00 AM is midnight, not 12 AM or 24:00 + if (oneBasedHour && value == 12) + value = 0; + + if (oneBasedHourOfDay && value == 24) + value = 0; + + // Assign the value and move on. + calendar.set(calendar_field, value); + } + + if (is2DigitYear) + { + // Apply the 80-20 heuristic to dermine the full year based on + // defaultCenturyStart. + int year = calendar.get(Calendar.YEAR); + if (calendar.getTime().compareTo(defaultCenturyStart) < 0) + calendar.set(Calendar.YEAR, year + 100); + } + if (! saw_timezone) + { + // Use the real rules to determine whether or not this + // particular time is in daylight savings. + calendar.clear (Calendar.DST_OFFSET); + calendar.clear (Calendar.ZONE_OFFSET); + } + return calendar.getTime(); + } + catch (IllegalArgumentException x) + { + pos.setErrorIndex(pos.getIndex()); + return null; + } + } + + /** + *

        + * Computes the time zone offset in milliseconds + * relative to GMT, based on the supplied + * String representation. + *

        + *

        + * The supplied String must be a three + * or four digit signed number, with an optional 'GMT' + * prefix. The first one or two digits represents the hours, + * while the last two represent the minutes. The + * two sets of digits can optionally be separated by + * ':'. The mandatory sign prefix (either '+' or '-') + * indicates the direction of the offset from GMT. + *

        + *

        + * For example, 'GMT+0200' specifies 2 hours after + * GMT, while '-05:00' specifies 5 hours prior to + * GMT. The special case of 'GMT' alone can be used + * to represent the offset, 0. + *

        + *

        + * If the String can not be parsed, + * the result will be null. The resulting offset + * is wrapped in an Integer object, in + * order to allow such failure to be represented. + *

        + * + * @param zoneString a string in the form + * (GMT)? sign hours : minutes + * where sign = '+' or '-', hours + * is a one or two digits representing + * a number between 0 and 23, and + * minutes is two digits representing + * a number between 0 and 59. + * @return the parsed offset, or null if parsing + * failed. + */ + private Integer computeOffset(String zoneString, ParsePosition pos) + { + Pattern pattern = + Pattern.compile("(GMT)?([+-])([012])?([0-9]):?([0-9]{2})"); + Matcher matcher = pattern.matcher(zoneString); + + // Match from start, but ignore trailing parts + boolean hasAll = matcher.lookingAt(); + try + { + // Do we have at least the sign, hour and minute? + matcher.group(2); + matcher.group(4); + matcher.group(5); + } + catch (IllegalStateException ise) + { + hasAll = false; + } + if (hasAll) + { + int sign = matcher.group(2).equals("+") ? 1 : -1; + int hour = Integer.parseInt(matcher.group(4)); + if (!matcher.group(3).equals("")) + hour += (Integer.parseInt(matcher.group(3)) * 10); + int minutes = Integer.parseInt(matcher.group(5)); + + if (hour > 23) + return null; + int offset = sign * ((hour * 60) + minutes) * 60000; + + // advance the index + pos.setIndex(pos.getIndex() + matcher.end()); + return Integer.valueOf(offset); + } + else if (zoneString.startsWith("GMT")) + { + pos.setIndex(pos.getIndex() + 3); + return Integer.valueOf(0); + } + return null; + } + + // Compute the start of the current century as defined by + // get2DigitYearStart. + private void computeCenturyStart() + { + int year = calendar.get(Calendar.YEAR); + calendar.set(Calendar.YEAR, year - 80); + set2DigitYearStart(calendar.getTime()); + } + + /** + * Returns a copy of this instance of + * SimpleDateFormat. The copy contains + * clones of the formatting symbols and the 2-digit + * year century start date. + */ + public Object clone() + { + SimpleDateFormat clone = (SimpleDateFormat) super.clone(); + clone.setDateFormatSymbols((DateFormatSymbols) formatData.clone()); + clone.set2DigitYearStart((Date) defaultCenturyStart.clone()); + return clone; + } + +} diff --git a/libjava/classpath/java/text/StringCharacterIterator.java b/libjava/classpath/java/text/StringCharacterIterator.java new file mode 100644 index 000000000..e94e0fbef --- /dev/null +++ b/libjava/classpath/java/text/StringCharacterIterator.java @@ -0,0 +1,369 @@ +/* StringCharacterIterator.java -- Iterate over a character range in a string + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.text; + +/** + * This class iterates over a range of characters in a String. + * For a given range of text, a beginning and ending index, + * as well as a current index are defined. These values can be queried + * by the methods in this interface. Additionally, various methods allow + * the index to be set. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Tom Tromey (tromey@cygnus.com) + */ +public final class StringCharacterIterator implements CharacterIterator +{ + /** + * This is the string to iterate over + */ + private String text; + + /** + * This is the value of the start position of the text range. + */ + private int begin; + + /** + * This is the value of the ending position of the text range. + */ + private int end; + + /** + * This is the current value of the scan index. + */ + private int index; + + /** + * This method initializes a new instance of + * StringCharacterIterator to iterate over the entire + * text of the specified String. The initial index + * value will be set to the first character in the string. + * + * @param text The String to iterate through (null + * not permitted). + * + * @throws NullPointerException if text is null. + */ + public StringCharacterIterator (String text) + { + this (text, 0, text.length (), 0); + } + + /*************************************************************************/ + + /** + * This method initializes a new instance of + * StringCharacterIterator to iterate over the entire + * text of the specified String. The initial index + * value will be set to the specified value. + * + * @param text The String to iterate through. + * @param index The initial index position. + */ + public StringCharacterIterator (String text, int index) + { + this (text, 0, text.length (), index); + } + + /*************************************************************************/ + + /** + * This method initializes a new instance of + * StringCharacterIterator that iterates over the text + * in a subrange of the specified String. The + * beginning and end of the range are specified by the caller, as is + * the initial index position. + * + * @param text The String to iterate through. + * @param begin The beginning position in the character range. + * @param end The ending position in the character range. + * @param index The initial index position. + * + * @throws IllegalArgumentException If any of the range values are + * invalid. + */ + public StringCharacterIterator (String text, int begin, int end, int index) + { + int len = text.length (); + + if ((begin < 0) || (begin > len)) + throw new IllegalArgumentException ("Bad begin position"); + + if ((end < begin) || (end > len)) + throw new IllegalArgumentException ("Bad end position"); + + if ((index < begin) || (index > end)) + throw new IllegalArgumentException ("Bad initial index position"); + + this.text = text; + this.begin = begin; + this.end = end; + this.index = index; + } + + /** + * This is a package level constructor that copies the text out of + * an existing StringCharacterIterator and resets the beginning and + * ending index. + * + * @param sci The StringCharacterIterator to copy the info from + * @param begin The beginning index of the range we are interested in. + * @param end The ending index of the range we are interested in. + */ + StringCharacterIterator (StringCharacterIterator sci, int begin, int end) + { + this (sci.text, begin, end, begin); + } + + /** + * This method returns the character at the current index position + * + * @return The character at the current index position. + */ + public char current () + { + return (index < end) ? text.charAt (index) : DONE; + } + + /*************************************************************************/ + + /** + * This method increments the current index and then returns the + * character at the new index value. If the index is already at + * getEndIndex () - 1, it will not be incremented. + * + * @return The character at the position of the incremented index + * value, or DONE if the index has reached + * getEndIndex () - 1. + */ + public char next () + { + if (index == end) + return DONE; + + ++index; + return current (); + } + + /*************************************************************************/ + + /** + * This method decrements the current index and then returns the + * character at the new index value. If the index value is already + * at the beginning index, it will not be decremented. + * + * @return The character at the position of the decremented index + * value, or DONE if index was already equal to the + * beginning index value. + */ + public char previous () + { + if (index == begin) + return DONE; + + --index; + return current (); + } + + /*************************************************************************/ + + /** + * This method sets the index value to the beginning of the range and returns + * the character there. + * + * @return The character at the beginning of the range, or + * DONE if the range is empty. + */ + public char first () + { + index = begin; + return current (); + } + + /*************************************************************************/ + + /** + * This method sets the index value to getEndIndex () - 1 and + * returns the character there. If the range is empty, then the index value + * will be set equal to the beginning index. + * + * @return The character at the end of the range, or + * DONE if the range is empty. + */ + public char last () + { + if (end == begin) + return DONE; + + index = end - 1; + return current (); + } + + /*************************************************************************/ + + /** + * This method returns the current value of the index. + * + * @return The current index value + */ + public int getIndex () + { + return index; + } + + /*************************************************************************/ + + /** + * This method sets the value of the index to the specified value, then + * returns the character at that position. + * + * @param index The new index value. + * + * @return The character at the new index value or DONE + * if the index value is equal to getEndIndex. + * + * @exception IllegalArgumentException If the specified index is not valid + */ + public char setIndex (int index) + { + if ((index < begin) || (index > end)) + throw new IllegalArgumentException ("Bad index specified"); + + this.index = index; + return current (); + } + + /*************************************************************************/ + + /** + * This method returns the character position of the first character in the + * range. + * + * @return The index of the first character in the range. + */ + public int getBeginIndex () + { + return begin; + } + + /*************************************************************************/ + + /** + * This method returns the character position of the end of the text range. + * This will actually be the index of the first character following the + * end of the range. In the event the text range is empty, this will be + * equal to the first character in the range. + * + * @return The index of the end of the range. + */ + public int getEndIndex () + { + return end; + } + + /*************************************************************************/ + + /** + * This method creates a copy of this CharacterIterator. + * + * @return A copy of this CharacterIterator. + */ + public Object clone () + { + return new StringCharacterIterator (text, begin, end, index); + } + + /*************************************************************************/ + + /** + * This method tests this object for equality againt the specified + * object. This will be true if and only if the specified object: + *

        + *

          + *
        • is not null.
        • + *
        • is an instance of StringCharacterIterator
        • + *
        • has the same text as this object
        • + *
        • has the same beginning, ending, and current index as this object.
        • + *
        + * + * @param obj The object to test for equality against. + * + * @return true if the specified object is equal to this + * object, false otherwise. + */ + public boolean equals (Object obj) + { + if (! (obj instanceof StringCharacterIterator)) + return false; + + StringCharacterIterator sci = (StringCharacterIterator) obj; + + return (begin == sci.begin + && end == sci.end + && index == sci.index + && text.equals (sci.text)); + } + + /** + * Return the hash code for this object. + * @return the hash code + */ + public int hashCode() + { + // Incorporate all the data in a goofy way. + return begin ^ end ^ index ^ text.hashCode(); + } + + /*************************************************************************/ + + /** + * This method allows other classes in java.text to change the value + * of the underlying text being iterated through. + * + * @param text The new String to iterate through. + */ + public void setText (String text) + { + this.text = text; + this.begin = 0; + this.end = text.length (); + this.index = 0; + } +} diff --git a/libjava/classpath/java/text/package.html b/libjava/classpath/java/text/package.html new file mode 100644 index 000000000..3c2e22ba5 --- /dev/null +++ b/libjava/classpath/java/text/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.text + + +

        Classes to iterate over strings and to format texts according to a +specific locale.

        + + + diff --git a/libjava/classpath/java/text/spi/BreakIteratorProvider.java b/libjava/classpath/java/text/spi/BreakIteratorProvider.java new file mode 100644 index 000000000..7e5e056b8 --- /dev/null +++ b/libjava/classpath/java/text/spi/BreakIteratorProvider.java @@ -0,0 +1,124 @@ +/* BreakIteratorProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.text.spi; + +import java.text.BreakIterator; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link BreakIteratorProvider} provides localized + * instances of {@link java.text.BreakIterator}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class BreakIteratorProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link BreakIteratorProvider}. + * Provided for implicit invocation by subclasses. + */ + protected BreakIteratorProvider() + { + } + + /** + * Returns a {@link java.text.BreakIterator} instance + * for character breaks in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for character breaks. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.BreakIterator#getCharacterInstance(java.util.Locale) + */ + public abstract BreakIterator getCharacterInstance(Locale locale); + + /** + * Returns a {@link java.text.BreakIterator} instance + * for line breaks in the specified {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for line breaks. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.BreakIterator#getLineInstance(java.util.Locale) + */ + public abstract BreakIterator getLineInstance(Locale locale); + + /** + * Returns a {@link java.text.BreakIterator} instance + * for sentence breaks in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for sentence breaks. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.BreakIterator#getSentenceInstance(java.util.Locale) + */ + public abstract BreakIterator getSentenceInstance(Locale locale); + + /** + * Returns a {@link java.text.BreakIterator} instance + * for word breaks in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for word breaks. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.BreakIterator#getWordInstance(java.util.Locale) + */ + public abstract BreakIterator getWordInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/CollatorProvider.java b/libjava/classpath/java/text/spi/CollatorProvider.java new file mode 100644 index 000000000..6d6f40939 --- /dev/null +++ b/libjava/classpath/java/text/spi/CollatorProvider.java @@ -0,0 +1,79 @@ +/* CollatorProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.text.spi; + +import java.text.Collator; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link CollatorProvider} provides localized + * instances of {@link java.text.Collator}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class CollatorProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link CollatorProvider}. + * Provided for implicit invocation by subclasses. + */ + protected CollatorProvider() + { + } + + /** + * Returns a {@link java.text.Collator} instance + * for the specified {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.Collator#getInstance(java.util.Locale) + */ + public abstract Collator getInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/DateFormatProvider.java b/libjava/classpath/java/text/spi/DateFormatProvider.java new file mode 100644 index 000000000..ea7ecd39e --- /dev/null +++ b/libjava/classpath/java/text/spi/DateFormatProvider.java @@ -0,0 +1,129 @@ +/* DateFormatProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.text.spi; + +import java.text.DateFormat; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link DateFormatProvider} provides localized + * instances of {@link java.text.DateFormat}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class DateFormatProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link DateFormatProvider}. + * Provided for implicit invocation by subclasses. + */ + protected DateFormatProvider() + { + } + + /** + * Returns a {@link java.text.DateFormat} instance + * for formatting dates with the given style in the specified + * {@link java.util.Locale}. + * + * @param style the formatting style; one of {@link DateFormat#SHORT}, + * {@link DateFormat#MEDIUM}, {@link DateFormat#LONG} + * or {@link DateFormat#FULL}. + * @param locale the desired locale. + * @return the localized instance for formatting dates. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the style is invalid or + * the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DateFormat#getDateInstance(int,java.util.Locale) + */ + public abstract DateFormat getDateInstance(int style, + Locale locale); + + /** + * Returns a {@link java.text.DateFormat} instance + * for formatting dates and times with the given style in the + * specified {@link java.util.Locale}. + * + * @param dateStyle the date formatting style; one of + * {@link DateFormat#SHORT}, {@link DateFormat#MEDIUM}, + * {@link DateFormat#LONG} or {@link DateFormat#FULL}. + * @param timeStyle the time formatting style; one of + * {@link DateFormat#SHORT}, {@link DateFormat#MEDIUM}, + * {@link DateFormat#LONG} or {@link DateFormat#FULL}. + * @param locale the desired locale. + * @return the localized instance for formatting dates. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if either style is invalid or + * the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DateFormat#getDateInstance(java.util.Locale) + */ + public abstract DateFormat getDateTimeInstance(int dateStyle, + int timeStyle, + Locale locale); + + /** + * Returns a {@link java.text.DateFormat} instance + * for formatting times with the given style in the specified + * {@link java.util.Locale}. + * + * @param style the formatting style; one of {@link DateFormat#SHORT}, + * {@link DateFormat#MEDIUM}, {@link DateFormat#LONG} + * or {@link DateFormat#FULL}. + * @param locale the desired locale. + * @return the localized instance for formatting times. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the style is invalid or + * the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DateFormat#getTimeInstance(int,java.util.Locale) + */ + public abstract DateFormat getTimeInstance(int style, + Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/DateFormatSymbolsProvider.java b/libjava/classpath/java/text/spi/DateFormatSymbolsProvider.java new file mode 100644 index 000000000..a0e97595f --- /dev/null +++ b/libjava/classpath/java/text/spi/DateFormatSymbolsProvider.java @@ -0,0 +1,79 @@ +/* DateFormatSymbolsProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.text.spi; + +import java.text.DateFormatSymbols; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link DateFormatSymbolsProvider} provides localized + * instances of {@link java.text.DateFormatSymbols}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class DateFormatSymbolsProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link DateFormatSymbolsProvider}. + * Provided for implicit invocation by subclasses. + */ + protected DateFormatSymbolsProvider() + { + } + + /** + * Returns a {@link java.text.DateFormatSymbols} instance + * for the specified {@link java.util.Locale}. + * + * @param locale the locale to express the symbols in. + * @return the localized instance. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DateFormatSymbols#getInstance(java.util.Locale) + */ + public abstract DateFormatSymbols getInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/DecimalFormatSymbolsProvider.java b/libjava/classpath/java/text/spi/DecimalFormatSymbolsProvider.java new file mode 100644 index 000000000..d772b1eee --- /dev/null +++ b/libjava/classpath/java/text/spi/DecimalFormatSymbolsProvider.java @@ -0,0 +1,79 @@ +/* DecimalFormatSymbolsProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.text.spi; + +import java.text.DecimalFormatSymbols; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link DecimalFormatSymbolsProvider} provides localized + * instances of {@link java.text.DecimalFormatSymbols}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class DecimalFormatSymbolsProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link DecimalFormatSymbolsProvider}. + * Provided for implicit invocation by subclasses. + */ + protected DecimalFormatSymbolsProvider() + { + } + + /** + * Returns a {@link java.text.DecimalFormatSymbols} instance + * for the specified {@link java.util.Locale}. + * + * @param locale the locale to express the symbols in. + * @return the localized instance. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.DecimalFormatSymbols#getInstance(java.util.Locale) + */ + public abstract DecimalFormatSymbols getInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/NumberFormatProvider.java b/libjava/classpath/java/text/spi/NumberFormatProvider.java new file mode 100644 index 000000000..e8a72e44c --- /dev/null +++ b/libjava/classpath/java/text/spi/NumberFormatProvider.java @@ -0,0 +1,129 @@ +/* NumberFormatProvider.java -- Providers of localized instances + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.text.spi; + +import java.text.NumberFormat; + +import java.util.Locale; + +import java.util.spi.LocaleServiceProvider; + +/** + * A {@link NumberFormatProvider} provides localized + * instances of {@link java.text.NumberFormat}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class NumberFormatProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link NumberFormatProvider}. + * Provided for implicit invocation by subclasses. + */ + protected NumberFormatProvider() + { + } + + /** + * Returns a {@link java.text.NumberFormat} instance + * for monetary values in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for monetary values. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.NumberFormat#getCurrencyInstance(java.util.Locale) + */ + public abstract NumberFormat getCurrencyInstance(Locale locale); + + /** + * Returns a {@link java.text.NumberFormat} instance + * for integers in the specified {@link java.util.Locale}. + * The returned instance should be configured to round + * floating point numbers to the nearest integer using + * {@link java.math.RoundingMode#HALF_EVEN} rounding, + * and to parse only the integer part of a number. + * + * @param locale the desired locale. + * @return the localized instance for integers. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.NumberFormat#getIntegerInstance(java.util.Locale) + * @see java.math.RoundingMode#HALF_EVEN + * @see java.text.NumberFormat#isParseIntegerOnly() + */ + public abstract NumberFormat getIntegerInstance(Locale locale); + + /** + * Returns a general-purpose {@link java.text.NumberFormat} + * instance in the specified {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return a general-purpose localized instance. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.NumberFormat#getNumberInstance(java.util.Locale) + */ + public abstract NumberFormat getNumberInstance(Locale locale); + + /** + * Returns a {@link java.text.NumberFormat} instance + * for percentage values in the specified + * {@link java.util.Locale}. + * + * @param locale the desired locale. + * @return the localized instance for percentage values. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.text.NumberFormat#getPercentInstance(java.util.Locale) + */ + public abstract NumberFormat getPercentInstance(Locale locale); + +} diff --git a/libjava/classpath/java/text/spi/package.html b/libjava/classpath/java/text/spi/package.html new file mode 100644 index 000000000..7f5232ce0 --- /dev/null +++ b/libjava/classpath/java/text/spi/package.html @@ -0,0 +1,50 @@ + + + + +GNU Classpath - java.text.spi + + + +

        +A series of service provider interfaces for use by the +classes in java.text. +

        +

        Since: 1.6

        + + diff --git a/libjava/classpath/java/util/.cvsignore b/libjava/classpath/java/util/.cvsignore new file mode 100644 index 000000000..d41ae8d81 --- /dev/null +++ b/libjava/classpath/java/util/.cvsignore @@ -0,0 +1 @@ +LocaleData.java diff --git a/libjava/classpath/java/util/AbstractCollection.java b/libjava/classpath/java/util/AbstractCollection.java new file mode 100644 index 000000000..d3406c230 --- /dev/null +++ b/libjava/classpath/java/util/AbstractCollection.java @@ -0,0 +1,482 @@ +/* AbstractCollection.java -- Abstract implementation of most of Collection + Copyright (C) 1998, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.lang.reflect.Array; + +/** + * A basic implementation of most of the methods in the Collection interface to + * make it easier to create a collection. To create an unmodifiable Collection, + * just subclass AbstractCollection and provide implementations of the + * iterator() and size() methods. The Iterator returned by iterator() need only + * provide implementations of hasNext() and next() (that is, it may throw an + * UnsupportedOperationException if remove() is called). To create a modifiable + * Collection, you must in addition provide an implementation of the + * add(Object) method and the Iterator returned by iterator() must provide an + * implementation of remove(). Other methods should be overridden if the + * backing data structure allows for a more efficient implementation. The + * precise implementation used by AbstractCollection is documented, so that + * subclasses can tell which methods could be implemented more efficiently. + *

        + * + * The programmer should provide a no-argument constructor, and one that + * accepts another Collection, as recommended by the Collection interface. + * Unfortunately, there is no way to enforce this in Java. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Collection + * @see AbstractSet + * @see AbstractList + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractCollection + implements Collection, Iterable +{ + /** + * The main constructor, for use by subclasses. + */ + protected AbstractCollection() + { + } + + /** + * Return an Iterator over this collection. The iterator must provide the + * hasNext and next methods and should in addition provide remove if the + * collection is modifiable. + * + * @return an iterator + */ + public abstract Iterator iterator(); + + /** + * Return the number of elements in this collection. If there are more than + * Integer.MAX_VALUE elements, return Integer.MAX_VALUE. + * + * @return the size + */ + public abstract int size(); + + /** + * Add an object to the collection (optional operation). This implementation + * always throws an UnsupportedOperationException - it should be + * overridden if the collection is to be modifiable. If the collection + * does not accept duplicates, simply return false. Collections may specify + * limitations on what may be added. + * + * @param o the object to add + * @return true if the add operation caused the Collection to change + * @throws UnsupportedOperationException if the add operation is not + * supported on this collection + * @throws NullPointerException if the collection does not support null + * @throws ClassCastException if the object is of the wrong type + * @throws IllegalArgumentException if some aspect of the object prevents + * it from being added + */ + public boolean add(E o) + { + throw new UnsupportedOperationException(); + } + + /** + * Add all the elements of a given collection to this collection (optional + * operation). This implementation obtains an Iterator over the given + * collection and iterates over it, adding each element with the + * add(Object) method (thus this method will fail with an + * UnsupportedOperationException if the add method does). The behavior is + * unspecified if the specified collection is modified during the iteration, + * including the special case of trying addAll(this) on a non-empty + * collection. + * + * @param c the collection to add the elements of to this collection + * @return true if the add operation caused the Collection to change + * @throws UnsupportedOperationException if the add operation is not + * supported on this collection + * @throws NullPointerException if the specified collection is null + * @throws ClassCastException if the type of any element in c is + * not a valid type for addition. + * @throws IllegalArgumentException if some aspect of any element + * in c prevents it being added. + * @throws NullPointerException if any element in c is null and this + * collection doesn't allow null values. + * @see #add(Object) + */ + public boolean addAll(Collection c) + { + Iterator itr = c.iterator(); + boolean modified = false; + int pos = c.size(); + while (--pos >= 0) + modified |= add(itr.next()); + return modified; + } + + /** + * Remove all elements from the collection (optional operation). This + * implementation obtains an iterator over the collection and calls next + * and remove on it repeatedly (thus this method will fail with an + * UnsupportedOperationException if the Iterator's remove method does) + * until there are no more elements to remove. + * Many implementations will have a faster way of doing this. + * + * @throws UnsupportedOperationException if the Iterator returned by + * iterator does not provide an implementation of remove + * @see Iterator#remove() + */ + public void clear() + { + Iterator itr = iterator(); + int pos = size(); + while (--pos >= 0) + { + itr.next(); + itr.remove(); + } + } + + /** + * Test whether this collection contains a given object. That is, if the + * collection has an element e such that (o == null ? e == null : + * o.equals(e)). This implementation obtains an iterator over the collection + * and iterates over it, testing each element for equality with the given + * object. If it is equal, true is returned. Otherwise false is returned when + * the end of the collection is reached. + * + * @param o the object to remove from this collection + * @return true if this collection contains an object equal to o + */ + public boolean contains(Object o) + { + Iterator itr = iterator(); + int pos = size(); + while (--pos >= 0) + if (equals(o, itr.next())) + return true; + return false; + } + + /** + * Tests whether this collection contains all the elements in a given + * collection. This implementation iterates over the given collection, + * testing whether each element is contained in this collection. If any one + * is not, false is returned. Otherwise true is returned. + * + * @param c the collection to test against + * @return true if this collection contains all the elements in the given + * collection + * @throws NullPointerException if the given collection is null + * @see #contains(Object) + */ + public boolean containsAll(Collection c) + { + Iterator itr = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (!contains(itr.next())) + return false; + return true; + } + + /** + * Test whether this collection is empty. This implementation returns + * size() == 0. + * + * @return true if this collection is empty. + * @see #size() + */ + public boolean isEmpty() + { + return size() == 0; + } + + /** + * Remove a single instance of an object from this collection (optional + * operation). That is, remove one element e such that + * (o == null ? e == null : o.equals(e)), if such an element + * exists. This implementation obtains an iterator over the collection + * and iterates over it, testing each element for equality with the given + * object. If it is equal, it is removed by the iterator's remove method + * (thus this method will fail with an UnsupportedOperationException if + * the Iterator's remove method does). After the first element has been + * removed, true is returned; if the end of the collection is reached, false + * is returned. + * + * @param o the object to remove from this collection + * @return true if the remove operation caused the Collection to change, or + * equivalently if the collection did contain o. + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @see Iterator#remove() + */ + public boolean remove(Object o) + { + Iterator itr = iterator(); + int pos = size(); + while (--pos >= 0) + if (equals(o, itr.next())) + { + itr.remove(); + return true; + } + return false; + } + + /** + * Remove from this collection all its elements that are contained in a given + * collection (optional operation). This implementation iterates over this + * collection, and for each element tests if it is contained in the given + * collection. If so, it is removed by the Iterator's remove method (thus + * this method will fail with an UnsupportedOperationException if the + * Iterator's remove method does). + * + * @param c the collection to remove the elements of + * @return true if the remove operation caused the Collection to change + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @throws NullPointerException if the collection, c, is null. + * @see Iterator#remove() + */ + public boolean removeAll(Collection c) + { + return removeAllInternal(c); + } + + /** + * Remove from this collection all its elements that are contained in a given + * collection (optional operation). This implementation iterates over this + * collection, and for each element tests if it is contained in the given + * collection. If so, it is removed by the Iterator's remove method (thus + * this method will fail with an UnsupportedOperationException if the + * Iterator's remove method does). This method is necessary for ArrayList, + * which cannot publicly override removeAll but can optimize this call. + * + * @param c the collection to remove the elements of + * @return true if the remove operation caused the Collection to change + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @throws NullPointerException if the collection, c, is null. + * @see Iterator#remove() + */ + // Package visible for use throughout java.util. + boolean removeAllInternal(Collection c) + { + Iterator itr = iterator(); + boolean modified = false; + int pos = size(); + while (--pos >= 0) + if (c.contains(itr.next())) + { + itr.remove(); + modified = true; + } + return modified; + } + + /** + * Remove from this collection all its elements that are not contained in a + * given collection (optional operation). This implementation iterates over + * this collection, and for each element tests if it is contained in the + * given collection. If not, it is removed by the Iterator's remove method + * (thus this method will fail with an UnsupportedOperationException if + * the Iterator's remove method does). + * + * @param c the collection to retain the elements of + * @return true if the remove operation caused the Collection to change + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @throws NullPointerException if the collection, c, is null. + * @see Iterator#remove() + */ + public boolean retainAll(Collection c) + { + return retainAllInternal(c); + } + + /** + * Remove from this collection all its elements that are not contained in a + * given collection (optional operation). This implementation iterates over + * this collection, and for each element tests if it is contained in the + * given collection. If not, it is removed by the Iterator's remove method + * (thus this method will fail with an UnsupportedOperationException if + * the Iterator's remove method does). This method is necessary for + * ArrayList, which cannot publicly override retainAll but can optimize + * this call. + * + * @param c the collection to retain the elements of + * @return true if the remove operation caused the Collection to change + * @throws UnsupportedOperationException if this collection's Iterator + * does not support the remove method + * @throws NullPointerException if the collection, c, is null. + * @see Iterator#remove() + */ + // Package visible for use throughout java.util. + boolean retainAllInternal(Collection c) + { + Iterator itr = iterator(); + boolean modified = false; + int pos = size(); + while (--pos >= 0) + if (!c.contains(itr.next())) + { + itr.remove(); + modified = true; + } + return modified; + } + + /** + * Return an array containing the elements of this collection. This + * implementation creates an Object array of size size() and then iterates + * over the collection, setting each element of the array from the value + * returned by the iterator. The returned array is safe, and is not backed + * by the collection. + * + * @return an array containing the elements of this collection + */ + public Object[] toArray() + { + Iterator itr = iterator(); + int size = size(); + Object[] a = new Object[size]; + for (int pos = 0; pos < size; pos++) + a[pos] = itr.next(); + return a; + } + + /** + * Copy the collection into a given array if it will fit, or into a + * dynamically created array of the same run-time type as the given array if + * not. If there is space remaining in the array, the first element after the + * end of the collection is set to null (this is only useful if the + * collection is known to contain no null elements, however). This + * implementation first tests whether the given array is large enough to hold + * all the elements of the collection. If not, the reflection API is used to + * allocate a new array of the same run-time type. Next an iterator is + * obtained over the collection and the elements are placed in the array as + * they are returned by the iterator. Finally the first spare element, if + * any, of the array is set to null, and the created array is returned. + * The returned array is safe; it is not backed by the collection. Note that + * null may not mark the last element, if the collection allows null + * elements. + * + * @param a the array to copy into, or of the correct run-time type + * @return the array that was produced + * @throws NullPointerException if the given array is null + * @throws ArrayStoreException if the type of the array precludes holding + * one of the elements of the Collection + */ + public T[] toArray(T[] a) + { + int size = size(); + if (a.length < size) + a = (T[]) Array.newInstance(a.getClass().getComponentType(), + size); + else if (a.length > size) + a[size] = null; + + Iterator itr = iterator(); + for (int pos = 0; pos < size; pos++) + a[pos] = (T) (itr.next()); + return a; + } + + /** + * Creates a String representation of the Collection. The string returned is + * of the form "[a, b, ...]" where a and b etc are the results of calling + * toString on the elements of the collection. This implementation obtains an + * Iterator over the Collection and adds each element to a StringBuffer as it + * is returned by the iterator. "" is inserted when the collection + * contains itself (only works for direct containment, not for collections + * inside collections). + * + * @return a String representation of the Collection + */ + public String toString() + { + Iterator itr = iterator(); + CPStringBuilder r = new CPStringBuilder("["); + boolean hasNext = itr.hasNext(); + while (hasNext) + { + Object o = itr.next(); + if (o == this) + r.append(""); + else + r.append(o); + hasNext = itr.hasNext(); + if (hasNext) + r.append(", "); + } + r.append("]"); + return r.toString(); + } + + /** + * Compare two objects according to Collection semantics. + * + * @param o1 the first object + * @param o2 the second object + * @return o1 == null ? o2 == null : o1.equals(o2) + */ + // Package visible for use throughout java.util. + // It may be inlined since it is final. + static final boolean equals(Object o1, Object o2) + { + return o1 == null ? o2 == null : o1.equals(o2); + } + + /** + * Hash an object according to Collection semantics. + * + * @param o the object to hash + * @return o1 == null ? 0 : o1.hashCode() + */ + // Package visible for use throughout java.util. + // It may be inlined since it is final. + static final int hashCode(Object o) + { + return o == null ? 0 : o.hashCode(); + } +} diff --git a/libjava/classpath/java/util/AbstractList.java b/libjava/classpath/java/util/AbstractList.java new file mode 100644 index 000000000..13ee28a04 --- /dev/null +++ b/libjava/classpath/java/util/AbstractList.java @@ -0,0 +1,1204 @@ +/* AbstractList.java -- Abstract implementation of most of List + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A basic implementation of most of the methods in the List interface to make + * it easier to create a List based on a random-access data structure. If + * the list is sequential (such as a linked list), use AbstractSequentialList. + * To create an unmodifiable list, it is only necessary to override the + * size() and get(int) methods (this contrasts with all other abstract + * collection classes which require an iterator to be provided). To make the + * list modifiable, the set(int, Object) method should also be overridden, and + * to make the list resizable, the add(int, Object) and remove(int) methods + * should be overridden too. Other methods should be overridden if the + * backing data structure allows for a more efficient implementation. + * The precise implementation used by AbstractList is documented, so that + * subclasses can tell which methods could be implemented more efficiently. + *

        + * + * As recommended by Collection and List, the subclass should provide at + * least a no-argument and a Collection constructor. This class is not + * synchronized. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see AbstractSequentialList + * @see AbstractCollection + * @see ListIterator + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractList + extends AbstractCollection + implements List +{ + /** + * A count of the number of structural modifications that have been made to + * the list (that is, insertions and removals). Structural modifications + * are ones which change the list size or affect how iterations would + * behave. This field is available for use by Iterator and ListIterator, + * in order to throw a {@link ConcurrentModificationException} in response + * to the next operation on the iterator. This fail-fast behavior + * saves the user from many subtle bugs otherwise possible from concurrent + * modification during iteration. + *

        + * + * To make lists fail-fast, increment this field by just 1 in the + * add(int, Object) and remove(int) methods. + * Otherwise, this field may be ignored. + */ + protected transient int modCount; + + /** + * The main constructor, for use by subclasses. + */ + protected AbstractList() + { + } + + /** + * Returns the elements at the specified position in the list. + * + * @param index the element to return + * @return the element at that position + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public abstract E get(int index); + + /** + * Insert an element into the list at a given position (optional operation). + * This shifts all existing elements from that position to the end one + * index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. This implementation + * always throws UnsupportedOperationException, and must be overridden to + * make a modifiable List. If you want fail-fast iterators, be sure to + * increment modCount when overriding this. + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @see #modCount + */ + public void add(int index, E o) + { + throw new UnsupportedOperationException(); + } + + /** + * Add an element to the end of the list (optional operation). If the list + * imposes restraints on what can be inserted, such as no null elements, + * this should be documented. This implementation calls + * add(size(), o);, and will fail if that version does. + * + * @param o the object to add + * @return true, as defined by Collection for a modified list + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @see #add(int, Object) + */ + public boolean add(E o) + { + add(size(), o); + return true; + } + + /** + * Insert the contents of a collection into the list at a given position + * (optional operation). Shift all elements at that position to the right + * by the number of elements inserted. This operation is undefined if + * this list is modified during the operation (for example, if you try + * to insert a list into itself). This implementation uses the iterator of + * the collection, repeatedly calling add(int, Object); this will fail + * if add does. This can often be made more efficient. + * + * @param index the location to insert the collection + * @param c the collection to insert + * @return true if the list was modified by this action, that is, if c is + * non-empty + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + * @see #add(int, Object) + */ + public boolean addAll(int index, Collection c) + { + Iterator itr = c.iterator(); + int size = c.size(); + for (int pos = size; pos > 0; pos--) + add(index++, itr.next()); + return size > 0; + } + + /** + * Clear the list, such that a subsequent call to isEmpty() would return + * true (optional operation). This implementation calls + * removeRange(0, size()), so it will fail unless remove + * or removeRange is overridden. + * + * @throws UnsupportedOperationException if this list does not support the + * clear operation + * @see #remove(int) + * @see #removeRange(int, int) + */ + public void clear() + { + removeRange(0, size()); + } + + /** + * Test whether this list is equal to another object. A List is defined to be + * equal to an object if and only if that object is also a List, and the two + * lists have the same sequence. Two lists l1 and l2 are equal if and only + * if l1.size() == l2.size(), and for every integer n between 0 + * and l1.size() - 1 inclusive, l1.get(n) == null ? + * l2.get(n) == null : l1.get(n).equals(l2.get(n)). + *

        + * + * This implementation returns true if the object is this, or false if the + * object is not a List. Otherwise, it iterates over both lists (with + * iterator()), returning false if two elements compare false or one list + * is shorter, and true if the iteration completes successfully. + * + * @param o the object to test for equality with this list + * @return true if o is equal to this list + * @see Object#equals(Object) + * @see #hashCode() + */ + public boolean equals(Object o) + { + if (o == this) + return true; + if (! (o instanceof List)) + return false; + int size = size(); + if (size != ((List) o).size()) + return false; + + Iterator itr1 = iterator(); + Iterator itr2 = ((List) o).iterator(); + + while (--size >= 0) + if (! equals(itr1.next(), itr2.next())) + return false; + return true; + } + + /** + * Obtains a hash code for this list. In order to obey the general + * contract of the hashCode method of class Object, this value is + * calculated as follows: + * +

        hashCode = 1;
        +Iterator i = list.iterator();
        +while (i.hasNext())
        +{
        +  Object obj = i.next();
        +  hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
        +}
        + * + * This ensures that the general contract of Object.hashCode() is adhered to. + * + * @return the hash code of this list + * + * @see Object#hashCode() + * @see #equals(Object) + */ + public int hashCode() + { + int hashCode = 1; + Iterator itr = iterator(); + int pos = size(); + while (--pos >= 0) + hashCode = 31 * hashCode + hashCode(itr.next()); + return hashCode; + } + + /** + * Obtain the first index at which a given object is to be found in this + * list. This implementation follows a listIterator() until a match is found, + * or returns -1 if the list end is reached. + * + * @param o the object to search for + * @return the least integer n such that o == null ? get(n) == null : + * o.equals(get(n)), or -1 if there is no such index + */ + public int indexOf(Object o) + { + ListIterator itr = listIterator(); + int size = size(); + for (int pos = 0; pos < size; pos++) + if (equals(o, itr.next())) + return pos; + return -1; + } + + /** + * Obtain an Iterator over this list, whose sequence is the list order. + * This implementation uses size(), get(int), and remove(int) of the + * backing list, and does not support remove unless the list does. This + * implementation is fail-fast if you correctly maintain modCount. + * Also, this implementation is specified by Sun to be distinct from + * listIterator, although you could easily implement it as + * return listIterator(0). + * + * @return an Iterator over the elements of this list, in order + * @see #modCount + */ + public Iterator iterator() + { + // Bah, Sun's implementation forbids using listIterator(0). + return new Iterator() + { + private int pos = 0; + private int size = size(); + private int last = -1; + private int knownMod = modCount; + + // This will get inlined, since it is private. + /** + * Checks for modifications made to the list from + * elsewhere while iteration is in progress. + * + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + private void checkMod() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Tests to see if there are any more objects to + * return. + * + * @return True if the end of the list has not yet been + * reached. + */ + public boolean hasNext() + { + return pos < size; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are + * no more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public E next() + { + checkMod(); + if (pos == size) + throw new NoSuchElementException(); + last = pos; + return get(pos++); + } + + /** + * Removes the last object retrieved by next() + * from the list, if the list supports object removal. + * + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + */ + public void remove() + { + checkMod(); + if (last < 0) + throw new IllegalStateException(); + AbstractList.this.remove(last); + pos--; + size--; + last = -1; + knownMod = modCount; + } + }; + } + + /** + * Obtain the last index at which a given object is to be found in this + * list. This implementation grabs listIterator(size()), then searches + * backwards for a match or returns -1. + * + * @return the greatest integer n such that o == null ? get(n) == null + * : o.equals(get(n)), or -1 if there is no such index + */ + public int lastIndexOf(Object o) + { + int pos = size(); + ListIterator itr = listIterator(pos); + while (--pos >= 0) + if (equals(o, itr.previous())) + return pos; + return -1; + } + + /** + * Obtain a ListIterator over this list, starting at the beginning. This + * implementation returns listIterator(0). + * + * @return a ListIterator over the elements of this list, in order, starting + * at the beginning + */ + public ListIterator listIterator() + { + return listIterator(0); + } + + /** + * Obtain a ListIterator over this list, starting at a given position. + * A first call to next() would return the same as get(index), and a + * first call to previous() would return the same as get(index - 1). + *

        + * + * This implementation uses size(), get(int), set(int, Object), + * add(int, Object), and remove(int) of the backing list, and does not + * support remove, set, or add unless the list does. This implementation + * is fail-fast if you correctly maintain modCount. + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from + * @return a ListIterator over the elements of this list, in order, starting + * at index + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @see #modCount + */ + public ListIterator listIterator(final int index) + { + if (index < 0 || index > size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + + return new ListIterator() + { + private int knownMod = modCount; + private int position = index; + private int lastReturned = -1; + private int size = size(); + + // This will get inlined, since it is private. + /** + * Checks for modifications made to the list from + * elsewhere while iteration is in progress. + * + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + private void checkMod() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Tests to see if there are any more objects to + * return. + * + * @return True if the end of the list has not yet been + * reached. + */ + public boolean hasNext() + { + return position < size; + } + + /** + * Tests to see if there are objects prior to the + * current position in the list. + * + * @return True if objects exist prior to the current + * position of the iterator. + */ + public boolean hasPrevious() + { + return position > 0; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public E next() + { + checkMod(); + if (position == size) + throw new NoSuchElementException(); + lastReturned = position; + return get(position++); + } + + /** + * Retrieves the previous object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * previous objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public E previous() + { + checkMod(); + if (position == 0) + throw new NoSuchElementException(); + lastReturned = --position; + return get(lastReturned); + } + + /** + * Returns the index of the next element in the + * list, which will be retrieved by next() + * + * @return The index of the next element. + */ + public int nextIndex() + { + return position; + } + + /** + * Returns the index of the previous element in the + * list, which will be retrieved by previous() + * + * @return The index of the previous element. + */ + public int previousIndex() + { + return position - 1; + } + + /** + * Removes the last object retrieved by next() + * or previous() from the list, if the list + * supports object removal. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void remove() + { + checkMod(); + if (lastReturned < 0) + throw new IllegalStateException(); + AbstractList.this.remove(lastReturned); + size--; + position = lastReturned; + lastReturned = -1; + knownMod = modCount; + } + + /** + * Replaces the last object retrieved by next() + * or previous with o, if the list supports object + * replacement and an add or remove operation has not already + * been performed. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list doesn't support + * the addition or removal of elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void set(E o) + { + checkMod(); + if (lastReturned < 0) + throw new IllegalStateException(); + AbstractList.this.set(lastReturned, o); + } + + /** + * Adds the supplied object before the element that would be returned + * by a call to next(), if the list supports addition. + * + * @param o The object to add to the list. + * @throws UnsupportedOperationException if the list doesn't support + * the addition of new elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void add(E o) + { + checkMod(); + AbstractList.this.add(position++, o); + size++; + lastReturned = -1; + knownMod = modCount; + } + }; + } + + /** + * Remove the element at a given position in this list (optional operation). + * Shifts all remaining elements to the left to fill the gap. This + * implementation always throws an UnsupportedOperationException. + * If you want fail-fast iterators, be sure to increment modCount when + * overriding this. + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @see #modCount + */ + public E remove(int index) + { + throw new UnsupportedOperationException(); + } + + /** + * Remove a subsection of the list. This is called by the clear and + * removeRange methods of the class which implements subList, which are + * difficult for subclasses to override directly. Therefore, this method + * should be overridden instead by the more efficient implementation, if one + * exists. Overriding this can reduce quadratic efforts to constant time + * in some cases! + *

        + * + * This implementation first checks for illegal or out of range arguments. It + * then obtains a ListIterator over the list using listIterator(fromIndex). + * It then calls next() and remove() on this iterator repeatedly, toIndex - + * fromIndex times. + * + * @param fromIndex the index, inclusive, to remove from. + * @param toIndex the index, exclusive, to remove to. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + */ + protected void removeRange(int fromIndex, int toIndex) + { + ListIterator itr = listIterator(fromIndex); + for (int index = fromIndex; index < toIndex; index++) + { + itr.next(); + itr.remove(); + } + } + + /** + * Replace an element of this list with another object (optional operation). + * This implementation always throws an UnsupportedOperationException. + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + */ + public E set(int index, E o) + { + throw new UnsupportedOperationException(); + } + + /** + * Obtain a List view of a subsection of this list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. + *

        + * + * This implementation returns a subclass of AbstractList. It stores, in + * private fields, the offset and size of the sublist, and the expected + * modCount of the backing list. If the backing list implements RandomAccess, + * the sublist will also. + *

        + * + * The subclass's set(int, Object), get(int), + * add(int, Object), remove(int), + * addAll(int, Collection) and + * removeRange(int, int) methods all delegate to the + * corresponding methods on the backing abstract list, after + * bounds-checking the index and adjusting for the offset. The + * addAll(Collection c) method merely returns addAll(size, c). + * The listIterator(int) method returns a "wrapper object" + * over a list iterator on the backing list, which is created with the + * corresponding method on the backing list. The iterator() + * method merely returns listIterator(), and the size() method + * merely returns the subclass's size field. + *

        + * + * All methods first check to see if the actual modCount of the backing + * list is equal to its expected value, and throw a + * ConcurrentModificationException if it is not. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() + * @throws IllegalArgumentException if fromIndex > toIndex + * @see ConcurrentModificationException + * @see RandomAccess + */ + public List subList(int fromIndex, int toIndex) + { + // This follows the specification of AbstractList, but is inconsistent + // with the one in List. Don't you love Sun's inconsistencies? + if (fromIndex > toIndex) + throw new IllegalArgumentException(fromIndex + " > " + toIndex); + if (fromIndex < 0 || toIndex > size()) + throw new IndexOutOfBoundsException(); + + if (this instanceof RandomAccess) + return new RandomAccessSubList(this, fromIndex, toIndex); + return new SubList(this, fromIndex, toIndex); + } + + /** + * This class follows the implementation requirements set forth in + * {@link AbstractList#subList(int, int)}. It matches Sun's implementation + * by using a non-public top-level class in the same package. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SubList extends AbstractList + { + // Package visible, for use by iterator. + /** The original list. */ + final AbstractList backingList; + /** The index of the first element of the sublist. */ + final int offset; + /** The size of the sublist. */ + int size; + + /** + * Construct the sublist. + * + * @param backing the list this comes from + * @param fromIndex the lower bound, inclusive + * @param toIndex the upper bound, exclusive + */ + SubList(AbstractList backing, int fromIndex, int toIndex) + { + backingList = backing; + modCount = backing.modCount; + offset = fromIndex; + size = toIndex - fromIndex; + } + + /** + * This method checks the two modCount fields to ensure that there has + * not been a concurrent modification, returning if all is okay. + * + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + */ + // This can be inlined. Package visible, for use by iterator. + void checkMod() + { + if (modCount != backingList.modCount) + throw new ConcurrentModificationException(); + } + + /** + * This method checks that a value is between 0 and size (inclusive). If + * it is not, an exception is thrown. + * + * @param index the value to check + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + // This will get inlined, since it is private. + private void checkBoundsInclusive(int index) + { + if (index < 0 || index > size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size); + } + + /** + * This method checks that a value is between 0 (inclusive) and size + * (exclusive). If it is not, an exception is thrown. + * + * @param index the value to check + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + // This will get inlined, since it is private. + private void checkBoundsExclusive(int index) + { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size); + } + + /** + * Specified by AbstractList.subList to return the private field size. + * + * @return the sublist size + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + */ + public int size() + { + checkMod(); + return size; + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to modify + * @param o the new value + * @return the old value + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws UnsupportedOperationException if the backing list does not + * support the set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to the backing list due + * to its type + * @throws IllegalArgumentException if o cannot be added to the backing list + * for some other reason + */ + public E set(int index, E o) + { + checkMod(); + checkBoundsExclusive(index); + return backingList.set(index + offset, o); + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to get from + * @return the object at that location + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public E get(int index) + { + checkMod(); + checkBoundsExclusive(index); + return backingList.get(index + offset); + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the index to insert at + * @param o the object to add + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws UnsupportedOperationException if the backing list does not + * support the add operation. + * @throws ClassCastException if o cannot be added to the backing list due + * to its type. + * @throws IllegalArgumentException if o cannot be added to the backing + * list for some other reason. + */ + public void add(int index, E o) + { + checkMod(); + checkBoundsInclusive(index); + backingList.add(index + offset, o); + size++; + modCount = backingList.modCount; + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the index to remove + * @return the removed object + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws UnsupportedOperationException if the backing list does not + * support the remove operation + */ + public E remove(int index) + { + checkMod(); + checkBoundsExclusive(index); + E o = backingList.remove(index + offset); + size--; + modCount = backingList.modCount; + return o; + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * This does no bounds checking, as it assumes it will only be called + * by trusted code like clear() which has already checked the bounds. + * + * @param fromIndex the lower bound, inclusive + * @param toIndex the upper bound, exclusive + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws UnsupportedOperationException if the backing list does + * not support removing elements. + */ + protected void removeRange(int fromIndex, int toIndex) + { + checkMod(); + + backingList.removeRange(offset + fromIndex, offset + toIndex); + size -= toIndex - fromIndex; + modCount = backingList.modCount; + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to insert at + * @param c the collection to insert + * @return true if this list was modified, in other words, c is non-empty + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(int index, Collection c) + { + checkMod(); + checkBoundsInclusive(index); + int csize = c.size(); + boolean result = backingList.addAll(offset + index, c); + size += csize; + modCount = backingList.modCount; + return result; + } + + /** + * Specified by AbstractList.subList to return addAll(size, c). + * + * @param c the collection to insert + * @return true if this list was modified, in other words, c is non-empty + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(Collection c) + { + return addAll(size, c); + } + + /** + * Specified by AbstractList.subList to return listIterator(). + * + * @return an iterator over the sublist + */ + public Iterator iterator() + { + return listIterator(); + } + + /** + * Specified by AbstractList.subList to return a wrapper around the + * backing list's iterator. + * + * @param index the start location of the iterator + * @return a list iterator over the sublist + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if the value is out of range + */ + public ListIterator listIterator(final int index) + { + checkMod(); + checkBoundsInclusive(index); + + return new ListIterator() + { + private final ListIterator i + = backingList.listIterator(index + offset); + private int position = index; + + /** + * Tests to see if there are any more objects to + * return. + * + * @return True if the end of the list has not yet been + * reached. + */ + public boolean hasNext() + { + return position < size; + } + + /** + * Tests to see if there are objects prior to the + * current position in the list. + * + * @return True if objects exist prior to the current + * position of the iterator. + */ + public boolean hasPrevious() + { + return position > 0; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public E next() + { + if (position == size) + throw new NoSuchElementException(); + position++; + return i.next(); + } + + /** + * Retrieves the previous object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * previous objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public E previous() + { + if (position == 0) + throw new NoSuchElementException(); + position--; + return i.previous(); + } + + /** + * Returns the index of the next element in the + * list, which will be retrieved by next() + * + * @return The index of the next element. + */ + public int nextIndex() + { + return i.nextIndex() - offset; + } + + /** + * Returns the index of the previous element in the + * list, which will be retrieved by previous() + * + * @return The index of the previous element. + */ + public int previousIndex() + { + return i.previousIndex() - offset; + } + + /** + * Removes the last object retrieved by next() + * from the list, if the list supports object removal. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + */ + public void remove() + { + i.remove(); + size--; + position = nextIndex(); + modCount = backingList.modCount; + } + + + /** + * Replaces the last object retrieved by next() + * or previous with o, if the list supports object + * replacement and an add or remove operation has not already + * been performed. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list doesn't support + * the addition or removal of elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void set(E o) + { + i.set(o); + } + + /** + * Adds the supplied object before the element that would be returned + * by a call to next(), if the list supports addition. + * + * @param o The object to add to the list. + * @throws UnsupportedOperationException if the list doesn't support + * the addition of new elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void add(E o) + { + i.add(o); + size++; + position++; + modCount = backingList.modCount; + } + + // Here is the reason why the various modCount fields are mostly + // ignored in this wrapper listIterator. + // If the backing listIterator is failfast, then the following holds: + // Using any other method on this list will call a corresponding + // method on the backing list *after* the backing listIterator + // is created, which will in turn cause a ConcurrentModException + // when this listIterator comes to use the backing one. So it is + // implicitly failfast. + // If the backing listIterator is NOT failfast, then the whole of + // this list isn't failfast, because the modCount field of the + // backing list is not valid. It would still be *possible* to + // make the iterator failfast wrt modifications of the sublist + // only, but somewhat pointless when the list can be changed under + // us. + // Either way, no explicit handling of modCount is needed. + // However modCount = backingList.modCount must be executed in add + // and remove, and size must also be updated in these two methods, + // since they do not go through the corresponding methods of the subList. + }; + } + } // class SubList + + /** + * This class is a RandomAccess version of SubList, as required by + * {@link AbstractList#subList(int, int)}. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class RandomAccessSubList extends SubList + implements RandomAccess + { + /** + * Construct the sublist. + * + * @param backing the list this comes from + * @param fromIndex the lower bound, inclusive + * @param toIndex the upper bound, exclusive + */ + RandomAccessSubList(AbstractList backing, int fromIndex, int toIndex) + { + super(backing, fromIndex, toIndex); + } + } // class RandomAccessSubList + +} // class AbstractList diff --git a/libjava/classpath/java/util/AbstractMap.java b/libjava/classpath/java/util/AbstractMap.java new file mode 100644 index 000000000..e0e35712a --- /dev/null +++ b/libjava/classpath/java/util/AbstractMap.java @@ -0,0 +1,819 @@ +/* AbstractMap.java -- Abstract implementation of most of Map + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Serializable; + +/** + * An abstract implementation of Map to make it easier to create your own + * implementations. In order to create an unmodifiable Map, subclass + * AbstractMap and implement the entrySet (usually via an + * AbstractSet). To make it modifiable, also implement put, + * and have entrySet().iterator() support remove. + *

        + * + * It is recommended that classes which extend this support at least the + * no-argument constructor, and a constructor which accepts another Map. + * Further methods in this class may be overridden if you have a more + * efficient implementation. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see Collection + * @see HashMap + * @see LinkedHashMap + * @see TreeMap + * @see WeakHashMap + * @see IdentityHashMap + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractMap implements Map +{ + /** + * A class containing an immutable key and value. The + * implementation of {@link Entry#setValue(V)} for this class + * simply throws an {@link UnsupportedOperationException}, + * thus preventing changes being made. This is useful when + * a static thread-safe view of a map is required. + * + * @since 1.6 + */ + public static class SimpleImmutableEntry + implements Entry, Serializable + { + /** + * Compatible with JDK 1.6 + */ + private static final long serialVersionUID = 7138329143949025153L; + + K key; + V value; + + public SimpleImmutableEntry(K key, V value) + { + this.key = key; + this.value = value; + } + + public SimpleImmutableEntry(Entry entry) + { + this(entry.getKey(), entry.getValue()); + } + + public K getKey() + { + return key; + } + + public V getValue() + { + return value; + } + + public V setValue(V value) + { + throw new UnsupportedOperationException("setValue not supported on immutable entry"); + } + } + +/** An "enum" of iterator types. */ + // Package visible for use by subclasses. + static final int KEYS = 0, + VALUES = 1, + ENTRIES = 2; + + /** + * The cache for {@link #keySet()}. + */ + // Package visible for use by subclasses. + Set keys; + + /** + * The cache for {@link #values()}. + */ + // Package visible for use by subclasses. + Collection values; + + /** + * The main constructor, for use by subclasses. + */ + protected AbstractMap() + { + } + + /** + * Returns a set view of the mappings in this Map. Each element in the + * set must be an implementation of Map.Entry. The set is backed by + * the map, so that changes in one show up in the other. Modifications + * made while an iterator is in progress cause undefined behavior. If + * the set supports removal, these methods must be valid: + * Iterator.remove, Set.remove, + * removeAll, retainAll, and clear. + * Element addition is not supported via this set. + * + * @return the entry set + * @see Map.Entry + */ + public abstract Set> entrySet(); + + /** + * Remove all entries from this Map (optional operation). This default + * implementation calls entrySet().clear(). NOTE: If the entry set does + * not permit clearing, then this will fail, too. Subclasses often + * override this for efficiency. Your implementation of entrySet() should + * not call AbstractMap.clear unless you want an infinite loop. + * + * @throws UnsupportedOperationException if entrySet().clear() + * does not support clearing. + * @see Set#clear() + */ + public void clear() + { + entrySet().clear(); + } + + /** + * Create a shallow copy of this Map, no keys or values are copied. The + * default implementation simply calls super.clone(). + * + * @return the shallow clone + * @throws CloneNotSupportedException if a subclass is not Cloneable + * @see Cloneable + * @see Object#clone() + */ + protected Object clone() throws CloneNotSupportedException + { + AbstractMap copy = (AbstractMap) super.clone(); + // Clear out the caches; they are stale. + copy.keys = null; + copy.values = null; + return copy; + } + + /** + * Returns true if this contains a mapping for the given key. This + * implementation does a linear search, O(n), over the + * entrySet(), returning true if a match + * is found, false if the iteration ends. Many subclasses + * can implement this more efficiently. + * + * @param key the key to search for + * @return true if the map contains the key + * @throws NullPointerException if key is null but the map + * does not permit null keys + * @see #containsValue(Object) + */ + public boolean containsKey(Object key) + { + Iterator> entries = entrySet().iterator(); + int pos = size(); + while (--pos >= 0) + if (equals(key, entries.next().getKey())) + return true; + return false; + } + + /** + * Returns true if this contains at least one mapping with the given value. + * This implementation does a linear search, O(n), over the + * entrySet(), returning true if a match + * is found, false if the iteration ends. A match is + * defined as a value, v, where (value == null ? v == null : + * value.equals(v)). Subclasses are unlikely to implement + * this more efficiently. + * + * @param value the value to search for + * @return true if the map contains the value + * @see #containsKey(Object) + */ + public boolean containsValue(Object value) + { + Iterator> entries = entrySet().iterator(); + int pos = size(); + while (--pos >= 0) + if (equals(value, entries.next().getValue())) + return true; + return false; + } + + /** + * Compares the specified object with this map for equality. Returns + * true if the other object is a Map with the same mappings, + * that is,
        + * o instanceof Map && entrySet().equals(((Map) o).entrySet(); + * + * @param o the object to be compared + * @return true if the object equals this map + * @see Set#equals(Object) + */ + public boolean equals(Object o) + { + return (o == this + || (o instanceof Map + && entrySet().equals(((Map) o).entrySet()))); + } + + /** + * Returns the value mapped by the given key. Returns null if + * there is no mapping. However, in Maps that accept null values, you + * must rely on containsKey to determine if a mapping exists. + * This iteration takes linear time, searching entrySet().iterator() of + * the key. Many implementations override this method. + * + * @param key the key to look up + * @return the value associated with the key, or null if key not in map + * @throws NullPointerException if this map does not accept null keys + * @see #containsKey(Object) + */ + public V get(Object key) + { + Iterator> entries = entrySet().iterator(); + int pos = size(); + while (--pos >= 0) + { + Map.Entry entry = entries.next(); + if (equals(key, entry.getKey())) + return entry.getValue(); + } + return null; + } + + /** + * Returns the hash code for this map. As defined in Map, this is the sum + * of all hashcodes for each Map.Entry object in entrySet, or basically + * entrySet().hashCode(). + * + * @return the hash code + * @see Map.Entry#hashCode() + * @see Set#hashCode() + */ + public int hashCode() + { + return entrySet().hashCode(); + } + + /** + * Returns true if the map contains no mappings. This is implemented by + * size() == 0. + * + * @return true if the map is empty + * @see #size() + */ + public boolean isEmpty() + { + return size() == 0; + } + + /** + * Returns a set view of this map's keys. The set is backed by the map, + * so changes in one show up in the other. Modifications while an iteration + * is in progress produce undefined behavior. The set supports removal + * if entrySet() does, but does not support element addition. + *

        + * + * This implementation creates an AbstractSet, where the iterator wraps + * the entrySet iterator, size defers to the Map's size, and contains + * defers to the Map's containsKey. The set is created on first use, and + * returned on subsequent uses, although since no synchronization occurs, + * there is a slight possibility of creating two sets. + * + * @return a Set view of the keys + * @see Set#iterator() + * @see #size() + * @see #containsKey(Object) + * @see #values() + */ + public Set keySet() + { + if (keys == null) + keys = new AbstractSet() + { + /** + * Retrieves the number of keys in the backing map. + * + * @return The number of keys. + */ + public int size() + { + return AbstractMap.this.size(); + } + + /** + * Returns true if the backing map contains the + * supplied key. + * + * @param key The key to search for. + * @return True if the key was found, false otherwise. + */ + public boolean contains(Object key) + { + return containsKey(key); + } + + /** + * Returns an iterator which iterates over the keys + * in the backing map, using a wrapper around the + * iterator returned by entrySet(). + * + * @return An iterator over the keys. + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * The iterator returned by entrySet(). + */ + private final Iterator> map_iterator + = entrySet().iterator(); + + /** + * Returns true if a call to next() will + * return another key. + * + * @return True if the iterator has not yet reached + * the last key. + */ + public boolean hasNext() + { + return map_iterator.hasNext(); + } + + /** + * Returns the key from the next entry retrieved + * by the underlying entrySet() iterator. + * + * @return The next key. + */ + public K next() + { + return map_iterator.next().getKey(); + } + + /** + * Removes the map entry which has a key equal + * to that returned by the last call to + * next(). + * + * @throws UnsupportedOperationException if the + * map doesn't support removal. + */ + public void remove() + { + map_iterator.remove(); + } + }; + } + }; + return keys; + } + + /** + * Associates the given key to the given value (optional operation). If the + * map already contains the key, its value is replaced. This implementation + * simply throws an UnsupportedOperationException. Be aware that in a map + * that permits null values, a null return does not always + * imply that the mapping was created. + * + * @param key the key to map + * @param value the value to be mapped + * @return the previous value of the key, or null if there was no mapping + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the key or value is of the wrong type + * @throws IllegalArgumentException if something about this key or value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null keys or values + * @see #containsKey(Object) + */ + public V put(K key, V value) + { + throw new UnsupportedOperationException(); + } + + /** + * Copies all entries of the given map to this one (optional operation). If + * the map already contains a key, its value is replaced. This implementation + * simply iterates over the map's entrySet(), calling put, + * so it is not supported if puts are not. + * + * @param m the mapping to load into this map + * @throws UnsupportedOperationException if the operation is not supported + * by this map. + * @throws ClassCastException if a key or value is of the wrong type for + * adding to this map. + * @throws IllegalArgumentException if something about a key or value + * prevents it from existing in this map. + * @throws NullPointerException if the map forbids null keys or values. + * @throws NullPointerException if m is null. + * @see #put(Object, Object) + */ + public void putAll(Map m) + { + // FIXME: bogus circumlocution. + Iterator entries2 = m.entrySet().iterator(); + Iterator> entries + = (Iterator>) entries2; + int pos = m.size(); + while (--pos >= 0) + { + Map.Entry entry = entries.next(); + put(entry.getKey(), entry.getValue()); + } + } + + /** + * Removes the mapping for this key if present (optional operation). This + * implementation iterates over the entrySet searching for a matching + * key, at which point it calls the iterator's remove method. + * It returns the result of getValue() on the entry, if found, + * or null if no entry is found. Note that maps which permit null values + * may also return null if the key was removed. If the entrySet does not + * support removal, this will also fail. This is O(n), so many + * implementations override it for efficiency. + * + * @param key the key to remove + * @return the value the key mapped to, or null if not present. + * Null may also be returned if null values are allowed + * in the map and the value of this mapping is null. + * @throws UnsupportedOperationException if deletion is unsupported + * @see Iterator#remove() + */ + public V remove(Object key) + { + Iterator> entries = entrySet().iterator(); + int pos = size(); + while (--pos >= 0) + { + Map.Entry entry = entries.next(); + if (equals(key, entry.getKey())) + { + // Must get the value before we remove it from iterator. + V r = entry.getValue(); + entries.remove(); + return r; + } + } + return null; + } + + /** + * Returns the number of key-value mappings in the map. If there are more + * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. This is + * implemented as entrySet().size(). + * + * @return the number of mappings + * @see Set#size() + */ + public int size() + { + return entrySet().size(); + } + + /** + * Returns a String representation of this map. This is a listing of the + * map entries (which are specified in Map.Entry as being + * getKey() + "=" + getValue()), separated by a comma and + * space (", "), and surrounded by braces ('{' and '}'). This implementation + * uses a StringBuffer and iterates over the entrySet to build the String. + * Note that this can fail with an exception if underlying keys or + * values complete abruptly in toString(). + * + * @return a String representation + * @see Map.Entry#toString() + */ + public String toString() + { + Iterator> entries = entrySet().iterator(); + CPStringBuilder r = new CPStringBuilder("{"); + for (int pos = size(); pos > 0; pos--) + { + Map.Entry entry = entries.next(); + r.append(entry.getKey()); + r.append('='); + r.append(entry.getValue()); + if (pos > 1) + r.append(", "); + } + r.append("}"); + return r.toString(); + } + + /** + * Returns a collection or bag view of this map's values. The collection + * is backed by the map, so changes in one show up in the other. + * Modifications while an iteration is in progress produce undefined + * behavior. The collection supports removal if entrySet() does, but + * does not support element addition. + *

        + * + * This implementation creates an AbstractCollection, where the iterator + * wraps the entrySet iterator, size defers to the Map's size, and contains + * defers to the Map's containsValue. The collection is created on first + * use, and returned on subsequent uses, although since no synchronization + * occurs, there is a slight possibility of creating two collections. + * + * @return a Collection view of the values + * @see Collection#iterator() + * @see #size() + * @see #containsValue(Object) + * @see #keySet() + */ + public Collection values() + { + if (values == null) + values = new AbstractCollection() + { + /** + * Returns the number of values stored in + * the backing map. + * + * @return The number of values. + */ + public int size() + { + return AbstractMap.this.size(); + } + + /** + * Returns true if the backing map contains + * the supplied value. + * + * @param value The value to search for. + * @return True if the value was found, false otherwise. + */ + public boolean contains(Object value) + { + return containsValue(value); + } + + /** + * Returns an iterator which iterates over the + * values in the backing map, by using a wrapper + * around the iterator returned by entrySet(). + * + * @return An iterator over the values. + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * The iterator returned by entrySet(). + */ + private final Iterator> map_iterator + = entrySet().iterator(); + + /** + * Returns true if a call to next() will + * return another value. + * + * @return True if the iterator has not yet reached + * the last value. + */ + public boolean hasNext() + { + return map_iterator.hasNext(); + } + + /** + * Returns the value from the next entry retrieved + * by the underlying entrySet() iterator. + * + * @return The next value. + */ + public V next() + { + return map_iterator.next().getValue(); + } + + /** + * Removes the map entry which has a key equal + * to that returned by the last call to + * next(). + * + * @throws UnsupportedOperationException if the + * map doesn't support removal. + */ + public void remove() + { + map_iterator.remove(); + } + }; + } + }; + return values; + } + + /** + * Compare two objects according to Collection semantics. + * + * @param o1 the first object + * @param o2 the second object + * @return o1 == o2 || (o1 != null && o1.equals(o2)) + */ + // Package visible for use throughout java.util. + // It may be inlined since it is final. + static final boolean equals(Object o1, Object o2) + { + return o1 == o2 || (o1 != null && o1.equals(o2)); + } + + /** + * Hash an object according to Collection semantics. + * + * @param o the object to hash + * @return o1 == null ? 0 : o1.hashCode() + */ + // Package visible for use throughout java.util. + // It may be inlined since it is final. + static final int hashCode(Object o) + { + return o == null ? 0 : o.hashCode(); + } + + /** + * A class which implements Map.Entry. It is shared by HashMap, TreeMap, + * Hashtable, and Collections. It is not specified by the JDK, but makes + * life much easier. + * + * @author Jon Zeppieri + * @author Eric Blake (ebb9@email.byu.edu) + * + * @since 1.6 + */ + public static class SimpleEntry implements Entry, Serializable + { + + /** + * Compatible with JDK 1.6 + */ + private static final long serialVersionUID = -8499721149061103585L; + + /** + * The key. Package visible for direct manipulation. + */ + K key; + + /** + * The value. Package visible for direct manipulation. + */ + V value; + + /** + * Basic constructor initializes the fields. + * @param newKey the key + * @param newValue the value + */ + public SimpleEntry(K newKey, V newValue) + { + key = newKey; + value = newValue; + } + + public SimpleEntry(Entry entry) + { + this(entry.getKey(), entry.getValue()); + } + + /** + * Compares the specified object with this entry. Returns true only if + * the object is a mapping of identical key and value. In other words, + * this must be:
        + *

        (o instanceof Map.Entry)
        +     *       && (getKey() == null ? ((HashMap) o).getKey() == null
        +     *           : getKey().equals(((HashMap) o).getKey()))
        +     *       && (getValue() == null ? ((HashMap) o).getValue() == null
        +     *           : getValue().equals(((HashMap) o).getValue()))
        + * + * @param o the object to compare + * @return true if it is equal + */ + public boolean equals(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + // Optimize for our own entries. + if (o instanceof SimpleEntry) + { + SimpleEntry e = (SimpleEntry) o; + return (AbstractMap.equals(key, e.key) + && AbstractMap.equals(value, e.value)); + } + Map.Entry e = (Map.Entry) o; + return (AbstractMap.equals(key, e.getKey()) + && AbstractMap.equals(value, e.getValue())); + } + + /** + * Get the key corresponding to this entry. + * + * @return the key + */ + public K getKey() + { + return key; + } + + /** + * Get the value corresponding to this entry. If you already called + * Iterator.remove(), the behavior undefined, but in this case it works. + * + * @return the value + */ + public V getValue() + { + return value; + } + + /** + * Returns the hash code of the entry. This is defined as the exclusive-or + * of the hashcodes of the key and value (using 0 for null). In other + * words, this must be:
        + *
        (getKey() == null ? 0 : getKey().hashCode())
        +     *       ^ (getValue() == null ? 0 : getValue().hashCode())
        + * + * @return the hash code + */ + public int hashCode() + { + return (AbstractMap.hashCode(key) ^ AbstractMap.hashCode(value)); + } + + /** + * Replaces the value with the specified object. This writes through + * to the map, unless you have already called Iterator.remove(). It + * may be overridden to restrict a null value. + * + * @param newVal the new value to store + * @return the old value + * @throws NullPointerException if the map forbids null values. + * @throws UnsupportedOperationException if the map doesn't support + * put(). + * @throws ClassCastException if the value is of a type unsupported + * by the map. + * @throws IllegalArgumentException if something else about this + * value prevents it being stored in the map. + */ + public V setValue(V newVal) + { + V r = value; + value = newVal; + return r; + } + + /** + * This provides a string representation of the entry. It is of the form + * "key=value", where string concatenation is used on key and value. + * + * @return the string representation + */ + public String toString() + { + return key + "=" + value; + } + } // class SimpleEntry + + +} diff --git a/libjava/classpath/java/util/AbstractSequentialList.java b/libjava/classpath/java/util/AbstractSequentialList.java new file mode 100644 index 000000000..81b0714e1 --- /dev/null +++ b/libjava/classpath/java/util/AbstractSequentialList.java @@ -0,0 +1,235 @@ +/* AbstractSequentialList.java -- List implementation for sequential access + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Abstract superclass to make it easier to implement the List interface when + * backed by a sequential-access store, such as a linked list. For random + * access data, use AbstractList. This class implements the random access + * methods (get, set, add, and + * remove) atop the list iterator, opposite of AbstractList's + * approach of implementing the iterator atop random access. + *

        + * + * To implement a list, you need an implementation for size() + * and listIterator. With just hasNext, + * next, hasPrevious, previous, + * nextIndex, and previousIndex, you have an + * unmodifiable list. For a modifiable one, add set, and for + * a variable-size list, add add and remove. + *

        + * + * The programmer should provide a no-argument constructor, and one that + * accepts another Collection, as recommended by the Collection interface. + * Unfortunately, there is no way to enforce this in Java. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see AbstractList + * @see AbstractCollection + * @see ListIterator + * @see LinkedList + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractSequentialList extends AbstractList +{ + /** + * The main constructor, for use by subclasses. + */ + protected AbstractSequentialList() + { + } + + /** + * Returns a ListIterator over the list, starting from position index. + * Subclasses must provide an implementation of this method. + * + * @param index the starting position of the list + * @return the list iterator + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public abstract ListIterator listIterator(int index); + + /** + * Insert an element into the list at a given position (optional operation). + * This shifts all existing elements from that position to the end one + * index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. This iteration + * uses listIterator(index).add(o). + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason. + * @throws NullPointerException if o is null and the list does not permit + * the addition of null values. + */ + public void add(int index, E o) + { + listIterator(index).add(o); + } + + /** + * Insert the contents of a collection into the list at a given position + * (optional operation). Shift all elements at that position to the right + * by the number of elements inserted. This operation is undefined if + * this list is modified during the operation (for example, if you try + * to insert a list into itself). + *

        + * + * This implementation grabs listIterator(index), then proceeds to use add + * for each element returned by c's iterator. Sun's online specs are wrong, + * claiming that this also calls next(): listIterator.add() correctly + * skips the added element. + * + * @param index the location to insert the collection + * @param c the collection to insert + * @return true if the list was modified by this action, that is, if c is + * non-empty + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + * @throws NullPointerException if an object, o, in c is null and the list + * does not permit the addition of null values. + * @see #add(int, Object) + */ + public boolean addAll(int index, Collection c) + { + Iterator ci = c.iterator(); + int size = c.size(); + ListIterator i = listIterator(index); + for (int pos = size; pos > 0; pos--) + i.add(ci.next()); + return size > 0; + } + + /** + * Get the element at a given index in this list. This implementation + * returns listIterator(index).next(). + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public E get(int index) + { + // This is a legal listIterator position, but an illegal get. + if (index == size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + return listIterator(index).next(); + } + + /** + * Obtain an Iterator over this list, whose sequence is the list order. This + * implementation returns listIterator(). + * + * @return an Iterator over the elements of this list, in order + */ + public Iterator iterator() + { + return listIterator(); + } + + /** + * Remove the element at a given position in this list (optional operation). + * Shifts all remaining elements to the left to fill the gap. This + * implementation uses listIterator(index) and ListIterator.remove(). + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public E remove(int index) + { + // This is a legal listIterator position, but an illegal remove. + if (index == size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + ListIterator i = listIterator(index); + E removed = i.next(); + i.remove(); + return removed; + } + + /** + * Replace an element of this list with another object (optional operation). + * This implementation uses listIterator(index) and ListIterator.set(o). + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and the list does not allow + * a value to be set to null. + */ + public E set(int index, E o) + { + // This is a legal listIterator position, but an illegal set. + if (index == size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + ListIterator i = listIterator(index); + E old = i.next(); + i.set(o); + return old; + } +} diff --git a/libjava/classpath/java/util/AbstractSet.java b/libjava/classpath/java/util/AbstractSet.java new file mode 100644 index 000000000..af1e4d125 --- /dev/null +++ b/libjava/classpath/java/util/AbstractSet.java @@ -0,0 +1,146 @@ +/* AbstractSet.java -- Abstract implementation of most of Set + Copyright (C) 1998, 2000, 2001, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An abstract implementation of Set to make it easier to create your own + * implementations. In order to create a Set, subclass AbstractSet and + * implement the same methods that are required for AbstractCollection + * (although these methods must of course meet the requirements that Set puts + * on them - specifically, no element may be in the set more than once). This + * class simply provides implementations of equals() and hashCode() to fulfil + * the requirements placed on them by the Set interface. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see AbstractCollection + * @see Set + * @see HashSet + * @see TreeSet + * @see LinkedHashSet + * @since 1.2 + * @status updated to 1.4 + */ +public abstract class AbstractSet + extends AbstractCollection + implements Set +{ + /** + * The main constructor, for use by subclasses. + */ + protected AbstractSet() + { + } + + /** + * Tests whether the given object is equal to this Set. This implementation + * first checks whether this set is the given object, and returns + * true if so. Otherwise, if o is a Set and is the same size as this one, it + * returns the result of calling containsAll on the given Set. Otherwise, it + * returns false. + * + * @param o the Object to be tested for equality with this Set + * @return true if the given object is equal to this Set + */ + public boolean equals(Object o) + { + return (o == this + || (o instanceof Set && ((Set) o).size() == size() + && containsAll((Collection) o))); + } + + /** + * Returns a hash code for this Set. The hash code of a Set is the sum of the + * hash codes of all its elements, except that the hash code of null is + * defined to be zero. This implementation obtains an Iterator over the Set, + * and sums the results. + * + * @return a hash code for this Set + */ + public int hashCode() + { + Iterator itr = iterator(); + int hash = 0; + int pos = size(); + while (--pos >= 0) + hash += hashCode(itr.next()); + return hash; + } + + /** + * Removes from this set all elements in the given collection (optional + * operation). This implementation uses size() to determine + * the smaller collection. Then, if this set is smaller, it iterates + * over the set, calling Iterator.remove if the collection contains + * the element. If this set is larger, it iterates over the collection, + * calling Set.remove for all elements in the collection. Note that + * this operation will fail if a remove methods is not supported. + * + * @param c the collection of elements to remove + * @return true if the set was modified as a result + * @throws UnsupportedOperationException if remove is not supported + * @throws NullPointerException if the collection is null + * @see AbstractCollection#remove(Object) + * @see Collection#contains(Object) + * @see Iterator#remove() + */ + public boolean removeAll(Collection c) + { + int oldsize = size(); + int count = c.size(); + if (oldsize < count) + { + Iterator i; + for (i = iterator(), count = oldsize; count > 0; count--) + { + if (c.contains(i.next())) + i.remove(); + } + } + else + { + Iterator i; + for (i = c.iterator(); count > 0; count--) + remove(i.next()); + } + return oldsize != size(); + } +} diff --git a/libjava/classpath/java/util/ArrayList.java b/libjava/classpath/java/util/ArrayList.java new file mode 100644 index 000000000..0da2193af --- /dev/null +++ b/libjava/classpath/java/util/ArrayList.java @@ -0,0 +1,603 @@ +/* ArrayList.java -- JDK1.2's answer to Vector; this is an array-backed + implementation of the List interface + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * An array-backed implementation of the List interface. This implements + * all optional list operations, and permits null elements, so that it is + * better than Vector, which it replaces. Random access is roughly constant + * time, and iteration is roughly linear time, so it is nice and fast, with + * less overhead than a LinkedList. + *

        + * + * Each list has a capacity, and as the array reaches that capacity it + * is automatically transferred to a larger array. You also have access to + * ensureCapacity and trimToSize to control the backing array's size, avoiding + * reallocation or wasted memory. + *

        + * + * ArrayList is not synchronized, so if you need multi-threaded access, + * consider using:
        + * List l = Collections.synchronizedList(new ArrayList(...)); + *

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Jon A. Zeppieri + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see LinkedList + * @see Vector + * @see Collections#synchronizedList(List) + * @see AbstractList + * @status updated to 1.4 + */ +public class ArrayList extends AbstractList + implements List, RandomAccess, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2 + */ + private static final long serialVersionUID = 8683452581122892189L; + + /** + * The default capacity for new ArrayLists. + */ + private static final int DEFAULT_CAPACITY = 10; + + /** + * The number of elements in this list. + * @serial the list size + */ + private int size; + + /** + * Where the data is stored. + */ + private transient E[] data; + + /** + * Construct a new ArrayList with the supplied initial capacity. + * + * @param capacity initial capacity of this ArrayList + * @throws IllegalArgumentException if capacity is negative + */ + public ArrayList(int capacity) + { + // Must explicitly check, to get correct exception. + if (capacity < 0) + throw new IllegalArgumentException(); + data = (E[]) new Object[capacity]; + } + + /** + * Construct a new ArrayList with the default capacity (16). + */ + public ArrayList() + { + this(DEFAULT_CAPACITY); + } + + /** + * Construct a new ArrayList, and initialize it with the elements + * in the supplied Collection. The initial capacity is 110% of the + * Collection's size. + * + * @param c the collection whose elements will initialize this list + * @throws NullPointerException if c is null + */ + public ArrayList(Collection c) + { + this((int) (c.size() * 1.1f)); + addAll(c); + } + + /** + * Trims the capacity of this List to be equal to its size; + * a memory saver. + */ + public void trimToSize() + { + // Not a structural change from the perspective of iterators on this list, + // so don't update modCount. + if (size != data.length) + { + E[] newData = (E[]) new Object[size]; + System.arraycopy(data, 0, newData, 0, size); + data = newData; + } + } + + /** + * Guarantees that this list will have at least enough capacity to + * hold minCapacity elements. This implementation will grow the list to + * max(current * 2, minCapacity) if (minCapacity > current). The JCL says + * explictly that "this method increases its capacity to minCap", while + * the JDK 1.3 online docs specify that the list will grow to at least the + * size specified. + * + * @param minCapacity the minimum guaranteed capacity + */ + public void ensureCapacity(int minCapacity) + { + int current = data.length; + + if (minCapacity > current) + { + E[] newData = (E[]) new Object[Math.max(current * 2, minCapacity)]; + System.arraycopy(data, 0, newData, 0, size); + data = newData; + } + } + + /** + * Returns the number of elements in this list. + * + * @return the list size + */ + public int size() + { + return size; + } + + /** + * Checks if the list is empty. + * + * @return true if there are no elements + */ + public boolean isEmpty() + { + return size == 0; + } + + /** + * Returns true iff element is in this ArrayList. + * + * @param e the element whose inclusion in the List is being tested + * @return true if the list contains e + */ + public boolean contains(Object e) + { + return indexOf(e) != -1; + } + + /** + * Returns the lowest index at which element appears in this List, or + * -1 if it does not appear. + * + * @param e the element whose inclusion in the List is being tested + * @return the index where e was found + */ + public int indexOf(Object e) + { + for (int i = 0; i < size; i++) + if (equals(e, data[i])) + return i; + return -1; + } + + /** + * Returns the highest index at which element appears in this List, or + * -1 if it does not appear. + * + * @param e the element whose inclusion in the List is being tested + * @return the index where e was found + */ + public int lastIndexOf(Object e) + { + for (int i = size - 1; i >= 0; i--) + if (equals(e, data[i])) + return i; + return -1; + } + + /** + * Creates a shallow copy of this ArrayList (elements are not cloned). + * + * @return the cloned object + */ + public Object clone() + { + ArrayList clone = null; + try + { + clone = (ArrayList) super.clone(); + clone.data = (E[]) data.clone(); + } + catch (CloneNotSupportedException e) + { + // Impossible to get here. + } + return clone; + } + + /** + * Returns an Object array containing all of the elements in this ArrayList. + * The array is independent of this list. + * + * @return an array representation of this list + */ + public Object[] toArray() + { + E[] array = (E[]) new Object[size]; + System.arraycopy(data, 0, array, 0, size); + return array; + } + + /** + * Returns an Array whose component type is the runtime component type of + * the passed-in Array. The returned Array is populated with all of the + * elements in this ArrayList. If the passed-in Array is not large enough + * to store all of the elements in this List, a new Array will be created + * and returned; if the passed-in Array is larger than the size + * of this List, then size() index will be set to null. + * + * @param a the passed-in Array + * @return an array representation of this list + * @throws ArrayStoreException if the runtime type of a does not allow + * an element in this list + * @throws NullPointerException if a is null + */ + public T[] toArray(T[] a) + { + if (a.length < size) + a = (T[]) Array.newInstance(a.getClass().getComponentType(), size); + else if (a.length > size) + a[size] = null; + System.arraycopy(data, 0, a, 0, size); + return a; + } + + /** + * Retrieves the element at the user-supplied index. + * + * @param index the index of the element we are fetching + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public E get(int index) + { + checkBoundExclusive(index); + return data[index]; + } + + /** + * Sets the element at the specified index. The new element, e, + * can be an object of any type or null. + * + * @param index the index at which the element is being set + * @param e the element to be set + * @return the element previously at the specified index + * @throws IndexOutOfBoundsException if index < 0 || index >= 0 + */ + public E set(int index, E e) + { + checkBoundExclusive(index); + E result = data[index]; + data[index] = e; + return result; + } + + /** + * Appends the supplied element to the end of this list. + * The element, e, can be an object of any type or null. + * + * @param e the element to be appended to this list + * @return true, the add will always succeed + */ + public boolean add(E e) + { + modCount++; + if (size == data.length) + ensureCapacity(size + 1); + data[size++] = e; + return true; + } + + /** + * Adds the supplied element at the specified index, shifting all + * elements currently at that index or higher one to the right. + * The element, e, can be an object of any type or null. + * + * @param index the index at which the element is being added + * @param e the item being added + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public void add(int index, E e) + { + checkBoundInclusive(index); + modCount++; + if (size == data.length) + ensureCapacity(size + 1); + if (index != size) + System.arraycopy(data, index, data, index + 1, size - index); + data[index] = e; + size++; + } + + /** + * Removes the element at the user-supplied index. + * + * @param index the index of the element to be removed + * @return the removed Object + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public E remove(int index) + { + checkBoundExclusive(index); + E r = data[index]; + modCount++; + if (index != --size) + System.arraycopy(data, index + 1, data, index, size - index); + // Aid for garbage collection by releasing this pointer. + data[size] = null; + return r; + } + + /** + * Removes all elements from this List + */ + public void clear() + { + if (size > 0) + { + modCount++; + // Allow for garbage collection. + Arrays.fill(data, 0, size, null); + size = 0; + } + } + + /** + * Add each element in the supplied Collection to this List. It is undefined + * what happens if you modify the list while this is taking place; for + * example, if the collection contains this list. c can contain objects + * of any type, as well as null values. + * + * @param c a Collection containing elements to be added to this List + * @return true if the list was modified, in other words c is not empty + * @throws NullPointerException if c is null + */ + public boolean addAll(Collection c) + { + return addAll(size, c); + } + + /** + * Add all elements in the supplied collection, inserting them beginning + * at the specified index. c can contain objects of any type, as well + * as null values. + * + * @param index the index at which the elements will be inserted + * @param c the Collection containing the elements to be inserted + * @throws IndexOutOfBoundsException if index < 0 || index > 0 + * @throws NullPointerException if c is null + */ + public boolean addAll(int index, Collection c) + { + checkBoundInclusive(index); + Iterator itr = c.iterator(); + int csize = c.size(); + + modCount++; + if (csize + size > data.length) + ensureCapacity(size + csize); + int end = index + csize; + if (size > 0 && index != size) + System.arraycopy(data, index, data, end, size - index); + size += csize; + for ( ; index < end; index++) + data[index] = itr.next(); + return csize > 0; + } + + /** + * Removes all elements in the half-open interval [fromIndex, toIndex). + * Does nothing when toIndex is equal to fromIndex. + * + * @param fromIndex the first index which will be removed + * @param toIndex one greater than the last index which will be removed + * @throws IndexOutOfBoundsException if fromIndex > toIndex + */ + protected void removeRange(int fromIndex, int toIndex) + { + int change = toIndex - fromIndex; + if (change > 0) + { + modCount++; + System.arraycopy(data, toIndex, data, fromIndex, size - toIndex); + size -= change; + } + else if (change < 0) + throw new IndexOutOfBoundsException(); + } + + /** + * Checks that the index is in the range of possible elements (inclusive). + * + * @param index the index to check + * @throws IndexOutOfBoundsException if index > size + */ + private void checkBoundInclusive(int index) + { + // Implementation note: we do not check for negative ranges here, since + // use of a negative index will cause an ArrayIndexOutOfBoundsException, + // a subclass of the required exception, with no effort on our part. + if (index > size) + raiseBoundsError(index); + } + + /** + * Checks that the index is in the range of existing elements (exclusive). + * + * @param index the index to check + * @throws IndexOutOfBoundsException if index >= size + */ + private void checkBoundExclusive(int index) + { + // Implementation note: we do not check for negative ranges here, since + // use of a negative index will cause an ArrayIndexOutOfBoundsException, + // a subclass of the required exception, with no effort on our part. + if (index >= size) + raiseBoundsError(index); + } + + /** + * Raise the ArrayIndexOfOutBoundsException. + * + * @param index the index of the access + * @throws IndexOutOfBoundsException unconditionally + */ + private void raiseBoundsError(int index) + { + // Implementaion note: put in a separate method to make the JITs job easier + // (separate common from uncommon code at method boundaries when trivial to + // do so). + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); + } + + + /** + * Remove from this list all elements contained in the given collection. + * This is not public, due to Sun's API, but this performs in linear + * time while the default behavior of AbstractList would be quadratic. + * + * @param c the collection to filter out + * @return true if this list changed + * @throws NullPointerException if c is null + */ + boolean removeAllInternal(Collection c) + { + int i; + int j; + for (i = 0; i < size; i++) + if (c.contains(data[i])) + break; + if (i == size) + return false; + + modCount++; + for (j = i++; i < size; i++) + if (! c.contains(data[i])) + data[j++] = data[i]; + size -= i - j; + return true; + } + + /** + * Retain in this vector only the elements contained in the given collection. + * This is not public, due to Sun's API, but this performs in linear + * time while the default behavior of AbstractList would be quadratic. + * + * @param c the collection to filter by + * @return true if this vector changed + * @throws NullPointerException if c is null + * @since 1.2 + */ + boolean retainAllInternal(Collection c) + { + int i; + int j; + for (i = 0; i < size; i++) + if (! c.contains(data[i])) + break; + if (i == size) + return false; + + modCount++; + for (j = i++; i < size; i++) + if (c.contains(data[i])) + data[j++] = data[i]; + size -= i - j; + return true; + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the size field (int), the length of the backing array + * (int), followed by its elements (Objects) in proper order. + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + // The 'size' field. + s.defaultWriteObject(); + // We serialize unused list entries to preserve capacity. + int len = data.length; + s.writeInt(len); + // it would be more efficient to just write "size" items, + // this need readObject read "size" items too. + for (int i = 0; i < size; i++) + s.writeObject(data[i]); + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the size field (int), the length of the backing array + * (int), followed by its elements (Objects) in proper order. + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + // the `size' field. + s.defaultReadObject(); + int capacity = s.readInt(); + data = (E[]) new Object[capacity]; + for (int i = 0; i < size; i++) + data[i] = (E) s.readObject(); + } +} diff --git a/libjava/classpath/java/util/Arrays.java b/libjava/classpath/java/util/Arrays.java new file mode 100644 index 000000000..dad55c459 --- /dev/null +++ b/libjava/classpath/java/util/Arrays.java @@ -0,0 +1,4034 @@ +/* Arrays.java -- Utility class with methods to operate on arrays + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * This class contains various static utility methods performing operations on + * arrays, and a method to provide a List "view" of an array to facilitate + * using arrays with Collection-based APIs. All methods throw a + * {@link NullPointerException} if the parameter array is null. + *

        + * + * Implementations may use their own algorithms, but must obey the general + * properties; for example, the sort must be stable and n*log(n) complexity. + * Sun's implementation of sort, and therefore ours, is a tuned quicksort, + * adapted from Jon L. Bentley and M. Douglas McIlroy's "Engineering a Sort + * Function", Software-Practice and Experience, Vol. 23(11) P. 1249-1265 + * (November 1993). This algorithm offers n*log(n) performance on many data + * sets that cause other quicksorts to degrade to quadratic performance. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Comparable + * @see Comparator + * @since 1.2 + * @status updated to 1.4 + */ +public class Arrays +{ + /** + * This class is non-instantiable. + */ + private Arrays() + { + } + + +// binarySearch + /** + * Perform a binary search of a byte array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(byte[] a, byte key) + { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a byte array for a key. The range + * must be sorted (as by the sort(byte[], int, int) method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if low > hi + * @throws ArrayIndexOutOfBoundsException if low < 0 or + * hi > a.length. + */ + public static int binarySearch(byte[] a, int low, int hi, byte key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >>> 1; + final byte d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a char array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(char[] a, char key) + { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a char array for a key. The range + * must be sorted (as by the sort(char[], int, int) method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if low > hi + * @throws ArrayIndexOutOfBoundsException if low < 0 or + * hi > a.length. + */ + public static int binarySearch(char[] a, int low, int hi, char key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >>> 1; + final char d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a short array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(short[] a, short key) + { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a short array for a key. The range + * must be sorted (as by the sort(short[], int, int) method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if low > hi + * @throws ArrayIndexOutOfBoundsException if low < 0 or + * hi > a.length. + */ + public static int binarySearch(short[] a, int low, int hi, short key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >>> 1; + final short d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of an int array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(int[] a, int key) + { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of an integer array for a key. The range + * must be sorted (as by the sort(int[], int, int) method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if low > hi + * @throws ArrayIndexOutOfBoundsException if low < 0 or + * hi > a.length. + */ + public static int binarySearch(int[] a, int low, int hi, int key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >>> 1; + final int d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a long array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(long[] a, long key) + { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a long array for a key. The range + * must be sorted (as by the sort(long[], int, int) method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if low > hi + * @throws ArrayIndexOutOfBoundsException if low < 0 or + * hi > a.length. + */ + public static int binarySearch(long[] a, int low, int hi, long key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >>> 1; + final long d = a[mid]; + if (d == key) + return mid; + else if (d > key) + hi = mid - 1; + else + // This gets the insertion point right on the last loop. + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a float array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(float[] a, float key) + { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a float array for a key. The range + * must be sorted (as by the sort(float[], int, int) method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if low > hi + * @throws ArrayIndexOutOfBoundsException if low < 0 or + * hi > a.length. + */ + public static int binarySearch(float[] a, int low, int hi, float key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); + // Must use Float.compare to take into account NaN, +-0. + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >>> 1; + final int r = Float.compare(a[mid], key); + if (r == 0) + return mid; + else if (r > 0) + hi = mid - 1; + else + // This gets the insertion point right on the last loop + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of a double array for a key. The array must be + * sorted (as by the sort() method) - if it is not, the behaviour of this + * method is undefined, and may be an infinite loop. If the array contains + * the key more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(double[] a, double key) + { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key); + } + + /** + * Perform a binary search of a range of a double array for a key. The range + * must be sorted (as by the sort(double[], int, int) method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws IllegalArgumentException if low > hi + * @throws ArrayIndexOutOfBoundsException if low < 0 or + * hi > a.length. + */ + public static int binarySearch(double[] a, int low, int hi, double key) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); + // Must use Double.compare to take into account NaN, +-0. + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >>> 1; + final int r = Double.compare(a[mid], key); + if (r == 0) + return mid; + else if (r > 0) + hi = mid - 1; + else + // This gets the insertion point right on the last loop + low = ++mid; + } + return -mid - 1; + } + + /** + * Perform a binary search of an Object array for a key, using the natural + * ordering of the elements. The array must be sorted (as by the sort() + * method) - if it is not, the behaviour of this method is undefined, and may + * be an infinite loop. Further, the key must be comparable with every item + * in the array. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this (JCL) + * implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws ClassCastException if key could not be compared with one of the + * elements of a + * @throws NullPointerException if a null element in a is compared + */ + public static int binarySearch(Object[] a, Object key) + { + if (a.length == 0) + return -1; + return binarySearch(a, key, null); + } + + /** + * Perform a binary search of a range of an Object array for a key. The range + * must be sorted (as by the sort(Object[], int, int) method) - + * if it is not, the behaviour of this method is undefined, and may be an + * infinite loop. If the array contains the key more than once, any one of + * them may be found. Note: although the specification allows for an infinite + * loop if the array is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + */ + public static int binarySearch(Object[] a, int low, int hi, Object key) + { + return binarySearch(a, low, hi, key, null); + } + + /** + * Perform a binary search of an Object array for a key, using a supplied + * Comparator. The array must be sorted (as by the sort() method with the + * same Comparator) - if it is not, the behaviour of this method is + * undefined, and may be an infinite loop. Further, the key must be + * comparable with every item in the array. If the array contains the key + * more than once, any one of them may be found. Note: although the + * specification allows for an infinite loop if the array is unsorted, it + * will not happen in this (JCL) implementation. + * + * @param a the array to search (must be sorted) + * @param key the value to search for + * @param c the comparator by which the array is sorted; or null to + * use the elements' natural order + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws ClassCastException if key could not be compared with one of the + * elements of a + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + */ + public static int binarySearch(T[] a, T key, Comparator c) + { + if (a.length == 0) + return -1; + return binarySearch(a, 0, a.length - 1, key, c); + } + + /** + * Perform a binary search of a range of an Object array for a key using + * a {@link Comparator}. The range must be sorted (as by the + * sort(Object[], int, int) method) - if it is not, the + * behaviour of this method is undefined, and may be an infinite loop. If + * the array contains the key more than once, any one of them may be found. + * Note: although the specification allows for an infinite loop if the array + * is unsorted, it will not happen in this implementation. + * + * @param a the array to search (must be sorted) + * @param low the lowest index to search from. + * @param hi the highest index to search to. + * @param key the value to search for + * @param c the comparator by which the array is sorted; or null to + * use the elements' natural order + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value. + * @throws ClassCastException if key could not be compared with one of the + * elements of a + * @throws IllegalArgumentException if low > hi + * @throws ArrayIndexOutOfBoundsException if low < 0 or + * hi > a.length. + */ + public static int binarySearch(T[] a, int low, int hi, T key, + Comparator c) + { + if (low > hi) + throw new IllegalArgumentException("The start index is higher than " + + "the finish index."); + if (low < 0 || hi > a.length) + throw new ArrayIndexOutOfBoundsException("One of the indices is out " + + "of bounds."); + int mid = 0; + while (low <= hi) + { + mid = (low + hi) >>> 1; + // NOTE: Please keep the order of a[mid] and key. Although + // not required by the specs, the RI has it in this order as + // well, and real programs (erroneously) depend on it. + final int d = Collections.compare(a[mid], key, c); + if (d == 0) + return mid; + else if (d > 0) + hi = mid - 1; + else + // This gets the insertion point right on the last loop + low = ++mid; + } + return -mid - 1; + } + + +// equals + /** + * Compare two boolean arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(boolean[] a1, boolean[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two byte arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(byte[] a1, byte[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two char arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(char[] a1, char[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two short arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(short[] a1, short[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two int arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(int[] a1, int[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two long arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(long[] a1, long[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (a1[i] != a2[i]) + return false; + return true; + } + return false; + } + + /** + * Compare two float arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(float[] a1, float[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // Must use Float.compare to take into account NaN, +-0. + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (Float.compare(a1[i], a2[i]) != 0) + return false; + return true; + } + return false; + } + + /** + * Compare two double arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a2 is of the same length + * as a1, and for each 0 <= i < a1.length, a1[i] == a2[i] + */ + public static boolean equals(double[] a1, double[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // Must use Double.compare to take into account NaN, +-0. + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (Double.compare(a1[i], a2[i]) != 0) + return false; + return true; + } + return false; + } + + /** + * Compare two Object arrays for equality. + * + * @param a1 the first array to compare + * @param a2 the second array to compare + * @return true if a1 and a2 are both null, or if a1 is of the same length + * as a2, and for each 0 <= i < a.length, a1[i] == null ? + * a2[i] == null : a1[i].equals(a2[i]). + */ + public static boolean equals(Object[] a1, Object[] a2) + { + // Quick test which saves comparing elements of the same array, and also + // catches the case that both are null. + if (a1 == a2) + return true; + + if (null == a1 || null == a2) + return false; + + // If they're the same length, test each element + if (a1.length == a2.length) + { + int i = a1.length; + while (--i >= 0) + if (! AbstractCollection.equals(a1[i], a2[i])) + return false; + return true; + } + return false; + } + + +// fill + /** + * Fill an array with a boolean value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(boolean[] a, boolean val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a boolean value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(boolean[] a, int fromIndex, int toIndex, boolean val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a byte value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(byte[] a, byte val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a byte value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(byte[] a, int fromIndex, int toIndex, byte val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a char value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(char[] a, char val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a char value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(char[] a, int fromIndex, int toIndex, char val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a short value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(short[] a, short val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a short value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(short[] a, int fromIndex, int toIndex, short val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with an int value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(int[] a, int val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with an int value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(int[] a, int fromIndex, int toIndex, int val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a long value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(long[] a, long val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a long value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(long[] a, int fromIndex, int toIndex, long val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a float value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(float[] a, float val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a float value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(float[] a, int fromIndex, int toIndex, float val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with a double value. + * + * @param a the array to fill + * @param val the value to fill it with + */ + public static void fill(double[] a, double val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with a double value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(double[] a, int fromIndex, int toIndex, double val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + /** + * Fill an array with an Object value. + * + * @param a the array to fill + * @param val the value to fill it with + * @throws ClassCastException if val is not an instance of the element + * type of a. + */ + public static void fill(Object[] a, Object val) + { + fill(a, 0, a.length, val); + } + + /** + * Fill a range of an array with an Object value. + * + * @param a the array to fill + * @param fromIndex the index to fill from, inclusive + * @param toIndex the index to fill to, exclusive + * @param val the value to fill with + * @throws ClassCastException if val is not an instance of the element + * type of a. + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void fill(Object[] a, int fromIndex, int toIndex, Object val) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + for (int i = fromIndex; i < toIndex; i++) + a[i] = val; + } + + +// sort + // Thanks to Paul Fisher (rao@gnu.org) for finding this quicksort algorithm + // as specified by Sun and porting it to Java. The algorithm is an optimised + // quicksort, as described in Jon L. Bentley and M. Douglas McIlroy's + // "Engineering a Sort Function", Software-Practice and Experience, Vol. + // 23(11) P. 1249-1265 (November 1993). This algorithm gives n*log(n) + // performance on many arrays that would take quadratic time with a standard + // quicksort. + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the byte array to sort + */ + public static void sort(byte[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the byte array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(byte[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, byte[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, byte[] a) + { + byte c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, byte[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(byte[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = from + count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = array[b] - array[from]) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = array[c] - array[from]) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the char array to sort + */ + public static void sort(char[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the char array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(char[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, char[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, char[] a) + { + char c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, char[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(char[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = from + count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = array[b] - array[from]) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = array[c] - array[from]) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the short array to sort + */ + public static void sort(short[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the short array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(short[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, short[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, short[] a) + { + short c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, short[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(short[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = from + count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = array[b] - array[from]) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = array[c] - array[from]) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the int array to sort + */ + public static void sort(int[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the int array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(int[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, int[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, int[] a) + { + int c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, int[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Compares two integers in natural order, since a - b is inadequate. + * + * @param a the first int + * @param b the second int + * @return < 0, 0, or > 0 accorting to the comparison + */ + private static int compare(int a, int b) + { + return a < b ? -1 : a == b ? 0 : 1; + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(int[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = from + count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = compare(array[b], array[from])) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = compare(array[c], array[from])) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the long array to sort + */ + public static void sort(long[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the long array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(long[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, long[] d) + { + return (d[a] < d[b] + ? (d[b] < d[c] ? b : d[a] < d[c] ? c : a) + : (d[b] > d[c] ? b : d[a] > d[c] ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, long[] a) + { + long c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, long[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Compares two longs in natural order, since a - b is inadequate. + * + * @param a the first long + * @param b the second long + * @return < 0, 0, or > 0 accorting to the comparison + */ + private static int compare(long a, long b) + { + return a < b ? -1 : a == b ? 0 : 1; + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(long[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; j > from && array[j - 1] > array[j]; j--) + swap(j, j - 1, array); + return; + } + + // Determine a good median element. + int mid = from + count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = compare(array[b], array[from])) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = compare(array[c], array[from])) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the float array to sort + */ + public static void sort(float[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the float array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(float[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, float[] d) + { + return (Float.compare(d[a], d[b]) < 0 + ? (Float.compare(d[b], d[c]) < 0 ? b + : Float.compare(d[a], d[c]) < 0 ? c : a) + : (Float.compare(d[b], d[c]) > 0 ? b + : Float.compare(d[a], d[c]) > 0 ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, float[] a) + { + float c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, float[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(float[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; + j > from && Float.compare(array[j - 1], array[j]) > 0; + j--) + { + swap(j, j - 1, array); + } + return; + } + + // Determine a good median element. + int mid = from + count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = Float.compare(array[b], array[from])) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = Float.compare(array[c], array[from])) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the double array to sort + */ + public static void sort(double[] a) + { + qsort(a, 0, a.length); + } + + /** + * Performs a stable sort on the elements, arranging them according to their + * natural order. + * + * @param a the double array to sort + * @param fromIndex the first index to sort (inclusive) + * @param toIndex the last index to sort (exclusive) + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws ArrayIndexOutOfBoundsException if fromIndex < 0 + * || toIndex > a.length + */ + public static void sort(double[] a, int fromIndex, int toIndex) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException(); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + qsort(a, fromIndex, toIndex - fromIndex); + } + + /** + * Finds the index of the median of three array elements. + * + * @param a the first index + * @param b the second index + * @param c the third index + * @param d the array + * @return the index (a, b, or c) which has the middle value of the three + */ + private static int med3(int a, int b, int c, double[] d) + { + return (Double.compare(d[a], d[b]) < 0 + ? (Double.compare(d[b], d[c]) < 0 ? b + : Double.compare(d[a], d[c]) < 0 ? c : a) + : (Double.compare(d[b], d[c]) > 0 ? b + : Double.compare(d[a], d[c]) > 0 ? c : a)); + } + + /** + * Swaps the elements at two locations of an array + * + * @param i the first index + * @param j the second index + * @param a the array + */ + private static void swap(int i, int j, double[] a) + { + double c = a[i]; + a[i] = a[j]; + a[j] = c; + } + + /** + * Swaps two ranges of an array. + * + * @param i the first range start + * @param j the second range start + * @param n the element count + * @param a the array + */ + private static void vecswap(int i, int j, int n, double[] a) + { + for ( ; n > 0; i++, j++, n--) + swap(i, j, a); + } + + /** + * Performs a recursive modified quicksort. + * + * @param array the array to sort + * @param from the start index (inclusive) + * @param count the number of elements to sort + */ + private static void qsort(double[] array, int from, int count) + { + // Use an insertion sort on small arrays. + if (count <= 7) + { + for (int i = from + 1; i < from + count; i++) + for (int j = i; + j > from && Double.compare(array[j - 1], array[j]) > 0; + j--) + { + swap(j, j - 1, array); + } + return; + } + + // Determine a good median element. + int mid = from + count / 2; + int lo = from; + int hi = from + count - 1; + + if (count > 40) + { // big arrays, pseudomedian of 9 + int s = count / 8; + lo = med3(lo, lo + s, lo + 2 * s, array); + mid = med3(mid - s, mid, mid + s, array); + hi = med3(hi - 2 * s, hi - s, hi, array); + } + mid = med3(lo, mid, hi, array); + + int a, b, c, d; + int comp; + + // Pull the median element out of the fray, and use it as a pivot. + swap(from, mid, array); + a = b = from; + c = d = from + count - 1; + + // Repeatedly move b and c to each other, swapping elements so + // that all elements before index b are less than the pivot, and all + // elements after index c are greater than the pivot. a and b track + // the elements equal to the pivot. + while (true) + { + while (b <= c && (comp = Double.compare(array[b], array[from])) <= 0) + { + if (comp == 0) + { + swap(a, b, array); + a++; + } + b++; + } + while (c >= b && (comp = Double.compare(array[c], array[from])) >= 0) + { + if (comp == 0) + { + swap(c, d, array); + d--; + } + c--; + } + if (b > c) + break; + swap(b, c, array); + b++; + c--; + } + + // Swap pivot(s) back in place, the recurse on left and right sections. + hi = from + count; + int span; + span = Math.min(a - from, b - a); + vecswap(from, b - span, span, array); + + span = Math.min(d - c, hi - d - 1); + vecswap(b, hi - span, span, array); + + span = b - a; + if (span > 1) + qsort(array, from, span); + + span = d - c; + if (span > 1) + qsort(array, hi - span, span); + } + + /** + * Sort an array of Objects according to their natural ordering. The sort is + * guaranteed to be stable, that is, equal elements will not be reordered. + * The sort algorithm is a mergesort with the merge omitted if the last + * element of one half comes before the first element of the other half. This + * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a + * copy of the array. + * + * @param a the array to be sorted + * @throws ClassCastException if any two elements are not mutually + * comparable + * @throws NullPointerException if an element is null (since + * null.compareTo cannot work) + * @see Comparable + */ + public static void sort(Object[] a) + { + sort(a, 0, a.length, null); + } + + /** + * Sort an array of Objects according to a Comparator. The sort is + * guaranteed to be stable, that is, equal elements will not be reordered. + * The sort algorithm is a mergesort with the merge omitted if the last + * element of one half comes before the first element of the other half. This + * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a + * copy of the array. + * + * @param a the array to be sorted + * @param c a Comparator to use in sorting the array; or null to indicate + * the elements' natural order + * @throws ClassCastException if any two elements are not mutually + * comparable by the Comparator provided + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + */ + public static void sort(T[] a, Comparator c) + { + sort(a, 0, a.length, c); + } + + /** + * Sort an array of Objects according to their natural ordering. The sort is + * guaranteed to be stable, that is, equal elements will not be reordered. + * The sort algorithm is a mergesort with the merge omitted if the last + * element of one half comes before the first element of the other half. This + * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a + * copy of the array. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element to be sorted + * @param toIndex the index of the last element to be sorted plus one + * @throws ClassCastException if any two elements are not mutually + * comparable + * @throws NullPointerException if an element is null (since + * null.compareTo cannot work) + * @throws ArrayIndexOutOfBoundsException if fromIndex and toIndex + * are not in range. + * @throws IllegalArgumentException if fromIndex > toIndex + */ + public static void sort(Object[] a, int fromIndex, int toIndex) + { + sort(a, fromIndex, toIndex, null); + } + + /** + * Sort an array of Objects according to a Comparator. The sort is + * guaranteed to be stable, that is, equal elements will not be reordered. + * The sort algorithm is a mergesort with the merge omitted if the last + * element of one half comes before the first element of the other half. This + * algorithm gives guaranteed O(n*log(n)) time, at the expense of making a + * copy of the array. + * + * @param a the array to be sorted + * @param fromIndex the index of the first element to be sorted + * @param toIndex the index of the last element to be sorted plus one + * @param c a Comparator to use in sorting the array; or null to indicate + * the elements' natural order + * @throws ClassCastException if any two elements are not mutually + * comparable by the Comparator provided + * @throws ArrayIndexOutOfBoundsException if fromIndex and toIndex + * are not in range. + * @throws IllegalArgumentException if fromIndex > toIndex + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + */ + public static void sort(T[] a, int fromIndex, int toIndex, + Comparator c) + { + if (fromIndex > toIndex) + throw new IllegalArgumentException("fromIndex " + fromIndex + + " > toIndex " + toIndex); + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(); + + // In general, the code attempts to be simple rather than fast, the + // idea being that a good optimising JIT will be able to optimise it + // better than I can, and if I try it will make it more confusing for + // the JIT. First presort the array in chunks of length 6 with insertion + // sort. A mergesort would give too much overhead for this length. + for (int chunk = fromIndex; chunk < toIndex; chunk += 6) + { + int end = Math.min(chunk + 6, toIndex); + for (int i = chunk + 1; i < end; i++) + { + if (Collections.compare(a[i - 1], a[i], c) > 0) + { + // not already sorted + int j = i; + T elem = a[j]; + do + { + a[j] = a[j - 1]; + j--; + } + while (j > chunk + && Collections.compare(a[j - 1], elem, c) > 0); + a[j] = elem; + } + } + } + + int len = toIndex - fromIndex; + // If length is smaller or equal 6 we are done. + if (len <= 6) + return; + + T[] src = a; + T[] dest = (T[]) new Object[len]; + T[] t = null; // t is used for swapping src and dest + + // The difference of the fromIndex of the src and dest array. + int srcDestDiff = -fromIndex; + + // The merges are done in this loop + for (int size = 6; size < len; size <<= 1) + { + for (int start = fromIndex; start < toIndex; start += size << 1) + { + // mid is the start of the second sublist; + // end the start of the next sublist (or end of array). + int mid = start + size; + int end = Math.min(toIndex, mid + size); + + // The second list is empty or the elements are already in + // order - no need to merge + if (mid >= end + || Collections.compare(src[mid - 1], src[mid], c) <= 0) + { + System.arraycopy(src, start, + dest, start + srcDestDiff, end - start); + + // The two halves just need swapping - no need to merge + } + else if (Collections.compare(src[start], src[end - 1], c) > 0) + { + System.arraycopy(src, start, + dest, end - size + srcDestDiff, size); + System.arraycopy(src, mid, + dest, start + srcDestDiff, end - mid); + + } + else + { + // Declare a lot of variables to save repeating + // calculations. Hopefully a decent JIT will put these + // in registers and make this fast + int p1 = start; + int p2 = mid; + int i = start + srcDestDiff; + + // The main merge loop; terminates as soon as either + // half is ended + while (p1 < mid && p2 < end) + { + dest[i++] = + src[(Collections.compare(src[p1], src[p2], c) <= 0 + ? p1++ : p2++)]; + } + + // Finish up by copying the remainder of whichever half + // wasn't finished. + if (p1 < mid) + System.arraycopy(src, p1, dest, i, mid - p1); + else + System.arraycopy(src, p2, dest, i, end - p2); + } + } + // swap src and dest ready for the next merge + t = src; + src = dest; + dest = t; + fromIndex += srcDestDiff; + toIndex += srcDestDiff; + srcDestDiff = -srcDestDiff; + } + + // make sure the result ends up back in the right place. Note + // that src and dest may have been swapped above, so src + // contains the sorted array. + if (src != a) + { + // Note that fromIndex == 0. + System.arraycopy(src, 0, a, srcDestDiff, toIndex); + } + } + + /** + * Returns a list "view" of the specified array. This method is intended to + * make it easy to use the Collections API with existing array-based APIs and + * programs. Changes in the list or the array show up in both places. The + * list does not support element addition or removal, but does permit + * value modification. The returned list implements both Serializable and + * RandomAccess. + * + * @param a the array to return a view of (null not permitted) + * @return a fixed-size list, changes to which "write through" to the array + * + * @throws NullPointerException if a is null. + * @see Serializable + * @see RandomAccess + * @see Arrays.ArrayList + */ + public static List asList(final T... a) + { + return new Arrays.ArrayList(a); + } + + /** + * Returns the hashcode of an array of long numbers. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. This has the same + * data, but represents longs in their wrapper class, Long. + * For null, 0 is returned. + * + * @param v an array of long numbers for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(long[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + { + int elt = (int) (v[i] ^ (v[i] >>> 32)); + result = 31 * result + elt; + } + return result; + } + + /** + * Returns the hashcode of an array of integer numbers. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. This has the same + * data, but represents ints in their wrapper class, Integer. + * For null, 0 is returned. + * + * @param v an array of integer numbers for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(int[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + result = 31 * result + v[i]; + return result; + } + + /** + * Returns the hashcode of an array of short numbers. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. This has the same + * data, but represents shorts in their wrapper class, Short. + * For null, 0 is returned. + * + * @param v an array of short numbers for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(short[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + result = 31 * result + v[i]; + return result; + } + + /** + * Returns the hashcode of an array of characters. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. This has the same + * data, but represents chars in their wrapper class, Character. + * For null, 0 is returned. + * + * @param v an array of characters for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(char[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + result = 31 * result + v[i]; + return result; + } + + /** + * Returns the hashcode of an array of bytes. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. This has the same + * data, but represents bytes in their wrapper class, Byte. + * For null, 0 is returned. + * + * @param v an array of bytes for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(byte[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + result = 31 * result + v[i]; + return result; + } + + /** + * Returns the hashcode of an array of booleans. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. This has the same + * data, but represents booleans in their wrapper class, + * Boolean. For null, 0 is returned. + * + * @param v an array of booleans for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(boolean[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + result = 31 * result + (v[i] ? 1231 : 1237); + return result; + } + + /** + * Returns the hashcode of an array of floats. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. This has the same + * data, but represents floats in their wrapper class, Float. + * For null, 0 is returned. + * + * @param v an array of floats for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(float[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + result = 31 * result + Float.floatToIntBits(v[i]); + return result; + } + + /** + * Returns the hashcode of an array of doubles. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. This has the same + * data, but represents doubles in their wrapper class, Double. + * For null, 0 is returned. + * + * @param v an array of doubles for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(double[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + { + long l = Double.doubleToLongBits(v[i]); + int elt = (int) (l ^ (l >>> 32)); + result = 31 * result + elt; + } + return result; + } + + /** + * Returns the hashcode of an array of objects. If two arrays + * are equal, according to equals(), they should have the + * same hashcode. The hashcode returned by the method is equal to that + * obtained by the corresponding List object. + * For null, 0 is returned. + * + * @param v an array of integer numbers for which the hash code should be + * computed. + * @return the hash code of the array, or 0 if null was given. + * @since 1.5 + */ + public static int hashCode(Object[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + { + int elt = v[i] == null ? 0 : v[i].hashCode(); + result = 31 * result + elt; + } + return result; + } + + public static int deepHashCode(Object[] v) + { + if (v == null) + return 0; + int result = 1; + for (int i = 0; i < v.length; ++i) + { + int elt; + if (v[i] == null) + elt = 0; + else if (v[i] instanceof boolean[]) + elt = hashCode((boolean[]) v[i]); + else if (v[i] instanceof byte[]) + elt = hashCode((byte[]) v[i]); + else if (v[i] instanceof char[]) + elt = hashCode((char[]) v[i]); + else if (v[i] instanceof short[]) + elt = hashCode((short[]) v[i]); + else if (v[i] instanceof int[]) + elt = hashCode((int[]) v[i]); + else if (v[i] instanceof long[]) + elt = hashCode((long[]) v[i]); + else if (v[i] instanceof float[]) + elt = hashCode((float[]) v[i]); + else if (v[i] instanceof double[]) + elt = hashCode((double[]) v[i]); + else if (v[i] instanceof Object[]) + elt = hashCode((Object[]) v[i]); + else + elt = v[i].hashCode(); + result = 31 * result + elt; + } + return result; + } + + /** @since 1.5 */ + public static boolean deepEquals(Object[] v1, Object[] v2) + { + if (v1 == null) + return v2 == null; + if (v2 == null || v1.length != v2.length) + return false; + + for (int i = 0; i < v1.length; ++i) + { + Object e1 = v1[i]; + Object e2 = v2[i]; + + if (e1 == e2) + continue; + if (e1 == null || e2 == null) + return false; + + boolean check; + if (e1 instanceof boolean[] && e2 instanceof boolean[]) + check = equals((boolean[]) e1, (boolean[]) e2); + else if (e1 instanceof byte[] && e2 instanceof byte[]) + check = equals((byte[]) e1, (byte[]) e2); + else if (e1 instanceof char[] && e2 instanceof char[]) + check = equals((char[]) e1, (char[]) e2); + else if (e1 instanceof short[] && e2 instanceof short[]) + check = equals((short[]) e1, (short[]) e2); + else if (e1 instanceof int[] && e2 instanceof int[]) + check = equals((int[]) e1, (int[]) e2); + else if (e1 instanceof long[] && e2 instanceof long[]) + check = equals((long[]) e1, (long[]) e2); + else if (e1 instanceof float[] && e2 instanceof float[]) + check = equals((float[]) e1, (float[]) e2); + else if (e1 instanceof double[] && e2 instanceof double[]) + check = equals((double[]) e1, (double[]) e2); + else if (e1 instanceof Object[] && e2 instanceof Object[]) + check = equals((Object[]) e1, (Object[]) e2); + else + check = e1.equals(e2); + if (! check) + return false; + } + + return true; + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(boolean[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(byte[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(char[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(short[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(int[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(long[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(float[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(double[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + /** + * Returns a String representation of the argument array. Returns "null" + * if a is null. + * @param v the array to represent + * @return a String representing this array + * @since 1.5 + */ + public static String toString(Object[] v) + { + if (v == null) + return "null"; + CPStringBuilder b = new CPStringBuilder("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + b.append(v[i]); + } + b.append("]"); + return b.toString(); + } + + private static void deepToString(Object[] v, CPStringBuilder b, HashSet seen) + { + b.append("["); + for (int i = 0; i < v.length; ++i) + { + if (i > 0) + b.append(", "); + Object elt = v[i]; + if (elt == null) + b.append("null"); + else if (elt instanceof boolean[]) + b.append(toString((boolean[]) elt)); + else if (elt instanceof byte[]) + b.append(toString((byte[]) elt)); + else if (elt instanceof char[]) + b.append(toString((char[]) elt)); + else if (elt instanceof short[]) + b.append(toString((short[]) elt)); + else if (elt instanceof int[]) + b.append(toString((int[]) elt)); + else if (elt instanceof long[]) + b.append(toString((long[]) elt)); + else if (elt instanceof float[]) + b.append(toString((float[]) elt)); + else if (elt instanceof double[]) + b.append(toString((double[]) elt)); + else if (elt instanceof Object[]) + { + Object[] os = (Object[]) elt; + if (seen.contains(os)) + b.append("[...]"); + else + { + seen.add(os); + deepToString(os, b, seen); + } + } + else + b.append(elt); + } + b.append("]"); + } + + /** @since 1.5 */ + public static String deepToString(Object[] v) + { + if (v == null) + return "null"; + HashSet seen = new HashSet(); + CPStringBuilder b = new CPStringBuilder(); + deepToString(v, b, seen); + return b.toString(); + } + + /** + * Inner class used by {@link #asList(Object[])} to provide a list interface + * to an array. The name, though it clashes with java.util.ArrayList, is + * Sun's choice for Serialization purposes. Element addition and removal + * is prohibited, but values can be modified. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ + private static final class ArrayList extends AbstractList + implements Serializable, RandomAccess + { + // We override the necessary methods, plus others which will be much + // more efficient with direct iteration rather than relying on iterator(). + + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -2764017481108945198L; + + /** + * The array we are viewing. + * @serial the array + */ + private final E[] a; + + /** + * Construct a list view of the array. + * @param a the array to view + * @throws NullPointerException if a is null + */ + ArrayList(E[] a) + { + // We have to explicitly check. + if (a == null) + throw new NullPointerException(); + this.a = a; + } + + /** + * Returns the object at the specified index in + * the array. + * + * @param index The index to retrieve an object from. + * @return The object at the array index specified. + */ + public E get(int index) + { + return a[index]; + } + + /** + * Returns the size of the array. + * + * @return The size. + */ + public int size() + { + return a.length; + } + + /** + * Replaces the object at the specified index + * with the supplied element. + * + * @param index The index at which to place the new object. + * @param element The new object. + * @return The object replaced by this operation. + */ + public E set(int index, E element) + { + E old = a[index]; + a[index] = element; + return old; + } + + /** + * Returns true if the array contains the + * supplied object. + * + * @param o The object to look for. + * @return True if the object was found. + */ + public boolean contains(Object o) + { + return lastIndexOf(o) >= 0; + } + + /** + * Returns the first index at which the + * object, o, occurs in the array. + * + * @param o The object to search for. + * @return The first relevant index. + */ + public int indexOf(Object o) + { + int size = a.length; + for (int i = 0; i < size; i++) + if (ArrayList.equals(o, a[i])) + return i; + return -1; + } + + /** + * Returns the last index at which the + * object, o, occurs in the array. + * + * @param o The object to search for. + * @return The last relevant index. + */ + public int lastIndexOf(Object o) + { + int i = a.length; + while (--i >= 0) + if (ArrayList.equals(o, a[i])) + return i; + return -1; + } + + /** + * Transforms the list into an array of + * objects, by simplying cloning the array + * wrapped by this list. + * + * @return A clone of the internal array. + */ + public Object[] toArray() + { + return (Object[]) a.clone(); + } + + /** + * Copies the objects from this list into + * the supplied array. The supplied array + * is shrunk or enlarged to the size of the + * internal array, and filled with its objects. + * + * @param array The array to fill with the objects in this list. + * @return The array containing the objects in this list, + * which may or may not be == to array. + */ + public T[] toArray(T[] array) + { + int size = a.length; + if (array.length < size) + array = (T[]) Array.newInstance(array.getClass().getComponentType(), + size); + else if (array.length > size) + array[size] = null; + + System.arraycopy(a, 0, array, 0, size); + return array; + } + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with false to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return false. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * false to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(boolean[],int,int) + */ + public static boolean[] copyOf(boolean[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with false + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with false will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(boolean[],int) + */ + public static boolean[] copyOfRange(boolean[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + boolean[] newArray = new boolean[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, false); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with (byte)0 to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return (byte)0. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * (byte)0 to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(byte[],int,int) + */ + public static byte[] copyOf(byte[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with (byte)0 + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with (byte)0 will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(byte[],int) + */ + public static byte[] copyOfRange(byte[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + byte[] newArray = new byte[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, (byte)0); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with '\0' to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return '\0'. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * '\0' to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(char[],int,int) + */ + public static char[] copyOf(char[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with '\0' + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with '\0' will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(char[],int) + */ + public static char[] copyOfRange(char[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + char[] newArray = new char[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, '\0'); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with 0d to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return 0d. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * 0d to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(double[],int,int) + */ + public static double[] copyOf(double[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with 0d + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with 0d will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(double[],int) + */ + public static double[] copyOfRange(double[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + double[] newArray = new double[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, 0d); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with 0f to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return 0f. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * 0f to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(float[],int,int) + */ + public static float[] copyOf(float[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with 0f + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with 0f will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(float[],int) + */ + public static float[] copyOfRange(float[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + float[] newArray = new float[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, 0f); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with 0 to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return 0. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * 0 to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(int[],int,int) + */ + public static int[] copyOf(int[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with 0 + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with 0 will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(int[],int) + */ + public static int[] copyOfRange(int[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + int[] newArray = new int[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, 0); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with 0L to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return 0L. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * 0L to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(long[],int,int) + */ + public static long[] copyOf(long[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with 0L + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with 0L will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(long[],int) + */ + public static long[] copyOfRange(long[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + long[] newArray = new long[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, 0L); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with (short)0 to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return (short)0. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * (short)0 to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(short[],int,int) + */ + public static short[] copyOf(short[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with (short)0 + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with (short)0 will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(short[],int) + */ + public static short[] copyOfRange(short[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + short[] newArray = new short[to - from]; + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, (short)0); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with null to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return null. + * This is equivalent to calling + * copyOfRange(original, 0, newLength). + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @return a copy of the original array, truncated or padded with + * null to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(T[],int,int) + */ + public static T[] copyOf(T[] original, int newLength) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with null + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with null will be + * returned). The returned array is always of length + * to - from. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(T[],int) + */ + public static T[] copyOfRange(T[] original, int from, int to) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + Class elemType = original.getClass().getComponentType(); + T[] newArray = (T[]) Array.newInstance(elemType, to - from); + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, null); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } + + /** + * Returns a copy of the supplied array, truncating or padding as + * necessary with null to obtain the specified length. + * Indices that are valid for both arrays will return the same value. + * Indices that only exist in the returned array (due to the new length + * being greater than the original length) will return null. + * This is equivalent to calling + * copyOfRange(original, 0, newLength, newType). The returned + * array will be of the specified type, newType. + * + * @param original the original array to be copied. + * @param newLength the length of the returned array. + * @param newType the type of the returned array. + * @return a copy of the original array, truncated or padded with + * null to obtain the required length. + * @throws NegativeArraySizeException if newLength is negative. + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOfRange(U[],int,int,Class) + */ + public static T[] copyOf(U[] original, int newLength, + Class newType) + { + if (newLength < 0) + throw new NegativeArraySizeException("The array size is negative."); + return copyOfRange(original, 0, newLength, newType); + } + + /** + * Copies the specified range of the supplied array to a new + * array, padding as necessary with null + * if to is greater than the length of the original + * array. from must be in the range zero to + * original.length and can not be greater than + * to. The initial element of the + * returned array will be equal to original[from], + * except where from is equal to to + * (where a zero-length array will be returned) or + * from is equal to original.length + * (where an array padded with null will be + * returned). The returned array is always of length + * to - from and will be of the specified type, + * newType. + * + * @param original the array from which to copy. + * @param from the initial index of the range, inclusive. + * @param to the final index of the range, exclusive. + * @param newType the type of the returned array. + * @return a copy of the specified range, with padding to + * obtain the required length. + * @throws ArrayIndexOutOfBoundsException if from < 0 + * or from > original.length + * @throws IllegalArgumentException if from > to + * @throws NullPointerException if original is null. + * @since 1.6 + * @see #copyOf(T[],int) + */ + public static T[] copyOfRange(U[] original, int from, int to, + Class newType) + { + if (from > to) + throw new IllegalArgumentException("The initial index is after " + + "the final index."); + T[] newArray = (T[]) Array.newInstance(newType.getComponentType(), + to - from); + if (to > original.length) + { + System.arraycopy(original, from, newArray, 0, + original.length - from); + fill(newArray, original.length, newArray.length, null); + } + else + System.arraycopy(original, from, newArray, 0, to - from); + return newArray; + } +} diff --git a/libjava/classpath/java/util/BitSet.java b/libjava/classpath/java/util/BitSet.java new file mode 100644 index 000000000..1072978a4 --- /dev/null +++ b/libjava/classpath/java/util/BitSet.java @@ -0,0 +1,758 @@ +/* BitSet.java -- A vector of bits. + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Serializable; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * hashCode algorithm taken from JDK 1.2 docs. + */ + +/** + * This class can be thought of in two ways. You can see it as a + * vector of bits or as a set of non-negative integers. The name + * BitSet is a bit misleading. + * + * It is implemented by a bit vector, but its equally possible to see + * it as set of non-negative integer; each integer in the set is + * represented by a set bit at the corresponding index. The size of + * this structure is determined by the highest integer in the set. + * + * You can union, intersect and build (symmetric) remainders, by + * invoking the logical operations and, or, andNot, resp. xor. + * + * This implementation is NOT synchronized against concurrent access from + * multiple threads. Specifically, if one thread is reading from a bitset + * while another thread is simultaneously modifying it, the results are + * undefined. + * + * @author Jochen Hoenicke + * @author Tom Tromey (tromey@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class BitSet implements Cloneable, Serializable +{ + /** + * Compatible with JDK 1.0. + */ + private static final long serialVersionUID = 7997698588986878753L; + + /** + * A common mask. + */ + private static final int LONG_MASK = 0x3f; + + /** + * The actual bits. + * @serial the i'th bit is in bits[i/64] at position i%64 (where position + * 0 is the least significant). + */ + private long[] bits; + + /** + * Create a new empty bit set. All bits are initially false. + */ + public BitSet() + { + this(64); + } + + /** + * Create a new empty bit set, with a given size. This + * constructor reserves enough space to represent the integers + * from 0 to nbits-1. + * + * @param nbits the initial size of the bit set + * @throws NegativeArraySizeException if nbits < 0 + */ + public BitSet(int nbits) + { + if (nbits < 0) + throw new NegativeArraySizeException(); + + int length = nbits >>> 6; + if ((nbits & LONG_MASK) != 0) + ++length; + bits = new long[length]; + } + + /** + * Performs the logical AND operation on this bit set and the + * given set. This means it builds the intersection + * of the two sets. The result is stored into this bit set. + * + * @param bs the second bit set + * @throws NullPointerException if bs is null + */ + public void and(BitSet bs) + { + int max = Math.min(bits.length, bs.bits.length); + int i; + for (i = 0; i < max; ++i) + bits[i] &= bs.bits[i]; + while (i < bits.length) + bits[i++] = 0; + } + + /** + * Performs the logical AND operation on this bit set and the + * complement of the given bs. This means it + * selects every element in the first set, that isn't in the + * second set. The result is stored into this bit set and is + * effectively the set difference of the two. + * + * @param bs the second bit set + * @throws NullPointerException if bs is null + * @since 1.2 + */ + public void andNot(BitSet bs) + { + int i = Math.min(bits.length, bs.bits.length); + while (--i >= 0) + bits[i] &= ~bs.bits[i]; + } + + /** + * Returns the number of bits set to true. + * + * @return the number of true bits + * @since 1.4 + */ + public int cardinality() + { + int card = 0; + for (int i = bits.length - 1; i >= 0; i--) + { + long a = bits[i]; + // Take care of common cases. + if (a == 0) + continue; + if (a == -1) + { + card += 64; + continue; + } + + // Successively collapse alternating bit groups into a sum. + a = ((a >> 1) & 0x5555555555555555L) + (a & 0x5555555555555555L); + a = ((a >> 2) & 0x3333333333333333L) + (a & 0x3333333333333333L); + int b = (int) ((a >>> 32) + a); + b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f); + b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff); + card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff); + } + return card; + } + + /** + * Sets all bits in the set to false. + * + * @since 1.4 + */ + public void clear() + { + Arrays.fill(bits, 0); + } + + /** + * Removes the integer pos from this set. That is + * the corresponding bit is cleared. If the index is not in the set, + * this method does nothing. + * + * @param pos a non-negative integer + * @throws IndexOutOfBoundsException if pos < 0 + */ + public void clear(int pos) + { + int offset = pos >> 6; + ensure(offset); + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + bits[offset] &= ~(1L << pos); + } + + /** + * Sets the bits between from (inclusive) and to (exclusive) to false. + * + * @param from the start range (inclusive) + * @param to the end range (exclusive) + * @throws IndexOutOfBoundsException if from < 0 || to < 0 || + * from > to + * @since 1.4 + */ + public void clear(int from, int to) + { + if (from < 0 || from > to) + throw new IndexOutOfBoundsException(); + if (from == to) + return; + int lo_offset = from >>> 6; + int hi_offset = to >>> 6; + ensure(hi_offset); + if (lo_offset == hi_offset) + { + bits[hi_offset] &= ((1L << from) - 1) | (-1L << to); + return; + } + + bits[lo_offset] &= (1L << from) - 1; + bits[hi_offset] &= -1L << to; + for (int i = lo_offset + 1; i < hi_offset; i++) + bits[i] = 0; + } + + /** + * Create a clone of this bit set, that is an instance of the same + * class and contains the same elements. But it doesn't change when + * this bit set changes. + * + * @return the clone of this object. + */ + public Object clone() + { + try + { + BitSet bs = (BitSet) super.clone(); + bs.bits = (long[]) bits.clone(); + return bs; + } + catch (CloneNotSupportedException e) + { + // Impossible to get here. + return null; + } + } + + /** + * Returns true if the obj is a bit set that contains + * exactly the same elements as this bit set, otherwise false. + * + * @param obj the object to compare to + * @return true if obj equals this bit set + */ + public boolean equals(Object obj) + { + if (!(obj instanceof BitSet)) + return false; + BitSet bs = (BitSet) obj; + int max = Math.min(bits.length, bs.bits.length); + int i; + for (i = 0; i < max; ++i) + if (bits[i] != bs.bits[i]) + return false; + // If one is larger, check to make sure all extra bits are 0. + for (int j = i; j < bits.length; ++j) + if (bits[j] != 0) + return false; + for (int j = i; j < bs.bits.length; ++j) + if (bs.bits[j] != 0) + return false; + return true; + } + + /** + * Sets the bit at the index to the opposite value. + * + * @param index the index of the bit + * @throws IndexOutOfBoundsException if index is negative + * @since 1.4 + */ + public void flip(int index) + { + int offset = index >> 6; + ensure(offset); + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + bits[offset] ^= 1L << index; + } + + /** + * Sets a range of bits to the opposite value. + * + * @param from the low index (inclusive) + * @param to the high index (exclusive) + * @throws IndexOutOfBoundsException if from > to || from < 0 || + * to < 0 + * @since 1.4 + */ + public void flip(int from, int to) + { + if (from < 0 || from > to) + throw new IndexOutOfBoundsException(); + if (from == to) + return; + int lo_offset = from >>> 6; + int hi_offset = to >>> 6; + ensure(hi_offset); + if (lo_offset == hi_offset) + { + bits[hi_offset] ^= (-1L << from) & ((1L << to) - 1); + return; + } + + bits[lo_offset] ^= -1L << from; + bits[hi_offset] ^= (1L << to) - 1; + for (int i = lo_offset + 1; i < hi_offset; i++) + bits[i] ^= -1; + } + + /** + * Returns true if the integer bitIndex is in this bit + * set, otherwise false. + * + * @param pos a non-negative integer + * @return the value of the bit at the specified position + * @throws IndexOutOfBoundsException if the pos is negative + */ + public boolean get(int pos) + { + int offset = pos >> 6; + if (offset >= bits.length) + return false; + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + return (bits[offset] & (1L << pos)) != 0; + } + + /** + * Returns a new BitSet composed of a range of bits from + * this one. + * + * @param from the low index (inclusive) + * @param to the high index (exclusive) + * @throws IndexOutOfBoundsException if from > to || from < 0 || + * to < 0 + * @since 1.4 + */ + public BitSet get(int from, int to) + { + if (from < 0 || from > to) + throw new IndexOutOfBoundsException(); + BitSet bs = new BitSet(to - from); + int lo_offset = from >>> 6; + if (lo_offset >= bits.length || to == from) + return bs; + + int lo_bit = from & LONG_MASK; + int hi_offset = to >>> 6; + if (lo_bit == 0) + { + int len = Math.min(hi_offset - lo_offset + 1, bits.length - lo_offset); + System.arraycopy(bits, lo_offset, bs.bits, 0, len); + if (hi_offset < bits.length) + bs.bits[hi_offset - lo_offset] &= (1L << to) - 1; + return bs; + } + + int len = Math.min(hi_offset, bits.length - 1); + int reverse = 64 - lo_bit; + int i; + for (i = 0; lo_offset < len; lo_offset++, i++) + bs.bits[i] = ((bits[lo_offset] >>> lo_bit) + | (bits[lo_offset + 1] << reverse)); + if ((to & LONG_MASK) > lo_bit) + bs.bits[i++] = bits[lo_offset] >>> lo_bit; + if (hi_offset < bits.length) + bs.bits[i - 1] &= (1L << (to - from)) - 1; + return bs; + } + + /** + * Returns a hash code value for this bit set. The hash code of + * two bit sets containing the same integers is identical. The algorithm + * used to compute it is as follows: + * + * Suppose the bits in the BitSet were to be stored in an array of + * long integers called bits, in such a manner that + * bit k is set in the BitSet (for non-negative values + * of k) if and only if + * + * ((k/64) < bits.length) + * && ((bits[k/64] & (1L << (bit % 64))) != 0) + * + * + * Then the following definition of the hashCode method + * would be a correct implementation of the actual algorithm: + * + * +

        public int hashCode()
        +{
        +  long h = 1234;
        +  for (int i = bits.length-1; i >= 0; i--)
        +  {
        +    h ^= bits[i] * (i + 1);
        +  }
        +
        +  return (int)((h >> 32) ^ h);
        +}
        + * + * Note that the hash code values changes, if the set is changed. + * + * @return the hash code value for this bit set. + */ + public int hashCode() + { + long h = 1234; + for (int i = bits.length; i > 0; ) + h ^= i * bits[--i]; + return (int) ((h >> 32) ^ h); + } + + /** + * Returns true if the specified BitSet and this one share at least one + * common true bit. + * + * @param set the set to check for intersection + * @return true if the sets intersect + * @throws NullPointerException if set is null + * @since 1.4 + */ + public boolean intersects(BitSet set) + { + int i = Math.min(bits.length, set.bits.length); + while (--i >= 0) + if ((bits[i] & set.bits[i]) != 0) + return true; + return false; + } + + /** + * Returns true if this set contains no true bits. + * + * @return true if all bits are false + * @since 1.4 + */ + public boolean isEmpty() + { + for (int i = bits.length - 1; i >= 0; i--) + if (bits[i] != 0) + return false; + return true; + } + + /** + * Returns the logical number of bits actually used by this bit + * set. It returns the index of the highest set bit plus one. + * Note that this method doesn't return the number of set bits. + * + * @return the index of the highest set bit plus one. + */ + public int length() + { + // Set i to highest index that contains a non-zero value. + int i; + for (i = bits.length - 1; i >= 0 && bits[i] == 0; --i) + ; + + // if i < 0 all bits are cleared. + if (i < 0) + return 0; + + // Now determine the exact length. + long b = bits[i]; + int len = (i + 1) * 64; + // b >= 0 checks if the highest bit is zero. + while (b >= 0) + { + --len; + b <<= 1; + } + + return len; + } + + /** + * Returns the index of the next false bit, from the specified bit + * (inclusive). + * + * @param from the start location + * @return the first false bit + * @throws IndexOutOfBoundsException if from is negative + * @since 1.4 + */ + public int nextClearBit(int from) + { + int offset = from >> 6; + long mask = 1L << from; + while (offset < bits.length) + { + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + long h = bits[offset]; + do + { + if ((h & mask) == 0) + return from; + mask <<= 1; + from++; + } + while (mask != 0); + mask = 1; + offset++; + } + return from; + } + + /** + * Returns the index of the next true bit, from the specified bit + * (inclusive). If there is none, -1 is returned. You can iterate over + * all true bits with this loop:
        + * +
        for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
        +{
        +  // operate on i here
        +}
        + * + * @param from the start location + * @return the first true bit, or -1 + * @throws IndexOutOfBoundsException if from is negative + * @since 1.4 + */ + public int nextSetBit(int from) + { + int offset = from >> 6; + long mask = 1L << from; + while (offset < bits.length) + { + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + long h = bits[offset]; + do + { + if ((h & mask) != 0) + return from; + mask <<= 1; + from++; + } + while (mask != 0); + mask = 1; + offset++; + } + return -1; + } + + /** + * Performs the logical OR operation on this bit set and the + * given set. This means it builds the union + * of the two sets. The result is stored into this bit set, which + * grows as necessary. + * + * @param bs the second bit set + * @throws NullPointerException if bs is null + */ + public void or(BitSet bs) + { + ensure(bs.bits.length - 1); + for (int i = bs.bits.length - 1; i >= 0; i--) + bits[i] |= bs.bits[i]; + } + + /** + * Add the integer bitIndex to this set. That is + * the corresponding bit is set to true. If the index was already in + * the set, this method does nothing. The size of this structure + * is automatically increased as necessary. + * + * @param pos a non-negative integer. + * @throws IndexOutOfBoundsException if pos is negative + */ + public void set(int pos) + { + int offset = pos >> 6; + ensure(offset); + // ArrayIndexOutOfBoundsException subclasses IndexOutOfBoundsException, + // so we'll just let that be our exception. + bits[offset] |= 1L << pos; + } + + /** + * Sets the bit at the given index to the specified value. The size of + * this structure is automatically increased as necessary. + * + * @param index the position to set + * @param value the value to set it to + * @throws IndexOutOfBoundsException if index is negative + * @since 1.4 + */ + public void set(int index, boolean value) + { + if (value) + set(index); + else + clear(index); + } + + /** + * Sets the bits between from (inclusive) and to (exclusive) to true. + * + * @param from the start range (inclusive) + * @param to the end range (exclusive) + * @throws IndexOutOfBoundsException if from < 0 || from > to || + * to < 0 + * @since 1.4 + */ + public void set(int from, int to) + { + if (from < 0 || from > to) + throw new IndexOutOfBoundsException(); + if (from == to) + return; + int lo_offset = from >>> 6; + int hi_offset = to >>> 6; + ensure(hi_offset); + if (lo_offset == hi_offset) + { + bits[hi_offset] |= (-1L << from) & ((1L << to) - 1); + return; + } + + bits[lo_offset] |= -1L << from; + bits[hi_offset] |= (1L << to) - 1; + for (int i = lo_offset + 1; i < hi_offset; i++) + bits[i] = -1; + } + + /** + * Sets the bits between from (inclusive) and to (exclusive) to the + * specified value. + * + * @param from the start range (inclusive) + * @param to the end range (exclusive) + * @param value the value to set it to + * @throws IndexOutOfBoundsException if from < 0 || from > to || + * to < 0 + * @since 1.4 + */ + public void set(int from, int to, boolean value) + { + if (value) + set(from, to); + else + clear(from, to); + } + + /** + * Returns the number of bits actually used by this bit set. Note + * that this method doesn't return the number of set bits, and that + * future requests for larger bits will make this automatically grow. + * + * @return the number of bits currently used. + */ + public int size() + { + return bits.length * 64; + } + + /** + * Returns the string representation of this bit set. This + * consists of a comma separated list of the integers in this set + * surrounded by curly braces. There is a space after each comma. + * A sample string is thus "{1, 3, 53}". + * @return the string representation. + */ + public String toString() + { + CPStringBuilder r = new CPStringBuilder("{"); + boolean first = true; + for (int i = 0; i < bits.length; ++i) + { + long bit = 1; + long word = bits[i]; + if (word == 0) + continue; + for (int j = 0; j < 64; ++j) + { + if ((word & bit) != 0) + { + if (! first) + r.append(", "); + r.append(64 * i + j); + first = false; + } + bit <<= 1; + } + } + return r.append("}").toString(); + } + + /** + * Performs the logical XOR operation on this bit set and the + * given set. This means it builds the symmetric + * remainder of the two sets (the elements that are in one set, + * but not in the other). The result is stored into this bit set, + * which grows as necessary. + * + * @param bs the second bit set + * @throws NullPointerException if bs is null + */ + public void xor(BitSet bs) + { + ensure(bs.bits.length - 1); + for (int i = bs.bits.length - 1; i >= 0; i--) + bits[i] ^= bs.bits[i]; + } + + /** + * Make sure the vector is big enough. + * + * @param lastElt the size needed for the bits array + */ + private void ensure(int lastElt) + { + if (lastElt >= bits.length) + { + long[] nd = new long[lastElt + 1]; + System.arraycopy(bits, 0, nd, 0, bits.length); + bits = nd; + } + } + + // This is used by EnumSet for efficiency. + final boolean containsAll(BitSet other) + { + for (int i = other.bits.length - 1; i >= 0; i--) + { + if ((bits[i] & other.bits[i]) != other.bits[i]) + return false; + } + return true; + } +} diff --git a/libjava/classpath/java/util/Calendar.java b/libjava/classpath/java/util/Calendar.java new file mode 100644 index 000000000..8123b1706 --- /dev/null +++ b/libjava/classpath/java/util/Calendar.java @@ -0,0 +1,1620 @@ +/* Calendar.java -- + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import java.text.DateFormatSymbols; + +/** + * This class is an abstract base class for Calendars, which can be + * used to convert between Date objects and a set of + * integer fields which represent YEAR, + * MONTH, DAY, etc. The Date + * object represents a time in milliseconds since the Epoch.
        + * + * This class is locale sensitive. To get the Object matching the + * current locale you can use getInstance. You can even provide + * a locale or a timezone. getInstance returns currently + * a GregorianCalendar for the current date.
        + * + * If you want to convert a date from the Year, Month, Day, DayOfWeek, + * etc. Representation to a Date-Object, you can create + * a new Calendar with getInstance(), + * clear() all fields, set(int,int) the + * fields you need and convert it with getTime().
        + * + * If you want to convert a Date-object to the Calendar + * representation, create a new Calendar, assign the + * Date-Object with setTime(), and read the + * fields with get(int).
        + * + * When computing the date from time fields, it may happen, that there + * are either two few fields set, or some fields are inconsistent. This + * cases will handled in a calendar specific way. Missing fields are + * replaced by the fields of the epoch: 1970 January 1 00:00.
        + * + * To understand, how the day of year is computed out of the fields + * look at the following table. It is traversed from top to bottom, + * and for the first line all fields are set, that line is used to + * compute the day.
        + * + * +
        month + day_of_month
        +month + week_of_month + day_of_week
        +month + day_of_week_of_month + day_of_week
        +day_of_year
        +day_of_week + week_of_year
        + * + * The hour_of_day-field takes precedence over the ampm and + * hour_of_ampm fields.
        + * + * Note: This can differ for non-Gregorian calendar.
        + * + * To convert a calendar to a human readable form and vice versa, use + * the java.text.DateFormat class.
        + * + * Other useful things you can do with an calendar, is + * rolling fields (that means increase/decrease a + * specific field by one, propagating overflows), or + * adding/substracting a fixed amount to a field. + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * @author Jochen Hoenicke (Jochen.Hoenicke@Informatik.Uni-Oldenburg.de) + * @author Warren Levy (warrenl@cygnus.com) + * @author Jeff Sturm (jsturm@one-point.com) + * @author Tom Tromey (tromey@redhat.com) + * @author Bryce McKinlay (mckinlay@redhat.com) + * @author Ingo Proetel (proetel@aicas.com) + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Noa Resare (noa@resare.com) + * @author Sven de Marothy (sven@physto.se) + * @author David Gilbert (david.gilbert@object-refinery.com) + * @author Olivier Jolly (olivier.jolly@pcedev.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Date + * @see GregorianCalendar + * @see TimeZone + * @see java.text.DateFormat + */ +public abstract class Calendar + implements Serializable, Cloneable, Comparable +{ + /** + * Constant representing the era time field. + */ + public static final int ERA = 0; + + /** + * Constant representing the year time field. + */ + public static final int YEAR = 1; + + /** + * Constant representing the month time field. This field + * should contain one of the JANUARY,...,DECEMBER constants below. + */ + public static final int MONTH = 2; + + /** + * Constant representing the week of the year field. + * @see #setFirstDayOfWeek(int) + */ + public static final int WEEK_OF_YEAR = 3; + + /** + * Constant representing the week of the month time field. + * @see #setFirstDayOfWeek(int) + */ + public static final int WEEK_OF_MONTH = 4; + + /** + * Constant representing the day time field, synonym for DAY_OF_MONTH. + */ + public static final int DATE = 5; + + /** + * Constant representing the day time field. + */ + public static final int DAY_OF_MONTH = 5; + + /** + * Constant representing the day of year time field. This is + * 1 for the first day in month. + */ + public static final int DAY_OF_YEAR = 6; + + /** + * Constant representing the day of week time field. This field + * should contain one of the SUNDAY,...,SATURDAY constants below. + */ + public static final int DAY_OF_WEEK = 7; + + /** + * Constant representing the day-of-week-in-month field. For + * instance this field contains 2 for the second thursday in a + * month. If you give a negative number here, the day will count + * from the end of the month. + */ + public static final int DAY_OF_WEEK_IN_MONTH = 8; + + /** + * Constant representing the part of the day for 12-hour clock. This + * should be one of AM or PM. + */ + public static final int AM_PM = 9; + + /** + * Constant representing the hour time field for 12-hour clock. + */ + public static final int HOUR = 10; + + /** + * Constant representing the hour of day time field for 24-hour clock. + */ + public static final int HOUR_OF_DAY = 11; + + /** + * Constant representing the minute of hour time field. + */ + public static final int MINUTE = 12; + + /** + * Constant representing the second time field. + */ + public static final int SECOND = 13; + + /** + * Constant representing the millisecond time field. + */ + public static final int MILLISECOND = 14; + + /** + * Constant representing the time zone offset time field for the + * time given in the other fields. It is measured in + * milliseconds. The default is the offset of the time zone. + */ + public static final int ZONE_OFFSET = 15; + + /** + * Constant representing the daylight saving time offset in + * milliseconds. The default is the value given by the time zone. + */ + public static final int DST_OFFSET = 16; + + /** + * Number of time fields. + */ + public static final int FIELD_COUNT = 17; + + /** + * Constant representing Sunday. + */ + public static final int SUNDAY = 1; + + /** + * Constant representing Monday. + */ + public static final int MONDAY = 2; + + /** + * Constant representing Tuesday. + */ + public static final int TUESDAY = 3; + + /** + * Constant representing Wednesday. + */ + public static final int WEDNESDAY = 4; + + /** + * Constant representing Thursday. + */ + public static final int THURSDAY = 5; + + /** + * Constant representing Friday. + */ + public static final int FRIDAY = 6; + + /** + * Constant representing Saturday. + */ + public static final int SATURDAY = 7; + + /** + * Constant representing January. + */ + public static final int JANUARY = 0; + + /** + * Constant representing February. + */ + public static final int FEBRUARY = 1; + + /** + * Constant representing March. + */ + public static final int MARCH = 2; + + /** + * Constant representing April. + */ + public static final int APRIL = 3; + + /** + * Constant representing May. + */ + public static final int MAY = 4; + + /** + * Constant representing June. + */ + public static final int JUNE = 5; + + /** + * Constant representing July. + */ + public static final int JULY = 6; + + /** + * Constant representing August. + */ + public static final int AUGUST = 7; + + /** + * Constant representing September. + */ + public static final int SEPTEMBER = 8; + + /** + * Constant representing October. + */ + public static final int OCTOBER = 9; + + /** + * Constant representing November. + */ + public static final int NOVEMBER = 10; + + /** + * Constant representing December. + */ + public static final int DECEMBER = 11; + + /** + * Constant representing Undecimber. This is an artificial name useful + * for lunar calendars. + */ + public static final int UNDECIMBER = 12; + + /** + * Useful constant for 12-hour clock. + */ + public static final int AM = 0; + + /** + * Useful constant for 12-hour clock. + */ + public static final int PM = 1; + + /** + * A style specifier for {@link #getDisplayNames(int,int,Locale)} + * stating that names should be returned in both long and short variants. + * + * @since 1.6 + * @see #SHORT + * @see #LONG + */ + public static final int ALL_STYLES = 0; + + /** + * A style specifier for {@link #getDisplayName(int,int,Locale)} + * and {@link #getDisplayNames(int,int,Locale)} stating that names + * should be returned in their short variant if applicable. + * + * @since 1.6 + */ + public static final int SHORT = 1; + + /** + * A style specifier for {@link #getDisplayName(int,int,Locale)} + * and {@link #getDisplayNames(int,int,Locale)} stating that names + * should be returned in their long variant if applicable. + * + * @since 1.6 + */ + public static final int LONG = 2; + + /** + * The time fields. The array is indexed by the constants YEAR to + * DST_OFFSET. + * @serial + */ + protected int[] fields = new int[FIELD_COUNT]; + + /** + * The flags which tell if the fields above have a value. + * @serial + */ + protected boolean[] isSet = new boolean[FIELD_COUNT]; + + /** + * The time in milliseconds since the epoch. + * @serial + */ + protected long time; + + /** + * Tells if the above field has a valid value. + * @serial + */ + protected boolean isTimeSet; + + /** + * Tells if the fields have a valid value. This superseeds the isSet + * array. + * @serial + */ + protected boolean areFieldsSet; + + /** + * The time zone of this calendar. Used by sub classes to do UTC / local + * time conversion. Sub classes can access this field with getTimeZone(). + * @serial + */ + private TimeZone zone; + + /** + * This is the default calendar class, that is returned on + * java.util.Calendar.getInstance(). + * XXX - this isn't localized anywhere, is it? + * @see java.util.Calendar#getInstance() + */ + private static final String calendarClassName = "java.util.GregorianCalendar"; + + /** + * Specifies if the date/time interpretation should be lenient. + * If the flag is set, a date such as "February 30, 1996" will be + * treated as the 29th day after the February 1. If this flag + * is false, such dates will cause an exception. + * @serial + */ + private boolean lenient; + + /** + * Sets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @serial + */ + private int firstDayOfWeek; + + /** + * Sets how many days are required in the first week of the year. + * If the first day of the year should be the first week you should + * set this value to 1. If the first week must be a full week, set + * it to 7. + * @serial + */ + private int minimalDaysInFirstWeek; + + /** + * Is set to true if DST_OFFSET is explicitly set. In that case + * it's value overrides the value computed from the current + * time and the timezone. + */ + private boolean explicitDSTOffset = false; + + /** + * The version of the serialized data on the stream. + *
        0 or not present
        + *
        JDK 1.1.5 or later.
        + *
        1
        + *
        JDK 1.1.6 or later. This always writes a correct `time' value + * on the stream, as well as the other fields, to be compatible with + * earlier versions
        + * @since JDK1.1.6 + * @serial + */ + private int serialVersionOnStream = 1; + + /** + * XXX - I have not checked the compatibility. The documentation of + * the serialized-form is quite hairy... + */ + static final long serialVersionUID = -1807547505821590642L; + + /** + * The name of the resource bundle. Used only by getBundle() + */ + private static final String bundleName = "gnu.java.locale.LocaleInformation"; + + /** + * get resource bundle: + * The resources should be loaded via this method only. Iff an application + * uses this method, the resourcebundle is required. + */ + private static ResourceBundle getBundle(Locale locale) + { + return ResourceBundle.getBundle(bundleName, locale, + ClassLoader.getSystemClassLoader()); + } + + /** + * The set of properties for obtaining the minimum number of days in + * the first week. + */ + private static transient final Properties properties; + + /** + * Reads in the properties. + */ + static + { + properties = new Properties(); + try + { + properties.load(Calendar.class.getResourceAsStream("weeks.properties")); + } + catch (IOException exception) + { + System.out.println("Failed to load weeks resource: " + exception); + } + } + + /** + * Constructs a new Calendar with the default time zone and the default + * locale. + */ + protected Calendar() + { + this(TimeZone.getDefault(), Locale.getDefault()); + } + + /** + * Constructs a new Calendar with the given time zone and the given + * locale. + * @param zone a time zone. + * @param locale a locale. + */ + protected Calendar(TimeZone zone, Locale locale) + { + this.zone = zone; + lenient = true; + String[] days = { "", "sun", "mon", "tue", "wed", "thu", "fri", "sat" }; + + String country = locale.getCountry(); + String min = properties.getProperty("minDays." + country); + if (min == null) + min = properties.getProperty("minDays.DEFAULT"); + String first = properties.getProperty("firstDay." + country); + if (first == null) + first = properties.getProperty("firstDay.DEFAULT"); + try + { + if (min != null) + minimalDaysInFirstWeek = Integer.parseInt(min); + } + catch (NumberFormatException ex) + { + minimalDaysInFirstWeek = 1; + } + + firstDayOfWeek = 1; + if (first != null) + for (int i = 0; i < 8; i++) + if (days[i].equals(first)) + firstDayOfWeek = i; + + clear(); + } + + /** + * Creates a calendar representing the actual time, using the default + * time zone and locale. + * + * @return The new calendar. + */ + public static synchronized Calendar getInstance() + { + return getInstance(TimeZone.getDefault(), Locale.getDefault()); + } + + /** + * Creates a calendar representing the actual time, using the given + * time zone and the default locale. + * + * @param zone a time zone (null not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if zone is null. + */ + public static synchronized Calendar getInstance(TimeZone zone) + { + return getInstance(zone, Locale.getDefault()); + } + + /** + * Creates a calendar representing the actual time, using the default + * time zone and the given locale. + * + * @param locale a locale (null not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if locale is null. + */ + public static synchronized Calendar getInstance(Locale locale) + { + return getInstance(TimeZone.getDefault(), locale); + } + + /** + * Cache of locale->calendar-class mappings. This avoids having to do a ResourceBundle + * lookup for every getInstance call. + */ + private static final HashMap cache = new HashMap(); + + /** Preset argument types for calendar-class constructor lookup. */ + private static Class[] ctorArgTypes = new Class[] + { + TimeZone.class, Locale.class + }; + + /** + * Creates a calendar representing the actual time, using the given + * time zone and locale. + * + * @param zone a time zone (null not permitted). + * @param locale a locale (null not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if zone or locale + * is null. + */ + public static synchronized Calendar getInstance(TimeZone zone, Locale locale) + { + Class calendarClass = cache.get(locale); + Throwable exception = null; + + try + { + if (calendarClass == null) + { + calendarClass = Class.forName(calendarClassName); + if (Calendar.class.isAssignableFrom(calendarClass)) + cache.put(locale, calendarClass); + } + + // GregorianCalendar is by far the most common case. Optimize by + // avoiding reflection. + if (calendarClass == GregorianCalendar.class) + return new GregorianCalendar(zone, locale); + + if (Calendar.class.isAssignableFrom(calendarClass)) + { + Constructor ctor = calendarClass.getConstructor(ctorArgTypes); + return (Calendar) ctor.newInstance(new Object[] { zone, locale }); + } + } + catch (ClassNotFoundException ex) + { + exception = ex; + } + catch (IllegalAccessException ex) + { + exception = ex; + } + catch (NoSuchMethodException ex) + { + exception = ex; + } + catch (InstantiationException ex) + { + exception = ex; + } + catch (InvocationTargetException ex) + { + exception = ex; + } + + throw new RuntimeException("Error instantiating calendar for locale " + + locale, exception); + } + + /** + * Gets the set of locales for which a Calendar is available. + * @exception MissingResourceException if locale data couldn't be found. + * @return the set of locales. + */ + public static synchronized Locale[] getAvailableLocales() + { + ResourceBundle rb = getBundle(new Locale("", "")); + return (Locale[]) rb.getObject("availableLocales"); + } + + /** + * Converts the time field values (fields) to + * milliseconds since the epoch UTC (time). Override + * this method if you write your own Calendar. */ + protected abstract void computeTime(); + + /** + * Converts the milliseconds since the epoch UTC + * (time) to time fields + * (fields). Override this method if you write your + * own Calendar. + */ + protected abstract void computeFields(); + + /** + * Converts the time represented by this object to a + * Date-Object. + * @return the Date. + */ + public final Date getTime() + { + if (! isTimeSet) + computeTime(); + return new Date(time); + } + + /** + * Sets this Calendar's time to the given Date. All time fields + * are invalidated by this method. + * + * @param date the date (null not permitted). + * + * @throws NullPointerException if date is null. + */ + public final void setTime(Date date) + { + setTimeInMillis(date.getTime()); + } + + /** + * Returns the time represented by this Calendar. + * @return the time in milliseconds since the epoch. + * @specnote This was made public in 1.4. + */ + public long getTimeInMillis() + { + if (! isTimeSet) + computeTime(); + return time; + } + + /** + * Sets this Calendar's time to the given Time. All time fields + * are invalidated by this method. + * @param time the time in milliseconds since the epoch + * @specnote This was made public in 1.4. + */ + public void setTimeInMillis(long time) + { + clear(); + this.time = time; + isTimeSet = true; + computeFields(); + } + + /** + * Gets the value of the specified field. They are recomputed + * if they are invalid. + * @param field the time field. One of the time field constants. + * @return the value of the specified field + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + * @specnote Not final since JDK 1.4 + */ + public int get(int field) + { + // If the requested field is invalid, force all fields to be recomputed. + if (! isSet[field]) + areFieldsSet = false; + complete(); + return fields[field]; + } + + /** + * Gets the value of the specified field. This method doesn't + * recompute the fields, if they are invalid. + * @param field the time field. One of the time field constants. + * @return the value of the specified field, undefined if + * areFieldsSet or isSet[field] is false. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + */ + protected final int internalGet(int field) + { + return fields[field]; + } + + /** + * Sets the time field with the given value. This does invalidate + * the time in milliseconds. + * @param field the time field. One of the time field constants + * @param value the value to be set. + * @throws ArrayIndexOutOfBoundsException if field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + * @specnote Not final since JDK 1.4 + */ + public void set(int field, int value) + { + if (isTimeSet) + for (int i = 0; i < FIELD_COUNT; i++) + isSet[i] = false; + isTimeSet = false; + fields[field] = value; + isSet[field] = true; + + // The five valid date patterns, in order of priority + // 1 YEAR + MONTH + DAY_OF_MONTH + // 2 YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK + // 3 YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK + // 4 YEAR + DAY_OF_YEAR + // 5 YEAR + DAY_OF_WEEK + WEEK_OF_YEAR + switch (field) + { + case MONTH: // pattern 1,2 or 3 + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case DAY_OF_MONTH: // pattern 1 + isSet[YEAR] = true; + isSet[MONTH] = true; + isSet[WEEK_OF_MONTH] = true; + isSet[DAY_OF_WEEK] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case WEEK_OF_MONTH: // pattern 2 + if (! isSet[DAY_OF_WEEK]) + fields[DAY_OF_WEEK] = getFirstDayOfWeek(); + isSet[YEAR] = true; + isSet[MONTH] = true; + isSet[DAY_OF_WEEK] = true; + isSet[DAY_OF_MONTH] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case DAY_OF_WEEK_IN_MONTH: // pattern 3 + if (! isSet[DAY_OF_WEEK]) + fields[DAY_OF_WEEK] = getFirstDayOfWeek(); + isSet[YEAR] = true; + isSet[MONTH] = true; + isSet[DAY_OF_WEEK] = true; + isSet[DAY_OF_YEAR] = false; + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case DAY_OF_YEAR: // pattern 4 + isSet[YEAR] = true; + isSet[MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[WEEK_OF_YEAR] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + break; + case WEEK_OF_YEAR: // pattern 5 + if (! isSet[DAY_OF_WEEK]) + fields[DAY_OF_WEEK] = getFirstDayOfWeek(); + isSet[YEAR] = true; + isSet[DAY_OF_WEEK] = true; + isSet[MONTH] = false; + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + break; + case AM_PM: + isSet[HOUR] = true; + isSet[HOUR_OF_DAY] = false; + break; + case HOUR_OF_DAY: + isSet[AM_PM] = false; + isSet[HOUR] = false; + break; + case HOUR: + isSet[AM_PM] = true; + isSet[HOUR_OF_DAY] = false; + break; + case DST_OFFSET: + explicitDSTOffset = true; + } + + // May have crossed over a DST boundary. + if (! explicitDSTOffset && (field != DST_OFFSET && field != ZONE_OFFSET)) + isSet[DST_OFFSET] = false; + } + + /** + * Sets the fields for year, month, and date + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + */ + public final void set(int year, int month, int date) + { + isTimeSet = false; + fields[YEAR] = year; + fields[MONTH] = month; + fields[DATE] = date; + isSet[YEAR] = isSet[MONTH] = isSet[DATE] = true; + isSet[WEEK_OF_YEAR] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[ERA] = false; + + if (! explicitDSTOffset) + isSet[DST_OFFSET] = false; // May have crossed a DST boundary. + } + + /** + * Sets the fields for year, month, date, hour, and minute + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + * @param hour the hour of day. + * @param minute the minute. + */ + public final void set(int year, int month, int date, int hour, int minute) + { + set(year, month, date); + fields[HOUR_OF_DAY] = hour; + fields[MINUTE] = minute; + isSet[HOUR_OF_DAY] = isSet[MINUTE] = true; + isSet[AM_PM] = false; + isSet[HOUR] = false; + } + + /** + * Sets the fields for year, month, date, hour, and minute + * @param year the year. + * @param month the month, one of the constants JANUARY..UNDICEMBER. + * @param date the day of the month + * @param hour the hour of day. + * @param minute the minute. + * @param second the second. + */ + public final void set(int year, int month, int date, int hour, int minute, + int second) + { + set(year, month, date, hour, minute); + fields[SECOND] = second; + isSet[SECOND] = true; + } + + /** + * Clears the values of all the time fields. + */ + public final void clear() + { + isTimeSet = false; + areFieldsSet = false; + int zoneOffs = zone.getRawOffset(); + int[] tempFields = + { + 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, + 0, 0, zoneOffs, 0 + }; + fields = tempFields; + for (int i = 0; i < FIELD_COUNT; i++) + isSet[i] = false; + } + + /** + * Clears the values of the specified time field. + * @param field the time field. One of the time field constants. + * @throws ArrayIndexOutOfBoundsException if field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + */ + public final void clear(int field) + { + int[] tempFields = + { + 1, 1970, JANUARY, 1, 1, 1, 1, THURSDAY, 1, AM, 0, 0, 0, + 0, 0, zone.getRawOffset(), 0 + }; + complete(); + isTimeSet = false; + areFieldsSet = false; + isSet[field] = false; + fields[field] = tempFields[field]; + } + + /** + * Determines if the specified field has a valid value. + * @return true if the specified field has a value. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + */ + public final boolean isSet(int field) + { + return isSet[field]; + } + + /** + * Fills any unset fields in the time field list + */ + protected void complete() + { + if (! isTimeSet) + computeTime(); + if (! areFieldsSet) + computeFields(); + } + + /** + * Compares the given calendar with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, that represents + * the same time (but doesn't necessary have the same fields). + */ + public boolean equals(Object o) + { + if (! (o instanceof Calendar)) + return false; + Calendar cal = (Calendar) o; + if (getTimeInMillis() == ((Calendar) o).getTimeInMillis() + && cal.getFirstDayOfWeek() == getFirstDayOfWeek() + && cal.isLenient() == isLenient() + && cal.getMinimalDaysInFirstWeek() == getMinimalDaysInFirstWeek()) + { + TimeZone self = getTimeZone(); + TimeZone oth = cal.getTimeZone(); + return self == null ? oth == null : self.equals(oth); + } + return false; + } + + /** + * Returns a hash code for this calendar. + * @return a hash code, which fullfits the general contract of + * hashCode() + */ + public int hashCode() + { + long time = getTimeInMillis(); + int val = (int) ((time & 0xffffffffL) ^ (time >> 32)); + val += (getFirstDayOfWeek() + (isLenient() ? 1230 : 1237) + + getMinimalDaysInFirstWeek()); + TimeZone self = getTimeZone(); + if (self != null) + val ^= self.hashCode(); + return val; + } + + /** + * Compares the given calendar with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, and this calendar + * represents a smaller time than the calendar o. + * @exception ClassCastException if o is not an calendar. + * @since JDK1.2 you don't need to override this method + */ + public boolean before(Object o) + { + return getTimeInMillis() < ((Calendar) o).getTimeInMillis(); + } + + /** + * Compares the given calendar with this. + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, and this calendar + * represents a bigger time than the calendar o. + * @exception ClassCastException if o is not an calendar. + * @since JDK1.2 you don't need to override this method + */ + public boolean after(Object o) + { + return getTimeInMillis() > ((Calendar) o).getTimeInMillis(); + } + + /** + * Adds the specified amount of time to the given time field. The + * amount may be negative to subtract the time. If the field overflows + * it does what you expect: Jan, 25 + 10 Days is Feb, 4. + * @param field the time field. One of the time field constants. + * @param amount the amount of time. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + */ + public abstract void add(int field, int amount); + + /** + * Rolls the specified time field up or down. This means add one + * to the specified field, but don't change the other fields. If + * the maximum for this field is reached, start over with the + * minimum value.
        + * + * Note: There may be situation, where the other + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically converted to July, 1. + * @param field the time field. One of the time field constants. + * @param up the direction, true for up, false for down. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + */ + public abstract void roll(int field, boolean up); + + /** + * Rolls up or down the specified time field by the given amount. + * A negative amount rolls down. The default implementation is + * call roll(int, boolean) for the specified amount. + * + * Subclasses should override this method to do more intuitiv things. + * + * @param field the time field. One of the time field constants. + * @param amount the amount to roll by, positive for rolling up, + * negative for rolling down. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + * @since JDK1.2 + */ + public void roll(int field, int amount) + { + while (amount > 0) + { + roll(field, true); + amount--; + } + while (amount < 0) + { + roll(field, false); + amount++; + } + } + + /** + * Sets the time zone to the specified value. + * @param zone the new time zone + */ + public void setTimeZone(TimeZone zone) + { + this.zone = zone; + computeTime(); + computeFields(); + } + + /** + * Gets the time zone of this calendar + * @return the current time zone. + */ + public TimeZone getTimeZone() + { + return zone; + } + + /** + * Specifies if the date/time interpretation should be lenient. + * If the flag is set, a date such as "February 30, 1996" will be + * treated as the 29th day after the February 1. If this flag + * is false, such dates will cause an exception. + * @param lenient true, if the date should be interpreted linient, + * false if it should be interpreted strict. + */ + public void setLenient(boolean lenient) + { + this.lenient = lenient; + } + + /** + * Tells if the date/time interpretation is lenient. + * @return true, if the date should be interpreted linient, + * false if it should be interpreted strict. + */ + public boolean isLenient() + { + return lenient; + } + + /** + * Sets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @param value the first day of week. One of SUNDAY to SATURDAY. + */ + public void setFirstDayOfWeek(int value) + { + firstDayOfWeek = value; + } + + /** + * Gets what the first day of week is. This is used for + * WEEK_OF_MONTH and WEEK_OF_YEAR fields. + * @return the first day of week. One of SUNDAY to SATURDAY. + */ + public int getFirstDayOfWeek() + { + return firstDayOfWeek; + } + + /** + * Sets how many days are required in the first week of the year. + * If the first day of the year should be the first week you should + * set this value to 1. If the first week must be a full week, set + * it to 7. + * @param value the minimal days required in the first week. + */ + public void setMinimalDaysInFirstWeek(int value) + { + minimalDaysInFirstWeek = value; + } + + /** + * Gets how many days are required in the first week of the year. + * @return the minimal days required in the first week. + * @see #setMinimalDaysInFirstWeek + */ + public int getMinimalDaysInFirstWeek() + { + return minimalDaysInFirstWeek; + } + + /** + * Gets the smallest value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the smallest value. + */ + public abstract int getMinimum(int field); + + /** + * Gets the biggest value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the biggest value. + */ + public abstract int getMaximum(int field); + + /** + * Gets the greatest minimum value that is allowed for the specified field. + * @param field the time field. One of the time field constants. + * @return the greatest minimum value. + */ + public abstract int getGreatestMinimum(int field); + + /** + * Gets the smallest maximum value that is allowed for the + * specified field. For example this is 28 for DAY_OF_MONTH. + * @param field the time field. One of the time field constants. + * @return the least maximum value. + */ + public abstract int getLeastMaximum(int field); + + /** + * Gets the actual minimum value that is allowed for the specified field. + * This value is dependent on the values of the other fields. + * @param field the time field. One of the time field constants. + * @return the actual minimum value. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + * @since jdk1.2 + */ + public int getActualMinimum(int field) + { + Calendar tmp = (Calendar) clone(); // To avoid restoring state + int min = tmp.getGreatestMinimum(field); + int end = tmp.getMinimum(field); + tmp.set(field, min); + for (; min > end; min--) + { + tmp.add(field, -1); // Try to get smaller + if (tmp.get(field) != min - 1) + break; // Done if not successful + } + return min; + } + + /** + * Gets the actual maximum value that is allowed for the specified field. + * This value is dependent on the values of the other fields. + * @param field the time field. One of the time field constants. + * @return the actual maximum value. + * @throws ArrayIndexOutOfBoundsException if the field is outside + * the valid range. The value of field must be >= 0 and + * <= FIELD_COUNT. + * @since jdk1.2 + */ + public int getActualMaximum(int field) + { + Calendar tmp = (Calendar) clone(); // To avoid restoring state + int max = tmp.getLeastMaximum(field); + int end = tmp.getMaximum(field); + tmp.set(field, max); + for (; max < end; max++) + { + tmp.add(field, 1); + if (tmp.get(field) != max + 1) + break; + } + return max; + } + + /** + * Compares the time of two calendar instances. + * @param cal the calendar to which the time should be compared. + * @return 0 if the two calendars are set to the same time, + * less than 0 if the time of this calendar is before that of + * cal, or more than 0 if the time of this calendar is after + * that of cal. + * + * @param cal the calendar to compare this instance with. + * @throws NullPointerException if cal is null. + * @throws IllegalArgumentException if either calendar has fields set to + * invalid values. + * @since 1.5 + */ + public int compareTo(Calendar cal) + { + long t1 = getTimeInMillis(); + long t2 = cal.getTimeInMillis(); + if(t1 == t2) + return 0; + if(t1 > t2) + return 1; + return -1; + } + + /** + * Return a clone of this object. + */ + public Object clone() + { + try + { + Calendar cal = (Calendar) super.clone(); + cal.fields = (int[]) fields.clone(); + cal.isSet = (boolean[]) isSet.clone(); + return cal; + } + catch (CloneNotSupportedException ex) + { + return null; + } + } + + private static final String[] fieldNames = + { + ",ERA=", ",YEAR=", ",MONTH=", + ",WEEK_OF_YEAR=", + ",WEEK_OF_MONTH=", + ",DAY_OF_MONTH=", + ",DAY_OF_YEAR=", ",DAY_OF_WEEK=", + ",DAY_OF_WEEK_IN_MONTH=", + ",AM_PM=", ",HOUR=", + ",HOUR_OF_DAY=", ",MINUTE=", + ",SECOND=", ",MILLISECOND=", + ",ZONE_OFFSET=", ",DST_OFFSET=" + }; + + /** + * Returns a string representation of this object. It is mainly + * for debugging purposes and its content is implementation + * specific. + */ + public String toString() + { + CPStringBuilder sb = new CPStringBuilder(getClass().getName()); + sb.append('['); + sb.append("time="); + if (isTimeSet) + sb.append(time); + else + sb.append("?"); + sb.append(",zone=" + zone); + sb.append(",areFieldsSet=" + areFieldsSet); + for (int i = 0; i < FIELD_COUNT; i++) + { + sb.append(fieldNames[i]); + if (isSet[i]) + sb.append(fields[i]); + else + sb.append("?"); + } + sb.append(",lenient=").append(lenient); + sb.append(",firstDayOfWeek=").append(firstDayOfWeek); + sb.append(",minimalDaysInFirstWeek=").append(minimalDaysInFirstWeek); + sb.append("]"); + return sb.toString(); + } + + /** + * Saves the state of the object to the stream. Ideally we would + * only write the time field, but we need to be compatible with + * earlier versions.
        + * + * This doesn't write the JDK1.1 field nextStamp to the stream, as + * I don't know what it is good for, and because the documentation + * says, that it could be omitted. */ + private void writeObject(ObjectOutputStream stream) throws IOException + { + if (! isTimeSet) + computeTime(); + stream.defaultWriteObject(); + } + + /** + * Reads the object back from stream (deserialization). + */ + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException + { + stream.defaultReadObject(); + if (! isTimeSet) + computeTime(); + + if (serialVersionOnStream > 1) + { + // This is my interpretation of the serial number: + // Sun wants to remove all fields from the stream someday + // and will then increase the serialVersion number again. + // We prepare to be compatible. + fields = new int[FIELD_COUNT]; + isSet = new boolean[FIELD_COUNT]; + areFieldsSet = false; + } + } + + /** + * Returns a localised textual representation of the current value + * of the given field using the specified style. If there is no + * applicable textual representation (e.g. the field has a numeric + * value), then null is returned. If one does exist, + * then the value is obtained from {@link #get(int)} and converted + * appropriately. For example, if the MONTH field is + * requested, then get(MONTH) is called. This is then + * converted to a textual representation based on its value and + * the style requested; if the LONG style is requested + * and the returned value is 11 from a + * {@link GregorianCalendar} implementation, then "December" + * is returned. By default, a textual representation is available + * for all fields which have an applicable value obtainable from + * {@link java.text.DateFormatSymbols}. + * + * @param field the calendar field whose textual representation should + * be obtained. + * @param style the style to use; either {@link #LONG} or {@link #SHORT}. + * @param locale the locale to use for translation. + * @return the textual representation of the given field in the specified + * style, or null if none is applicable. + * @throws IllegalArgumentException if field or style + * or invalid, or the calendar is non-lenient + * and has invalid values. + * @throws NullPointerException if locale is null. + * @since 1.6 + */ + public String getDisplayName(int field, int style, Locale locale) + { + if (field < 0 || field >= FIELD_COUNT) + throw new IllegalArgumentException("The field value, " + field + + ", is invalid."); + if (style != SHORT && style != LONG) + throw new IllegalArgumentException("The style must be either " + + "short or long."); + if (field == YEAR || field == WEEK_OF_YEAR || + field == WEEK_OF_MONTH || field == DAY_OF_MONTH || + field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH || + field == HOUR || field == HOUR_OF_DAY || field == MINUTE || + field == SECOND || field == MILLISECOND) + return null; + + int value = get(field); + DateFormatSymbols syms = DateFormatSymbols.getInstance(locale); + if (field == ERA) + return syms.getEras()[value]; + if (field == MONTH) + if (style == LONG) + return syms.getMonths()[value]; + else + return syms.getShortMonths()[value]; + if (field == DAY_OF_WEEK) + if (style == LONG) + return syms.getWeekdays()[value]; + else + return syms.getShortWeekdays()[value]; + if (field == AM_PM) + return syms.getAmPmStrings()[value]; + if (field == ZONE_OFFSET) + if (style == LONG) + return syms.getZoneStrings()[value][1]; + else + return syms.getZoneStrings()[value][2]; + if (field == DST_OFFSET) + if (style == LONG) + return syms.getZoneStrings()[value][3]; + else + return syms.getZoneStrings()[value][4]; + + throw new InternalError("Failed to resolve field " + field + + " with style " + style + " for locale " + + locale); + } + + /** + * Returns a map linking all specified textual representations + * of the given field to their numerical values. The textual + * representations included are determined by the specified + * style and locale. For example, if the style LONG + * is specified and the German locale, then the map will + * contain "Montag" to {@link #MONDAY}, "Dienstag" to + * {@link #TUESDAY}, "Mittwoch" to {@link #WEDNESDAY} and + * so on. The default implementation uses the values returned + * by {@link DateFormatSymbols} so, for example, the style + * {@link #ALL_STYLES} and the field {@link #MONTH} will return + * a map filled with the values returned from + * {@link DateFormatSymbols#getMonths()} and + * {@link DateFormatSymbols#getShortMonths()}. If there are + * no textual representations for a given field (usually because + * it is purely numeric, such as the year in the + * {@link GregorianCalendar}), null is returned. + * + * @param field the calendar field whose textual representation should + * be obtained. + * @param style the style to use; either {@link #LONG}, {@link #SHORT} + * or {@link ALL_STYLES}. + * @param locale the locale to use for translation. + * @return a map of the textual representations of the given field in the + * specified style to their numeric values, or null + * if none is applicable. + * @throws IllegalArgumentException if field or style + * or invalid, or the calendar is non-lenient + * and has invalid values. + * @throws NullPointerException if locale is null. + * @since 1.6 + */ + public Map getDisplayNames(int field, int style, Locale locale) + { + if (field < 0 || field >= FIELD_COUNT) + throw new IllegalArgumentException("The field value, " + field + + ", is invalid."); + if (style != SHORT && style != LONG && style != ALL_STYLES) + throw new IllegalArgumentException("The style must be either " + + "short, long or all styles."); + if (field == YEAR || field == WEEK_OF_YEAR || + field == WEEK_OF_MONTH || field == DAY_OF_MONTH || + field == DAY_OF_YEAR || field == DAY_OF_WEEK_IN_MONTH || + field == HOUR || field == HOUR_OF_DAY || field == MINUTE || + field == SECOND || field == MILLISECOND) + return null; + + DateFormatSymbols syms = DateFormatSymbols.getInstance(locale); + Map map = new HashMap(); + if (field == ERA) + { + String[] eras = syms.getEras(); + for (int a = 0; a < eras.length; ++a) + map.put(eras[a], a); + return map; + } + if (field == MONTH) + { + if (style == LONG || style == ALL_STYLES) + { + String[] months = syms.getMonths(); + for (int a = 0; a < months.length; ++a) + map.put(months[a], a); + } + if (style == SHORT || style == ALL_STYLES) + { + String[] months = syms.getShortMonths(); + for (int a = 0; a < months.length; ++a) + map.put(months[a], a); + } + return map; + } + if (field == DAY_OF_WEEK) + { + if (style == LONG || style == ALL_STYLES) + { + String[] weekdays = syms.getWeekdays(); + for (int a = SUNDAY; a < weekdays.length; ++a) + map.put(weekdays[a], a); + } + if (style == SHORT || style == ALL_STYLES) + { + String[] weekdays = syms.getShortWeekdays(); + for (int a = SUNDAY; a < weekdays.length; ++a) + map.put(weekdays[a], a); + } + return map; + } + if (field == AM_PM) + { + String[] ampms = syms.getAmPmStrings(); + for (int a = 0; a < ampms.length; ++a) + map.put(ampms[a], a); + return map; + } + if (field == ZONE_OFFSET) + { + String[][] zones = syms.getZoneStrings(); + for (int a = 0; a < zones.length; ++a) + { + if (style == LONG || style == ALL_STYLES) + map.put(zones[a][1], a); + if (style == SHORT || style == ALL_STYLES) + map.put(zones[a][2], a); + } + return map; + } + if (field == DST_OFFSET) + { + String[][] zones = syms.getZoneStrings(); + for (int a = 0; a < zones.length; ++a) + { + if (style == LONG || style == ALL_STYLES) + map.put(zones[a][3], a); + if (style == SHORT || style == ALL_STYLES) + map.put(zones[a][4], a); + } + return map; + } + + throw new InternalError("Failed to resolve field " + field + + " with style " + style + " for locale " + + locale); + } + +} diff --git a/libjava/classpath/java/util/Collection.java b/libjava/classpath/java/util/Collection.java new file mode 100644 index 000000000..b57566f70 --- /dev/null +++ b/libjava/classpath/java/util/Collection.java @@ -0,0 +1,290 @@ +/* Collection.java -- Interface that represents a collection of objects + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Interface that represents a collection of objects. This interface is the + * root of the collection hierarchy, and does not provide any guarantees about + * the order of its elements or whether or not duplicate elements are + * permitted. + *

        + * All methods of this interface that are defined to modify the collection are + * defined as optional. An optional operation may throw an + * UnsupportedOperationException if the data backing this collection does not + * support such a modification. This may mean that the data structure is + * immutable, or that it is read-only but may change ("unmodifiable"), or + * that it is modifiable but of fixed size (such as an array), or any number + * of other combinations. + *

        + * A class that wishes to implement this interface should consider subclassing + * AbstractCollection, which provides basic implementations of most of the + * methods of this interface. Classes that are prepared to make guarantees + * about ordering or about absence of duplicate elements should consider + * implementing List or Set respectively, both of which are subinterfaces of + * Collection. + *

        + * A general-purpose implementation of the Collection interface should in most + * cases provide at least two constructors: One which takes no arguments and + * creates an empty collection, and one which takes a Collection as an argument + * and returns a collection containing the same elements (that is, creates a + * copy of the argument using its own implementation). + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see List + * @see Set + * @see Map + * @see SortedSet + * @see SortedMap + * @see HashSet + * @see TreeSet + * @see ArrayList + * @see LinkedList + * @see Vector + * @see Collections + * @see Arrays + * @see AbstractCollection + * @since 1.2 + * @status updated to 1.4 + */ +public interface Collection extends Iterable +{ + /** + * Add an element to this collection. + * + * @param o the object to add. + * @return true if the collection was modified as a result of this action. + * @throws UnsupportedOperationException if this collection does not + * support the add operation. + * @throws ClassCastException if o cannot be added to this collection due + * to its type. + * @throws NullPointerException if o is null and this collection doesn't + * support the addition of null values. + * @throws IllegalArgumentException if o cannot be added to this + * collection for some other reason. + */ + boolean add(E o); + + /** + * Add the contents of a given collection to this collection. + * + * @param c the collection to add. + * @return true if the collection was modified as a result of this action. + * @throws UnsupportedOperationException if this collection does not + * support the addAll operation. + * @throws ClassCastException if some element of c cannot be added to this + * collection due to its type. + * @throws NullPointerException if some element of c is null and this + * collection does not support the addition of null values. + * @throws NullPointerException if c itself is null. + * @throws IllegalArgumentException if some element of c cannot be added + * to this collection for some other reason. + */ + boolean addAll(Collection c); + + /** + * Clear the collection, such that a subsequent call to isEmpty() would + * return true. + * + * @throws UnsupportedOperationException if this collection does not + * support the clear operation. + */ + void clear(); + + /** + * Test whether this collection contains a given object as one of its + * elements. + * + * @param o the element to look for. + * @return true if this collection contains at least one element e such that + * o == null ? e == null : o.equals(e). + * @throws ClassCastException if the type of o is not a valid type for this + * collection. + * @throws NullPointerException if o is null and this collection doesn't + * support null values. + */ + boolean contains(Object o); + + /** + * Test whether this collection contains every element in a given collection. + * + * @param c the collection to test for. + * @return true if for every element o in c, contains(o) would return true. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support null values. + * @throws NullPointerException if c itself is null. + */ + boolean containsAll(Collection c); + + /** + * Test whether this collection is equal to some object. The Collection + * interface does not explicitly require any behaviour from this method, and + * it may be left to the default implementation provided by Object. The Set + * and List interfaces do, however, require specific behaviour from this + * method. + *

        + * If an implementation of Collection, which is not also an implementation of + * Set or List, should choose to implement this method, it should take care + * to obey the contract of the equals method of Object. In particular, care + * should be taken to return false when o is a Set or a List, in order to + * preserve the symmetry of the relation. + * + * @param o the object to compare to this collection. + * @return true if the o is equal to this collection. + */ + boolean equals(Object o); + + /** + * Obtain a hash code for this collection. The Collection interface does not + * explicitly require any behaviour from this method, and it may be left to + * the default implementation provided by Object. The Set and List interfaces + * do, however, require specific behaviour from this method. + *

        + * If an implementation of Collection, which is not also an implementation of + * Set or List, should choose to implement this method, it should take care + * to obey the contract of the hashCode method of Object. Note that this + * method renders it impossible to correctly implement both Set and List, as + * the required implementations are mutually exclusive. + * + * @return a hash code for this collection. + */ + int hashCode(); + + /** + * Test whether this collection is empty, that is, if size() == 0. + * + * @return true if this collection contains no elements. + */ + boolean isEmpty(); + + /** + * Obtain an Iterator over this collection. + * + * @return an Iterator over the elements of this collection, in any order. + */ + Iterator iterator(); + + /** + * Remove a single occurrence of an object from this collection. That is, + * remove an element e, if one exists, such that o == null ? e == null + * : o.equals(e). + * + * @param o the object to remove. + * @return true if the collection changed as a result of this call, that is, + * if the collection contained at least one occurrence of o. + * @throws UnsupportedOperationException if this collection does not + * support the remove operation. + * @throws ClassCastException if the type of o is not a valid type + * for this collection. + * @throws NullPointerException if o is null and the collection doesn't + * support null values. + */ + boolean remove(Object o); + + /** + * Remove all elements of a given collection from this collection. That is, + * remove every element e such that c.contains(e). + * + * @param c The collection of objects to be removed. + * @return true if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the removeAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support removing null values. + * @throws NullPointerException if c itself is null. + */ + boolean removeAll(Collection c); + + /** + * Remove all elements of this collection that are not contained in a given + * collection. That is, remove every element e such that !c.contains(e). + * + * @param c The collection of objects to be retained. + * @return true if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the retainAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support retaining null values. + * @throws NullPointerException if c itself is null. + */ + boolean retainAll(Collection c); + + /** + * Get the number of elements in this collection. + * + * @return the number of elements in the collection. + */ + int size(); + + /** + * Copy the current contents of this collection into an array. + * + * @return an array of type Object[] and length equal to the size of this + * collection, containing the elements currently in this collection, in + * any order. + */ + Object[] toArray(); + + /** + * Copy the current contents of this collection into an array. If the array + * passed as an argument has length less than the size of this collection, an + * array of the same run-time type as a, and length equal to the size of this + * collection, is allocated using Reflection. Otherwise, a itself is used. + * The elements of this collection are copied into it, and if there is space + * in the array, the following element is set to null. The resultant array is + * returned. + * Note: The fact that the following element is set to null is only useful + * if it is known that this collection does not contain any null elements. + * + * @param a the array to copy this collection into. + * @return an array containing the elements currently in this collection, in + * any order. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + T[] toArray(T[] a); +} diff --git a/libjava/classpath/java/util/Collections.java b/libjava/classpath/java/util/Collections.java new file mode 100644 index 000000000..828c6ecea --- /dev/null +++ b/libjava/classpath/java/util/Collections.java @@ -0,0 +1,7623 @@ +/* Collections.java -- Utility class with methods to operate on collections + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Serializable; + +/** + * Utility class consisting of static methods that operate on, or return + * Collections. Contains methods to sort, search, reverse, fill and shuffle + * Collections, methods to facilitate interoperability with legacy APIs that + * are unaware of collections, a method to return a list which consists of + * multiple copies of one element, and methods which "wrap" collections to give + * them extra properties, such as thread-safety and unmodifiability. + *

        + * + * All methods which take a collection throw a {@link NullPointerException} if + * that collection is null. Algorithms which can change a collection may, but + * are not required, to throw the {@link UnsupportedOperationException} that + * the underlying collection would throw during an attempt at modification. + * For example, + * Collections.singleton("").addAll(Collections.EMPTY_SET) + * does not throw a exception, even though addAll is an unsupported operation + * on a singleton; the reason for this is that addAll did not attempt to + * modify the set. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Collection + * @see Set + * @see List + * @see Map + * @see Arrays + * @since 1.2 + * @status updated to 1.5 + */ +public class Collections +{ + /** + * Constant used to decide cutoff for when a non-RandomAccess list should + * be treated as sequential-access. Basically, quadratic behavior is + * acceptable for small lists when the overhead is so small in the first + * place. I arbitrarily set it to 16, so it may need some tuning. + */ + private static final int LARGE_LIST_SIZE = 16; + + /** + * Determines if a list should be treated as a sequential-access one. + * Rather than the old method of JDK 1.3 of assuming only instanceof + * AbstractSequentialList should be sequential, this uses the new method + * of JDK 1.4 of assuming anything that does NOT implement RandomAccess + * and exceeds a large (unspecified) size should be sequential. + * + * @param l the list to check + * @return true if it should be treated as sequential-access + */ + private static boolean isSequential(List l) + { + return ! (l instanceof RandomAccess) && l.size() > LARGE_LIST_SIZE; + } + + /** + * This class is non-instantiable. + */ + private Collections() + { + } + + /** + * An immutable, serializable, empty Set. + * @see Serializable + */ + public static final Set EMPTY_SET = new EmptySet(); + + /** + * Returns an immutable, serializable parameterized empty set. + * Unlike the constant EMPTY_SET, the set returned by + * this method is type-safe. + * + * @return an empty parameterized set. + * @since 1.5 + */ + public static final Set emptySet() + { + /* FIXME: Could this be optimized? */ + return new EmptySet(); + } + + /** + * The implementation of {@link #EMPTY_SET}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptySet extends AbstractSet + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1582296315990362920L; + + /** + * A private constructor adds overhead. + */ + EmptySet() + { + } + + /** + * The size: always 0! + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * Returns an iterator that does not iterate. + * @return A non-iterating iterator. + */ + // This is really cheating! I think it's perfectly valid, though. + public Iterator iterator() + { + return (Iterator) EMPTY_LIST.iterator(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractSet. + /** + * The empty set never contains anything. + * @param o The object to search for. + * @return false. + */ + public boolean contains(Object o) + { + return false; + } + + /** + * This is true only if the given collection is also empty. + * @param c The collection of objects which are to be compared + * against the members of this set. + * @return true if c is empty. + */ + public boolean containsAll(Collection c) + { + return c.isEmpty(); + } + + /** + * Equal only if the other set is empty. + * @param o The object to compare with this set. + * @return true if o is an empty instance of Set. + */ + public boolean equals(Object o) + { + return o instanceof Set && ((Set) o).isEmpty(); + } + + /** + * The hashcode is always 0. + * @return 0. + */ + public int hashCode() + { + return 0; + } + + /** + * Always succeeds with a false result. + * @param o The object to remove. + * @return false. + */ + public boolean remove(Object o) + { + return false; + } + + /** + * Always succeeds with a false result. + * @param c The collection of objects which should + * all be removed from this set. + * @return false. + */ + public boolean removeAll(Collection c) + { + return false; + } + + /** + * Always succeeds with a false result. + * @param c The collection of objects which should + * all be retained within this set. + * @return false. + */ + public boolean retainAll(Collection c) + { + return false; + } + + /** + * The array is always empty. + * @return A new array with a size of 0. + */ + public Object[] toArray() + { + return new Object[0]; + } + + /** + * We don't even need to use reflection! + * @param a An existing array, which can be empty. + * @return The original array with any existing + * initial element set to null. + */ + public E[] toArray(E[] a) + { + if (a.length > 0) + a[0] = null; + return a; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptySet + + /** + * An immutable, serializable, empty List, which implements RandomAccess. + * @see Serializable + * @see RandomAccess + */ + public static final List EMPTY_LIST = new EmptyList(); + + /** + * Returns an immutable, serializable parameterized empty list. + * Unlike the constant EMPTY_LIST, the list returned by + * this method is type-safe. + * + * @return an empty parameterized list. + * @since 1.5 + */ + public static final List emptyList() + { + /* FIXME: Could this be optimized? */ + return new EmptyList(); + } + + /** + * The implementation of {@link #EMPTY_LIST}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptyList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 8842843931221139166L; + + /** + * A private constructor adds overhead. + */ + EmptyList() + { + } + + /** + * The size is always 0. + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * No matter the index, it is out of bounds. This + * method never returns, throwing an exception instead. + * + * @param index The index of the element to retrieve. + * @return the object at the specified index. + * @throws IndexOutOfBoundsException as any given index + * is outside the bounds of an empty array. + */ + public T get(int index) + { + throw new IndexOutOfBoundsException(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * Never contains anything. + * @param o The object to search for. + * @return false. + */ + public boolean contains(Object o) + { + return false; + } + + /** + * This is true only if the given collection is also empty. + * @param c The collection of objects, which should be compared + * against the members of this list. + * @return true if c is also empty. + */ + public boolean containsAll(Collection c) + { + return c.isEmpty(); + } + + /** + * Equal only if the other list is empty. + * @param o The object to compare against this list. + * @return true if o is also an empty instance of + * List. + */ + public boolean equals(Object o) + { + return o instanceof List && ((List) o).isEmpty(); + } + + /** + * The hashcode is always 1. + * @return 1. + */ + public int hashCode() + { + return 1; + } + + /** + * Returns -1. + * @param o The object to search for. + * @return -1. + */ + public int indexOf(Object o) + { + return -1; + } + + /** + * Returns -1. + * @param o The object to search for. + * @return -1. + */ + public int lastIndexOf(Object o) + { + return -1; + } + + /** + * Always succeeds with false result. + * @param o The object to remove. + * @return -1. + */ + public boolean remove(Object o) + { + return false; + } + + /** + * Always succeeds with false result. + * @param c The collection of objects which should + * all be removed from this list. + * @return false. + */ + public boolean removeAll(Collection c) + { + return false; + } + + /** + * Always succeeds with false result. + * @param c The collection of objects which should + * all be retained within this list. + * @return false. + */ + public boolean retainAll(Collection c) + { + return false; + } + + /** + * The array is always empty. + * @return A new array with a size of 0. + */ + public Object[] toArray() + { + return new Object[0]; + } + + /** + * We don't even need to use reflection! + * @param a An existing array, which can be empty. + * @return The original array with any existing + * initial element set to null. + */ + public E[] toArray(E[] a) + { + if (a.length > 0) + a[0] = null; + return a; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptyList + + /** + * An immutable, serializable, empty Map. + * @see Serializable + */ + public static final Map EMPTY_MAP = new EmptyMap(); + + /** + * Returns an immutable, serializable parameterized empty map. + * Unlike the constant EMPTY_MAP, the map returned by + * this method is type-safe. + * + * @return an empty parameterized map. + * @since 1.5 + */ + public static final Map emptyMap() + { + /* FIXME: Could this be optimized? */ + return new EmptyMap(); + } + + /** + * The implementation of {@link #EMPTY_MAP}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptyMap extends AbstractMap + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 6428348081105594320L; + + /** + * A private constructor adds overhead. + */ + EmptyMap() + { + } + + /** + * There are no entries. + * @return The empty set. + */ + public Set> entrySet() + { + return EMPTY_SET; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractMap. + /** + * No entries! + * @param key The key to search for. + * @return false. + */ + public boolean containsKey(Object key) + { + return false; + } + + /** + * No entries! + * @param value The value to search for. + * @return false. + */ + public boolean containsValue(Object value) + { + return false; + } + + /** + * Equal to all empty maps. + * @param o The object o to compare against this map. + * @return true if o is also an empty instance of + * Map. + */ + public boolean equals(Object o) + { + return o instanceof Map && ((Map) o).isEmpty(); + } + + /** + * No mappings, so this returns null. + * @param o The key of the object to retrieve. + * @return null. + */ + public V get(Object o) + { + return null; + } + + /** + * The hashcode is always 0. + * @return 0. + */ + public int hashCode() + { + return 0; + } + + /** + * No entries. + * @return The empty set. + */ + public Set keySet() + { + return EMPTY_SET; + } + + /** + * Remove always succeeds, with null result. + * @param o The key of the mapping to remove. + * @return null, as there is never a mapping for o. + */ + public V remove(Object o) + { + return null; + } + + /** + * Size is always 0. + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * No entries. Technically, EMPTY_SET, while more specific than a general + * Collection, will work. Besides, that's what the JDK uses! + * @return The empty set. + */ + public Collection values() + { + return EMPTY_SET; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptyMap + + + /** + * Compare two objects with or without a Comparator. If c is null, uses the + * natural ordering. Slightly slower than doing it inline if the JVM isn't + * clever, but worth it for removing a duplicate of the search code. + * Note: This code is also used in Arrays (for sort as well as search). + */ + static final int compare(T o1, T o2, Comparator c) + { + return c == null ? ((Comparable) o1).compareTo(o2) : c.compare(o1, o2); + } + + /** + * Perform a binary search of a List for a key, using the natural ordering of + * the elements. The list must be sorted (as by the sort() method) - if it is + * not, the behavior of this method is undefined, and may be an infinite + * loop. Further, the key must be comparable with every item in the list. If + * the list contains the key more than once, any one of them may be found. + *

        + * + * This algorithm behaves in log(n) time for {@link RandomAccess} lists, + * and uses a linear search with O(n) link traversals and log(n) comparisons + * with {@link AbstractSequentialList} lists. Note: although the + * specification allows for an infinite loop if the list is unsorted, it will + * not happen in this (Classpath) implementation. + * + * @param l the list to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value + * @throws ClassCastException if key could not be compared with one of the + * elements of l + * @throws NullPointerException if a null element has compareTo called + * @see #sort(List) + */ + public static int binarySearch(List> l, + T key) + { + return binarySearch(l, key, null); + } + + /** + * Perform a binary search of a List for a key, using a supplied Comparator. + * The list must be sorted (as by the sort() method with the same Comparator) + * - if it is not, the behavior of this method is undefined, and may be an + * infinite loop. Further, the key must be comparable with every item in the + * list. If the list contains the key more than once, any one of them may be + * found. If the comparator is null, the elements' natural ordering is used. + *

        + * + * This algorithm behaves in log(n) time for {@link RandomAccess} lists, + * and uses a linear search with O(n) link traversals and log(n) comparisons + * with {@link AbstractSequentialList} lists. Note: although the + * specification allows for an infinite loop if the list is unsorted, it will + * not happen in this (Classpath) implementation. + * + * @param l the list to search (must be sorted) + * @param key the value to search for + * @param c the comparator by which the list is sorted + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value + * @throws ClassCastException if key could not be compared with one of the + * elements of l + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + * @see #sort(List, Comparator) + */ + public static int binarySearch(List l, T key, + Comparator c) + { + int pos = 0; + int low = 0; + int hi = l.size() - 1; + + // We use a linear search with log(n) comparisons using an iterator + // if the list is sequential-access. + if (isSequential(l)) + { + ListIterator itr = ((List) l).listIterator(); + int i = 0; + T o = itr.next(); // Assumes list is not empty (see isSequential) + boolean forward = true; + while (low <= hi) + { + pos = (low + hi) >>> 1; + if (i < pos) + { + if (!forward) + itr.next(); // Changing direction first. + for ( ; i != pos; i++, o = itr.next()) + ; + forward = true; + } + else + { + if (forward) + itr.previous(); // Changing direction first. + for ( ; i != pos; i--, o = itr.previous()) + ; + forward = false; + } + final int d = compare(o, key, c); + if (d == 0) + return pos; + else if (d > 0) + hi = pos - 1; + else + // This gets the insertion point right on the last loop + low = ++pos; + } + } + else + { + while (low <= hi) + { + pos = (low + hi) >>> 1; + final int d = compare(((List) l).get(pos), key, c); + if (d == 0) + return pos; + else if (d > 0) + hi = pos - 1; + else + // This gets the insertion point right on the last loop + low = ++pos; + } + } + + // If we failed to find it, we do the same whichever search we did. + return -pos - 1; + } + + /** + * Copy one list to another. If the destination list is longer than the + * source list, the remaining elements are unaffected. This method runs in + * linear time. + * + * @param dest the destination list + * @param source the source list + * @throws IndexOutOfBoundsException if the destination list is shorter + * than the source list (the destination will be unmodified) + * @throws UnsupportedOperationException if dest.listIterator() does not + * support the set operation + */ + public static void copy(List dest, List source) + { + int pos = source.size(); + if (dest.size() < pos) + throw new IndexOutOfBoundsException("Source does not fit in dest"); + + Iterator i1 = source.iterator(); + ListIterator i2 = dest.listIterator(); + + while (--pos >= 0) + { + i2.next(); + i2.set(i1.next()); + } + } + + /** + * Returns an Enumeration over a collection. This allows interoperability + * with legacy APIs that require an Enumeration as input. + * + * @param c the Collection to iterate over + * @return an Enumeration backed by an Iterator over c + */ + public static Enumeration enumeration(Collection c) + { + final Iterator i = c.iterator(); + return new Enumeration() + { + /** + * Returns true if there are more elements to + * be enumerated. + * + * @return The result of hasNext() + * called on the underlying iterator. + */ + public final boolean hasMoreElements() + { + return i.hasNext(); + } + + /** + * Returns the next element to be enumerated. + * + * @return The result of next() + * called on the underlying iterator. + */ + public final T nextElement() + { + return i.next(); + } + }; + } + + /** + * Replace every element of a list with a given value. This method runs in + * linear time. + * + * @param l the list to fill. + * @param val the object to vill the list with. + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation. + */ + public static void fill(List l, T val) + { + ListIterator itr = l.listIterator(); + for (int i = l.size() - 1; i >= 0; --i) + { + itr.next(); + itr.set(val); + } + } + + /** + * Returns the starting index where the specified sublist first occurs + * in a larger list, or -1 if there is no matching position. If + * target.size() > source.size(), this returns -1, + * otherwise this implementation uses brute force, checking for + * source.sublist(i, i + target.size()).equals(target) + * for all possible i. + * + * @param source the list to search + * @param target the sublist to search for + * @return the index where found, or -1 + * @since 1.4 + */ + public static int indexOfSubList(List source, List target) + { + int ssize = source.size(); + for (int i = 0, j = target.size(); j <= ssize; i++, j++) + if (source.subList(i, j).equals(target)) + return i; + return -1; + } + + /** + * Returns the starting index where the specified sublist last occurs + * in a larger list, or -1 if there is no matching position. If + * target.size() > source.size(), this returns -1, + * otherwise this implementation uses brute force, checking for + * source.sublist(i, i + target.size()).equals(target) + * for all possible i. + * + * @param source the list to search + * @param target the sublist to search for + * @return the index where found, or -1 + * @since 1.4 + */ + public static int lastIndexOfSubList(List source, List target) + { + int ssize = source.size(); + for (int i = ssize - target.size(), j = ssize; i >= 0; i--, j--) + if (source.subList(i, j).equals(target)) + return i; + return -1; + } + + /** + * Returns an ArrayList holding the elements visited by a given + * Enumeration. This method exists for interoperability between legacy + * APIs and the new Collection API. + * + * @param e the enumeration to put in a list + * @return a list containing the enumeration elements + * @see ArrayList + * @since 1.4 + */ + public static ArrayList list(Enumeration e) + { + ArrayList l = new ArrayList(); + while (e.hasMoreElements()) + l.add(e.nextElement()); + return l; + } + + /** + * Find the maximum element in a Collection, according to the natural + * ordering of the elements. This implementation iterates over the + * Collection, so it works in linear time. + * + * @param c the Collection to find the maximum element of + * @return the maximum element of c + * @exception NoSuchElementException if c is empty + * @exception ClassCastException if elements in c are not mutually comparable + * @exception NullPointerException if null.compareTo is called + */ + public static > + T max(Collection c) + { + return max(c, null); + } + + /** + * Find the maximum element in a Collection, according to a specified + * Comparator. This implementation iterates over the Collection, so it + * works in linear time. + * + * @param c the Collection to find the maximum element of + * @param order the Comparator to order the elements by, or null for natural + * ordering + * @return the maximum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null is compared by natural ordering + * (only possible when order is null) + */ + public static T max(Collection c, + Comparator order) + { + Iterator itr = c.iterator(); + T max = itr.next(); // throws NoSuchElementException + int csize = c.size(); + for (int i = 1; i < csize; i++) + { + T o = itr.next(); + if (compare(max, o, order) < 0) + max = o; + } + return max; + } + + /** + * Find the minimum element in a Collection, according to the natural + * ordering of the elements. This implementation iterates over the + * Collection, so it works in linear time. + * + * @param c the Collection to find the minimum element of + * @return the minimum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null.compareTo is called + */ + public static > + T min(Collection c) + { + return min(c, null); + } + + /** + * Find the minimum element in a Collection, according to a specified + * Comparator. This implementation iterates over the Collection, so it + * works in linear time. + * + * @param c the Collection to find the minimum element of + * @param order the Comparator to order the elements by, or null for natural + * ordering + * @return the minimum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null is compared by natural ordering + * (only possible when order is null) + */ + public static T min(Collection c, + Comparator order) + { + Iterator itr = c.iterator(); + T min = itr.next(); // throws NoSuchElementExcception + int csize = c.size(); + for (int i = 1; i < csize; i++) + { + T o = itr.next(); + if (compare(min, o, order) > 0) + min = o; + } + return min; + } + + /** + * Creates an immutable list consisting of the same object repeated n times. + * The returned object is tiny, consisting of only a single reference to the + * object and a count of the number of elements. It is Serializable, and + * implements RandomAccess. You can use it in tandem with List.addAll for + * fast list construction. + * + * @param n the number of times to repeat the object + * @param o the object to repeat + * @return a List consisting of n copies of o + * @throws IllegalArgumentException if n < 0 + * @see List#addAll(Collection) + * @see Serializable + * @see RandomAccess + */ + public static List nCopies(final int n, final T o) + { + return new CopiesList(n, o); + } + + /** + * The implementation of {@link #nCopies(int, Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class CopiesList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 2739099268398711800L; + + /** + * The count of elements in this list. + * @serial the list size + */ + private final int n; + + /** + * The repeated list element. + * @serial the list contents + */ + private final T element; + + /** + * Constructs the list. + * + * @param n the count + * @param o the object + * @throws IllegalArgumentException if n < 0 + */ + CopiesList(int n, T o) + { + if (n < 0) + throw new IllegalArgumentException(); + this.n = n; + element = o; + } + + /** + * The size is fixed. + * @return The size of the list. + */ + public int size() + { + return n; + } + + /** + * The same element is returned. + * @param index The index of the element to be returned (irrelevant + * as the list contains only copies of element). + * @return The element used by this list. + */ + public T get(int index) + { + if (index < 0 || index >= n) + throw new IndexOutOfBoundsException(); + return element; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * This list only contains one element. + * @param o The object to search for. + * @return true if o is the element used by this list. + */ + public boolean contains(Object o) + { + return n > 0 && equals(o, element); + } + + /** + * The index is either 0 or -1. + * @param o The object to find the index of. + * @return 0 if o == element, -1 if not. + */ + public int indexOf(Object o) + { + return (n > 0 && equals(o, element)) ? 0 : -1; + } + + /** + * The index is either n-1 or -1. + * @param o The object to find the last index of. + * @return The last index in the list if o == element, + * -1 if not. + */ + public int lastIndexOf(Object o) + { + return equals(o, element) ? n - 1 : -1; + } + + /** + * A subList is just another CopiesList. + * @param from The starting bound of the sublist. + * @param to The ending bound of the sublist. + * @return A list of copies containing from - to + * elements, all of which are equal to the element + * used by this list. + */ + public List subList(int from, int to) + { + if (from < 0 || to > n) + throw new IndexOutOfBoundsException(); + return new CopiesList(to - from, element); + } + + /** + * The array is easy. + * @return An array of size n filled with copies of + * the element used by this list. + */ + public Object[] toArray() + { + Object[] a = new Object[n]; + Arrays.fill(a, element); + return a; + } + + /** + * The string is easy to generate. + * @return A string representation of the list. + */ + public String toString() + { + CPStringBuilder r = new CPStringBuilder("{"); + for (int i = n - 1; --i > 0; ) + r.append(element).append(", "); + r.append(element).append("}"); + return r.toString(); + } + } // class CopiesList + + /** + * Replace all instances of one object with another in the specified list. + * The list does not change size. An element e is replaced if + * oldval == null ? e == null : oldval.equals(e). + * + * @param list the list to iterate over + * @param oldval the element to replace + * @param newval the new value for the element + * @return true if a replacement occurred. + * @throws UnsupportedOperationException if the list iterator does not allow + * for the set operation + * @throws ClassCastException if newval is of a type which cannot be added + * to the list + * @throws IllegalArgumentException if some other aspect of newval stops + * it being added to the list + * @since 1.4 + */ + public static boolean replaceAll(List list, T oldval, T newval) + { + ListIterator itr = list.listIterator(); + boolean replace_occured = false; + for (int i = list.size(); --i >= 0; ) + if (AbstractCollection.equals(oldval, itr.next())) + { + itr.set(newval); + replace_occured = true; + } + return replace_occured; + } + + /** + * Reverse a given list. This method works in linear time. + * + * @param l the list to reverse + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void reverse(List l) + { + ListIterator i1 = l.listIterator(); + int pos1 = 1; + int pos2 = l.size(); + ListIterator i2 = l.listIterator(pos2); + while (pos1 < pos2) + { + Object o1 = i1.next(); + Object o2 = i2.previous(); + i1.set(o2); + i2.set(o1); + ++pos1; + --pos2; + } + } + + /** + * Get a comparator that implements the reverse of the ordering + * specified by the given Comparator. If the Comparator is null, + * this is equivalent to {@link #reverseOrder()}. The return value + * of this method is Serializable, if the specified Comparator is + * either Serializable or null. + * + * @param c the comparator to invert + * @return a comparator that imposes reverse ordering + * @see Comparable + * @see Serializable + * + * @since 1.5 + */ + public static Comparator reverseOrder(final Comparator c) + { + if (c == null) + return (Comparator) rcInstance; + return new ReverseComparator () + { + public int compare(T a, T b) + { + return - c.compare(a, b); + } + }; + } + + /** + * Get a comparator that implements the reverse of natural ordering. In + * other words, this sorts Comparable objects opposite of how their + * compareTo method would sort. This makes it easy to sort into reverse + * order, by simply passing Collections.reverseOrder() to the sort method. + * The return value of this method is Serializable. + * + * @return a comparator that imposes reverse natural ordering + * @see Comparable + * @see Serializable + */ + public static Comparator reverseOrder() + { + return (Comparator) rcInstance; + } + + /** + * The object for {@link #reverseOrder()}. + */ + private static final ReverseComparator rcInstance = new ReverseComparator(); + + /** + * The implementation of {@link #reverseOrder()}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class ReverseComparator + implements Comparator, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 7207038068494060240L; + + /** + * A private constructor adds overhead. + */ + ReverseComparator() + { + } + + /** + * Compare two objects in reverse natural order. + * + * @param a the first object + * @param b the second object + * @return <, ==, or > 0 according to b.compareTo(a) + */ + public int compare(T a, T b) + { + return ((Comparable) b).compareTo(a); + } + } + + /** + * Rotate the elements in a list by a specified distance. After calling this + * method, the element now at index i was formerly at index + * (i - distance) mod list.size(). The list size is unchanged. + *

        + * + * For example, suppose a list contains [t, a, n, k, s]. After + * either Collections.rotate(l, 4) or + * Collections.rotate(l, -1), the new contents are + * [s, t, a, n, k]. This can be applied to sublists to rotate + * just a portion of the list. For example, to move element a + * forward two positions in the original example, use + * Collections.rotate(l.subList(1, 3+1), -1), which will + * result in [t, n, k, a, s]. + *

        + * + * If the list is small or implements {@link RandomAccess}, the + * implementation exchanges the first element to its destination, then the + * displaced element, and so on until a circuit has been completed. The + * process is repeated if needed on the second element, and so forth, until + * all elements have been swapped. For large non-random lists, the + * implementation breaks the list into two sublists at index + * -distance mod size, calls {@link #reverse(List)} on the + * pieces, then reverses the overall list. + * + * @param list the list to rotate + * @param distance the distance to rotate by; unrestricted in value + * @throws UnsupportedOperationException if the list does not support set + * @since 1.4 + */ + public static void rotate(List list, int distance) + { + int size = list.size(); + if (size == 0) + return; + distance %= size; + if (distance == 0) + return; + if (distance < 0) + distance += size; + + if (isSequential(list)) + { + reverse(list); + reverse(list.subList(0, distance)); + reverse(list.subList(distance, size)); + } + else + { + // Determine the least common multiple of distance and size, as there + // are (distance / LCM) loops to cycle through. + int a = size; + int lcm = distance; + int b = a % lcm; + while (b != 0) + { + a = lcm; + lcm = b; + b = a % lcm; + } + + // Now, make the swaps. We must take the remainder every time through + // the inner loop so that we don't overflow i to negative values. + List objList = (List) list; + while (--lcm >= 0) + { + Object o = objList.get(lcm); + for (int i = lcm + distance; i != lcm; i = (i + distance) % size) + o = objList.set(i, o); + objList.set(lcm, o); + } + } + } + + /** + * Shuffle a list according to a default source of randomness. The algorithm + * used iterates backwards over the list, swapping each element with an + * element randomly selected from the elements in positions less than or + * equal to it (using r.nextInt(int)). + *

        + * + * This algorithm would result in a perfectly fair shuffle (that is, each + * element would have an equal chance of ending up in any position) if r were + * a perfect source of randomness. In practice the results are merely very + * close to perfect. + *

        + * + * This method operates in linear time. To do this on large lists which do + * not implement {@link RandomAccess}, a temporary array is used to acheive + * this speed, since it would be quadratic access otherwise. + * + * @param l the list to shuffle + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void shuffle(List l) + { + if (defaultRandom == null) + { + synchronized (Collections.class) + { + if (defaultRandom == null) + defaultRandom = new Random(); + } + } + shuffle(l, defaultRandom); + } + + /** + * Cache a single Random object for use by shuffle(List). This improves + * performance as well as ensuring that sequential calls to shuffle() will + * not result in the same shuffle order occurring: the resolution of + * System.currentTimeMillis() is not sufficient to guarantee a unique seed. + */ + private static Random defaultRandom = null; + + /** + * Shuffle a list according to a given source of randomness. The algorithm + * used iterates backwards over the list, swapping each element with an + * element randomly selected from the elements in positions less than or + * equal to it (using r.nextInt(int)). + *

        + * + * This algorithm would result in a perfectly fair shuffle (that is, each + * element would have an equal chance of ending up in any position) if r were + * a perfect source of randomness. In practise (eg if r = new Random()) the + * results are merely very close to perfect. + *

        + * + * This method operates in linear time. To do this on large lists which do + * not implement {@link RandomAccess}, a temporary array is used to acheive + * this speed, since it would be quadratic access otherwise. + * + * @param l the list to shuffle + * @param r the source of randomness to use for the shuffle + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void shuffle(List l, Random r) + { + int lsize = l.size(); + List list = (List) l; + ListIterator i = list.listIterator(lsize); + boolean sequential = isSequential(l); + Object[] a = null; // stores a copy of the list for the sequential case + + if (sequential) + a = list.toArray(); + + for (int pos = lsize - 1; pos > 0; --pos) + { + // Obtain a random position to swap with. pos + 1 is used so that the + // range of the random number includes the current position. + int swap = r.nextInt(pos + 1); + + // Swap the desired element. + Object o; + if (sequential) + { + o = a[swap]; + a[swap] = i.previous(); + } + else + o = list.set(swap, i.previous()); + + i.set(o); + } + } + + /** + * Returns the frequency of the specified object within the supplied + * collection. The frequency represents the number of occurrences of + * elements within the collection which return true when + * compared with the object using the equals method. + * + * @param c the collection to scan for occurrences of the object. + * @param o the object to locate occurrances of within the collection. + * @throws NullPointerException if the collection is null. + * @since 1.5 + */ + public static int frequency (Collection c, Object o) + { + int result = 0; + final Iterator it = c.iterator(); + while (it.hasNext()) + { + Object v = it.next(); + if (AbstractCollection.equals(o, v)) + ++result; + } + return result; + } + + /** + * Adds all the specified elements to the given collection, in a similar + * way to the addAll method of the Collection. + * However, this is a variable argument method which allows the new elements + * to be specified individually or in array form, as opposed to the list + * required by the collection's addAll method. This has + * benefits in both simplicity (multiple elements can be added without + * having to be wrapped inside a grouping structure) and efficiency + * (as a redundant list doesn't have to be created to add an individual + * set of elements or an array). + * + * @param c the collection to which the elements should be added. + * @param a the elements to be added to the collection. + * @return true if the collection changed its contents as a result. + * @throws UnsupportedOperationException if the collection does not support + * addition. + * @throws NullPointerException if one or more elements in a are null, + * and the collection does not allow null + * elements. This exception is also thrown + * if either c or a + * are null. + * @throws IllegalArgumentException if the collection won't allow an element + * to be added for some other reason. + * @since 1.5 + */ + public static boolean addAll(Collection c, T... a) + { + boolean overall = false; + + for (T element : a) + { + boolean result = c.add(element); + if (result) + overall = true; + } + return overall; + } + + /** + * Returns true if the two specified collections have no elements in + * common. This method may give unusual results if one or both collections + * use a non-standard equality test. In the trivial case of comparing + * a collection with itself, this method returns true if, and only if, + * the collection is empty. + * + * @param c1 the first collection to compare. + * @param c2 the second collection to compare. + * @return true if the collections are disjoint. + * @throws NullPointerException if either collection is null. + * @since 1.5 + */ + public static boolean disjoint(Collection c1, Collection c2) + { + Collection oc1 = (Collection) c1; + final Iterator it = oc1.iterator(); + while (it.hasNext()) + if (c2.contains(it.next())) + return false; + return true; + } + + + /** + * Obtain an immutable Set consisting of a single element. The return value + * of this method is Serializable. + * + * @param o the single element + * @return an immutable Set containing only o + * @see Serializable + */ + public static Set singleton(T o) + { + return new SingletonSet(o); + } + + /** + * The implementation of {@link #singleton(Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonSet extends AbstractSet + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3193687207550431679L; + + + /** + * The single element; package visible for use in nested class. + * @serial the singleton + */ + final T element; + + /** + * Construct a singleton. + * @param o the element + */ + SingletonSet(T o) + { + element = o; + } + + /** + * The size: always 1! + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Returns an iterator over the lone element. + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * Flag to indicate whether or not the element has + * been retrieved. + */ + private boolean hasNext = true; + + /** + * Returns true if elements still remain to be + * iterated through. + * + * @return true if the element has not yet been returned. + */ + public boolean hasNext() + { + return hasNext; + } + + /** + * Returns the element. + * + * @return The element used by this singleton. + * @throws NoSuchElementException if the object + * has already been retrieved. + */ + public T next() + { + if (hasNext) + { + hasNext = false; + return element; + } + else + throw new NoSuchElementException(); + } + + /** + * Removes the element from the singleton. + * As this set is immutable, this will always + * throw an exception. + * + * @throws UnsupportedOperationException as the + * singleton set doesn't support + * remove(). + */ + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractSet. + /** + * The set only contains one element. + * + * @param o The object to search for. + * @return true if o == the element of the singleton. + */ + public boolean contains(Object o) + { + return equals(o, element); + } + + /** + * This is true if the other collection only contains the element. + * + * @param c A collection to compare against this singleton. + * @return true if c only contains either no elements or + * elements equal to the element in this singleton. + */ + public boolean containsAll(Collection c) + { + Iterator i = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (! equals(i.next(), element)) + return false; + return true; + } + + /** + * The hash is just that of the element. + * + * @return The hashcode of the element. + */ + public int hashCode() + { + return hashCode(element); + } + + /** + * Returning an array is simple. + * + * @return An array containing the element. + */ + public Object[] toArray() + { + return new Object[] {element}; + } + + /** + * Obvious string. + * + * @return The string surrounded by enclosing + * square brackets. + */ + public String toString() + { + return "[" + element + "]"; + } + } // class SingletonSet + + /** + * Obtain an immutable List consisting of a single element. The return value + * of this method is Serializable, and implements RandomAccess. + * + * @param o the single element + * @return an immutable List containing only o + * @see Serializable + * @see RandomAccess + * @since 1.3 + */ + public static List singletonList(T o) + { + return new SingletonList(o); + } + + /** + * The implementation of {@link #singletonList(Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3093736618740652951L; + + /** + * The single element. + * @serial the singleton + */ + private final T element; + + /** + * Construct a singleton. + * @param o the element + */ + SingletonList(T o) + { + element = o; + } + + /** + * The size: always 1! + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Only index 0 is valid. + * @param index The index of the element + * to retrieve. + * @return The singleton's element if the + * index is 0. + * @throws IndexOutOfBoundsException if + * index is not 0. + */ + public T get(int index) + { + if (index == 0) + return element; + throw new IndexOutOfBoundsException(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * The set only contains one element. + * + * @param o The object to search for. + * @return true if o == the singleton element. + */ + public boolean contains(Object o) + { + return equals(o, element); + } + + /** + * This is true if the other collection only contains the element. + * + * @param c A collection to compare against this singleton. + * @return true if c only contains either no elements or + * elements equal to the element in this singleton. + */ + public boolean containsAll(Collection c) + { + Iterator i = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (! equals(i.next(), element)) + return false; + return true; + } + + /** + * Speed up the hashcode computation. + * + * @return The hashcode of the list, based + * on the hashcode of the singleton element. + */ + public int hashCode() + { + return 31 + hashCode(element); + } + + /** + * Either the list has it or not. + * + * @param o The object to find the first index of. + * @return 0 if o is the singleton element, -1 if not. + */ + public int indexOf(Object o) + { + return equals(o, element) ? 0 : -1; + } + + /** + * Either the list has it or not. + * + * @param o The object to find the last index of. + * @return 0 if o is the singleton element, -1 if not. + */ + public int lastIndexOf(Object o) + { + return equals(o, element) ? 0 : -1; + } + + /** + * Sublists are limited in scope. + * + * @param from The starting bound for the sublist. + * @param to The ending bound for the sublist. + * @return Either an empty list if both bounds are + * 0 or 1, or this list if the bounds are 0 and 1. + * @throws IllegalArgumentException if from > to + * @throws IndexOutOfBoundsException if either bound is greater + * than 1. + */ + public List subList(int from, int to) + { + if (from == to && (to == 0 || to == 1)) + return EMPTY_LIST; + if (from == 0 && to == 1) + return this; + if (from > to) + throw new IllegalArgumentException(); + throw new IndexOutOfBoundsException(); + } + + /** + * Returning an array is simple. + * + * @return An array containing the element. + */ + public Object[] toArray() + { + return new Object[] {element}; + } + + /** + * Obvious string. + * + * @return The string surrounded by enclosing + * square brackets. + */ + public String toString() + { + return "[" + element + "]"; + } + } // class SingletonList + + /** + * Obtain an immutable Map consisting of a single key-value pair. + * The return value of this method is Serializable. + * + * @param key the single key + * @param value the single value + * @return an immutable Map containing only the single key-value pair + * @see Serializable + * @since 1.3 + */ + public static Map singletonMap(K key, V value) + { + return new SingletonMap(key, value); + } + + /** + * The implementation of {@link #singletonMap(Object, Object)}. This class + * name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonMap extends AbstractMap + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -6979724477215052911L; + + /** + * The single key. + * @serial the singleton key + */ + private final K k; + + /** + * The corresponding value. + * @serial the singleton value + */ + private final V v; + + /** + * Cache the entry set. + */ + private transient Set> entries; + + /** + * Construct a singleton. + * @param key the key + * @param value the value + */ + SingletonMap(K key, V value) + { + k = key; + v = value; + } + + /** + * There is a single immutable entry. + * + * @return A singleton containing the map entry. + */ + public Set> entrySet() + { + if (entries == null) + { + Map.Entry entry = new AbstractMap.SimpleEntry(k, v) + { + /** + * Sets the value of the map entry to the supplied value. + * An exception is always thrown, as the map is immutable. + * + * @param o The new value. + * @return The old value. + * @throws UnsupportedOperationException as setting the value + * is not supported. + */ + public V setValue(V o) + { + throw new UnsupportedOperationException(); + } + }; + entries = singleton(entry); + } + return entries; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractMap. + /** + * Single entry. + * + * @param key The key to look for. + * @return true if the key is the same as the one used by + * this map. + */ + public boolean containsKey(Object key) + { + return equals(key, k); + } + + /** + * Single entry. + * + * @param value The value to look for. + * @return true if the value is the same as the one used by + * this map. + */ + public boolean containsValue(Object value) + { + return equals(value, v); + } + + /** + * Single entry. + * + * @param key The key of the value to be retrieved. + * @return The singleton value if the key is the same as the + * singleton key, null otherwise. + */ + public V get(Object key) + { + return equals(key, k) ? v : null; + } + + /** + * Calculate the hashcode directly. + * + * @return The hashcode computed from the singleton key + * and the singleton value. + */ + public int hashCode() + { + return hashCode(k) ^ hashCode(v); + } + + /** + * Return the keyset. + * + * @return A singleton containing the key. + */ + public Set keySet() + { + if (keys == null) + keys = singleton(k); + return keys; + } + + /** + * The size: always 1! + * + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Return the values. Technically, a singleton, while more specific than + * a general Collection, will work. Besides, that's what the JDK uses! + * + * @return A singleton containing the value. + */ + public Collection values() + { + if (values == null) + values = singleton(v); + return values; + } + + /** + * Obvious string. + * + * @return A string containing the string representations of the key + * and its associated value. + */ + public String toString() + { + return "{" + k + "=" + v + "}"; + } + } // class SingletonMap + + /** + * Sort a list according to the natural ordering of its elements. The list + * must be modifiable, but can be of fixed size. The sort algorithm is + * precisely that used by Arrays.sort(Object[]), which offers guaranteed + * nlog(n) performance. This implementation dumps the list into an array, + * sorts the array, and then iterates over the list setting each element from + * the array. + * + * @param l the List to sort (null not permitted) + * @throws ClassCastException if some items are not mutually comparable + * @throws UnsupportedOperationException if the List is not modifiable + * @throws NullPointerException if the list is null, or contains + * some element that is null. + * @see Arrays#sort(Object[]) + */ + public static > void sort(List l) + { + sort(l, null); + } + + /** + * Sort a list according to a specified Comparator. The list must be + * modifiable, but can be of fixed size. The sort algorithm is precisely that + * used by Arrays.sort(Object[], Comparator), which offers guaranteed + * nlog(n) performance. This implementation dumps the list into an array, + * sorts the array, and then iterates over the list setting each element from + * the array. + * + * @param l the List to sort (null not permitted) + * @param c the Comparator specifying the ordering for the elements, or + * null for natural ordering + * @throws ClassCastException if c will not compare some pair of items + * @throws UnsupportedOperationException if the List is not modifiable + * @throws NullPointerException if the List is null or + * null is compared by natural ordering (only possible + * when c is null) + * + * @see Arrays#sort(Object[], Comparator) + */ + public static void sort(List l, Comparator c) + { + T[] a = (T[]) l.toArray(); + Arrays.sort(a, c); + ListIterator i = l.listIterator(); + for (int pos = 0, alen = a.length; pos < alen; pos++) + { + i.next(); + i.set(a[pos]); + } + } + + /** + * Swaps the elements at the specified positions within the list. Equal + * positions have no effect. + * + * @param l the list to work on + * @param i the first index to swap + * @param j the second index + * @throws UnsupportedOperationException if list.set is not supported + * @throws IndexOutOfBoundsException if either i or j is < 0 or >= + * list.size() + * @since 1.4 + */ + public static void swap(List l, int i, int j) + { + List list = (List) l; + list.set(i, list.set(j, list.get(i))); + } + + + /** + * Returns a synchronized (thread-safe) collection wrapper backed by the + * given collection. Notice that element access through the iterators + * is thread-safe, but if the collection can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:
        + *
        +   * Collection c = Collections.synchronizedCollection(new Collection(...));
        +   * ...
        +   * synchronized (c)
        +   *   {
        +   *     Iterator i = c.iterator();
        +   *     while (i.hasNext())
        +   *       foo(i.next());
        +   *   }
        +   * 

        + * + * Since the collection might be a List or a Set, and those have incompatible + * equals and hashCode requirements, this relies on Object's implementation + * rather than passing those calls on to the wrapped collection. The returned + * Collection implements Serializable, but can only be serialized if + * the collection it wraps is likewise Serializable. + * + * @param c the collection to wrap + * @return a synchronized view of the collection + * @see Serializable + */ + public static Collection synchronizedCollection(Collection c) + { + return new SynchronizedCollection(c); + } + + /** + * The implementation of {@link #synchronizedCollection(Collection)}. This + * class name is required for compatibility with Sun's JDK serializability. + * Package visible, so that collections such as the one for + * Hashtable.values() can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedCollection + implements Collection, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3053995032091335093L; + + /** + * The wrapped collection. Package visible for use by subclasses. + * @serial the real collection + */ + final Collection c; + + /** + * The object to synchronize on. When an instance is created via public + * methods, it will be this; but other uses like SynchronizedMap.values() + * must specify another mutex. Package visible for use by subclasses. + * @serial the lock + */ + final Object mutex; + + /** + * Wrap a given collection. + * @param c the collection to wrap + * @throws NullPointerException if c is null + */ + SynchronizedCollection(Collection c) + { + this.c = c; + mutex = this; + if (c == null) + throw new NullPointerException(); + } + + /** + * Called only by trusted code to specify the mutex as well as the + * collection. + * @param sync the mutex + * @param c the collection + */ + SynchronizedCollection(Object sync, Collection c) + { + this.c = c; + mutex = sync; + } + + /** + * Adds the object to the underlying collection, first + * obtaining a lock on the mutex. + * + * @param o The object to add. + * @return true if the collection was modified as a result + * of this action. + * @throws UnsupportedOperationException if this collection does not + * support the add operation. + * @throws ClassCastException if o cannot be added to this collection due + * to its type. + * @throws NullPointerException if o is null and this collection doesn't + * support the addition of null values. + * @throws IllegalArgumentException if o cannot be added to this + * collection for some other reason. + */ + public boolean add(T o) + { + synchronized (mutex) + { + return c.add(o); + } + } + + /** + * Adds the objects in col to the underlying collection, first + * obtaining a lock on the mutex. + * + * @param col The collection to take the new objects from. + * @return true if the collection was modified as a result + * of this action. + * @throws UnsupportedOperationException if this collection does not + * support the addAll operation. + * @throws ClassCastException if some element of col cannot be added to this + * collection due to its type. + * @throws NullPointerException if some element of col is null and this + * collection does not support the addition of null values. + * @throws NullPointerException if col itself is null. + * @throws IllegalArgumentException if some element of col cannot be added + * to this collection for some other reason. + */ + public boolean addAll(Collection col) + { + synchronized (mutex) + { + return c.addAll(col); + } + } + + /** + * Removes all objects from the underlying collection, + * first obtaining a lock on the mutex. + * + * @throws UnsupportedOperationException if this collection does not + * support the clear operation. + */ + public void clear() + { + synchronized (mutex) + { + c.clear(); + } + } + + /** + * Checks for the existence of o within the underlying + * collection, first obtaining a lock on the mutex. + * + * @param o the element to look for. + * @return true if this collection contains at least one + * element e such that o == null ? e == null : o.equals(e). + * @throws ClassCastException if the type of o is not a valid type for this + * collection. + * @throws NullPointerException if o is null and this collection doesn't + * support null values. + */ + public boolean contains(Object o) + { + synchronized (mutex) + { + return c.contains(o); + } + } + + /** + * Checks for the existence of each object in cl + * within the underlying collection, first obtaining + * a lock on the mutex. + * + * @param c1 the collection to test for. + * @return true if for every element o in c, contains(o) + * would return true. + * @throws ClassCastException if the type of any element in cl is not a valid + * type for this collection. + * @throws NullPointerException if some element of cl is null and this + * collection does not support null values. + * @throws NullPointerException if cl itself is null. + */ + public boolean containsAll(Collection c1) + { + synchronized (mutex) + { + return c.containsAll(c1); + } + } + + /** + * Returns true if there are no objects in the underlying + * collection. A lock on the mutex is obtained before the + * check is performed. + * + * @return true if this collection contains no elements. + */ + public boolean isEmpty() + { + synchronized (mutex) + { + return c.isEmpty(); + } + } + + /** + * Returns a synchronized iterator wrapper around the underlying + * collection's iterator. A lock on the mutex is obtained before + * retrieving the collection's iterator. + * + * @return An iterator over the elements in the underlying collection, + * which returns each element in any order. + */ + public Iterator iterator() + { + synchronized (mutex) + { + return new SynchronizedIterator(mutex, c.iterator()); + } + } + + /** + * Removes the specified object from the underlying collection, + * first obtaining a lock on the mutex. + * + * @param o The object to remove. + * @return true if the collection changed as a result of this call, that is, + * if the collection contained at least one occurrence of o. + * @throws UnsupportedOperationException if this collection does not + * support the remove operation. + * @throws ClassCastException if the type of o is not a valid type + * for this collection. + * @throws NullPointerException if o is null and the collection doesn't + * support null values. + */ + public boolean remove(Object o) + { + synchronized (mutex) + { + return c.remove(o); + } + } + + /** + * Removes all elements, e, of the underlying + * collection for which col.contains(e) + * returns true. A lock on the mutex is obtained + * before the operation proceeds. + * + * @param col The collection of objects to be removed. + * @return true if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the removeAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support removing null values. + * @throws NullPointerException if c itself is null. + */ + public boolean removeAll(Collection col) + { + synchronized (mutex) + { + return c.removeAll(col); + } + } + + /** + * Retains all elements, e, of the underlying + * collection for which col.contains(e) + * returns true. That is, every element that doesn't + * exist in col is removed. A lock on the mutex is obtained + * before the operation proceeds. + * + * @param col The collection of objects to be removed. + * @return true if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the removeAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support removing null values. + * @throws NullPointerException if c itself is null. + */ + public boolean retainAll(Collection col) + { + synchronized (mutex) + { + return c.retainAll(col); + } + } + + /** + * Retrieves the size of the underlying collection. + * A lock on the mutex is obtained before the collection + * is accessed. + * + * @return The size of the collection. + */ + public int size() + { + synchronized (mutex) + { + return c.size(); + } + } + + /** + * Returns an array containing each object within the underlying + * collection. A lock is obtained on the mutex before the collection + * is accessed. + * + * @return An array of objects, matching the collection in size. The + * elements occur in any order. + */ + public Object[] toArray() + { + synchronized (mutex) + { + return c.toArray(); + } + } + + /** + * Copies the elements in the underlying collection to the supplied + * array. If a.length < size(), a new array of the + * same run-time type is created, with a size equal to that of + * the collection. If a.length > size(), then the + * elements from 0 to size() - 1 contain the elements + * from this collection. The following element is set to null + * to indicate the end of the collection objects. However, this + * only makes a difference if null is not a permitted value within + * the collection. + * Before the copying takes place, a lock is obtained on the mutex. + * + * @param a An array to copy elements to. + * @return An array containing the elements of the underlying collection. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + public T[] toArray(T[] a) + { + synchronized (mutex) + { + return c.toArray(a); + } + } + + /** + * Returns a string representation of the underlying collection. + * A lock is obtained on the mutex before the string is created. + * + * @return A string representation of the collection. + */ + public String toString() + { + synchronized (mutex) + { + return c.toString(); + } + } + } // class SynchronizedCollection + + /** + * The implementation of the various iterator methods in the + * synchronized classes. These iterators must "sync" on the same object + * as the collection they iterate over. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SynchronizedIterator implements Iterator + { + /** + * The object to synchronize on. Package visible for use by subclass. + */ + final Object mutex; + + /** + * The wrapped iterator. + */ + private final Iterator i; + + /** + * Only trusted code creates a wrapper, with the specified sync. + * @param sync the mutex + * @param i the wrapped iterator + */ + SynchronizedIterator(Object sync, Iterator i) + { + this.i = i; + mutex = sync; + } + + /** + * Retrieves the next object in the underlying collection. + * A lock is obtained on the mutex before the collection is accessed. + * + * @return The next object in the collection. + * @throws NoSuchElementException if there are no more elements + */ + public T next() + { + synchronized (mutex) + { + return i.next(); + } + } + + /** + * Returns true if objects can still be retrieved from the iterator + * using next(). A lock is obtained on the mutex before + * the collection is accessed. + * + * @return true if at least one element is still to be returned by + * next(). + */ + public boolean hasNext() + { + synchronized (mutex) + { + return i.hasNext(); + } + } + + /** + * Removes the object that was last returned by next() + * from the underlying collection. Only one call to this method is + * allowed per call to the next() method, and it does + * not affect the value that will be returned by next(). + * Thus, if element n was retrieved from the collection by + * next(), it is this element that gets removed. + * Regardless of whether this takes place or not, element n+1 is + * still returned on the subsequent next() call. + * + * @throws IllegalStateException if next has not yet been called or remove + * has already been called since the last call to next. + * @throws UnsupportedOperationException if this Iterator does not support + * the remove operation. + */ + public void remove() + { + synchronized (mutex) + { + i.remove(); + } + } + } // class SynchronizedIterator + + /** + * Returns a synchronized (thread-safe) list wrapper backed by the + * given list. Notice that element access through the iterators + * is thread-safe, but if the list can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:
        + *

        +   * List l = Collections.synchronizedList(new List(...));
        +   * ...
        +   * synchronized (l)
        +   *   {
        +   *     Iterator i = l.iterator();
        +   *     while (i.hasNext())
        +   *       foo(i.next());
        +   *   }
        +   * 

        + * + * The returned List implements Serializable, but can only be serialized if + * the list it wraps is likewise Serializable. In addition, if the wrapped + * list implements RandomAccess, this does too. + * + * @param l the list to wrap + * @return a synchronized view of the list + * @see Serializable + * @see RandomAccess + */ + public static List synchronizedList(List l) + { + if (l instanceof RandomAccess) + return new SynchronizedRandomAccessList(l); + return new SynchronizedList(l); + } + + /** + * The implementation of {@link #synchronizedList(List)} for sequential + * lists. This class name is required for compatibility with Sun's JDK + * serializability. Package visible, so that lists such as Vector.subList() + * can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedList extends SynchronizedCollection + implements List + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -7754090372962971524L; + + /** + * The wrapped list; stored both here and in the superclass to avoid + * excessive casting. Package visible for use by subclass. + * @serial the wrapped list + */ + final List list; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + SynchronizedList(List l) + { + super(l); + list = l; + } + + /** + * Called only by trusted code to specify the mutex as well as the list. + * @param sync the mutex + * @param l the list + */ + SynchronizedList(Object sync, List l) + { + super(sync, l); + list = l; + } + + /** + * Insert an element into the underlying list at a given position (optional + * operation). This shifts all existing elements from that position to the + * end one index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. Before the + * addition takes place, a lock is obtained on the mutex. + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + public void add(int index, T o) + { + synchronized (mutex) + { + list.add(index, o); + } + } + + /** + * Add the contents of a collection to the underlying list at the given + * index (optional operation). If the list imposes restraints on what + * can be inserted, such as no null elements, this should be documented. + * A lock is obtained on the mutex before any of the elements are added. + * + * @param index the index at which to insert + * @param c the collection to add + * @return true, as defined by Collection for a modified list + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + public boolean addAll(int index, Collection c) + { + synchronized (mutex) + { + return list.addAll(index, c); + } + } + + /** + * Tests whether the underlying list is equal to the supplied object. + * The object is deemed to be equal if it is also a List + * of equal size and with the same elements (i.e. each element, e1, + * in list, l1, and each element, e2, in l2, must return true for + * e1 == null ? e2 == null : e1.equals(e2). Before the + * comparison is made, a lock is obtained on the mutex. + * + * @param o The object to test for equality with the underlying list. + * @return true if o is equal to the underlying list under the above + * definition. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return list.equals(o); + } + } + + /** + * Retrieves the object at the specified index. A lock + * is obtained on the mutex before the list is accessed. + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public T get(int index) + { + synchronized (mutex) + { + return list.get(index); + } + } + + /** + * Obtains a hashcode for the underlying list, first obtaining + * a lock on the mutex. The calculation of the hashcode is + * detailed in the documentation for the List + * interface. + * + * @return The hashcode of the underlying list. + * @see List#hashCode() + */ + public int hashCode() + { + synchronized (mutex) + { + return list.hashCode(); + } + } + + /** + * Obtain the first index at which a given object is to be found in the + * underlying list. A lock is obtained on the mutex before the list is + * accessed. + * + * @param o the object to search for + * @return the least integer n such that o == null ? get(n) == null : + * o.equals(get(n)), or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + + public int indexOf(Object o) + { + synchronized (mutex) + { + return list.indexOf(o); + } + } + + /** + * Obtain the last index at which a given object is to be found in this + * underlying list. A lock is obtained on the mutex before the list + * is accessed. + * + * @return the greatest integer n such that o == null ? get(n) == null + * : o.equals(get(n)), or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + public int lastIndexOf(Object o) + { + synchronized (mutex) + { + return list.lastIndexOf(o); + } + } + + /** + * Retrieves a synchronized wrapper around the underlying list's + * list iterator. A lock is obtained on the mutex before the + * list iterator is retrieved. + * + * @return A list iterator over the elements in the underlying list. + * The list iterator allows additional list-specific operations + * to be performed, in addition to those supplied by the + * standard iterator. + */ + public ListIterator listIterator() + { + synchronized (mutex) + { + return new SynchronizedListIterator(mutex, list.listIterator()); + } + } + + /** + * Retrieves a synchronized wrapper around the underlying list's + * list iterator. A lock is obtained on the mutex before the + * list iterator is retrieved. The iterator starts at the + * index supplied, leading to the element at that index being + * the first one returned by next(). Calling + * previous() from this initial position returns + * index - 1. + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from + * @return A list iterator over the elements in the underlying list. + * The list iterator allows additional list-specific operations + * to be performed, in addition to those supplied by the + * standard iterator. + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + synchronized (mutex) + { + return new SynchronizedListIterator(mutex, + list.listIterator(index)); + } + } + + /** + * Remove the element at a given position in the underlying list (optional + * operation). All remaining elements are shifted to the left to fill the gap. + * A lock on the mutex is obtained before the element is removed. + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public T remove(int index) + { + synchronized (mutex) + { + return list.remove(index); + } + } + + /** + * Replace an element of the underlying list with another object (optional + * operation). A lock is obtained on the mutex before the element is + * replaced. + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation. + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + public T set(int index, T o) + { + synchronized (mutex) + { + return list.set(index, o); + } + } + + /** + * Obtain a List view of a subsection of the underlying list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. A lock is obtained + * on the mutex before the creation of the sublist. The returned list + * is also synchronized, using the same mutex. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex + */ + public List subList(int fromIndex, int toIndex) + { + synchronized (mutex) + { + return new SynchronizedList(mutex, + list.subList(fromIndex, toIndex)); + } + } + } // class SynchronizedList + + /** + * The implementation of {@link #synchronizedList(List)} for random-access + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedRandomAccessList + extends SynchronizedList implements RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1530674583602358482L; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + SynchronizedRandomAccessList(List l) + { + super(l); + } + + /** + * Called only by trusted code to specify the mutex as well as the + * collection. + * @param sync the mutex + * @param l the list + */ + SynchronizedRandomAccessList(Object sync, List l) + { + super(sync, l); + } + + /** + * Obtain a List view of a subsection of the underlying list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. A lock is obtained + * on the mutex before the creation of the sublist. The returned list + * is also synchronized, using the same mutex. Random accessibility + * is also extended to the new list. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex + */ + public List subList(int fromIndex, int toIndex) + { + synchronized (mutex) + { + return new SynchronizedRandomAccessList(mutex, + list.subList(fromIndex, + toIndex)); + } + } + } // class SynchronizedRandomAccessList + + /** + * The implementation of {@link SynchronizedList#listIterator()}. This + * iterator must "sync" on the same object as the list it iterates over. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedListIterator + extends SynchronizedIterator implements ListIterator + { + /** + * The wrapped iterator, stored both here and in the superclass to + * avoid excessive casting. + */ + private final ListIterator li; + + /** + * Only trusted code creates a wrapper, with the specified sync. + * @param sync the mutex + * @param li the wrapped iterator + */ + SynchronizedListIterator(Object sync, ListIterator li) + { + super(sync, li); + this.li = li; + } + + /** + * Insert an element into the underlying list at the current position of + * the iterator (optional operation). The element is inserted in between + * the element that would be returned by previous() and the + * element that would be returned by next(). After the + * insertion, a subsequent call to next is unaffected, but + * a call to previous returns the item that was added. The values returned + * by nextIndex() and previousIndex() are incremented. A lock is obtained + * on the mutex before the addition takes place. + * + * @param o the object to insert into the list + * @throws ClassCastException if the object is of a type which cannot be added + * to this list. + * @throws IllegalArgumentException if some other aspect of the object stops + * it being added to this list. + * @throws UnsupportedOperationException if this ListIterator does not + * support the add operation. + */ + public void add(T o) + { + synchronized (mutex) + { + li.add(o); + } + } + + /** + * Tests whether there are elements remaining in the underlying list + * in the reverse direction. In other words, previous() + * will not fail with a NoSuchElementException. A lock is obtained + * on the mutex before the check takes place. + * + * @return true if the list continues in the reverse direction + */ + public boolean hasPrevious() + { + synchronized (mutex) + { + return li.hasPrevious(); + } + } + + /** + * Find the index of the element that would be returned by a call to + * next(). If hasNext() returns false, this + * returns the list size. A lock is obtained on the mutex before the + * query takes place. + * + * @return the index of the element that would be returned by next() + */ + public int nextIndex() + { + synchronized (mutex) + { + return li.nextIndex(); + } + } + + /** + * Obtain the previous element from the underlying list. Repeated + * calls to previous may be used to iterate backwards over the entire list, + * or calls to next and previous may be used together to go forwards and + * backwards. Alternating calls to next and previous will return the same + * element. A lock is obtained on the mutex before the object is retrieved. + * + * @return the next element in the list in the reverse direction + * @throws NoSuchElementException if there are no more elements + */ + public T previous() + { + synchronized (mutex) + { + return li.previous(); + } + } + + /** + * Find the index of the element that would be returned by a call to + * previous. If hasPrevious() returns false, this returns -1. + * A lock is obtained on the mutex before the query takes place. + * + * @return the index of the element that would be returned by previous() + */ + public int previousIndex() + { + synchronized (mutex) + { + return li.previousIndex(); + } + } + + /** + * Replace the element last returned by a call to next() or + * previous() with a given object (optional operation). This + * method may only be called if neither add() nor + * remove() have been called since the last call to + * next() or previous. A lock is obtained + * on the mutex before the list is modified. + * + * @param o the object to replace the element with + * @throws ClassCastException the object is of a type which cannot be added + * to this list + * @throws IllegalArgumentException some other aspect of the object stops + * it being added to this list + * @throws IllegalStateException if neither next or previous have been + * called, or if add or remove has been called since the last call + * to next or previous + * @throws UnsupportedOperationException if this ListIterator does not + * support the set operation + */ + public void set(T o) + { + synchronized (mutex) + { + li.set(o); + } + } + } // class SynchronizedListIterator + + /** + * Returns a synchronized (thread-safe) map wrapper backed by the given + * map. Notice that element access through the collection views and their + * iterators are thread-safe, but if the map can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:
        + *

        +   * Map m = Collections.synchronizedMap(new Map(...));
        +   * ...
        +   * Set s = m.keySet(); // safe outside a synchronized block
        +   * synchronized (m) // synch on m, not s
        +   *   {
        +   *     Iterator i = s.iterator();
        +   *     while (i.hasNext())
        +   *       foo(i.next());
        +   *   }
        +   * 

        + * + * The returned Map implements Serializable, but can only be serialized if + * the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a synchronized view of the map + * @see Serializable + */ + public static Map synchronizedMap(Map m) + { + return new SynchronizedMap(m); + } + + /** + * The implementation of {@link #synchronizedMap(Map)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SynchronizedMap implements Map, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1978198479659022715L; + + /** + * The wrapped map. + * @serial the real map + */ + private final Map m; + + /** + * The object to synchronize on. When an instance is created via public + * methods, it will be this; but other uses like + * SynchronizedSortedMap.subMap() must specify another mutex. Package + * visible for use by subclass. + * @serial the lock + */ + final Object mutex; + + /** + * Cache the entry set. + */ + private transient Set> entries; + + /** + * Cache the key set. + */ + private transient Set keys; + + /** + * Cache the value collection. + */ + private transient Collection values; + + /** + * Wrap a given map. + * @param m the map to wrap + * @throws NullPointerException if m is null + */ + SynchronizedMap(Map m) + { + this.m = m; + mutex = this; + if (m == null) + throw new NullPointerException(); + } + + /** + * Called only by trusted code to specify the mutex as well as the map. + * @param sync the mutex + * @param m the map + */ + SynchronizedMap(Object sync, Map m) + { + this.m = m; + mutex = sync; + } + + /** + * Clears all the entries from the underlying map. A lock is obtained + * on the mutex before the map is cleared. + * + * @throws UnsupportedOperationException if clear is not supported + */ + public void clear() + { + synchronized (mutex) + { + m.clear(); + } + } + + /** + * Returns true if the underlying map contains a entry for the given key. + * A lock is obtained on the mutex before the map is queried. + * + * @param key the key to search for. + * @return true if the underlying map contains the key. + * @throws ClassCastException if the key is of an inappropriate type. + * @throws NullPointerException if key is null but the map + * does not permit null keys. + */ + public boolean containsKey(Object key) + { + synchronized (mutex) + { + return m.containsKey(key); + } + } + + /** + * Returns true if the underlying map contains at least one entry with the + * given value. In other words, returns true if a value v exists where + * (value == null ? v == null : value.equals(v)). This usually + * requires linear time. A lock is obtained on the mutex before the map + * is queried. + * + * @param value the value to search for + * @return true if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + public boolean containsValue(Object value) + { + synchronized (mutex) + { + return m.containsValue(value); + } + } + + // This is one of the ickiest cases of nesting I've ever seen. It just + // means "return a SynchronizedSet, except that the iterator() method + // returns an SynchronizedIterator whose next() method returns a + // synchronized wrapper around its normal return value". + public Set> entrySet() + { + // Define this here to spare some nesting. + class SynchronizedMapEntry implements Map.Entry + { + final Map.Entry e; + SynchronizedMapEntry(Map.Entry o) + { + e = o; + } + + /** + * Returns true if the object, o, implements Map.Entry + * with the same key and value as the underlying entry. A lock is + * obtained on the mutex before the comparison takes place. + * + * @param o The object to compare with this entry. + * @return true if o is equivalent to the underlying map entry. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return e.equals(o); + } + } + + /** + * Returns the key used in the underlying map entry. A lock is obtained + * on the mutex before the key is retrieved. + * + * @return The key of the underlying map entry. + */ + public K getKey() + { + synchronized (mutex) + { + return e.getKey(); + } + } + + /** + * Returns the value used in the underlying map entry. A lock is obtained + * on the mutex before the value is retrieved. + * + * @return The value of the underlying map entry. + */ + public V getValue() + { + synchronized (mutex) + { + return e.getValue(); + } + } + + /** + * Computes the hash code for the underlying map entry. + * This computation is described in the documentation for the + * Map interface. A lock is obtained on the mutex + * before the underlying map is accessed. + * + * @return The hash code of the underlying map entry. + * @see Map#hashCode() + */ + public int hashCode() + { + synchronized (mutex) + { + return e.hashCode(); + } + } + + /** + * Replaces the value in the underlying map entry with the specified + * object (optional operation). A lock is obtained on the mutex + * before the map is altered. The map entry, in turn, will alter + * the underlying map object. The operation is undefined if the + * remove() method of the iterator has been called + * beforehand. + * + * @param value the new value to store + * @return the old value + * @throws UnsupportedOperationException if the operation is not supported. + * @throws ClassCastException if the value is of the wrong type. + * @throws IllegalArgumentException if something about the value + * prevents it from existing in this map. + * @throws NullPointerException if the map forbids null values. + */ + public V setValue(V value) + { + synchronized (mutex) + { + return e.setValue(value); + } + } + + /** + * Returns a textual representation of the underlying map entry. + * A lock is obtained on the mutex before the entry is accessed. + * + * @return The contents of the map entry in String form. + */ + public String toString() + { + synchronized (mutex) + { + return e.toString(); + } + } + } // class SynchronizedMapEntry + + // Now the actual code. + if (entries == null) + synchronized (mutex) + { + entries = new SynchronizedSet>(mutex, m.entrySet()) + { + /** + * Returns an iterator over the set. The iterator has no specific order, + * unless further specified. A lock is obtained on the set's mutex + * before the iterator is created. The created iterator is also + * thread-safe. + * + * @return A synchronized set iterator. + */ + public Iterator> iterator() + { + synchronized (super.mutex) + { + return new SynchronizedIterator>(super.mutex, + c.iterator()) + { + /** + * Retrieves the next map entry from the iterator. + * A lock is obtained on the iterator's mutex before + * the entry is created. The new map entry is enclosed in + * a thread-safe wrapper. + * + * @return A synchronized map entry. + */ + public Map.Entry next() + { + synchronized (super.mutex) + { + return new SynchronizedMapEntry(super.next()); + } + } + }; + } + } + }; + } + return entries; + } + + /** + * Returns true if the object, o, is also an instance + * of Map and contains an equivalent + * entry set to that of the underlying map. A lock + * is obtained on the mutex before the objects are + * compared. + * + * @param o The object to compare. + * @return true if o and the underlying map are equivalent. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return m.equals(o); + } + } + + /** + * Returns the value associated with the given key, or null + * if no such mapping exists. An ambiguity exists with maps + * that accept null values as a return value of null could + * be due to a non-existent mapping or simply a null value + * for that key. To resolve this, containsKey + * should be used. A lock is obtained on the mutex before + * the value is retrieved from the underlying map. + * + * @param key The key of the required mapping. + * @return The value associated with the given key, or + * null if no such mapping exists. + * @throws ClassCastException if the key is an inappropriate type. + * @throws NullPointerException if this map does not accept null keys. + */ + public V get(Object key) + { + synchronized (mutex) + { + return m.get(key); + } + } + + /** + * Calculates the hash code of the underlying map as the + * sum of the hash codes of all entries. A lock is obtained + * on the mutex before the hash code is computed. + * + * @return The hash code of the underlying map. + */ + public int hashCode() + { + synchronized (mutex) + { + return m.hashCode(); + } + } + + /** + * Returns true if the underlying map contains no entries. + * A lock is obtained on the mutex before the map is examined. + * + * @return true if the map is empty. + */ + public boolean isEmpty() + { + synchronized (mutex) + { + return m.isEmpty(); + } + } + + /** + * Returns a thread-safe set view of the keys in the underlying map. The + * set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. If the set supports removal, these methods remove the + * underlying mapping from the map: Iterator.remove, + * Set.remove, removeAll, retainAll, + * and clear. Element addition, via add or + * addAll, is not supported via this set. A lock is obtained + * on the mutex before the set is created. + * + * @return A synchronized set containing the keys of the underlying map. + */ + public Set keySet() + { + if (keys == null) + synchronized (mutex) + { + keys = new SynchronizedSet(mutex, m.keySet()); + } + return keys; + } + + /** + * Associates the given key to the given value (optional operation). If the + * underlying map already contains the key, its value is replaced. Be aware + * that in a map that permits null values, a null return does not + * always imply that the mapping was created. A lock is obtained on the mutex + * before the modification is made. + * + * @param key the key to map. + * @param value the value to be mapped. + * @return the previous value of the key, or null if there was no mapping + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the key or value is of the wrong type + * @throws IllegalArgumentException if something about this key or value + * prevents it from existing in this map + * @throws NullPointerException if either the key or the value is null, + * and the map forbids null keys or values + * @see #containsKey(Object) + */ + public V put(K key, V value) + { + synchronized (mutex) + { + return m.put(key, value); + } + } + + /** + * Copies all entries of the given map to the underlying one (optional + * operation). If the map already contains a key, its value is replaced. + * A lock is obtained on the mutex before the operation proceeds. + * + * @param map the mapping to load into this map + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if a key or value is of the wrong type + * @throws IllegalArgumentException if something about a key or value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null keys or values, or + * if m is null. + * @see #put(Object, Object) + */ + public void putAll(Map map) + { + synchronized (mutex) + { + m.putAll(map); + } + } + + /** + * Removes the mapping for the key, o, if present (optional operation). If + * the key is not present, this returns null. Note that maps which permit + * null values may also return null if the key was removed. A prior + * containsKey() check is required to avoid this ambiguity. + * Before the mapping is removed, a lock is obtained on the mutex. + * + * @param o the key to remove + * @return the value the key mapped to, or null if not present + * @throws UnsupportedOperationException if deletion is unsupported + * @throws NullPointerException if the key is null and this map doesn't + * support null keys. + * @throws ClassCastException if the type of the key is not a valid type + * for this map. + */ + public V remove(Object o) + { + synchronized (mutex) + { + return m.remove(o); + } + } + + /** + * Retrieves the size of the underlying map. A lock + * is obtained on the mutex before access takes place. + * Maps with a size greater than Integer.MAX_VALUE + * return Integer.MAX_VALUE instead. + * + * @return The size of the underlying map. + */ + public int size() + { + synchronized (mutex) + { + return m.size(); + } + } + + /** + * Returns a textual representation of the underlying + * map. A lock is obtained on the mutex before the map + * is accessed. + * + * @return The map in String form. + */ + public String toString() + { + synchronized (mutex) + { + return m.toString(); + } + } + + /** + * Returns a synchronized collection view of the values in the underlying + * map. The collection is backed by the map, so that changes in one show up in + * the other. Modifications made while an iterator is in progress cause + * undefined behavior. If the collection supports removal, these methods + * remove the underlying mapping from the map: Iterator.remove, + * Collection.remove, removeAll, + * retainAll, and clear. Element addition, via + * add or addAll, is not supported via this + * collection. A lock is obtained on the mutex before the collection + * is created. + * + * @return the collection of all values in the underlying map. + */ + public Collection values() + { + if (values == null) + synchronized (mutex) + { + values = new SynchronizedCollection(mutex, m.values()); + } + return values; + } + } // class SynchronizedMap + + /** + * Returns a synchronized (thread-safe) set wrapper backed by the given + * set. Notice that element access through the iterator is thread-safe, but + * if the set can be structurally modified (adding or removing elements) + * then you should synchronize around the iteration to avoid + * non-deterministic behavior:
        + *

        +   * Set s = Collections.synchronizedSet(new Set(...));
        +   * ...
        +   * synchronized (s)
        +   *   {
        +   *     Iterator i = s.iterator();
        +   *     while (i.hasNext())
        +   *       foo(i.next());
        +   *   }
        +   * 

        + * + * The returned Set implements Serializable, but can only be serialized if + * the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a synchronized view of the set + * @see Serializable + */ + public static Set synchronizedSet(Set s) + { + return new SynchronizedSet(s); + } + + /** + * The implementation of {@link #synchronizedSet(Set)}. This class + * name is required for compatibility with Sun's JDK serializability. + * Package visible, so that sets such as Hashtable.keySet() + * can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedSet extends SynchronizedCollection + implements Set + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 487447009682186044L; + + /** + * Wrap a given set. + * @param s the set to wrap + * @throws NullPointerException if s is null + */ + SynchronizedSet(Set s) + { + super(s); + } + + /** + * Called only by trusted code to specify the mutex as well as the set. + * @param sync the mutex + * @param s the set + */ + SynchronizedSet(Object sync, Set s) + { + super(sync, s); + } + + /** + * Returns true if the object, o, is a Set + * of the same size as the underlying set, and contains + * each element, e, which occurs in the underlying set. + * A lock is obtained on the mutex before the comparison + * takes place. + * + * @param o The object to compare against. + * @return true if o is an equivalent set. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return c.equals(o); + } + } + + /** + * Computes the hash code for the underlying set as the + * sum of the hash code of all elements within the set. + * A lock is obtained on the mutex before the computation + * occurs. + * + * @return The hash code for the underlying set. + */ + public int hashCode() + { + synchronized (mutex) + { + return c.hashCode(); + } + } + } // class SynchronizedSet + + /** + * Returns a synchronized (thread-safe) sorted map wrapper backed by the + * given map. Notice that element access through the collection views, + * subviews, and their iterators are thread-safe, but if the map can be + * structurally modified (adding or removing elements) then you should + * synchronize around the iteration to avoid non-deterministic behavior:
        + *

        +   * SortedMap m = Collections.synchronizedSortedMap(new SortedMap(...));
        +   * ...
        +   * Set s = m.keySet(); // safe outside a synchronized block
        +   * SortedMap m2 = m.headMap(foo); // safe outside a synchronized block
        +   * Set s2 = m2.keySet(); // safe outside a synchronized block
        +   * synchronized (m) // synch on m, not m2, s or s2
        +   *   {
        +   *     Iterator i = s.iterator();
        +   *     while (i.hasNext())
        +   *       foo(i.next());
        +   *     i = s2.iterator();
        +   *     while (i.hasNext())
        +   *       bar(i.next());
        +   *   }
        +   * 

        + * + * The returned SortedMap implements Serializable, but can only be + * serialized if the map it wraps is likewise Serializable. + * + * @param m the sorted map to wrap + * @return a synchronized view of the sorted map + * @see Serializable + */ + public static SortedMap synchronizedSortedMap(SortedMap m) + { + return new SynchronizedSortedMap(m); + } + + /** + * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedSortedMap + extends SynchronizedMap + implements SortedMap + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -8798146769416483793L; + + /** + * The wrapped map; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped map + */ + private final SortedMap sm; + + /** + * Wrap a given map. + * @param sm the map to wrap + * @throws NullPointerException if sm is null + */ + SynchronizedSortedMap(SortedMap sm) + { + super(sm); + this.sm = sm; + } + + /** + * Called only by trusted code to specify the mutex as well as the map. + * @param sync the mutex + * @param sm the map + */ + SynchronizedSortedMap(Object sync, SortedMap sm) + { + super(sync, sm); + this.sm = sm; + } + + /** + * Returns the comparator used in sorting the underlying map, or null if + * it is the keys' natural ordering. A lock is obtained on the mutex + * before the comparator is retrieved. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + synchronized (mutex) + { + return sm.comparator(); + } + } + + /** + * Returns the first, lowest sorted, key from the underlying map. + * A lock is obtained on the mutex before the map is accessed. + * + * @return the first key. + * @throws NoSuchElementException if this map is empty. + */ + public K firstKey() + { + synchronized (mutex) + { + return sm.firstKey(); + } + } + + /** + * Returns a submap containing the keys from the first + * key (as returned by firstKey()) to + * the key before that specified. The submap supports all + * operations supported by the underlying map and all actions + * taking place on the submap are also reflected in the underlying + * map. A lock is obtained on the mutex prior to submap creation. + * This operation is equivalent to subMap(firstKey(), toKey). + * The submap retains the thread-safe status of this map. + * + * @param toKey the exclusive upper range of the submap. + * @return a submap from firstKey() to the + * the key preceding toKey. + * @throws ClassCastException if toKey is not comparable to the underlying + * map's contents. + * @throws IllegalArgumentException if toKey is outside the map's range. + * @throws NullPointerException if toKey is null. but the map does not allow + * null keys. + */ + public SortedMap headMap(K toKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, sm.headMap(toKey)); + } + } + + /** + * Returns the last, highest sorted, key from the underlying map. + * A lock is obtained on the mutex before the map is accessed. + * + * @return the last key. + * @throws NoSuchElementException if this map is empty. + */ + public K lastKey() + { + synchronized (mutex) + { + return sm.lastKey(); + } + } + + /** + * Returns a submap containing the keys from fromKey to + * the key before toKey. The submap supports all + * operations supported by the underlying map and all actions + * taking place on the submap are also reflected in the underlying + * map. A lock is obtained on the mutex prior to submap creation. + * The submap retains the thread-safe status of this map. + * + * @param fromKey the inclusive lower range of the submap. + * @param toKey the exclusive upper range of the submap. + * @return a submap from fromKey to the key preceding toKey. + * @throws ClassCastException if fromKey or toKey is not comparable + * to the underlying map's contents. + * @throws IllegalArgumentException if fromKey or toKey is outside the map's + * range. + * @throws NullPointerException if fromKey or toKey is null. but the map does + * not allow null keys. + */ + public SortedMap subMap(K fromKey, K toKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, + sm.subMap(fromKey, toKey)); + } + } + + /** + * Returns a submap containing all the keys from fromKey onwards. + * The submap supports all operations supported by the underlying + * map and all actions taking place on the submap are also reflected + * in the underlying map. A lock is obtained on the mutex prior to + * submap creation. The submap retains the thread-safe status of + * this map. + * + * @param fromKey the inclusive lower range of the submap. + * @return a submap from fromKey to lastKey(). + * @throws ClassCastException if fromKey is not comparable to the underlying + * map's contents. + * @throws IllegalArgumentException if fromKey is outside the map's range. + * @throws NullPointerException if fromKey is null. but the map does not allow + * null keys. + */ + public SortedMap tailMap(K fromKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, sm.tailMap(fromKey)); + } + } + } // class SynchronizedSortedMap + + /** + * Returns a synchronized (thread-safe) sorted set wrapper backed by the + * given set. Notice that element access through the iterator and through + * subviews are thread-safe, but if the set can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:
        + *

        +   * SortedSet s = Collections.synchronizedSortedSet(new SortedSet(...));
        +   * ...
        +   * SortedSet s2 = s.headSet(foo); // safe outside a synchronized block
        +   * synchronized (s) // synch on s, not s2
        +   *   {
        +   *     Iterator i = s2.iterator();
        +   *     while (i.hasNext())
        +   *       foo(i.next());
        +   *   }
        +   * 

        + * + * The returned SortedSet implements Serializable, but can only be + * serialized if the set it wraps is likewise Serializable. + * + * @param s the sorted set to wrap + * @return a synchronized view of the sorted set + * @see Serializable + */ + public static SortedSet synchronizedSortedSet(SortedSet s) + { + return new SynchronizedSortedSet(s); + } + + /** + * The implementation of {@link #synchronizedSortedSet(SortedSet)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedSortedSet + extends SynchronizedSet + implements SortedSet + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 8695801310862127406L; + + /** + * The wrapped set; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped set + */ + private final SortedSet ss; + + /** + * Wrap a given set. + * @param ss the set to wrap + * @throws NullPointerException if ss is null + */ + SynchronizedSortedSet(SortedSet ss) + { + super(ss); + this.ss = ss; + } + + /** + * Called only by trusted code to specify the mutex as well as the set. + * @param sync the mutex + * @param ss the set + */ + SynchronizedSortedSet(Object sync, SortedSet ss) + { + super(sync, ss); + this.ss = ss; + } + + /** + * Returns the comparator used in sorting the underlying set, or null if + * it is the elements' natural ordering. A lock is obtained on the mutex + * before the comparator is retrieved. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + synchronized (mutex) + { + return ss.comparator(); + } + } + + /** + * Returns the first, lowest sorted, element from the underlying set. + * A lock is obtained on the mutex before the set is accessed. + * + * @return the first element. + * @throws NoSuchElementException if this set is empty. + */ + public T first() + { + synchronized (mutex) + { + return ss.first(); + } + } + + /** + * Returns a subset containing the element from the first + * element (as returned by first()) to + * the element before that specified. The subset supports all + * operations supported by the underlying set and all actions + * taking place on the subset are also reflected in the underlying + * set. A lock is obtained on the mutex prior to subset creation. + * This operation is equivalent to subSet(first(), toElement). + * The subset retains the thread-safe status of this set. + * + * @param toElement the exclusive upper range of the subset. + * @return a subset from first() to the + * the element preceding toElement. + * @throws ClassCastException if toElement is not comparable to the underlying + * set's contents. + * @throws IllegalArgumentException if toElement is outside the set's range. + * @throws NullPointerException if toElement is null. but the set does not allow + * null elements. + */ + public SortedSet headSet(T toElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, ss.headSet(toElement)); + } + } + + /** + * Returns the last, highest sorted, element from the underlying set. + * A lock is obtained on the mutex before the set is accessed. + * + * @return the last element. + * @throws NoSuchElementException if this set is empty. + */ + public T last() + { + synchronized (mutex) + { + return ss.last(); + } + } + + /** + * Returns a subset containing the elements from fromElement to + * the element before toElement. The subset supports all + * operations supported by the underlying set and all actions + * taking place on the subset are also reflected in the underlying + * set. A lock is obtained on the mutex prior to subset creation. + * The subset retains the thread-safe status of this set. + * + * @param fromElement the inclusive lower range of the subset. + * @param toElement the exclusive upper range of the subset. + * @return a subset from fromElement to the element preceding toElement. + * @throws ClassCastException if fromElement or toElement is not comparable + * to the underlying set's contents. + * @throws IllegalArgumentException if fromElement or toElement is outside the set's + * range. + * @throws NullPointerException if fromElement or toElement is null. but the set does + * not allow null elements. + */ + public SortedSet subSet(T fromElement, T toElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, + ss.subSet(fromElement, + toElement)); + } + } + + /** + * Returns a subset containing all the elements from fromElement onwards. + * The subset supports all operations supported by the underlying + * set and all actions taking place on the subset are also reflected + * in the underlying set. A lock is obtained on the mutex prior to + * subset creation. The subset retains the thread-safe status of + * this set. + * + * @param fromElement the inclusive lower range of the subset. + * @return a subset from fromElement to last(). + * @throws ClassCastException if fromElement is not comparable to the underlying + * set's contents. + * @throws IllegalArgumentException if fromElement is outside the set's range. + * @throws NullPointerException if fromElement is null. but the set does not allow + * null elements. + */ + public SortedSet tailSet(T fromElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, ss.tailSet(fromElement)); + } + } + } // class SynchronizedSortedSet + + + /** + * Returns an unmodifiable view of the given collection. This allows + * "read-only" access, although changes in the backing collection show up + * in this view. Attempts to modify the collection directly or via iterators + * will fail with {@link UnsupportedOperationException}. Although this view + * prevents changes to the structure of the collection and its elements, the values + * referenced by the objects in the collection can still be modified. + *

        + * + * Since the collection might be a List or a Set, and those have incompatible + * equals and hashCode requirements, this relies on Object's implementation + * rather than passing those calls on to the wrapped collection. The returned + * Collection implements Serializable, but can only be serialized if + * the collection it wraps is likewise Serializable. + * + * @param c the collection to wrap + * @return a read-only view of the collection + * @see Serializable + */ + public static Collection unmodifiableCollection(Collection c) + { + return new UnmodifiableCollection(c); + } + + /** + * The implementation of {@link #unmodifiableCollection(Collection)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableCollection + implements Collection, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1820017752578914078L; + + /** + * The wrapped collection. Package visible for use by subclasses. + * @serial the real collection + */ + final Collection c; + + /** + * Wrap a given collection. + * @param c the collection to wrap + * @throws NullPointerException if c is null + */ + UnmodifiableCollection(Collection c) + { + this.c = c; + if (c == null) + throw new NullPointerException(); + } + + /** + * Blocks the addition of elements to the underlying collection. + * This method never returns, throwing an exception instead. + * + * @param o the object to add. + * @return true if the collection was modified as a result of this action. + * @throws UnsupportedOperationException as an unmodifiable collection does not + * support the add operation. + */ + public boolean add(T o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the addition of a collection of elements to the underlying + * collection. This method never returns, throwing an exception instead. + * + * @param c the collection to add. + * @return true if the collection was modified as a result of this action. + * @throws UnsupportedOperationException as an unmodifiable collection does not + * support the addAll operation. + */ + public boolean addAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the clearing of the underlying collection. This method never + * returns, throwing an exception instead. + * + * @throws UnsupportedOperationException as an unmodifiable collection does + * not support the clear() operation. + */ + public void clear() + { + throw new UnsupportedOperationException(); + } + + /** + * Test whether the underlying collection contains a given object as one of its + * elements. + * + * @param o the element to look for. + * @return true if the underlying collection contains at least + * one element e such that + * o == null ? e == null : o.equals(e). + * @throws ClassCastException if the type of o is not a valid type for the + * underlying collection. + * @throws NullPointerException if o is null and the underlying collection + * doesn't support null values. + */ + public boolean contains(Object o) + { + return c.contains(o); + } + + /** + * Test whether the underlying collection contains every element in a given + * collection. + * + * @param c1 the collection to test for. + * @return true if for every element o in c, contains(o) would + * return true. + * @throws ClassCastException if the type of any element in c is not a valid + * type for the underlying collection. + * @throws NullPointerException if some element of c is null and the underlying + * collection does not support null values. + * @throws NullPointerException if c itself is null. + */ + public boolean containsAll(Collection c1) + { + return c.containsAll(c1); + } + + /** + * Tests whether the underlying collection is empty, that is, + * if size() == 0. + * + * @return true if this collection contains no elements. + */ + public boolean isEmpty() + { + return c.isEmpty(); + } + + /** + * Obtain an Iterator over the underlying collection, which maintains + * its unmodifiable nature. + * + * @return an UnmodifiableIterator over the elements of the underlying + * collection, in any order. + */ + public Iterator iterator() + { + return new UnmodifiableIterator(c.iterator()); + } + + /** + * Blocks the removal of an object from the underlying collection. + * This method never returns, throwing an exception instead. + * + * @param o The object to remove. + * @return true if the object was removed (i.e. the underlying + * collection returned 1 or more instances of o). + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the remove() operation. + */ + public boolean remove(Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of a collection of objects from the underlying + * collection. This method never returns, throwing an exception + * instead. + * + * @param c The collection of objects to remove. + * @return true if the collection was modified. + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the removeAll() operation. + */ + public boolean removeAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of all elements from the underlying collection, + * except those in the supplied collection. This method never returns, + * throwing an exception instead. + * + * @param c The collection of objects to retain. + * @return true if the collection was modified. + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the retainAll() operation. + */ + public boolean retainAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Retrieves the number of elements in the underlying collection. + * + * @return the number of elements in the collection. + */ + public int size() + { + return c.size(); + } + + /** + * Copy the current contents of the underlying collection into an array. + * + * @return an array of type Object[] with a length equal to the size of the + * underlying collection and containing the elements currently in + * the underlying collection, in any order. + */ + public Object[] toArray() + { + return c.toArray(); + } + + /** + * Copy the current contents of the underlying collection into an array. If + * the array passed as an argument has length less than the size of the + * underlying collection, an array of the same run-time type as a, with a length + * equal to the size of the underlying collection, is allocated using reflection. + * Otherwise, a itself is used. The elements of the underlying collection are + * copied into it, and if there is space in the array, the following element is + * set to null. The resultant array is returned. + * Note: The fact that the following element is set to null is only useful + * if it is known that this collection does not contain any null elements. + * + * @param a the array to copy this collection into. + * @return an array containing the elements currently in the underlying + * collection, in any order. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + public S[] toArray(S[] a) + { + return c.toArray(a); + } + + /** + * A textual representation of the unmodifiable collection. + * + * @return The unmodifiable collection in the form of a String. + */ + public String toString() + { + return c.toString(); + } + } // class UnmodifiableCollection + + /** + * The implementation of the various iterator methods in the + * unmodifiable classes. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableIterator implements Iterator + { + /** + * The wrapped iterator. + */ + private final Iterator i; + + /** + * Only trusted code creates a wrapper. + * @param i the wrapped iterator + */ + UnmodifiableIterator(Iterator i) + { + this.i = i; + } + + /** + * Obtains the next element in the underlying collection. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public T next() + { + return i.next(); + } + + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by next(). When this method + * returns true, an exception will not be thrown on calling + * next(). + * + * @return true if there is at least one more element in the underlying + * collection. + */ + public boolean hasNext() + { + return i.hasNext(); + } + + /** + * Blocks the removal of elements from the underlying collection by the + * iterator. + * + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the removal of elements by its iterator. + */ + public void remove() + { + throw new UnsupportedOperationException(); + } + } // class UnmodifiableIterator + + /** + * Returns an unmodifiable view of the given list. This allows + * "read-only" access, although changes in the backing list show up + * in this view. Attempts to modify the list directly, via iterators, or + * via sublists, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the list and + * its elements, the values referenced by the objects in the list can + * still be modified. + *

        + * + * The returned List implements Serializable, but can only be serialized if + * the list it wraps is likewise Serializable. In addition, if the wrapped + * list implements RandomAccess, this does too. + * + * @param l the list to wrap + * @return a read-only view of the list + * @see Serializable + * @see RandomAccess + */ + public static List unmodifiableList(List l) + { + if (l instanceof RandomAccess) + return new UnmodifiableRandomAccessList(l); + return new UnmodifiableList(l); + } + + /** + * The implementation of {@link #unmodifiableList(List)} for sequential + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableList extends UnmodifiableCollection + implements List + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -283967356065247728L; + + + /** + * The wrapped list; stored both here and in the superclass to avoid + * excessive casting. Package visible for use by subclass. + * @serial the wrapped list + */ + final List list; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + UnmodifiableList(List l) + { + super(l); + list = (List) l; + } + + /** + * Blocks the addition of an element to the underlying + * list at a specific index. This method never returns, + * throwing an exception instead. + * + * @param index The index at which to place the new element. + * @param o the object to add. + * @throws UnsupportedOperationException as an unmodifiable + * list doesn't support the add() operation. + */ + public void add(int index, T o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the addition of a collection of elements to the + * underlying list at a specific index. This method never + * returns, throwing an exception instead. + * + * @param index The index at which to place the new element. + * @param c the collections of objects to add. + * @throws UnsupportedOperationException as an unmodifiable + * list doesn't support the addAll() operation. + */ + public boolean addAll(int index, Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Returns true if the object, o, is an instance of + * List with the same size and elements + * as the underlying list. + * + * @param o The object to compare. + * @return true if o is equivalent to the underlying list. + */ + public boolean equals(Object o) + { + return list.equals(o); + } + + /** + * Retrieves the element at a given index in the underlying list. + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public T get(int index) + { + return list.get(index); + } + + /** + * Computes the hash code for the underlying list. + * The exact computation is described in the documentation + * of the List interface. + * + * @return The hash code of the underlying list. + * @see List#hashCode() + */ + public int hashCode() + { + return list.hashCode(); + } + + /** + * Obtain the first index at which a given object is to be found in the + * underlying list. + * + * @param o the object to search for + * @return the least integer n such that o == null ? get(n) == null : + * o.equals(get(n)), or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for the underlying list. + * @throws NullPointerException if o is null and the underlying + * list does not support null values. + */ + public int indexOf(Object o) + { + return list.indexOf(o); + } + + /** + * Obtain the last index at which a given object is to be found in the + * underlying list. + * + * @return the greatest integer n such that o == null ? get(n) == null + * : o.equals(get(n)), or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for the underlying list. + * @throws NullPointerException if o is null and the underlying + * list does not support null values. + */ + public int lastIndexOf(Object o) + { + return list.lastIndexOf(o); + } + + /** + * Obtains a list iterator over the underlying list, starting at the beginning + * and maintaining the unmodifiable nature of this list. + * + * @return a UnmodifiableListIterator over the elements of the + * underlying list, in order, starting at the beginning. + */ + public ListIterator listIterator() + { + return new UnmodifiableListIterator(list.listIterator()); + } + + /** + * Obtains a list iterator over the underlying list, starting at the specified + * index and maintaining the unmodifiable nature of this list. An initial call + * to next() will retrieve the element at the specified index, + * and an initial call to previous() will retrieve the element + * at index - 1. + * + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from. + * @return a UnmodifiableListIterator over the elements of the + * underlying list, in order, starting at the specified index. + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + return new UnmodifiableListIterator(list.listIterator(index)); + } + + /** + * Blocks the removal of the element at the specified index. + * This method never returns, throwing an exception instead. + * + * @param index The index of the element to remove. + * @return the removed element. + * @throws UnsupportedOperationException as an unmodifiable + * list does not support the remove() + * operation. + */ + public T remove(int index) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the replacement of the element at the specified index. + * This method never returns, throwing an exception instead. + * + * @param index The index of the element to replace. + * @param o The new object to place at the specified index. + * @return the replaced element. + * @throws UnsupportedOperationException as an unmodifiable + * list does not support the set() + * operation. + */ + public T set(int index, T o) + { + throw new UnsupportedOperationException(); + } + + /** + * Obtain a List view of a subsection of the underlying list, from + * fromIndex (inclusive) to toIndex (exclusive). If the two indices + * are equal, the sublist is empty. The returned list will be + * unmodifiable, like this list. Changes to the elements of the + * returned list will be reflected in the underlying list. No structural + * modifications can take place in either list. + * + * @param fromIndex the index that the returned list should start from + * (inclusive). + * @param toIndex the index that the returned list should go to (exclusive). + * @return a List backed by a subsection of the underlying list. + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex. + */ + public List subList(int fromIndex, int toIndex) + { + return unmodifiableList(list.subList(fromIndex, toIndex)); + } + } // class UnmodifiableList + + /** + * The implementation of {@link #unmodifiableList(List)} for random-access + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableRandomAccessList + extends UnmodifiableList implements RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -2542308836966382001L; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + UnmodifiableRandomAccessList(List l) + { + super(l); + } + } // class UnmodifiableRandomAccessList + + /** + * The implementation of {@link UnmodifiableList#listIterator()}. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableListIterator + extends UnmodifiableIterator implements ListIterator + { + /** + * The wrapped iterator, stored both here and in the superclass to + * avoid excessive casting. + */ + private final ListIterator li; + + /** + * Only trusted code creates a wrapper. + * @param li the wrapped iterator + */ + UnmodifiableListIterator(ListIterator li) + { + super(li); + this.li = li; + } + + /** + * Blocks the addition of an object to the list underlying this iterator. + * This method never returns, throwing an exception instead. + * + * @param o The object to add. + * @throws UnsupportedOperationException as the iterator of an unmodifiable + * list does not support the add() operation. + */ + public void add(T o) + { + throw new UnsupportedOperationException(); + } + + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by previous(). When this method + * returns true, an exception will not be thrown on calling + * previous(). + * + * @return true if there is at least one more element prior to the + * current position in the underlying list. + */ + public boolean hasPrevious() + { + return li.hasPrevious(); + } + + /** + * Find the index of the element that would be returned by a call to next. + * If hasNext() returns false, this returns the list size. + * + * @return the index of the element that would be returned by + * next(). + */ + public int nextIndex() + { + return li.nextIndex(); + } + + /** + * Obtains the previous element in the underlying list. + * + * @return the previous element in the list. + * @throws NoSuchElementException if there are no more prior elements. + */ + public T previous() + { + return li.previous(); + } + + /** + * Find the index of the element that would be returned by a call to + * previous. If hasPrevious() returns false, + * this returns -1. + * + * @return the index of the element that would be returned by + * previous(). + */ + public int previousIndex() + { + return li.previousIndex(); + } + + /** + * Blocks the replacement of an element in the list underlying this + * iterator. This method never returns, throwing an exception instead. + * + * @param o The new object to replace the existing one. + * @throws UnsupportedOperationException as the iterator of an unmodifiable + * list does not support the set() operation. + */ + public void set(T o) + { + throw new UnsupportedOperationException(); + } + } // class UnmodifiableListIterator + + /** + * Returns an unmodifiable view of the given map. This allows "read-only" + * access, although changes in the backing map show up in this view. + * Attempts to modify the map directly, or via collection views or their + * iterators will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the map and its + * entries, the values referenced by the objects in the map can still be + * modified. + *

        + * + * The returned Map implements Serializable, but can only be serialized if + * the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a read-only view of the map + * @see Serializable + */ + public static Map unmodifiableMap(Map m) + { + return new UnmodifiableMap(m); + } + + /** + * The implementation of {@link #unmodifiableMap(Map)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableMap implements Map, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -1034234728574286014L; + + /** + * The wrapped map. + * @serial the real map + */ + private final Map m; + + /** + * Cache the entry set. + */ + private transient Set> entries; + + /** + * Cache the key set. + */ + private transient Set keys; + + /** + * Cache the value collection. + */ + private transient Collection values; + + /** + * Wrap a given map. + * @param m the map to wrap + * @throws NullPointerException if m is null + */ + UnmodifiableMap(Map m) + { + this.m = (Map) m; + if (m == null) + throw new NullPointerException(); + } + + /** + * Blocks the clearing of entries from the underlying map. + * This method never returns, throwing an exception instead. + * + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the clear() operation. + */ + public void clear() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns true if the underlying map contains a mapping for + * the given key. + * + * @param key the key to search for + * @return true if the map contains the key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if key is null but the map + * does not permit null keys + */ + public boolean containsKey(Object key) + { + return m.containsKey(key); + } + + /** + * Returns true if the underlying map contains at least one mapping with + * the given value. In other words, it returns true if a value v exists where + * (value == null ? v == null : value.equals(v)). This usually + * requires linear time. + * + * @param value the value to search for + * @return true if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + public boolean containsValue(Object value) + { + return m.containsValue(value); + } + + /** + * Returns a unmodifiable set view of the entries in the underlying map. + * Each element in the set is a unmodifiable variant of Map.Entry. + * The set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the objects. + * + * @return the unmodifiable set view of all mapping entries. + * @see Map.Entry + */ + public Set> entrySet() + { + if (entries == null) + entries = new UnmodifiableEntrySet(m.entrySet()); + return entries; + } + + /** + * The implementation of {@link UnmodifiableMap#entrySet()}. This class + * name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableEntrySet + extends UnmodifiableSet> + implements Serializable + { + // Unmodifiable implementation of Map.Entry used as return value for + // UnmodifiableEntrySet accessors (iterator, toArray, toArray(Object[])) + private static final class UnmodifiableMapEntry + implements Map.Entry + { + private final Map.Entry e; + + private UnmodifiableMapEntry(Map.Entry e) + { + super(); + this.e = e; + } + + /** + * Returns true if the object, o, is also a map entry + * with an identical key and value. + * + * @param o the object to compare. + * @return true if o is an equivalent map entry. + */ + public boolean equals(Object o) + { + return e.equals(o); + } + + /** + * Returns the key of this map entry. + * + * @return the key. + */ + public K getKey() + { + return e.getKey(); + } + + /** + * Returns the value of this map entry. + * + * @return the value. + */ + public V getValue() + { + return e.getValue(); + } + + /** + * Computes the hash code of this map entry. The computation is + * described in the Map interface documentation. + * + * @return the hash code of this entry. + * @see Map#hashCode() + */ + public int hashCode() + { + return e.hashCode(); + } + + /** + * Blocks the alteration of the value of this map entry. This method + * never returns, throwing an exception instead. + * + * @param value The new value. + * @throws UnsupportedOperationException as an unmodifiable map entry + * does not support the setValue() operation. + */ + public V setValue(V value) + { + throw new UnsupportedOperationException(); + } + + /** + * Returns a textual representation of the map entry. + * + * @return The map entry as a String. + */ + public String toString() + { + return e.toString(); + } + } + + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 7854390611657943733L; + + /** + * Wrap a given set. + * @param s the set to wrap + */ + UnmodifiableEntrySet(Set> s) + { + super(s); + } + + // The iterator must return unmodifiable map entries. + public Iterator> iterator() + { + return new UnmodifiableIterator>(c.iterator()) + { + /** + * Obtains the next element from the underlying set of + * map entries. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public Map.Entry next() + { + final Map.Entry e = super.next(); + return new UnmodifiableMapEntry(e); + } + }; + } + + // The array returned is an array of UnmodifiableMapEntry instead of + // Map.Entry + public Object[] toArray() + { + Object[] mapEntryResult = super.toArray(); + UnmodifiableMapEntry result[] = null; + + if (mapEntryResult != null) + { + result = (UnmodifiableMapEntry[]) + new UnmodifiableMapEntry[mapEntryResult.length]; + for (int i = 0; i < mapEntryResult.length; ++i) + result[i] = new UnmodifiableMapEntry((Map.Entry)mapEntryResult[i]); + } + return result; + } + + // The array returned is an array of UnmodifiableMapEntry instead of + // Map.Entry + public S[] toArray(S[] array) + { + S[] result = super.toArray(array); + + if (result != null) + for (int i = 0; i < result.length; i++) + array[i] = + (S) new UnmodifiableMapEntry((Map.Entry) result[i]); + return array; + } + + + } // class UnmodifiableEntrySet + + /** + * Returns true if the object, o, is also an instance + * of Map with an equal set of map entries. + * + * @param o The object to compare. + * @return true if o is an equivalent map. + */ + public boolean equals(Object o) + { + return m.equals(o); + } + + /** + * Returns the value associated with the supplied key or + * null if no such mapping exists. An ambiguity can occur + * if null values are accepted by the underlying map. + * In this case, containsKey() can be used + * to separate the two possible cases of a null result. + * + * @param key The key to look up. + * @return the value associated with the key, or null if key not in map. + * @throws ClassCastException if the key is an inappropriate type. + * @throws NullPointerException if this map does not accept null keys. + * @see #containsKey(Object) + */ + public V get(Object key) + { + return m.get(key); + } + + /** + * Blocks the addition of a new entry to the underlying map. + * This method never returns, throwing an exception instead. + * + * @param key The new key. + * @param value The new value. + * @return the previous value of the key, or null if there was no mapping. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the put() operation. + */ + public V put(K key, V value) + { + throw new UnsupportedOperationException(); + } + + /** + * Computes the hash code for the underlying map, as the sum + * of the hash codes of all entries. + * + * @return The hash code of the underlying map. + * @see Map.Entry#hashCode() + */ + public int hashCode() + { + return m.hashCode(); + } + + /** + * Returns true if the underlying map contains no entries. + * + * @return true if the map is empty. + */ + public boolean isEmpty() + { + return m.isEmpty(); + } + + /** + * Returns a unmodifiable set view of the keys in the underlying map. + * The set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the keys. + * + * @return the set view of all keys. + */ + public Set keySet() + { + if (keys == null) + keys = new UnmodifiableSet(m.keySet()); + return keys; + } + + /** + * Blocks the addition of the entries in the supplied map. + * This method never returns, throwing an exception instead. + * + * @param m The map, the entries of which should be added + * to the underlying map. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the putAll operation. + */ + public void putAll(Map m) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of an entry from the map. + * This method never returns, throwing an exception instead. + * + * @param o The key of the entry to remove. + * @return The value the key was associated with, or null + * if no such mapping existed. Null is also returned + * if the removed entry had a null key. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the remove operation. + */ + public V remove(Object o) + { + throw new UnsupportedOperationException(); + } + + + /** + * Returns the number of key-value mappings in the underlying map. + * If there are more than Integer.MAX_VALUE mappings, Integer.MAX_VALUE + * is returned. + * + * @return the number of mappings. + */ + public int size() + { + return m.size(); + } + + /** + * Returns a textual representation of the map. + * + * @return The map in the form of a String. + */ + public String toString() + { + return m.toString(); + } + + /** + * Returns a unmodifiable collection view of the values in the underlying map. + * The collection is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the keys. + * + * @return the collection view of all values. + */ + public Collection values() + { + if (values == null) + values = new UnmodifiableCollection(m.values()); + return values; + } + } // class UnmodifiableMap + + /** + * Returns an unmodifiable view of the given set. This allows + * "read-only" access, although changes in the backing set show up + * in this view. Attempts to modify the set directly or via iterators + * will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the set and its + * entries, the values referenced by the objects in the set can still be + * modified. + *

        + * + * The returned Set implements Serializable, but can only be serialized if + * the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a read-only view of the set + * @see Serializable + */ + public static Set unmodifiableSet(Set s) + { + return new UnmodifiableSet(s); + } + + /** + * The implementation of {@link #unmodifiableSet(Set)}. This class + * name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSet extends UnmodifiableCollection + implements Set + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -9215047833775013803L; + + /** + * Wrap a given set. + * @param s the set to wrap + * @throws NullPointerException if s is null + */ + UnmodifiableSet(Set s) + { + super(s); + } + + /** + * Returns true if the object, o, is also an instance of + * Set of the same size and with the same entries. + * + * @return true if o is an equivalent set. + */ + public boolean equals(Object o) + { + return c.equals(o); + } + + /** + * Computes the hash code of this set, as the sum of the + * hash codes of all elements within the set. + * + * @return the hash code of the set. + */ + public int hashCode() + { + return c.hashCode(); + } + } // class UnmodifiableSet + + /** + * Returns an unmodifiable view of the given sorted map. This allows + * "read-only" access, although changes in the backing map show up in this + * view. Attempts to modify the map directly, via subviews, via collection + * views, or iterators, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the map and its + * entries, the values referenced by the objects in the map can still be + * modified. + *

        + * + * The returned SortedMap implements Serializable, but can only be + * serialized if the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a read-only view of the map + * @see Serializable + */ + public static SortedMap unmodifiableSortedMap(SortedMap m) + { + return new UnmodifiableSortedMap(m); + } + + /** + * The implementation of {@link #unmodifiableSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSortedMap + extends UnmodifiableMap + implements SortedMap + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -8806743815996713206L; + + /** + * The wrapped map; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped map + */ + private final SortedMap sm; + + /** + * Wrap a given map. + * @param sm the map to wrap + * @throws NullPointerException if sm is null + */ + UnmodifiableSortedMap(SortedMap sm) + { + super(sm); + this.sm = (SortedMap) sm; + } + + /** + * Returns the comparator used in sorting the underlying map, + * or null if it is the keys' natural ordering. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + return sm.comparator(); + } + + /** + * Returns the first (lowest sorted) key in the map. + * + * @return the first key. + * @throws NoSuchElementException if this map is empty. + */ + public K firstKey() + { + return sm.firstKey(); + } + + /** + * Returns a unmodifiable view of the portion of the map strictly less + * than toKey. The view is backed by the underlying map, so changes in + * one show up in the other. The submap supports all optional operations + * of the original. This operation is equivalent to + * subMap(firstKey(), toKey). + *

        + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of toKey. Note that the endpoint, toKey, + * is not included; if you want this value to be included, pass its successor + * object in to toKey. For example, for Integers, you could request + * headMap(new Integer(limit.intValue() + 1)). + * + * @param toKey the exclusive upper range of the submap. + * @return the submap. + * @throws ClassCastException if toKey is not comparable to the map contents. + * @throws IllegalArgumentException if this is a subMap, and toKey is out + * of range. + * @throws NullPointerException if toKey is null but the map does not allow + * null keys. + */ + public SortedMap headMap(K toKey) + { + return new UnmodifiableSortedMap(sm.headMap(toKey)); + } + + /** + * Returns the last (highest sorted) key in the map. + * + * @return the last key. + * @throws NoSuchElementException if this map is empty. + */ + public K lastKey() + { + return sm.lastKey(); + } + + /** + * Returns a unmodifiable view of the portion of the map greater than or + * equal to fromKey, and strictly less than toKey. The view is backed by + * the underlying map, so changes in one show up in the other. The submap + * supports all optional operations of the original. + *

        + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey and toKey. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you could request + * subMap(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1)) to reverse + * the inclusiveness of both endpoints. + * + * @param fromKey the inclusive lower range of the submap. + * @param toKey the exclusive upper range of the submap. + * @return the submap. + * @throws ClassCastException if fromKey or toKey is not comparable to + * the map contents. + * @throws IllegalArgumentException if this is a subMap, and fromKey or + * toKey is out of range. + * @throws NullPointerException if fromKey or toKey is null but the map + * does not allow null keys. + */ + public SortedMap subMap(K fromKey, K toKey) + { + return new UnmodifiableSortedMap(sm.subMap(fromKey, toKey)); + } + + /** + * Returns a unmodifiable view of the portion of the map greater than or + * equal to fromKey. The view is backed by the underlying map, so changes + * in one show up in the other. The submap supports all optional operations + * of the original. + *

        + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey. Note that the endpoint, fromKey, is + * included; if you do not want this value to be included, pass its successor object in + * to fromKey. For example, for Integers, you could request + * tailMap(new Integer(limit.intValue() + 1)). + * + * @param fromKey the inclusive lower range of the submap + * @return the submap + * @throws ClassCastException if fromKey is not comparable to the map + * contents + * @throws IllegalArgumentException if this is a subMap, and fromKey is out + * of range + * @throws NullPointerException if fromKey is null but the map does not allow + * null keys + */ + public SortedMap tailMap(K fromKey) + { + return new UnmodifiableSortedMap(sm.tailMap(fromKey)); + } + } // class UnmodifiableSortedMap + + /** + * Returns an unmodifiable view of the given sorted set. This allows + * "read-only" access, although changes in the backing set show up + * in this view. Attempts to modify the set directly, via subsets, or via + * iterators, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the set and its + * entries, the values referenced by the objects in the set can still be + * modified. + *

        + * + * The returns SortedSet implements Serializable, but can only be + * serialized if the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a read-only view of the set + * @see Serializable + */ + public static SortedSet unmodifiableSortedSet(SortedSet s) + { + return new UnmodifiableSortedSet(s); + } + + /** + * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSortedSet extends UnmodifiableSet + implements SortedSet + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -4929149591599911165L; + + /** + * The wrapped set; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped set + */ + private SortedSet ss; + + /** + * Wrap a given set. + * @param ss the set to wrap + * @throws NullPointerException if ss is null + */ + UnmodifiableSortedSet(SortedSet ss) + { + super(ss); + this.ss = ss; + } + + /** + * Returns the comparator used in sorting the underlying set, + * or null if it is the elements' natural ordering. + * + * @return the sorting comparator + */ + public Comparator comparator() + { + return ss.comparator(); + } + + /** + * Returns the first (lowest sorted) element in the underlying + * set. + * + * @return the first element. + * @throws NoSuchElementException if the set is empty. + */ + public T first() + { + return ss.first(); + } + + /** + * Returns a unmodifiable view of the portion of the set strictly + * less than toElement. The view is backed by the underlying set, + * so changes in one show up in the other. The subset supports + * all optional operations of the original. This operation + * is equivalent to subSet(first(), toElement). + *

        + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of toElement. Note that the endpoint, toElement, + * is not included; if you want this value included, pass its successor object in to + * toElement. For example, for Integers, you could request + * headSet(new Integer(limit.intValue() + 1)). + * + * @param toElement the exclusive upper range of the subset + * @return the subset. + * @throws ClassCastException if toElement is not comparable to the set + * contents. + * @throws IllegalArgumentException if this is a subSet, and toElement is out + * of range. + * @throws NullPointerException if toElement is null but the set does not + * allow null elements. + */ + public SortedSet headSet(T toElement) + { + return new UnmodifiableSortedSet(ss.headSet(toElement)); + } + + /** + * Returns the last (highest sorted) element in the underlying + * set. + * + * @return the last element. + * @throws NoSuchElementException if the set is empty. + */ + public T last() + { + return ss.last(); + } + + /** + * Returns a unmodifiable view of the portion of the set greater than or + * equal to fromElement, and strictly less than toElement. The view is backed by + * the underlying set, so changes in one show up in the other. The subset + * supports all optional operations of the original. + *

        + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement and toElement. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you can request + * subSet(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1)) to reverse + * the inclusiveness of both endpoints. + * + * @param fromElement the inclusive lower range of the subset. + * @param toElement the exclusive upper range of the subset. + * @return the subset. + * @throws ClassCastException if fromElement or toElement is not comparable + * to the set contents. + * @throws IllegalArgumentException if this is a subSet, and fromElement or + * toElement is out of range. + * @throws NullPointerException if fromElement or toElement is null but the + * set does not allow null elements. + */ + public SortedSet subSet(T fromElement, T toElement) + { + return new UnmodifiableSortedSet(ss.subSet(fromElement, toElement)); + } + + /** + * Returns a unmodifiable view of the portion of the set greater than or equal to + * fromElement. The view is backed by the underlying set, so changes in one show up + * in the other. The subset supports all optional operations of the original. + *

        + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement. Note that the endpoint, + * fromElement, is included; if you do not want this value to be included, pass its + * successor object in to fromElement. For example, for Integers, you could request + * tailSet(new Integer(limit.intValue() + 1)). + * + * @param fromElement the inclusive lower range of the subset + * @return the subset. + * @throws ClassCastException if fromElement is not comparable to the set + * contents. + * @throws IllegalArgumentException if this is a subSet, and fromElement is + * out of range. + * @throws NullPointerException if fromElement is null but the set does not + * allow null elements. + */ + public SortedSet tailSet(T fromElement) + { + return new UnmodifiableSortedSet(ss.tailSet(fromElement)); + } + } // class UnmodifiableSortedSet + + /** + *

        + * Returns a dynamically typesafe view of the given collection, + * where any modification is first checked to ensure that the type + * of the new data is appropriate. Although the addition of + * generics and parametrically-typed collections prevents an + * incorrect type of element being added to a collection at + * compile-time, via static type checking, this can be overridden by + * casting. In contrast, wrapping the collection within a + * dynamically-typesafe wrapper, using this and associated methods, + * guarantees that the collection will only contain + * elements of an appropriate type (provided it only contains such + * at the type of wrapping, and all subsequent access is via the + * wrapper). This can be useful for debugging the cause of a + * ClassCastException caused by erroneous casting, or + * for protecting collections from corruption by external libraries. + *

        + *

        + * Since the collection might be a List or a Set, and those + * have incompatible equals and hashCode requirements, this relies + * on Object's implementation rather than passing those calls on to + * the wrapped collection. The returned Collection implements + * Serializable, but can only be serialized if the collection it + * wraps is likewise Serializable. + *

        + * + * @param c the collection to wrap in a dynamically typesafe wrapper + * @param type the type of elements the collection should hold. + * @return a dynamically typesafe view of the collection. + * @see Serializable + * @since 1.5 + */ + public static Collection checkedCollection(Collection c, + Class type) + { + return new CheckedCollection(c, type); + } + + /** + * The implementation of {@link #checkedCollection(Collection,Class)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static class CheckedCollection + implements Collection, Serializable + { + /** + * Compatible with JDK 1.5. + */ + private static final long serialVersionUID = 1578914078182001775L; + + /** + * The wrapped collection. Package visible for use by subclasses. + * @serial the real collection + */ + final Collection c; + + /** + * The type of the elements of this collection. + * @serial the element type. + */ + final Class type; + + /** + * Wrap a given collection. + * @param c the collection to wrap + * @param type the type to wrap + * @throws NullPointerException if c is null + */ + CheckedCollection(Collection c, Class type) + { + this.c = c; + this.type = type; + if (c == null) + throw new NullPointerException(); + } + + /** + * Adds the supplied object to the collection, on the condition that + * it is of the correct type. + * + * @param o the object to add. + * @return true if the collection was modified as a result + * of this action. + * @throws ClassCastException if the object is not of the correct type. + */ + public boolean add(E o) + { + if (type.isInstance(o)) + return c.add(o); + else + throw new ClassCastException("The element is of the incorrect type."); + } + + /** + * Adds the elements of the specified collection to the backing collection, + * provided they are all of the correct type. + * + * @param coll the collection to add. + * @return true if the collection was modified as a result + * of this action. + * @throws ClassCastException if c contained elements of an + * incorrect type. + */ + public boolean addAll(Collection coll) + { + Collection typedColl = (Collection) c; + final Iterator it = typedColl.iterator(); + while (it.hasNext()) + { + final E element = it.next(); + if (!type.isInstance(element)) + throw new ClassCastException("A member of the collection is not of the correct type."); + } + return c.addAll(typedColl); + } + + /** + * Removes all elements from the underlying collection. + */ + public void clear() + { + c.clear(); + } + + /** + * Test whether the underlying collection contains a given object as one + * of its elements. + * + * @param o the element to look for. + * @return true if the underlying collection contains at least + * one element e such that + * o == null ? e == null : o.equals(e). + * @throws ClassCastException if the type of o is not a valid type for the + * underlying collection. + * @throws NullPointerException if o is null and the underlying collection + * doesn't support null values. + */ + public boolean contains(Object o) + { + return c.contains(o); + } + + /** + * Test whether the underlying collection contains every element in a given + * collection. + * + * @param coll the collection to test for. + * @return true if for every element o in c, contains(o) would + * return true. + * @throws ClassCastException if the type of any element in c is not a + * valid type for the underlying collection. + * @throws NullPointerException if some element of c is null and the + * underlying collection does not support + * null values. + * @throws NullPointerException if c itself is null. + */ + public boolean containsAll(Collection coll) + { + return c.containsAll(coll); + } + + /** + * Tests whether the underlying collection is empty, that is, + * if size() == 0. + * + * @return true if this collection contains no elements. + */ + public boolean isEmpty() + { + return c.isEmpty(); + } + + /** + * Obtain an Iterator over the underlying collection, which maintains + * its checked nature. + * + * @return a Iterator over the elements of the underlying + * collection, in any order. + */ + public Iterator iterator() + { + return new CheckedIterator(c.iterator(), type); + } + + /** + * Removes the supplied object from the collection, if it exists. + * + * @param o The object to remove. + * @return true if the object was removed (i.e. the underlying + * collection returned 1 or more instances of o). + */ + public boolean remove(Object o) + { + return c.remove(o); + } + + /** + * Removes all objects in the supplied collection from the backing + * collection, if they exist within it. + * + * @param coll the collection of objects to remove. + * @return true if the collection was modified. + */ + public boolean removeAll(Collection coll) + { + return c.removeAll(coll); + } + + /** + * Retains all objects specified by the supplied collection which exist + * within the backing collection, and removes all others. + * + * @param coll the collection of objects to retain. + * @return true if the collection was modified. + */ + public boolean retainAll(Collection coll) + { + return c.retainAll(coll); + } + + /** + * Retrieves the number of elements in the underlying collection. + * + * @return the number of elements in the collection. + */ + public int size() + { + return c.size(); + } + + /** + * Copy the current contents of the underlying collection into an array. + * + * @return an array of type Object[] with a length equal to the size of the + * underlying collection and containing the elements currently in + * the underlying collection, in any order. + */ + public Object[] toArray() + { + return c.toArray(); + } + + /** + *

        + * Copy the current contents of the underlying collection into an array. If + * the array passed as an argument has length less than the size of the + * underlying collection, an array of the same run-time type as a, with a + * length equal to the size of the underlying collection, is allocated + * using reflection. + *

        + *

        + * Otherwise, a itself is used. The elements of the underlying collection + * are copied into it, and if there is space in the array, the following + * element is set to null. The resultant array is returned. + *

        + *

        + * Note: The fact that the following element is set to null + * is only useful if it is known that this collection does not contain + * any null elements. + * + * @param a the array to copy this collection into. + * @return an array containing the elements currently in the underlying + * collection, in any order. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + public S[] toArray(S[] a) + { + return c.toArray(a); + } + + /** + * A textual representation of the unmodifiable collection. + * + * @return The checked collection in the form of a String. + */ + public String toString() + { + return c.toString(); + } + } // class CheckedCollection + + /** + * The implementation of the various iterator methods in the + * checked classes. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static class CheckedIterator + implements Iterator + { + /** + * The wrapped iterator. + */ + private final Iterator i; + + /** + * The type of the elements of this collection. + * @serial the element type. + */ + final Class type; + + /** + * Only trusted code creates a wrapper. + * @param i the wrapped iterator + * @param type the type of the elements within the checked list. + */ + CheckedIterator(Iterator i, Class type) + { + this.i = i; + this.type = type; + } + + /** + * Obtains the next element in the underlying collection. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public E next() + { + return i.next(); + } + + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by next(). When this method + * returns true, an exception will not be thrown on calling + * next(). + * + * @return true if there is at least one more element in the + * underlying collection. + */ + public boolean hasNext() + { + return i.hasNext(); + } + + /** + * Removes the next element from the collection. + */ + public void remove() + { + i.remove(); + } + } // class CheckedIterator + + /** + *

        + * Returns a dynamically typesafe view of the given list, + * where any modification is first checked to ensure that the type + * of the new data is appropriate. Although the addition of + * generics and parametrically-typed collections prevents an + * incorrect type of element being added to a collection at + * compile-time, via static type checking, this can be overridden by + * casting. In contrast, wrapping the collection within a + * dynamically-typesafe wrapper, using this and associated methods, + * guarantees that the collection will only contain + * elements of an appropriate type (provided it only contains such + * at the type of wrapping, and all subsequent access is via the + * wrapper). This can be useful for debugging the cause of a + * ClassCastException caused by erroneous casting, or + * for protecting collections from corruption by external libraries. + *

        + *

        + * The returned List implements Serializable, but can only be serialized if + * the list it wraps is likewise Serializable. In addition, if the wrapped + * list implements RandomAccess, this does too. + *

        + * + * @param l the list to wrap + * @param type the type of the elements within the checked list. + * @return a dynamically typesafe view of the list + * @see Serializable + * @see RandomAccess + */ + public static List checkedList(List l, Class type) + { + if (l instanceof RandomAccess) + return new CheckedRandomAccessList(l, type); + return new CheckedList(l, type); + } + + /** + * The implementation of {@link #checkedList(List,Class)} for sequential + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static class CheckedList + extends CheckedCollection + implements List + { + /** + * Compatible with JDK 1.5. + */ + private static final long serialVersionUID = 65247728283967356L; + + /** + * The wrapped list; stored both here and in the superclass to avoid + * excessive casting. Package visible for use by subclass. + * @serial the wrapped list + */ + final List list; + + /** + * Wrap a given list. + * @param l the list to wrap + * @param type the type of the elements within the checked list. + * @throws NullPointerException if l is null + */ + CheckedList(List l, Class type) + { + super(l, type); + list = l; + } + + /** + * Adds the supplied element to the underlying list at the specified + * index, provided it is of the right type. + * + * @param index The index at which to place the new element. + * @param o the object to add. + * @throws ClassCastException if the type of the object is not a + * valid type for the underlying collection. + */ + public void add(int index, E o) + { + if (type.isInstance(o)) + list.add(index, o); + else + throw new ClassCastException("The object is of the wrong type."); + } + + /** + * Adds the members of the supplied collection to the underlying + * collection at the specified index, provided they are all of the + * correct type. + * + * @param index the index at which to place the new element. + * @param coll the collections of objects to add. + * @throws ClassCastException if the type of any element in c is not a + * valid type for the underlying collection. + */ + public boolean addAll(int index, Collection coll) + { + Collection typedColl = (Collection) coll; + final Iterator it = typedColl.iterator(); + while (it.hasNext()) + { + if (!type.isInstance(it.next())) + throw new ClassCastException("A member of the collection is not of the correct type."); + } + return list.addAll(index, coll); + } + + /** + * Returns true if the object, o, is an instance of + * List with the same size and elements + * as the underlying list. + * + * @param o The object to compare. + * @return true if o is equivalent to the underlying list. + */ + public boolean equals(Object o) + { + return list.equals(o); + } + + /** + * Retrieves the element at a given index in the underlying list. + * + * @param index the index of the element to be returned + * @return the element at the specified index in the underlying list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public E get(int index) + { + return list.get(index); + } + + /** + * Computes the hash code for the underlying list. + * The exact computation is described in the documentation + * of the List interface. + * + * @return The hash code of the underlying list. + * @see List#hashCode() + */ + public int hashCode() + { + return list.hashCode(); + } + + /** + * Obtain the first index at which a given object is to be found in the + * underlying list. + * + * @param o the object to search for + * @return the least integer n such that o == null ? get(n) == null : + * o.equals(get(n)), or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for the underlying list. + * @throws NullPointerException if o is null and the underlying + * list does not support null values. + */ + public int indexOf(Object o) + { + return list.indexOf(o); + } + + /** + * Obtain the last index at which a given object is to be found in the + * underlying list. + * + * @return the greatest integer n such that + * o == null ? get(n) == null : o.equals(get(n)), + * or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for the underlying list. + * @throws NullPointerException if o is null and the underlying + * list does not support null values. + */ + public int lastIndexOf(Object o) + { + return list.lastIndexOf(o); + } + + /** + * Obtains a list iterator over the underlying list, starting at the + * beginning and maintaining the checked nature of this list. + * + * @return a CheckedListIterator over the elements of the + * underlying list, in order, starting at the beginning. + */ + public ListIterator listIterator() + { + return new CheckedListIterator(list.listIterator(), type); + } + + /** + * Obtains a list iterator over the underlying list, starting at the + * specified index and maintaining the checked nature of this list. An + * initial call to next() will retrieve the element at the + * specified index, and an initial call to previous() will + * retrieve the element at index - 1. + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from. + * @return a CheckedListIterator over the elements of the + * underlying list, in order, starting at the specified index. + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + return new CheckedListIterator(list.listIterator(index), type); + } + + /** + * Removes the element at the specified index. + * + * @param index The index of the element to remove. + * @return the removed element. + */ + public E remove(int index) + { + return list.remove(index); + } + + /** + * Replaces the element at the specified index in the underlying list + * with that supplied. + * + * @param index the index of the element to replace. + * @param o the new object to place at the specified index. + * @return the replaced element. + */ + public E set(int index, E o) + { + return list.set(index, o); + } + + /** + * Obtain a List view of a subsection of the underlying list, from + * fromIndex (inclusive) to toIndex (exclusive). If the two indices + * are equal, the sublist is empty. The returned list will be + * checked, like this list. Changes to the elements of the + * returned list will be reflected in the underlying list. The effect + * of structural modifications is undefined. + * + * @param fromIndex the index that the returned list should start from + * (inclusive). + * @param toIndex the index that the returned list should go + * to (exclusive). + * @return a List backed by a subsection of the underlying list. + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex. + */ + public List subList(int fromIndex, int toIndex) + { + return checkedList(list.subList(fromIndex, toIndex), type); + } + } // class CheckedList + + /** + * The implementation of {@link #checkedList(List)} for random-access + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static final class CheckedRandomAccessList + extends CheckedList + implements RandomAccess + { + /** + * Compatible with JDK 1.5. + */ + private static final long serialVersionUID = 1638200125423088369L; + + /** + * Wrap a given list. + * @param l the list to wrap + * @param type the type of the elements within the checked list. + * @throws NullPointerException if l is null + */ + CheckedRandomAccessList(List l, Class type) + { + super(l, type); + } + } // class CheckedRandomAccessList + + /** + * The implementation of {@link CheckedList#listIterator()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static final class CheckedListIterator + extends CheckedIterator + implements ListIterator + { + /** + * The wrapped iterator, stored both here and in the superclass to + * avoid excessive casting. + */ + private final ListIterator li; + + /** + * Only trusted code creates a wrapper. + * @param li the wrapped iterator + */ + CheckedListIterator(ListIterator li, Class type) + { + super(li, type); + this.li = li; + } + + /** + * Adds the supplied object at the current iterator position, provided + * it is of the correct type. + * + * @param o the object to add. + * @throws ClassCastException if the type of the object is not a + * valid type for the underlying collection. + */ + public void add(E o) + { + if (type.isInstance(o)) + li.add(o); + else + throw new ClassCastException("The object is of the wrong type."); + } + + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by previous(). When this method + * returns true, an exception will not be thrown on calling + * previous(). + * + * @return true if there is at least one more element prior + * to the current position in the underlying list. + */ + public boolean hasPrevious() + { + return li.hasPrevious(); + } + + /** + * Find the index of the element that would be returned by a call to next. + * If hasNext() returns false, this returns the + * list size. + * + * @return the index of the element that would be returned by + * next(). + */ + public int nextIndex() + { + return li.nextIndex(); + } + + /** + * Obtains the previous element in the underlying list. + * + * @return the previous element in the list. + * @throws NoSuchElementException if there are no more prior elements. + */ + public E previous() + { + return li.previous(); + } + + /** + * Find the index of the element that would be returned by a call to + * previous. If hasPrevious() returns false, + * this returns -1. + * + * @return the index of the element that would be returned by + * previous(). + */ + public int previousIndex() + { + return li.previousIndex(); + } + + /** + * Sets the next element to that supplied, provided that it is of the + * correct type. + * + * @param o The new object to replace the existing one. + * @throws ClassCastException if the type of the object is not a + * valid type for the underlying collection. + */ + public void set(E o) + { + if (type.isInstance(o)) + li.set(o); + else + throw new ClassCastException("The object is of the wrong type."); + } + } // class CheckedListIterator + + /** + *

        + * Returns a dynamically typesafe view of the given map, + * where any modification is first checked to ensure that the type + * of the new data is appropriate. Although the addition of + * generics and parametrically-typed collections prevents an + * incorrect type of element being added to a collection at + * compile-time, via static type checking, this can be overridden by + * casting. In contrast, wrapping the collection within a + * dynamically-typesafe wrapper, using this and associated methods, + * guarantees that the collection will only contain + * elements of an appropriate type (provided it only contains such + * at the type of wrapping, and all subsequent access is via the + * wrapper). This can be useful for debugging the cause of a + * ClassCastException caused by erroneous casting, or + * for protecting collections from corruption by external libraries. + *

        + *

        + * The returned Map implements Serializable, but can only be serialized if + * the map it wraps is likewise Serializable. + *

        + * + * @param m the map to wrap + * @param keyType the dynamic type of the map's keys. + * @param valueType the dynamic type of the map's values. + * @return a dynamically typesafe view of the map + * @see Serializable + */ + public static Map checkedMap(Map m, Class keyType, + Class valueType) + { + return new CheckedMap(m, keyType, valueType); + } + + /** + * The implementation of {@link #checkedMap(Map)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static class CheckedMap + implements Map, Serializable + { + /** + * Compatible with JDK 1.5. + */ + private static final long serialVersionUID = 5742860141034234728L; + + /** + * The wrapped map. + * @serial the real map + */ + private final Map m; + + /** + * The type of the map's keys. + * @serial the key type. + */ + final Class keyType; + + /** + * The type of the map's values. + * @serial the value type. + */ + final Class valueType; + + /** + * Cache the entry set. + */ + private transient Set> entries; + + /** + * Cache the key set. + */ + private transient Set keys; + + /** + * Cache the value collection. + */ + private transient Collection values; + + /** + * Wrap a given map. + * @param m the map to wrap + * @param keyType the dynamic type of the map's keys. + * @param valueType the dynamic type of the map's values. + * @throws NullPointerException if m is null + */ + CheckedMap(Map m, Class keyType, Class valueType) + { + this.m = m; + this.keyType = keyType; + this.valueType = valueType; + if (m == null) + throw new NullPointerException(); + } + + /** + * Clears all pairs from the map. + */ + public void clear() + { + m.clear(); + } + + /** + * Returns true if the underlying map contains a mapping for + * the given key. + * + * @param key the key to search for + * @return true if the map contains the key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if key is null but the map + * does not permit null keys + */ + public boolean containsKey(Object key) + { + return m.containsKey(key); + } + + /** + * Returns true if the underlying map contains at least one + * mapping with the given value. In other words, it returns + * true if a value v exists where + * (value == null ? v == null : value.equals(v)). + * This usually requires linear time. + * + * @param value the value to search for + * @return true if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + public boolean containsValue(Object value) + { + return m.containsValue(value); + } + + /** + *

        + * Returns a checked set view of the entries in the underlying map. + * Each element in the set is a unmodifiable variant of + * Map.Entry. + *

        + *

        + * The set is backed by the map, so that changes in one show up in the + * other. Modifications made while an iterator is in progress cause + * undefined behavior. + *

        + * + * @return the checked set view of all mapping entries. + * @see Map.Entry + */ + public Set> entrySet() + { + if (entries == null) + { + Class> klass = + (Class>) (Class) Map.Entry.class; + entries = new CheckedEntrySet,K,V>(m.entrySet(), + klass, + keyType, + valueType); + } + return entries; + } + + /** + * The implementation of {@link CheckedMap#entrySet()}. This class + * is not serializable. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static final class CheckedEntrySet + extends CheckedSet + { + /** + * The type of the map's keys. + * @serial the key type. + */ + private final Class keyType; + + /** + * The type of the map's values. + * @serial the value type. + */ + private final Class valueType; + + /** + * Wrap a given set of map entries. + * + * @param s the set to wrap. + * @param type the type of the set's entries. + * @param keyType the type of the map's keys. + * @param valueType the type of the map's values. + */ + CheckedEntrySet(Set s, Class type, Class keyType, + Class valueType) + { + super(s, type); + this.keyType = keyType; + this.valueType = valueType; + } + + // The iterator must return checked map entries. + public Iterator iterator() + { + return new CheckedIterator(c.iterator(), type) + { + /** + * Obtains the next element from the underlying set of + * map entries. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public E next() + { + final Map.Entry e = (Map.Entry) super.next(); + return (E) new Map.Entry() + { + /** + * Returns true if the object, o, is also a map + * entry with an identical key and value. + * + * @param o the object to compare. + * @return true if o is an equivalent map entry. + */ + public boolean equals(Object o) + { + return e.equals(o); + } + + /** + * Returns the key of this map entry. + * + * @return the key. + */ + public Object getKey() + { + return e.getKey(); + } + + /** + * Returns the value of this map entry. + * + * @return the value. + */ + public Object getValue() + { + return e.getValue(); + } + + /** + * Computes the hash code of this map entry. + * The computation is described in the Map + * interface documentation. + * + * @return the hash code of this entry. + * @see Map#hashCode() + */ + public int hashCode() + { + return e.hashCode(); + } + + /** + * Sets the value of this map entry, provided it is of the + * right type. + * + * @param value The new value. + * @throws ClassCastException if the type of the value is not + * a valid type for the underlying + * map. + */ + public Object setValue(Object value) + { + if (valueType.isInstance(value)) + return e.setValue(value); + else + throw new ClassCastException("The value is of the wrong type."); + } + + /** + * Returns a textual representation of the map entry. + * + * @return The map entry as a String. + */ + public String toString() + { + return e.toString(); + } + }; + } + }; + } + } // class CheckedEntrySet + + /** + * Returns true if the object, o, is also an instance + * of Map with an equal set of map entries. + * + * @param o The object to compare. + * @return true if o is an equivalent map. + */ + public boolean equals(Object o) + { + return m.equals(o); + } + + /** + * Returns the value associated with the supplied key or + * null if no such mapping exists. An ambiguity can occur + * if null values are accepted by the underlying map. + * In this case, containsKey() can be used + * to separate the two possible cases of a null result. + * + * @param key The key to look up. + * @return the value associated with the key, or null if key not in map. + * @throws ClassCastException if the key is an inappropriate type. + * @throws NullPointerException if this map does not accept null keys. + * @see #containsKey(Object) + */ + public V get(Object key) + { + return m.get(key); + } + + /** + * Adds a new pair to the map, provided both the key and the value are + * of the correct types. + * + * @param key The new key. + * @param value The new value. + * @return the previous value of the key, or null if there was no mapping. + * @throws ClassCastException if the type of the key or the value is + * not a valid type for the underlying map. + */ + public V put(K key, V value) + { + if (keyType.isInstance(key)) + { + if (valueType.isInstance(value)) + return m.put(key,value); + else + throw new ClassCastException("The value is of the wrong type."); + } + throw new ClassCastException("The key is of the wrong type."); + } + + /** + * Computes the hash code for the underlying map, as the sum + * of the hash codes of all entries. + * + * @return The hash code of the underlying map. + * @see Map.Entry#hashCode() + */ + public int hashCode() + { + return m.hashCode(); + } + + /** + * Returns true if the underlying map contains no entries. + * + * @return true if the map is empty. + */ + public boolean isEmpty() + { + return m.isEmpty(); + } + + /** + *

        + * Returns a checked set view of the keys in the underlying map. + * The set is backed by the map, so that changes in one show up in the + * other. + *

        + *

        + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the keys. + *

        + * + * @return the set view of all keys. + */ + public Set keySet() + { + if (keys == null) + keys = new CheckedSet(m.keySet(), keyType); + return keys; + } + + /** + * Adds all pairs within the supplied map to the underlying map, + * provided they are all have the correct key and value types. + * + * @param map the map, the entries of which should be added + * to the underlying map. + * @throws ClassCastException if the type of a key or value is + * not a valid type for the underlying map. + */ + public void putAll(Map map) + { + Map typedMap = (Map) map; + final Iterator> it = typedMap.entrySet().iterator(); + while (it.hasNext()) + { + final Map.Entry entry = it.next(); + if (!keyType.isInstance(entry.getKey())) + throw new ClassCastException("A key is of the wrong type."); + if (!valueType.isInstance(entry.getValue())) + throw new ClassCastException("A value is of the wrong type."); + } + m.putAll(typedMap); + } + + /** + * Removes a pair from the map. + * + * @param o The key of the entry to remove. + * @return The value the key was associated with, or null + * if no such mapping existed. Null is also returned + * if the removed entry had a null key. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the remove operation. + */ + public V remove(Object o) + { + return m.remove(o); + } + + + /** + * Returns the number of key-value mappings in the underlying map. + * If there are more than Integer.MAX_VALUE mappings, Integer.MAX_VALUE + * is returned. + * + * @return the number of mappings. + */ + public int size() + { + return m.size(); + } + + /** + * Returns a textual representation of the map. + * + * @return The map in the form of a String. + */ + public String toString() + { + return m.toString(); + } + + /** + *

        + * Returns a unmodifiable collection view of the values in the underlying + * map. The collection is backed by the map, so that changes in one show + * up in the other. + *

        + *

        + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the keys. + *

        + * + * @return the collection view of all values. + */ + public Collection values() + { + if (values == null) + values = new CheckedCollection(m.values(), valueType); + return values; + } + } // class CheckedMap + + /** + *

        + * Returns a dynamically typesafe view of the given set, + * where any modification is first checked to ensure that the type + * of the new data is appropriate. Although the addition of + * generics and parametrically-typed collections prevents an + * incorrect type of element being added to a collection at + * compile-time, via static type checking, this can be overridden by + * casting. In contrast, wrapping the collection within a + * dynamically-typesafe wrapper, using this and associated methods, + * guarantees that the collection will only contain + * elements of an appropriate type (provided it only contains such + * at the type of wrapping, and all subsequent access is via the + * wrapper). This can be useful for debugging the cause of a + * ClassCastException caused by erroneous casting, or + * for protecting collections from corruption by external libraries. + *

        + *

        + * The returned Set implements Serializable, but can only be serialized if + * the set it wraps is likewise Serializable. + *

        + * + * @param s the set to wrap. + * @param type the type of the elements within the checked list. + * @return a dynamically typesafe view of the set + * @see Serializable + */ + public static Set checkedSet(Set s, Class type) + { + return new CheckedSet(s, type); + } + + /** + * The implementation of {@link #checkedSet(Set)}. This class + * name is required for compatibility with Sun's JDK serializability. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static class CheckedSet + extends CheckedCollection + implements Set + { + /** + * Compatible with JDK 1.5. + */ + private static final long serialVersionUID = 4694047833775013803L; + + /** + * Wrap a given set. + * + * @param s the set to wrap + * @throws NullPointerException if s is null + */ + CheckedSet(Set s, Class type) + { + super(s, type); + } + + /** + * Returns true if the object, o, is also an instance of + * Set of the same size and with the same entries. + * + * @return true if o is an equivalent set. + */ + public boolean equals(Object o) + { + return c.equals(o); + } + + /** + * Computes the hash code of this set, as the sum of the + * hash codes of all elements within the set. + * + * @return the hash code of the set. + */ + public int hashCode() + { + return c.hashCode(); + } + } // class CheckedSet + + /** + *

        + * Returns a dynamically typesafe view of the given sorted map, + * where any modification is first checked to ensure that the type + * of the new data is appropriate. Although the addition of + * generics and parametrically-typed collections prevents an + * incorrect type of element being added to a collection at + * compile-time, via static type checking, this can be overridden by + * casting. In contrast, wrapping the collection within a + * dynamically-typesafe wrapper, using this and associated methods, + * guarantees that the collection will only contain + * elements of an appropriate type (provided it only contains such + * at the type of wrapping, and all subsequent access is via the + * wrapper). This can be useful for debugging the cause of a + * ClassCastException caused by erroneous casting, or + * for protecting collections from corruption by external libraries. + *

        + *

        + * The returned SortedMap implements Serializable, but can only be + * serialized if the map it wraps is likewise Serializable. + *

        + * + * @param m the map to wrap. + * @param keyType the dynamic type of the map's keys. + * @param valueType the dynamic type of the map's values. + * @return a dynamically typesafe view of the map + * @see Serializable + */ + public static SortedMap checkedSortedMap(SortedMap m, + Class keyType, + Class valueType) + { + return new CheckedSortedMap(m, keyType, valueType); + } + + /** + * The implementation of {@link #checkedSortedMap(SortedMap,Class,Class)}. + * This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static class CheckedSortedMap + extends CheckedMap + implements SortedMap + { + /** + * Compatible with JDK 1.5. + */ + private static final long serialVersionUID = 1599671320688067438L; + + /** + * The wrapped map; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped map + */ + private final SortedMap sm; + + /** + * Wrap a given map. + * + * @param sm the map to wrap + * @param keyType the dynamic type of the map's keys. + * @param valueType the dynamic type of the map's values. + * @throws NullPointerException if sm is null + */ + CheckedSortedMap(SortedMap sm, Class keyType, Class valueType) + { + super(sm, keyType, valueType); + this.sm = sm; + } + + /** + * Returns the comparator used in sorting the underlying map, + * or null if it is the keys' natural ordering. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + return sm.comparator(); + } + + /** + * Returns the first (lowest sorted) key in the map. + * + * @return the first key. + * @throws NoSuchElementException if this map is empty. + */ + public K firstKey() + { + return sm.firstKey(); + } + + /** + *

        + * Returns a checked view of the portion of the map strictly less + * than toKey. The view is backed by the underlying map, so changes in + * one show up in the other. The submap supports all optional operations + * of the original. This operation is equivalent to + * subMap(firstKey(), toKey). + *

        + *

        + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of toKey. Note that the endpoint, toKey, + * is not included; if you want this value to be included, pass its + * successor object in to toKey. For example, for Integers, you could + * request headMap(new Integer(limit.intValue() + 1)). + *

        + * + * @param toKey the exclusive upper range of the submap. + * @return the submap. + * @throws ClassCastException if toKey is not comparable to the map + * contents. + * @throws IllegalArgumentException if this is a subMap, and toKey is out + * of range. + * @throws NullPointerException if toKey is null but the map does not allow + * null keys. + */ + public SortedMap headMap(K toKey) + { + return new CheckedSortedMap(sm.headMap(toKey), keyType, valueType); + } + + /** + * Returns the last (highest sorted) key in the map. + * + * @return the last key. + * @throws NoSuchElementException if this map is empty. + */ + public K lastKey() + { + return sm.lastKey(); + } + + /** + *

        + * Returns a checked view of the portion of the map greater than or + * equal to fromKey, and strictly less than toKey. The view is backed by + * the underlying map, so changes in one show up in the other. The submap + * supports all optional operations of the original. + *

        + *

        + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey and toKey. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you could request + * subMap(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1)) to reverse + * the inclusiveness of both endpoints. + *

        + * + * @param fromKey the inclusive lower range of the submap. + * @param toKey the exclusive upper range of the submap. + * @return the submap. + * @throws ClassCastException if fromKey or toKey is not comparable to + * the map contents. + * @throws IllegalArgumentException if this is a subMap, and fromKey or + * toKey is out of range. + * @throws NullPointerException if fromKey or toKey is null but the map + * does not allow null keys. + */ + public SortedMap subMap(K fromKey, K toKey) + { + return new CheckedSortedMap(sm.subMap(fromKey, toKey), keyType, + valueType); + } + + /** + *

        + * Returns a checked view of the portion of the map greater than or + * equal to fromKey. The view is backed by the underlying map, so changes + * in one show up in the other. The submap supports all optional operations + * of the original. + *

        + *

        + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey. Note that the endpoint, + * fromKey, is included; if you do not want this value to be included, + * pass its successor object in to fromKey. For example, for Integers, + * you could request + * tailMap(new Integer(limit.intValue() + 1)). + *

        + * + * @param fromKey the inclusive lower range of the submap + * @return the submap + * @throws ClassCastException if fromKey is not comparable to the map + * contents + * @throws IllegalArgumentException if this is a subMap, and fromKey is out + * of range + * @throws NullPointerException if fromKey is null but the map does not + * allow null keys + */ + public SortedMap tailMap(K fromKey) + { + return new CheckedSortedMap(sm.tailMap(fromKey), keyType, + valueType); + } + } // class CheckedSortedMap + + /** + *

        + * Returns a dynamically typesafe view of the given sorted set, + * where any modification is first checked to ensure that the type + * of the new data is appropriate. Although the addition of + * generics and parametrically-typed collections prevents an + * incorrect type of element being added to a collection at + * compile-time, via static type checking, this can be overridden by + * casting. In contrast, wrapping the collection within a + * dynamically-typesafe wrapper, using this and associated methods, + * guarantees that the collection will only contain + * elements of an appropriate type (provided it only contains such + * at the type of wrapping, and all subsequent access is via the + * wrapper). This can be useful for debugging the cause of a + * ClassCastException caused by erroneous casting, or + * for protecting collections from corruption by external libraries. + *

        + *

        + * The returned SortedSet implements Serializable, but can only be + * serialized if the set it wraps is likewise Serializable. + *

        + * + * @param s the set to wrap. + * @param type the type of the set's elements. + * @return a dynamically typesafe view of the set + * @see Serializable + */ + public static SortedSet checkedSortedSet(SortedSet s, + Class type) + { + return new CheckedSortedSet(s, type); + } + + /** + * The implementation of {@link #checkedSortedSet(SortedSet,Class)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + private static class CheckedSortedSet + extends CheckedSet + implements SortedSet + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1599911165492914959L; + + /** + * The wrapped set; stored both here and in the superclass to avoid + * excessive casting. + * + * @serial the wrapped set + */ + private SortedSet ss; + + /** + * Wrap a given set. + * + * @param ss the set to wrap. + * @param type the type of the set's elements. + * @throws NullPointerException if ss is null + */ + CheckedSortedSet(SortedSet ss, Class type) + { + super(ss, type); + this.ss = ss; + } + + /** + * Returns the comparator used in sorting the underlying set, + * or null if it is the elements' natural ordering. + * + * @return the sorting comparator + */ + public Comparator comparator() + { + return ss.comparator(); + } + + /** + * Returns the first (lowest sorted) element in the underlying + * set. + * + * @return the first element. + * @throws NoSuchElementException if the set is empty. + */ + public E first() + { + return ss.first(); + } + + /** + *

        + * Returns a checked view of the portion of the set strictly + * less than toElement. The view is backed by the underlying set, + * so changes in one show up in the other. The subset supports + * all optional operations of the original. This operation + * is equivalent to subSet(first(), toElement). + *

        + *

        + * The returned set throws an IllegalArgumentException any time an + * element is used which is out of the range of toElement. Note that + * the endpoint, toElement, is not included; if you want this value + * included, pass its successor object in to toElement. For example, + * for Integers, you could request + * headSet(new Integer(limit.intValue() + 1)). + *

        + * + * @param toElement the exclusive upper range of the subset + * @return the subset. + * @throws ClassCastException if toElement is not comparable to the set + * contents. + * @throws IllegalArgumentException if this is a subSet, and toElement is + * out of range. + * @throws NullPointerException if toElement is null but the set does not + * allow null elements. + */ + public SortedSet headSet(E toElement) + { + return new CheckedSortedSet(ss.headSet(toElement), type); + } + + /** + * Returns the last (highest sorted) element in the underlying + * set. + * + * @return the last element. + * @throws NoSuchElementException if the set is empty. + */ + public E last() + { + return ss.last(); + } + + /** + *

        + * Returns a checked view of the portion of the set greater than or + * equal to fromElement, and strictly less than toElement. The view is + * backed by the underlying set, so changes in one show up in the other. + * The subset supports all optional operations of the original. + *

        + *

        + * The returned set throws an IllegalArgumentException any time an + * element is used which is out of the range of fromElement and toElement. + * Note that the lower endpoint is included, but the upper is not; if you + * want to change the inclusion or exclusion of an endpoint, pass its + * successor object in instead. For example, for Integers, you can request + * subSet(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1)) to reverse + * the inclusiveness of both endpoints. + *

        + * + * @param fromElement the inclusive lower range of the subset. + * @param toElement the exclusive upper range of the subset. + * @return the subset. + * @throws ClassCastException if fromElement or toElement is not comparable + * to the set contents. + * @throws IllegalArgumentException if this is a subSet, and fromElement or + * toElement is out of range. + * @throws NullPointerException if fromElement or toElement is null but the + * set does not allow null elements. + */ + public SortedSet subSet(E fromElement, E toElement) + { + return new CheckedSortedSet(ss.subSet(fromElement, toElement), type); + } + + /** + *

        + * Returns a checked view of the portion of the set greater than or equal + * to fromElement. The view is backed by the underlying set, so changes in + * one show up in the other. The subset supports all optional operations + * of the original. + *

        + *

        + * The returned set throws an IllegalArgumentException any time an + * element is used which is out of the range of fromElement. Note that + * the endpoint, fromElement, is included; if you do not want this value + * to be included, pass its successor object in to fromElement. For + * example, for Integers, you could request + * tailSet(new Integer(limit.intValue() + 1)). + *

        + * + * @param fromElement the inclusive lower range of the subset + * @return the subset. + * @throws ClassCastException if fromElement is not comparable to the set + * contents. + * @throws IllegalArgumentException if this is a subSet, and fromElement is + * out of range. + * @throws NullPointerException if fromElement is null but the set does not + * allow null elements. + */ + public SortedSet tailSet(E fromElement) + { + return new CheckedSortedSet(ss.tailSet(fromElement), type); + } + } // class CheckedSortedSet + + /** + * Returns a view of a {@link Deque} as a stack or LIFO (Last-In-First-Out) + * {@link Queue}. Each call to the LIFO queue corresponds to one + * equivalent method call to the underlying deque, with the exception + * of {@link Collection#addAll(Collection)}, which is emulated by a series + * of {@link Deque#push(E)} calls. + * + * @param deque the deque to convert to a LIFO queue. + * @return a LIFO queue. + * @since 1.6 + */ + public static Queue asLifoQueue(Deque deque) + { + return new LIFOQueue(deque); + } + + /** + * Returns a set backed by the supplied map. The resulting set + * has the same performance, concurrency and ordering characteristics + * as the original map. The supplied map must be empty and should not + * be used after the set is created. Each call to the set corresponds + * to one equivalent method call to the underlying map, with the exception + * of {@link Set#addAll(Collection)} which is emulated by a series of + * calls to put. + * + * @param map the map to convert to a set. + * @return a set backed by the supplied map. + * @throws IllegalArgumentException if the map is not empty. + * @since 1.6 + */ + public static Set newSetFromMap(Map map) + { + return new MapSet(map); + } + + /** + * The implementation of {@link #asLIFOQueue(Deque)}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ + private static class LIFOQueue + extends AbstractQueue + { + + /** + * The backing deque. + */ + private Deque deque; + + /** + * Constructs a new {@link LIFOQueue} with the specified + * backing {@link Deque}. + * + * @param deque the backing deque. + */ + public LIFOQueue(Deque deque) + { + this.deque = deque; + } + + public boolean add(T e) + { + return deque.offerFirst(e); + } + + public boolean addAll(Collection c) + { + boolean result = false; + final Iterator it = c.iterator(); + while (it.hasNext()) + result |= deque.offerFirst(it.next()); + return result; + } + + public void clear() + { + deque.clear(); + } + + public boolean isEmpty() + { + return deque.isEmpty(); + } + + public Iterator iterator() + { + return deque.iterator(); + } + + public boolean offer(T e) + { + return deque.offerFirst(e); + } + + public T peek() + { + return deque.peek(); + } + + public T poll() + { + return deque.poll(); + } + + public int size() + { + return deque.size(); + } + } // class LIFOQueue + + /** + * The implementation of {@link #newSetFromMap(Map)}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ + private static class MapSet + extends AbstractSet + { + + /** + * The backing map. + */ + private Map map; + + /** + * Constructs a new {@link MapSet} using the specified + * backing {@link Map}. + * + * @param map the backing map. + * @throws IllegalArgumentException if the map is not empty. + */ + public MapSet(Map map) + { + if (!map.isEmpty()) + throw new IllegalArgumentException("The map must be empty."); + this.map = map; + } + + public boolean add(E e) + { + return map.put(e, true) == null; + } + + public boolean addAll(Collection c) + { + boolean result = false; + final Iterator it = c.iterator(); + while (it.hasNext()) + result |= (map.put(it.next(), true) == null); + return result; + } + + public void clear() + { + map.clear(); + } + + public boolean contains(Object o) + { + return map.containsKey(o); + } + + public boolean isEmpty() + { + return map.isEmpty(); + } + + public Iterator iterator() + { + return map.keySet().iterator(); + } + + public boolean remove(Object o) + { + return map.remove(o) != null; + } + + public int size() + { + return map.size(); + } + } // class MapSet + +} // class Collections diff --git a/libjava/classpath/java/util/Comparator.java b/libjava/classpath/java/util/Comparator.java new file mode 100644 index 000000000..ca414e7b1 --- /dev/null +++ b/libjava/classpath/java/util/Comparator.java @@ -0,0 +1,119 @@ +/* Comparator.java -- Interface for objects that specify an ordering + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Interface for objects that specify an ordering between objects. The ordering + * should be total, such that any two objects of the correct type + * can be compared, and the comparison is reflexive, anti-symmetric, and + * transitive. It is also recommended that the comparator be consistent + * with equals, although this is not a strict requirement. A relation + * is consistent with equals if these two statements always have the same + * results (if no exceptions occur):
        + * compare((Object) e1, (Object) e2) == 0 and + * e1.equals((Object) e2)
        + * Comparators that violate consistency with equals may cause strange behavior + * in sorted lists and sets. For example, a case-sensitive dictionary order + * comparison of Strings is consistent with equals, but if it is + * case-insensitive it is not, because "abc" and "ABC" compare as equal even + * though "abc".equals("ABC") returns false. + *

        + * In general, Comparators should be Serializable, because when they are passed + * to Serializable data structures such as SortedMap or SortedSet, the entire + * data structure will only serialize correctly if the comparator is + * Serializable. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Comparable + * @see TreeMap + * @see TreeSet + * @see SortedMap + * @see SortedSet + * @see Arrays#sort(Object[], Comparator) + * @see java.io.Serializable + * @since 1.2 + * @status updated to 1.4 + */ +public interface Comparator +{ + /** + * Return an integer that is negative, zero or positive depending on whether + * the first argument is less than, equal to or greater than the second + * according to this ordering. This method should obey the following + * contract: + *

          + *
        • if compare(a, b) < 0 then compare(b, a) > 0
        • + *
        • if compare(a, b) throws an exception, so does compare(b, a)
        • + *
        • if compare(a, b) < 0 and compare(b, c) < 0 then compare(a, c) + * < 0
        • + *
        • if compare(a, b) == 0 then compare(a, c) and compare(b, c) must + * have the same sign
        • + *
        + * To be consistent with equals, the following additional constraint is + * in place: + *
          + *
        • if a.equals(b) or both a and b are null, then + * compare(a, b) == 0.
        • + *

        + * + * Although it is permissible for a comparator to provide an order + * inconsistent with equals, that should be documented. + * + * @param o1 the first object + * @param o2 the second object + * @return the comparison + * @throws ClassCastException if the elements are not of types that can be + * compared by this ordering. + */ + int compare(T o1, T o2); + + /** + * Return true if the object is equal to this object. To be + * considered equal, the argument object must satisfy the constraints + * of Object.equals(), be a Comparator, and impose the + * same ordering as this Comparator. The default implementation + * inherited from Object is usually adequate. + * + * @param obj The object + * @return true if it is a Comparator that imposes the same order + * @see Object#equals(Object) + */ + boolean equals(Object obj); +} diff --git a/libjava/classpath/java/util/ConcurrentModificationException.java b/libjava/classpath/java/util/ConcurrentModificationException.java new file mode 100644 index 000000000..3d7ae1084 --- /dev/null +++ b/libjava/classpath/java/util/ConcurrentModificationException.java @@ -0,0 +1,92 @@ +/* ConcurrentModificationException.java -- Data structure concurrently modified + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + */ + +/** + * Exception that is thrown by the collections classes when it is detected that + * a modification has been made to a data structure when this is not allowed, + * such as when a collection is structurally modified while an Iterator is + * operating over it. In cases where this can be detected, a + * ConcurrentModificationException will be thrown. An Iterator that detects + * this condition is referred to as fail-fast. Notice that this can occur + * even in single-threaded designs, if you call methods out of order. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Iterator + * @see ListIterator + * @see Vector + * @see LinkedList + * @see HashSet + * @see Hashtable + * @see TreeMap + * @see AbstractList + * @since 1.2 + * @status updated to 1.4 + */ +public class ConcurrentModificationException extends RuntimeException +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = -3666751008965953603L; + + /** + * Constructs a ConcurrentModificationException with no detail message. + */ + public ConcurrentModificationException() + { + } + + /** + * Constructs a ConcurrentModificationException with a detail message. + * + * @param detail the detail message for the exception + */ + public ConcurrentModificationException(String detail) + { + super(detail); + } +} diff --git a/libjava/classpath/java/util/Currency.java b/libjava/classpath/java/util/Currency.java new file mode 100644 index 000000000..d58082c17 --- /dev/null +++ b/libjava/classpath/java/util/Currency.java @@ -0,0 +1,471 @@ +/* Currency.java -- Representation of a currency + Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.locale.LocaleHelper; + +import java.io.IOException; +import java.io.ObjectStreamException; +import java.io.Serializable; + +import java.util.spi.CurrencyNameProvider; + +/** + * Representation of a currency for a particular locale. Each currency + * is identified by its ISO 4217 code, and only one instance of this + * class exists per currency. As a result, instances are created + * via the getInstance() methods rather than by using + * a constructor. + * + * @see java.util.Locale + * @author Guilhem Lavaux (guilhem.lavaux@free.fr) + * @author Dalibor Topic (robilad@kaffe.org) + * @author Bryce McKinlay (mckinlay@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.4 + */ +public final class Currency + implements Serializable +{ + /** + * For compatability with Sun's JDK + */ + static final long serialVersionUID = -158308464356906721L; + + /** + * The set of properties which map a currency to + * the currency information such as the ISO 4217 + * currency code and the number of decimal points. + * + * @see #getCurrencyCode() + * @serial ignored. + */ + private static transient Properties properties; + + /** + * The ISO 4217 currency code associated with this + * particular instance. + * + * @see #getCurrencyCode() + * @serial the ISO 4217 currency code + */ + private String currencyCode; + + /** + * The number of fraction digits associated with this + * particular instance. + * + * @see #getDefaultFractionDigits() + * @serial the number of fraction digits + */ + private transient int fractionDigits; + + /** + * A cached map of country codes + * instances to international currency code + * Strings. Seperating this + * from the Currency instances + * ensures we have a common lookup between + * the two getInstance() methods. + * + * @see #getInstance(java.util.Locale) + * @serial ignored. + */ + private static transient Map countryMap; + + /** + * A cache of Currency instances to + * ensure the singleton nature of this class. The key + * is the international currency code. + * + * @see #getInstance(java.util.Locale) + * @see #getInstance(java.lang.String) + * @see #readResolve() + * @serial ignored. + */ + private static transient Map cache; + + /** + * Instantiates the cache and reads in the properties. + */ + static + { + /* Create a hash map for the locale mappings */ + countryMap = new HashMap(); + /* Create a hash map for the cache */ + cache = new HashMap(); + /* Create the properties object */ + properties = new Properties(); + /* Try and load the properties from our iso4217.properties resource */ + try + { + properties.load(Currency.class.getResourceAsStream("iso4217.properties")); + } + catch (IOException exception) + { + throw new InternalError("Failed to load currency resource: " + exception); + } + } + + /** + * Default constructor for deserialization + */ + private Currency() + { + } + + /** + * Constructor to create a Currency object + * for a particular Locale. + * All components of the given locale, other than the + * country code, are ignored. The results of calling this + * method may vary over time, as the currency associated with + * a particular country changes. For countries without + * a given currency (e.g. Antarctica), the result is null. + * + * @param loc the locale for the new currency, or null if + * there is no country code specified or a currency + * for this country. + */ + private Currency(Locale loc) + { + String countryCode; + String currencyKey; + String fractionDigitsKey; + int commaPosition; + + /* Retrieve the country code from the locale */ + countryCode = loc.getCountry(); + /* If there is no country code, return */ + if (countryCode.equals("")) + { + throw new + IllegalArgumentException("Invalid (empty) country code for locale:" + + loc); + } + /* Construct the key for the currency */ + currencyKey = countryCode + ".currency"; + /* Construct the key for the fraction digits */ + fractionDigitsKey = countryCode + ".fractionDigits"; + /* Retrieve the currency */ + currencyCode = properties.getProperty(currencyKey); + /* Return if the currency code is null */ + if (currencyCode == null) + { + return; + } + /* Split off the first currency code (we only use the first for now) */ + commaPosition = currencyCode.indexOf(","); + if (commaPosition != -1) + { + currencyCode = currencyCode.substring(0, commaPosition); + } + /* Retrieve the fraction digits */ + fractionDigits = Integer.parseInt(properties.getProperty(fractionDigitsKey)); + } + + /** + * Constructor for the "XXX" special case. This allows + * a Currency to be constructed from an assumed good + * currency code. + * + * @param code the code to use. + */ + private Currency(String code) + { + currencyCode = code; + fractionDigits = -1; /* Pseudo currency */ + } + + /** + * Returns the ISO4217 currency code of this currency. + * + * @return a String containing currency code. + */ + public String getCurrencyCode() + { + return currencyCode; + } + + /** + * Returns the number of digits which occur after the decimal point + * for this particular currency. For example, currencies such + * as the U.S. dollar, the Euro and the Great British pound have two + * digits following the decimal point to indicate the value which exists + * in the associated lower-valued coinage (cents in the case of the first + * two, pennies in the latter). Some currencies such as the Japanese + * Yen have no digits after the decimal point. In the case of pseudo + * currencies, such as IMF Special Drawing Rights, -1 is returned. + * + * @return the number of digits after the decimal separator for this currency. + */ + public int getDefaultFractionDigits() + { + return fractionDigits; + } + + /** + * Builds a new currency instance for this locale. + * All components of the given locale, other than the + * country code, are ignored. The results of calling this + * method may vary over time, as the currency associated with + * a particular country changes. For countries without + * a given currency (e.g. Antarctica), the result is null. + * + * @param locale a Locale instance. + * @return a new Currency instance. + * @throws NullPointerException if the locale or its + * country code is null. + * @throws IllegalArgumentException if the country of + * the given locale is not a supported ISO3166 code. + */ + public static Currency getInstance(Locale locale) + { + /** + * The new instance must be the only available instance + * for the currency it supports. We ensure this happens, + * while maintaining a suitable performance level, by + * creating the appropriate object on the first call to + * this method, and returning the cached instance on + * later calls. + */ + Currency newCurrency; + + String country = locale.getCountry(); + if (locale == null || country == null) + { + throw new + NullPointerException("The locale or its country is null."); + } + + /* Check that country of locale given is valid. */ + if (country.length() != 2) + throw new IllegalArgumentException(); + + /* Attempt to get the currency from the cache */ + String code = (String) countryMap.get(country); + if (code == null) + { + /* Create the currency for this locale */ + newCurrency = new Currency(locale); + /* + * If the currency code is null, then creation failed + * and we return null. + */ + code = newCurrency.getCurrencyCode(); + if (code == null) + { + return null; + } + else + { + /* Cache it */ + countryMap.put(country, code); + cache.put(code, newCurrency); + } + } + else + { + newCurrency = (Currency) cache.get(code); + } + /* Return the instance */ + return newCurrency; + } + + /** + * Builds the currency corresponding to the specified currency code. + * + * @param currencyCode a string representing a currency code. + * @return a new Currency instance. + * @throws NullPointerException if currencyCode is null. + * @throws IllegalArgumentException if the supplied currency code + * is not a supported ISO 4217 code. + */ + public static Currency getInstance(String currencyCode) + { + Locale[] allLocales; + + /* + * Throw a null pointer exception explicitly if currencyCode is null. + * One is not thrown otherwise. It results in an + * IllegalArgumentException. + */ + if (currencyCode == null) + { + throw new NullPointerException("The supplied currency code is null."); + } + /* Nasty special case to allow an erroneous currency... blame Sun */ + if (currencyCode.equals("XXX")) + return new Currency("XXX"); + Currency newCurrency = (Currency) cache.get(currencyCode); + if (newCurrency == null) + { + /* Get all locales */ + allLocales = Locale.getAvailableLocales(); + /* Loop through each locale, looking for the code */ + for (int i = 0;i < allLocales.length; i++) + { + try + { + Currency testCurrency = getInstance (allLocales[i]); + if (testCurrency != null && + testCurrency.getCurrencyCode().equals(currencyCode)) + { + return testCurrency; + } + } + catch (IllegalArgumentException exception) + { + /* Ignore locales without valid countries */ + } + } + /* + * If we get this far, the code is not supported by any of + * our locales. + */ + throw new IllegalArgumentException("The currency code, " + currencyCode + + ", is not supported."); + } + else + { + return newCurrency; + } + } + + /** + * This method returns the symbol which precedes or follows a + * value in this particular currency in the default locale. + * In cases where there is no such symbol for the currency, + * the ISO 4217 currency code is returned. + * + * @return the currency symbol, or the ISO 4217 currency code if + * one doesn't exist. + */ + public String getSymbol() + { + return getSymbol(Locale.getDefault()); + } + + /** + *

        + * This method returns the symbol which precedes or follows a + * value in this particular currency. The returned value is + * the symbol used to denote the currency in the specified locale. + *

        + *

        + * For example, a supplied locale may specify a different symbol + * for the currency, due to conflicts with its own currency. + * This would be the case with the American currency, the dollar. + * Locales that also use a dollar-based currency (e.g. Canada, Australia) + * need to differentiate the American dollar using 'US$' rather than '$'. + * So, supplying one of these locales to getSymbol() would + * return this value, rather than the standard '$'. + *

        + *

        + * In cases where there is no such symbol for a particular currency, + * the ISO 4217 currency code is returned. + *

        + * + * @param locale the locale to express the symbol in. + * @return the currency symbol, or the ISO 4217 currency code if + * one doesn't exist. + * @throws NullPointerException if the locale is null. + */ + public String getSymbol(Locale locale) + { + String property = "currenciesSymbol." + currencyCode; + try + { + return ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + locale).getString(property); + } + catch (MissingResourceException exception) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (CurrencyNameProvider p : + ServiceLoader.load(CurrencyNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(locale)) + { + String localizedString = p.getSymbol(currencyCode, + locale); + if (localizedString != null) + return localizedString; + break; + } + } + } + if (locale.equals(Locale.ROOT)) // Base case + return currencyCode; + return getSymbol(LocaleHelper.getFallbackLocale(locale)); + } + + /** + * Returns the international ISO4217 currency code of this currency. + * + * @return a String containing the ISO4217 currency code. + */ + public String toString() + { + return getCurrencyCode(); + } + + /** + * Resolves the deserialized object to the singleton instance for its + * particular currency. The currency code of the deserialized instance + * is used to return the correct instance. + * + * @return the singleton instance for the currency specified by the + * currency code of the deserialized object. This replaces + * the deserialized object as the returned object from + * deserialization. + * @throws ObjectStreamException if a problem occurs with deserializing + * the object. + */ + private Object readResolve() + throws ObjectStreamException + { + return getInstance(currencyCode); + } + +} diff --git a/libjava/classpath/java/util/Date.java b/libjava/classpath/java/util/Date.java new file mode 100644 index 000000000..3f7ba6f59 --- /dev/null +++ b/libjava/classpath/java/util/Date.java @@ -0,0 +1,1256 @@ +/* java.util.Date + Copyright (C) 1998, 1999, 2000, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +/** + *

        + * This class represents a specific time in milliseconds since the epoch. + * The epoch is 1970, January 1 00:00:00.0000 UTC. + *

        + *

        + * Date is intended to reflect universal time coordinate (UTC), + * but this depends on the underlying host environment. Most operating systems + * don't handle the leap second, which occurs about once every year or + * so. The leap second is added to the last minute of the day on either + * the 30th of June or the 31st of December, creating a minute 61 seconds + * in length. + *

        + *

        + * The representations of the date fields are as follows: + *

          + *
        • + * Years are specified as the difference between the year + * and 1900. Thus, the final year used is equal to + * 1900 + y, where y is the input value. + *
        • + *
        • + * Months are represented using zero-based indexing, + * making 0 January and 11 December. + *
        • + *
        • + * Dates are represented with the usual values of + * 1 through to 31. + *
        • + *
        • + * Hours are represented in the twenty-four hour clock, + * with integer values from 0 to 23. 12am is 0, and + * 12pm is 12. + *
        • + *
        • + * Minutes are again as usual, with values from 0 to 59. + *
        • + *
        • + * Seconds are represented with the values 0 through to 61, + * with 60 and 61 being leap seconds (as per the ISO C standard). + *
        • + *
        + *

        + *

        + * Prior to JDK 1.1, this class was the sole class handling date and time + * related functionality. However, this particular solution was not + * amenable to internationalization. The new Calendar + * class should now be used to handle dates and times, with Date + * being used only for values in milliseconds since the epoch. The + * Calendar class, and its concrete implementations, handle + * the interpretation of these values into minutes, hours, days, months + * and years. The formatting and parsing of dates is left to the + * DateFormat class, which is able to handle the different + * types of date format which occur in different locales. + *

        + * + * @see Calendar + * @see GregorianCalendar + * @see java.text.DateFormat + * @author Jochen Hoenicke + * @author Per Bothner (bothner@cygnus.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class Date + implements Cloneable, Comparable, Serializable +{ + /** + * This is the serialization UID for this class + * for compatability with Sun's JDK. + */ + private static final long serialVersionUID = 7523967970034938905L; + + /** + * The time in milliseconds since the epoch. + */ + private transient long time; + + /** + * An array of week names used to map names to integer values. + */ + private static final String[] weekNames = { "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat" }; + /** + * An array of month names used to map names to integer values. + */ + private static final String[] monthNames = { "Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" }; + /** + * Creates a new Date Object representing the current time. + */ + public Date() + { + time = System.currentTimeMillis(); + } + + /** + * Creates a new Date Object representing the given time. + * + * @param time the time in milliseconds since the epoch. + */ + public Date(long time) + { + this.time = time; + } + + /** + * Creates a new Date Object representing the given time. + * + * @deprecated use new GregorianCalendar(year+1900, month, + * day) instead. + * @param year the difference between the required year and 1900. + * @param month the month as a value between 0 and 11. + * @param day the day as a value between 0 and 31. + */ + public Date(int year, int month, int day) + { + this(year, month, day, 0, 0, 0); + } + + /** + * Creates a new Date Object representing the given time. + * + * @deprecated use new GregorianCalendar(year+1900, month, + * day, hour, min) instead. + * @param year the difference between the required year and 1900. + * @param month the month as a value between 0 and 11. + * @param day the day as a value between 0 and 31. + * @param hour the hour as a value between 0 and 23, in 24-hour + * clock notation. + * @param min the minute as a value between 0 and 59. + */ + public Date(int year, int month, int day, int hour, int min) + { + this(year, month, day, hour, min, 0); + } + + /** + * Creates a new Date Object representing the given time. + * + * @deprecated use new GregorianCalendar(year+1900, month, + * day, hour, min, sec) instead. + * @param year the difference between the required year and 1900. + * @param month the month as a value between 0 and 11. + * @param day the day as a value between 0 and 31. + * @param hour the hour as a value between 0 and 23, in 24-hour + * clock notation. + * @param min the minute as a value between 0 and 59. + * @param sec the second as a value between 0 and 61 (with 60 + * and 61 being leap seconds). + */ + public Date(int year, int month, int day, int hour, int min, int sec) + { + GregorianCalendar cal = + new GregorianCalendar(year + 1900, month, day, hour, min, sec); + time = cal.getTimeInMillis(); + } + + /** + * Creates a new Date from the given string representation. This + * does the same as new Date(Date.parse(s)) + * @see #parse + * @deprecated use java.text.DateFormat.parse(s) instead. + */ + public Date(String s) + { + time = parse(s); + } + + /** + * Returns a copy of this Date object. + * + * @return a copy, or null if the object couldn't be + * cloned. + * @see Object#clone() + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ex) + { + return null; + } + } + + /** + * Returns the number of milliseconds since the epoch + * specified by the given arguments. The arguments are + * interpreted relative to UTC rather than the local + * time zone. + * + * @deprecated Use Calendar with a UTC + * TimeZone instead. + * @param year the difference between the required year and 1900. + * @param month the month as a value between 0 and 11. + * @param date the day as a value between 0 and 31. + * @param hrs the hour as a value between 0 and 23, in 24-hour + * clock notation. + * @param min the minute as a value between 0 and 59. + * @param sec the second as a value between 0 and 61 (with 60 + * and 61 being leap seconds). + * @return the time in milliseconds since the epoch. + */ + public static long UTC(int year, int month, int date, + int hrs, int min, int sec) + { + GregorianCalendar cal = + new GregorianCalendar(year + 1900, month, date, hrs, min, sec); + cal.set(Calendar.ZONE_OFFSET, 0); + cal.set(Calendar.DST_OFFSET, 0); + return cal.getTimeInMillis(); + } + + /** + * Gets the time represented by this object. + * + * @return the time in milliseconds since the epoch. + */ + public long getTime() + { + return time; + } + + /** + * Returns the number of minutes offset used with UTC to give the time + * represented by this object in the current time zone. The date information + * from this object is also used to determine whether or not daylight savings + * time is in effect. For example, the offset for the UK would be 0 if the + * month of the date object was January, and 1 if the month was August. + * + * @deprecated use + * Calendar.get(Calendar.ZONE_OFFSET)+Calendar.get(Calendar.DST_OFFSET) + * instead. + * @return The time zone offset in minutes of the local time zone + * relative to UTC. The time represented by this object is used to + * determine if we should use daylight savings. + */ + public int getTimezoneOffset() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return - (cal.get(Calendar.ZONE_OFFSET) + + cal.get(Calendar.DST_OFFSET)) / (60 * 1000); + } + + /** + * Sets the time which this object should represent. + * + * @param time the time in milliseconds since the epoch. + */ + public void setTime(long time) + { + this.time = time; + } + + /** + * Tests if this date is after the specified date. + * + * @param when the other date + * @return true, if the date represented by this object is + * strictly later than the time represented by when. + */ + public boolean after(Date when) + { + return time > when.time; + } + + /** + * Tests if this date is before the specified date. + * + * @param when the other date + * @return true, if the date represented by when is strictly later + * than the time represented by this object. + */ + public boolean before(Date when) + { + return time < when.time; + } + + /** + * Compares two dates for equality. + * + * @param obj the object to compare. + * @return true, if obj is a Date object and the time represented + * by obj is exactly the same as the time represented by this + * object. + */ + public boolean equals(Object obj) + { + return (obj instanceof Date && time == ((Date) obj).time); + } + + /** + * Compares two dates. + * + * @param when the other date. + * @return 0, if the date represented + * by obj is exactly the same as the time represented by this + * object, a negative if this Date is before the other Date, and + * a positive value otherwise. + */ + public int compareTo(Date when) + { + return (time < when.time) ? -1 : (time == when.time) ? 0 : 1; + } + + /** + * Computes the hash code of this Date as the + * XOR of the most significant and the least significant + * 32 bits of the 64 bit milliseconds value. + * + * @return the hash code. + */ + public int hashCode() + { + return (int) time ^ (int) (time >>> 32); + } + + /** + *

        + * Returns a string representation of this date using + * the following date format: + *

        + *

        + * day mon dd hh:mm:ss zz yyyy + *

        + *

        where the fields used here are: + *

          + *
        • + * day -- the day of the week + * (Sunday through to Saturday). + *
        • + *
        • + * mon -- the month (Jan to Dec). + *
        • + *
        • + * dd -- the day of the month + * as two decimal digits (01 to 31). + *
        • + *
        • + * hh -- the hour of the day + * as two decimal digits in 24-hour clock notation + * (01 to 23). + *
        • + *
        • + * mm -- the minute of the day + * as two decimal digits (01 to 59). + *
        • + *
        • + * ss -- the second of the day + * as two decimal digits (01 to 61). + *
        • + *
        • + * zz -- the time zone information if available. + * The possible time zones used include the abbreviations + * recognised by parse() (e.g. GMT, CET, etc.) + * and may reflect the fact that daylight savings time is in + * effect. The empty string is used if there is no time zone + * information. + *
        • + *
        • + * yyyy -- the year as four decimal digits. + *
        • + *
        + *

        + * The DateFormat class should now be + * preferred over using this method. + *

        + * + * @return A string of the form 'day mon dd hh:mm:ss zz yyyy' + * @see #parse(String) + * @see DateFormat + */ + public String toString() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + String day = "0" + cal.get(Calendar.DATE); + String hour = "0" + cal.get(Calendar.HOUR_OF_DAY); + String min = "0" + cal.get(Calendar.MINUTE); + String sec = "0" + cal.get(Calendar.SECOND); + String year = "000" + cal.get(Calendar.YEAR); + return weekNames[cal.get(Calendar.DAY_OF_WEEK) - 1] + " " + + monthNames[cal.get(Calendar.MONTH)] + " " + + day.substring(day.length() - 2) + " " + + hour.substring(hour.length() - 2) + ":" + + min.substring(min.length() - 2) + ":" + + sec.substring(sec.length() - 2) + " " + + + cal.getTimeZone().getDisplayName(cal.getTimeZone().inDaylightTime(this), + TimeZone.SHORT) + " " + + year.substring(year.length() - 4); + } + + /** + * Returns a locale-dependent string representation of this + * Date object. + * + * @deprecated Use DateFormat.format(Date) + * @return A locale-dependent string representation. + * @see #parse(String) + * @see DateFormat + */ + public String toLocaleString() + { + return java.text.DateFormat.getInstance().format(this); + } + + /** + *

        + * Returns a string representation of this Date + * object using GMT rather than the local timezone. + * The following date format is used: + *

        + *

        + * d mon yyyy hh:mm:ss GMT + *

        + *

        where the fields used here are: + *

          + *
        • + * d -- the day of the month + * as one or two decimal digits (1 to 31). + *
        • + *
        • + * mon -- the month (Jan to Dec). + *
        • + *
        • + * yyyy -- the year as four decimal digits. + *
        • + *
        • + * hh -- the hour of the day + * as two decimal digits in 24-hour clock notation + * (01 to 23). + *
        • + *
        • + * mm -- the minute of the day + * as two decimal digits (01 to 59). + *
        • + *
        • + * ss -- the second of the day + * as two decimal digits (01 to 61). + *
        • + *
        • + * GMT -- the literal string "GMT" + * indicating Greenwich Mean Time as opposed to + * the local timezone. + *
        • + *
        + * + * @deprecated Use DateFormat.format(Date) with a GMT TimeZone. + * @return A string of the form 'd mon yyyy hh:mm:ss GMT' using + * GMT as opposed to the local timezone. + * @see #parse(String) + * @see DateFormat + */ + public String toGMTString() + { + java.text.DateFormat format = java.text.DateFormat.getInstance(); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + return format.format(this); + } + + /** + * Parses the time zone string. + * + * @param tok The token containing the time zone. + * @param sign The sign (+ or -) used by the time zone. + * @return An integer representing the number of minutes offset + * from GMT for the time zone. + */ + private static int parseTz(String tok, char sign) + throws IllegalArgumentException + { + int num; + + try + { + // parseInt doesn't handle '+' so strip off sign. + num = Integer.parseInt(tok.substring(1)); + } + catch (NumberFormatException ex) + { + throw new IllegalArgumentException(tok); + } + + // Convert hours to minutes. + if (num < 24) + num *= 60; + else + num = (num / 100) * 60 + num % 100; + + return sign == '-' ? -num : num; + } + + /** + * Parses the month string. + * + * @param tok the token containing the month. + * @return An integer between 0 and 11, representing + * a month from January (0) to December (11), + * or -1 if parsing failed. + */ + private static int parseMonth(String tok) + { + // Initialize strings for month names. + // We could possibly use the fields of DateFormatSymbols but that is + // localized and thus might not match the English words specified. + String months[] = { "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", + "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", + "NOVEMBER", "DECEMBER" }; + + int i; + for (i = 0; i < 12; i++) + if (months[i].startsWith(tok)) + return i; + + // Return -1 if not found. + return -1; + } + + /** + * Parses the day of the week string. + * + * @param tok the token containing the day of the week. + * @return true if the token was parsed successfully. + */ + private static boolean parseDayOfWeek(String tok) + { + // Initialize strings for days of the week names. + // We could possibly use the fields of DateFormatSymbols but that is + // localized and thus might not match the English words specified. + String daysOfWeek[] = { "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", + "THURSDAY", "FRIDAY", "SATURDAY" }; + + int i; + for (i = 0; i < 7; i++) + if (daysOfWeek[i].startsWith(tok)) + return true; + + return false; + } + + /** + *

        + * Parses a String and returns the time, in milliseconds since the + * epoch, it represents. Most syntaxes are handled, including + * the IETF date standard "day, dd mon yyyy hh:mm:ss zz" (see + * toString() for definitions of these fields). + * Standard U.S. time zone abbreviations are recognised, in + * addition to time zone offsets in positive or negative minutes. + * If a time zone is specified, the specified time is assumed to + * be in UTC and the appropriate conversion is applied, following + * parsing, to convert this to the local time zone. If no zone + * is specified, the time is assumed to already be in the local + * time zone. + *

        + *

        + * The method parses the string progressively from left to right. + * At the end of the parsing process, either a time is returned + * or an IllegalArgumentException is thrown to signify + * failure. The ASCII characters A-Z, a-z, 0-9, and ',', '+', '-', + * ':' and '/' are the only characters permitted within the string, + * besides whitespace and characters enclosed within parantheses + * '(' and ')'. + *

        + *

        + * A sequence of consecutive digits are recognised as a number, + * and interpreted as follows: + *

          + *
        • + * A number preceded by a sign (+ or -) is taken to be a time zone + * offset. The time zone offset can be specified in either hours + * or minutes. The former is assumed if the number is less than 24. + * Otherwise, the offset is assumed to be in minutes. A - indicates + * a time zone west of GMT, while a + represents a time zone to the + * east of GMT. The time zones are always assumed to be relative + * to GMT, and a (redundant) specification of this can be included + * with the time zone. For example, '-9', 'utc-9' and 'GMT-9' all + * represent a time zone nine hours west of GMT. Similarly, + * '+4', 'ut+4' and 'UTC+4' all give 4 hours east of GMT. + *
        • + *
        • + * A number equal to or greater than 70 is regarded as a year specification. + * Values lower than 70 are only assumed to indicate a year if both the + * day of the month and the month itself have already been recognised. + * Year values less than 100 are interpreted as being relative to the current + * century when the Date class is initialised.. Given a century, + * x, the year is assumed to be within the range x - 80 to x + 19. The value + * itself is then used as a match against the two last digits of one of these + * years. For example, take x to be 2004. A two-digit year is assumed to fall + * within the range x - 80 (1924) and x + 19 (2023). Thus, any intepreted value + * between 0 and 23 is assumed to be 2000 to 2023 and values between 24 and 99 + * are taken as being 1924 to 1999. This only applies for the case of 2004. + * With a different year, the values will be interpreted differently. 2005 + * will used 0 to 24 as 2000 to 2024 and 25 to 99 as 1925 to 1999, for example. + * This behaviour differs from that of SimpleDateFormat and is + * time-dependent (a two-digit year will be interpreted differently depending + * on the time the code is run). + *
        • + *
        • + * Numbers followed by a colon are interpreted by first an hour, and then + * as a minute, once an hour has been found. + *
        • + *
        • + *
        • + * Numbers followed by a slash are regarded first as a month, and then as + * a day of the month once the month has been found. This follows the + * U.S. date format of mm/dd, rather than the European dd/mm. Months + * are converted to the recognised value - 1 before storage, in order + * to put the number within the range 0 to 11. + *
        • + *
        • + * Numbers followed by commas, whitespace, hyphens or the end of the string + * are interpreted in the following order: hour, minute, second, day of month. + * The first type not already recognised in the current string being parsed is + * assumed. + *
        • + *
        + *

        + *

        + * A sequence of consecutive alphabetic characters is recognised as a word, + * and interpreted as follows, in a case-insentive fashion: + *

          + *
        • + * The characters 'AM' or 'PM' restrict the hour value to a value between 0 + * and 12. In the latter case, 12 is added to the hour value before storage. + *
        • + *
        • + * Any words which match any prefix of one of the days of the week ('Monday', + * 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' and 'Sunday'), + * are simply ignored. + *
        • + *
        • + * Any words which match any prefix of one of the months of the year ('January', + * 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', + * 'October', 'November', 'December') are recognised and interpreted as the + * appropriate value between 0 and 11. The first match made against a + * month is the one used, in the order specified here. For example, 'Ma' is + * intepreted as 'March' (2) and not as 'May' (4). Similarly, 'Ju' is 'June', + * and not 'July'. + *
        • + *
        • + * The words 'GMT', 'UT' and 'UTC' are interpreted as specifying UTC as the + * time zone in use for this date. + *
        • + *
        • + * The word pairs 'EST'/'EDT', 'CST'/'CDT', 'MST'/'MDT' and 'PST'/'PDT' are + * interpreted as the appropriate U.S. time zone abbreviation. Each pair + * is the standard and daylight savings time zone specification, respectively, + * for each zone within the U.S, these being Eastern Standard/Daylight Time + * (-5), Central Standard/Daylight Time (-6), Mountain Standard/Daylight Time + * (-7) and Pacific Standard/Daylight Time (-8). + *
        • + *
        + * + * @param string The String to parse. + * @return The time in milliseconds since the epoch. + * @throws IllegalArgumentException if the string fails to parse. + * @deprecated Use DateFormat.parse(String) + * @see #toString() + * @see SimpleDateFormat + */ + public static long parse(String string) + { + // Initialize date/time fields before parsing begins. + int year = -1; + int month = -1; + int day = -1; + int hour = -1; + int minute = -1; + int second = -1; + int timezone = 0; + boolean localTimezone = true; + + // Trim out any nested stuff in parentheses now to make parsing easier. + CPStringBuilder buf = new CPStringBuilder(); + int parenNesting = 0; + int len = string.length(); + for (int i = 0; i < len; i++) + { + char ch = string.charAt(i); + if (ch >= 'a' && ch <= 'z') + ch -= 'a' - 'A'; + if (ch == '(') + parenNesting++; + else if (parenNesting == 0) + buf.append(ch); + else if (ch == ')') + parenNesting--; + } + int tmpMonth; + + // Make all chars upper case to simplify comparisons later. + // Also ignore commas; treat them as delimiters. + StringTokenizer strtok = new StringTokenizer(buf.toString(), " \t\n\r,"); + + while (strtok.hasMoreTokens()) + { + String tok = strtok.nextToken(); + char firstch = tok.charAt(0); + if ((firstch == '+' || firstch == '-') && year >= 0) + { + timezone = parseTz(tok, firstch); + localTimezone = false; + } + else if (firstch >= '0' && firstch <= '9') + { + int lastPunct = -1; + while (tok != null && tok.length() > 0) + { + int punctOffset = tok.length(); + int num = 0; + int punct; + for (int i = 0; ; i++) + { + if (i >= punctOffset) + { + punct = -1; + break; + } + else + { + punct = tok.charAt(i); + if (punct >= '0' && punct <= '9') + { + if (num > 999999999) // in case of overflow + throw new IllegalArgumentException(tok); + num = 10 * num + (punct - '0'); + } + else + { + punctOffset = i; + break; + } + } + + } + + if (punct == ':') + { + if (hour < 0) + hour = num; + else + minute = num; + } + else if (lastPunct == ':' && hour >= 0 && (minute < 0 || second < 0)) + { + if (minute < 0) + minute = num; + else + second = num; + } + else if ((num >= 70 + && (punct == ' ' || punct == ',' + || punct == '/' || punct < 0)) + || (num < 70 && day >= 0 && month >= 0 && year < 0)) + { + if (num >= 100) + year = num; + else + { + int curYear = 1900 + new Date().getYear(); + int firstYear = curYear - 80; + year = firstYear / 100 * 100 + num; + if (year < firstYear) + year += 100; + } + } + else if (punct == '/') + { + if (month < 0) + month = num - 1; + else + day = num; + } + else if (hour >= 0 && minute < 0) + minute = num; + else if (minute >= 0 && second < 0) + second = num; + else if (day < 0) + day = num; + else + throw new IllegalArgumentException(tok); + + // Advance string if there's more to process in this token. + if (punct < 0 || punctOffset + 1 >= tok.length()) + tok = null; + else + tok = tok.substring(punctOffset + 1); + lastPunct = punct; + } + } + else if (firstch >= 'A' && firstch <= 'Z') + { + if (tok.equals("AM")) + { + if (hour < 1 || hour > 12) + throw new IllegalArgumentException(tok); + if (hour == 12) + hour = 0; + } + else if (tok.equals("PM")) + { + if (hour < 1 || hour > 12) + throw new IllegalArgumentException(tok); + if (hour < 12) + hour += 12; + } + else if (parseDayOfWeek(tok)) + { /* Ignore it; throw the token away. */ } + else if (tok.equals("UT") || tok.equals("UTC") || tok.equals("GMT")) + localTimezone = false; + else if (tok.startsWith("UT") || tok.startsWith("GMT")) + { + int signOffset = 3; + if (tok.charAt(1) == 'T' && tok.charAt(2) != 'C') + signOffset = 2; + + char sign = tok.charAt(signOffset); + if (sign != '+' && sign != '-') + throw new IllegalArgumentException(tok); + + timezone = parseTz(tok.substring(signOffset), sign); + localTimezone = false; + } + else if ((tmpMonth = parseMonth(tok)) >= 0) + month = tmpMonth; + else if (tok.length() == 3 && tok.charAt(2) == 'T') + { + // Convert timezone offset from hours to minutes. + char ch = tok.charAt(0); + if (ch == 'E') + timezone = -5 * 60; + else if (ch == 'C') + timezone = -6 * 60; + else if (ch == 'M') + timezone = -7 * 60; + else if (ch == 'P') + timezone = -8 * 60; + else + throw new IllegalArgumentException(tok); + + // Shift 60 minutes for Daylight Savings Time. + if (tok.charAt(1) == 'D') + timezone += 60; + else if (tok.charAt(1) != 'S') + throw new IllegalArgumentException(tok); + + localTimezone = false; + } + else + throw new IllegalArgumentException(tok); + } + else + throw new IllegalArgumentException(tok); + } + + // Unspecified hours, minutes, or seconds should default to 0. + if (hour < 0) + hour = 0; + if (minute < 0) + minute = 0; + if (second < 0) + second = 0; + + // Throw exception if any other fields have not been recognized and set. + if (year < 0 || month < 0 || day < 0) + throw new IllegalArgumentException("Missing field"); + + // Return the time in either local time or relative to GMT as parsed. + // If no time-zone was specified, get the local one (in minutes) and + // convert to milliseconds before adding to the UTC. + GregorianCalendar cal + = new GregorianCalendar(year, month, day, hour, minute, second); + if (!localTimezone) + { + cal.set(Calendar.ZONE_OFFSET, timezone * 60 * 1000); + cal.set(Calendar.DST_OFFSET, 0); + } + return cal.getTimeInMillis(); + } + + /** + * Returns the difference between the year represented by this + * Date object and 1900. + * + * @return the year minus 1900 represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.YEAR) + * instead. Note the 1900 difference in the year. + * @see Calendar + * @see #setYear(int) + */ + public int getYear() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.YEAR) - 1900; + } + + /** + * Sets the year to the specified year, plus 1900. The other + * fields are only altered as required to match the same date + * and time in the new year. Usually, this will mean that + * the fields are not changed at all, but in the case of + * a leap day or leap second, the fields will change in + * relation to the existence of such an event in the new year. + * For example, if the date specifies February the 29th, 2000, + * then this will become March the 1st if the year is changed + * to 2001, as 2001 is not a leap year. Similarly, a seconds + * value of 60 or 61 may result in the seconds becoming 0 and + * the minute increasing by 1, if the new time does not include + * a leap second. + * + * @param year the year minus 1900. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.YEAR, year) instead. Note about the 1900 + * difference in year. + * @see #getYear() + * @see Calendar + */ + public void setYear(int year) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.YEAR, 1900 + year); + time = cal.getTimeInMillis(); + } + + /** + * Returns the month represented by this Date object, + * as a value between 0 (January) and 11 (December). + * + * @return the month represented by this date object (zero based). + * @deprecated Use Calendar instead of Date, and use get(Calendar.MONTH) + * instead. + * @see #setMonth(int) + * @see Calendar + */ + public int getMonth() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.MONTH); + } + + /** + * Sets the month to the given value. The other + * fields are only altered as necessary to match + * the same date and time in the new month. In most + * cases, the other fields won't change at all. However, + * in the case of a shorter month or a leap second, values + * may be adjusted. For example, if the day of the month + * is currently 31, and the month value is changed from + * January (0) to September (8), the date will become + * October the 1st, as September only has 30 days. Similarly, + * a seconds value of 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1, if the new time does + * not include a leap second. + * + * @param month the month, with a zero-based index + * from January. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.MONTH, month) instead. + * @see #getMonth() + * @see Calendar + */ + public void setMonth(int month) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.MONTH, month); + time = cal.getTimeInMillis(); + } + + /** + * Returns the day of the month of this Date + * object, as a value between 0 and 31. + * + * @return the day of month represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.DATE) + * instead. + * @see Calendar + * @see #setDate(int) + */ + public int getDate() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.DATE); + } + + /** + * Sets the date to the given value. The other + * fields are only altered as necessary to match + * the same date and time on the new day of the month. In most + * cases, the other fields won't change at all. However, + * in the case of a leap second or the day being out of + * the range of the current month, values + * may be adjusted. For example, if the day of the month + * is currently 30 and the month is June, a new day of the + * month value of 31 will cause the month to change to July, + * as June only has 30 days . Similarly, + * a seconds value of 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1, if the new time does + * not include a leap second. + * + * @param date the date. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.DATE, date) instead. + * @see Calendar + * @see #getDate() + */ + public void setDate(int date) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.DATE, date); + time = cal.getTimeInMillis(); + } + + /** + * Returns the day represented by this Date + * object as an integer between 0 (Sunday) and 6 (Saturday). + * + * @return the day represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.DAY_OF_WEEK) + * instead. + * @see Calendar + */ + public int getDay() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + // For Calendar, Sunday is 1. For Date, Sunday is 0. + return cal.get(Calendar.DAY_OF_WEEK) - 1; + } + + /** + * Returns the hours represented by this Date + * object as an integer between 0 and 23. + * + * @return the hours represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.HOUR_OF_DAY) + * instead. + * @see Calendar + * @see #setHours(int) + */ + public int getHours() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.HOUR_OF_DAY); + } + + /** + * Sets the hours to the given value. The other + * fields are only altered as necessary to match + * the same date and time in the new hour. In most + * cases, the other fields won't change at all. However, + * in the case of a leap second, values + * may be adjusted. For example, + * a seconds value of 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1 if the new hour does + * not contain a leap second. + * + * @param hours the hours. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.HOUR_OF_DAY, hours) instead. + * @see Calendar + * @see #getHours() + */ + public void setHours(int hours) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.HOUR_OF_DAY, hours); + time = cal.getTimeInMillis(); + } + + /** + * Returns the number of minutes represented by the Date + * object, as an integer between 0 and 59. + * + * @return the minutes represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.MINUTE) + * instead. + * @see Calendar + * @see #setMinutes(int) + */ + public int getMinutes() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.MINUTE); + } + + /** + * Sets the minutes to the given value. The other + * fields are only altered as necessary to match + * the same date and time in the new minute. In most + * cases, the other fields won't change at all. However, + * in the case of a leap second, values + * may be adjusted. For example, + * a seconds value of 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1 if the new minute does + * not contain a leap second. + * + * @param minutes the minutes. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.MINUTE, minutes) instead. + * @see Calendar + * @see #getMinutes() + */ + public void setMinutes(int minutes) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.MINUTE, minutes); + time = cal.getTimeInMillis(); + } + + /** + * Returns the number of seconds represented by the Date + * object, as an integer between 0 and 61 (60 and 61 being leap seconds). + * + * @return the seconds represented by this date object. + * @deprecated Use Calendar instead of Date, and use get(Calendar.SECOND) + * instead. + * @see Calendar + * @see #setSeconds(int) + */ + public int getSeconds() + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.SECOND); + } + + /** + * Sets the seconds to the given value. The other + * fields are only altered as necessary to match + * the same date and time in the new minute. In most + * cases, the other fields won't change at all. However, + * in the case of a leap second, values + * may be adjusted. For example, setting the + * seconds value to 60 or 61 (a leap second) may result + * in the seconds value being reset to 0 and the minutes + * value being incremented by 1, if the current time does + * not contain a leap second. + * + * @param seconds the seconds. + * @deprecated Use Calendar instead of Date, and use + * set(Calendar.SECOND, seconds) instead. + * @see Calendar + * @see #getSeconds() + */ + public void setSeconds(int seconds) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + cal.set(Calendar.SECOND, seconds); + time = cal.getTimeInMillis(); + } + + /** + * Deserializes a Date object from an + * input stream, setting the time (in milliseconds + * since the epoch) to the long value read from the + * stream. + * + * @param input the input stream. + * @throws IOException if an I/O error occurs in the stream. + * @throws ClassNotFoundException if the class of the + * serialized object could not be found. + */ + private void readObject(ObjectInputStream input) + throws IOException, ClassNotFoundException + { + input.defaultReadObject(); + time = input.readLong(); + } + + /** + * Serializes a Date object to an output stream, + * storing the time (in milliseconds since the epoch) as a long + * value in the stream. + * + * @serialdata A long value representing the offset from the epoch + * in milliseconds. This is the same value that is returned by the + * method getTime(). + * @param output the output stream. + * @throws IOException if an I/O error occurs in the stream. + */ + private void writeObject(ObjectOutputStream output) + throws IOException + { + output.defaultWriteObject(); + output.writeLong(time); + } + +} diff --git a/libjava/classpath/java/util/Dictionary.java b/libjava/classpath/java/util/Dictionary.java new file mode 100644 index 000000000..acd90eb04 --- /dev/null +++ b/libjava/classpath/java/util/Dictionary.java @@ -0,0 +1,136 @@ +/* Dictionary.java -- an abstract (and essentially worthless) + class which is Hashtable's superclass + Copyright (C) 1998, 2001, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A Dictionary maps keys to values; how it does that is + * implementation-specific. + * + * This is an abstract class which has really gone by the wayside. + * People at Javasoft are probably embarrassed by it. At this point, + * it might as well be an interface rather than a class, but it remains + * this poor, laughable skeleton for the sake of backwards compatibility. + * At any rate, this was what came before the {@link Map} interface + * in the Collections framework. + * + * @author Jon Zeppieri + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see Hashtable + * @since 1.0 + * @status updated to 1.4 + */ +public abstract class Dictionary +{ + // WARNING: Dictionary is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * Sole constructor (often called implicitly). + */ + public Dictionary() + { + } + + /** + * Returns an Enumeration of the values in this Dictionary. + * + * @return an Enumeration of the values + * @see #keys() + */ + public abstract Enumeration elements(); + + /** + * Returns the value associated with the supplied key, or null + * if no such value exists. Since Dictionaries are not allowed null keys + * or elements, a null result always means the key is not present. + * + * @param key the key to use to fetch the value + * @return the mapped value + * @throws NullPointerException if key is null + * @see #put(Object, Object) + */ + public abstract V get(Object key); + + /** + * Returns true when there are no elements in this Dictionary. + * + * @return size() == 0 + */ + public abstract boolean isEmpty(); + + /** + * Returns an Enumeration of the keys in this Dictionary + * + * @return an Enumeration of the keys + * @see #elements() + */ + public abstract Enumeration keys(); + + /** + * Inserts a new value into this Dictionary, located by the + * supplied key. Dictionary does not support null keys or values, so + * a null return can safely be interpreted as adding a new key. + * + * @param key the key which locates the value + * @param value the value to put into the Dictionary + * @return the previous value of the key, or null if there was none + * @throws NullPointerException if key or value is null + * @see #get(Object) + */ + public abstract V put(K key, V value); + + /** + * Removes from the Dictionary the value located by the given key. A null + * return safely means that the key was not mapped in the Dictionary. + * + * @param key the key used to locate the value to be removed + * @return the value associated with the removed key + * @throws NullPointerException if key is null + */ + public abstract V remove(Object key); + + /** + * Returns the number of values currently in this Dictionary. + * + * @return the number of keys in the Dictionary + */ + public abstract int size(); +} // class Dictionary diff --git a/libjava/classpath/java/util/DuplicateFormatFlagsException.java b/libjava/classpath/java/util/DuplicateFormatFlagsException.java new file mode 100644 index 000000000..38c37669d --- /dev/null +++ b/libjava/classpath/java/util/DuplicateFormatFlagsException.java @@ -0,0 +1,88 @@ +/* DuplicateFormatFlagsException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the flags supplied to the {@link Formatter#format()} + * method of a {@link Formatter} contain duplicates. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class DuplicateFormatFlagsException + extends IllegalFormatException +{ + private static final long serialVersionUID = 18890531L; + + /** + * The flags which contain a duplicate. + * + * @serial the flags containing a duplicate. + */ + // Note: name fixed by serialization. + private String flags; + + /** + * Constructs a new DuplicateFormatFlagsException + * which specifies that the supplied set of flags contains a + * duplicate. + * + * @param flags the flags containing a duplicate. + * @throws NullPointerException if flags is null. + */ + public DuplicateFormatFlagsException(String flags) + { + super("Duplicate flag passed in " + flags); + if (flags == null) + throw new + NullPointerException("Null flags value passed to constructor."); + this.flags = flags; + } + + /** + * Returns the flags which contain a duplicate. + * + * @return the flags. + */ + public String getFlags() + { + return flags; + } +} diff --git a/libjava/classpath/java/util/EmptyStackException.java b/libjava/classpath/java/util/EmptyStackException.java new file mode 100644 index 000000000..e8b4509ee --- /dev/null +++ b/libjava/classpath/java/util/EmptyStackException.java @@ -0,0 +1,69 @@ +/* EmptyStackException.java -- Attempt to pop from an empty stack + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + */ + +/** + * This exception is thrown by the Stack class when an attempt is made to pop + * or otherwise access elements from an empty stack. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Stack + * @since 1.0 + * @status updated to 1.4 + */ +public class EmptyStackException extends RuntimeException +{ + /** + * Compatible with JDK 1.0. + */ + private static final long serialVersionUID = 5084686378493302095L; + + /** + * Constructs an EmptyStackException with no detail message. + */ + public EmptyStackException() + { + } +} diff --git a/libjava/classpath/java/util/EnumMap.java b/libjava/classpath/java/util/EnumMap.java new file mode 100644 index 000000000..78f05002f --- /dev/null +++ b/libjava/classpath/java/util/EnumMap.java @@ -0,0 +1,405 @@ +/* EnumMap.java - Map where keys are enum constants + Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ + +public class EnumMap, V> + extends AbstractMap + implements Cloneable, Serializable +{ + private static final long serialVersionUID = 458661240069192865L; + + V[] store; + int cardinality; + Class enumClass; + + /** + * The cache for {@link #entrySet()}. + */ + transient Set> entries; + + static final Object emptySlot = new Object(); + + public EnumMap(Class keyType) + { + store = (V[]) new Object[keyType.getEnumConstants().length]; + Arrays.fill(store, emptySlot); + cardinality = 0; + enumClass = keyType; + } + + public EnumMap(EnumMap map) + { + store = (V[]) map.store.clone(); + cardinality = map.cardinality; + enumClass = map.enumClass; + } + + public EnumMap(Map map) + { + if (map instanceof EnumMap) + { + EnumMap other = (EnumMap) map; + store = (V[]) other.store.clone(); + cardinality = other.cardinality; + enumClass = other.enumClass; + } + else + { + for (K key : map.keySet()) + { + V value = map.get(key); + if (store == null) + { + enumClass = key.getDeclaringClass(); + store = (V[]) new Object[enumClass.getEnumConstants().length]; + } + int o = key.ordinal(); + if (store[o] == emptySlot) + ++cardinality; + store[o] = value; + } + // There must be a single element. + if (store == null) + throw new IllegalArgumentException("no elements in map"); + } + } + + public int size() + { + return cardinality; + } + + public boolean containsValue(Object value) + { + for (V i : store) + { + if (i != emptySlot && AbstractCollection.equals(i , value)) + return true; + } + return false; + } + + public boolean containsKey(Object key) + { + if (! (key instanceof Enum)) + return false; + Enum e = (Enum) key; + if (e.getDeclaringClass() != enumClass) + return false; + return store[e.ordinal()] != emptySlot; + } + + public V get(Object key) + { + if (! (key instanceof Enum)) + return null; + Enum e = (Enum) key; + if (e.getDeclaringClass() != enumClass) + return null; + V o = store[e.ordinal()]; + return o == emptySlot ? null : o; + } + + public V put(K key, V value) + { + int o = key.ordinal(); + V result; + if (store[o] == emptySlot) + { + result = null; + ++cardinality; + } + else + result = store[o]; + store[o] = value; + return result; + } + + public V remove(Object key) + { + if (! (key instanceof Enum)) + return null; + Enum e = (Enum) key; + if (e.getDeclaringClass() != enumClass) + return null; + V result = store[e.ordinal()]; + if (result == emptySlot) + result = null; + else + --cardinality; + store[e.ordinal()] = (V) emptySlot; + return result; + } + + public void putAll(Map map) + { + for (K key : map.keySet()) + { + V value = map.get(key); + + int o = key.ordinal(); + if (store[o] == emptySlot) + ++cardinality; + store[o] = value; + } + } + + public void clear() + { + Arrays.fill(store, emptySlot); + cardinality = 0; + } + + public Set keySet() + { + if (keys == null) + { + keys = new AbstractSet() + { + public int size() + { + return cardinality; + } + + public Iterator iterator() + { + return new Iterator() + { + int count = 0; + int index = -1; + + public boolean hasNext() + { + return count < cardinality; + } + + public K next() + { + ++count; + for (++index; store[index] == emptySlot; ++index) + ; + return enumClass.getEnumConstants()[index]; + } + + public void remove() + { + --cardinality; + store[index] = (V) emptySlot; + } + }; + } + + public void clear() + { + EnumMap.this.clear(); + } + + public boolean contains(Object o) + { + return contains(o); + } + + public boolean remove(Object o) + { + return EnumMap.this.remove(o) != null; + } + }; + } + return keys; + } + + public Collection values() + { + if (values == null) + { + values = new AbstractCollection() + { + public int size() + { + return cardinality; + } + + public Iterator iterator() + { + return new Iterator() + { + int count = 0; + int index = -1; + + public boolean hasNext() + { + return count < cardinality; + } + + public V next() + { + ++count; + for (++index; store[index] == emptySlot; ++index) + ; + return store[index]; + } + + public void remove() + { + --cardinality; + store[index] = (V) emptySlot; + } + }; + } + + public void clear() + { + EnumMap.this.clear(); + } + }; + } + return values; + } + + public Set> entrySet() + { + if (entries == null) + { + entries = new AbstractSet>() + { + public int size() + { + return cardinality; + } + + public Iterator> iterator() + { + return new Iterator>() + { + int count = 0; + int index = -1; + + public boolean hasNext() + { + return count < cardinality; + } + + public Map.Entry next() + { + ++count; + for (++index; store[index] == emptySlot; ++index) + ; + // FIXME: we could just return something that + // only knows the index. That would be cleaner. + return new AbstractMap.SimpleEntry(enumClass.getEnumConstants()[index], + store[index]) + { + public V setValue(V newVal) + { + value = newVal; + return put(key, newVal); + } + }; + } + + public void remove() + { + --cardinality; + store[index] = (V) emptySlot; + } + }; + } + + public void clear() + { + EnumMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry other = (Map.Entry) o; + return (containsKey(other.getKey()) + && AbstractCollection.equals(get(other.getKey()), + other.getValue())); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry other = (Map.Entry) o; + return EnumMap.this.remove(other.getKey()) != null; + } + }; + } + return entries; + } + + public boolean equals(Object o) + { + if (! (o instanceof EnumMap)) + return false; + EnumMap other = (EnumMap) o; + if (other.enumClass != enumClass || other.cardinality != cardinality) + return false; + return Arrays.equals(store, other.store); + } + + public EnumMap clone() + { + EnumMap result; + try + { + result = (EnumMap) super.clone(); + } + catch (CloneNotSupportedException ignore) + { + // Can't happen. + result = null; + } + result.store = (V[]) store.clone(); + return result; + } + +} diff --git a/libjava/classpath/java/util/EnumSet.java b/libjava/classpath/java/util/EnumSet.java new file mode 100644 index 000000000..60d010654 --- /dev/null +++ b/libjava/classpath/java/util/EnumSet.java @@ -0,0 +1,518 @@ +/* EnumSet.java - Set of enum objects + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + *

        + * Provides an efficient mechanism for recording a set of enumeration + * constants. As enumerations have a known set of possible values, certain + * assumptions can be made when creating a set of constants. The maximum + * size of the set will always be equal to the number of constants, and each + * value will always be one of these constants. As a result, the set only needs + * to store whether a particular constant is present or not rather than the + * values themselves. Each constant can thus be represented by a single bit. + *

        + *

        + * This class is designed to provide an alternative to using integer bit flags + * by providing a typesafe {@link Collection} interface with an underlying + * implementation that utilises the assumptions above to give an equivalent level + * of efficiency. The values in a {@link EnumSet} must all be from the same + * {@link Enum} type, which allows the contents to be packed into a bit vector. + * A containment test is then simply a matter of inspecting the appropriate bit, while + * addition involves setting the same. Such basic operations take place in constant + * time. + *

        + *

        + * The {@link Iterator} implementation traverses the values in the natural order + * of the enumeration provided by each constant's {@link Enum#ordinal()}. It is + * weakly consistent and will not throw a {@link ConcurrentModificationException}. + * This means that concurrent changes to the set may or may not be noticeable during + * traversal. + *

        + *

        + * As is usual with most collections, the set is not synchronized by default. This + * can be remedied by using the {@link Collections#synchronizedSet(Set)} method. Null + * elements are not supported and attempts to add one will throw a {@link NullPointerException}. + *

        + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @author Dalibor Topic (robilad@kaffe.org) + * @since 1.5 + */ + +// FIXME: serialization is special, uses SerializationProxy. +// of(E e) is the 'bottom' method that creates a real EnumSet. +public abstract class EnumSet> + extends AbstractSet + implements Cloneable, Serializable +{ + private static final long serialVersionUID = 4782406773684236311L; + + // These fields could go into the anonymous inner class in of(E), + // complementOf would need to be refactored then, though. + /** + * The store which maintains the bits used to represent + * the enumeration constants. + */ + BitSet store; + + /** + * The cardinality of the set (the current number + * of bits set). + */ + int cardinality; + + /** + * The enumeration used by this set. + */ + Class enumClass; + + /** + * Empty package-private constructor + */ + EnumSet() + { + } + + /** + * Returns a clone of the set. + * + * @return a clone of the set. + */ + public EnumSet clone() + { + EnumSet r; + + try + { + r = (EnumSet) super.clone(); + } + catch (CloneNotSupportedException _) + { + /* Can't happen */ + return null; + } + r.store = (BitSet) store.clone(); + return r; + } + + /** + * Returns a set for the given enumeration type where + * all the constants are present. + * + * @param eltType the type of enumeration to use for the set. + * @return an {@link EnumSet} with all the bits set. + * @throws NullPointerException if the element type is null. + */ + public static > EnumSet allOf(Class eltType) + { + // create an EnumSet from the list of values of the type + return copyOf(Arrays.asList(eltType.getEnumConstants())); + } + + /** + * Returns a set for the given enumeration type where + * none of the constants are present. + * + * @param eltType the type of enumeration to use for the set. + * @return an {@link EnumSet} with none of the bits set. + * @throws NullPointerException if the element type is null. + */ + public static > EnumSet noneOf(Class eltType) + { + return complementOf(allOf(eltType)); + } + + /** + * Returns a clone of the given set. + * + * @param other the set to clone. + * @return an {@link EnumSet} that is a clone of the given set. + * @throws NullPointerException if other is null. + */ + public static > EnumSet copyOf(EnumSet other) + { + return other.clone(); + } + + /** + * Creates an {@link EnumSet} using the contents of the given collection. + * If the collection is also an {@link EnumSet}, this method works the + * same as {@link #copyOf(EnumSet)}. Otherwise, the elements of the collection + * are inspected and used to populate the new set. + * + * @param other the collection to use to populate the new set. + * @return an {@link EnumSet} containing elements from the given collection. + * @throws NullPointerException if other is null. + * @throws IllegalArgumentException if the collection is empty. + */ + public static > EnumSet copyOf(Collection other) + { + if (other instanceof EnumSet) + return copyOf((EnumSet) other); + if (other.isEmpty()) + throw new IllegalArgumentException("Collection is empty"); + + EnumSet r = null; + + for (T val : other) + { + if (r == null) + r = of(val); + else + r.add(val); + } + + return r; + } + + /** + * Returns a set which is the inverse of the supplied set. + * If a constant is present in the current set, it will not be + * present in the new set and vice versa. + * + * @param other the set to provide the complement of. + * @return an {@link EnumSet} which is the inverse of the current one. + * @throws NullPointerException if other is null. + */ + public static > EnumSet complementOf(EnumSet other) + { + EnumSet r = other.clone(); + int numConstants = r.enumClass.getEnumConstants().length; + r.store.flip(0, numConstants); + r.cardinality = numConstants - other.cardinality; + return r; + } + + /** + * Creates a new {@link EnumSet} populated with the given element. + * + * @param first the element to use to populate the new set. + * @return an {@link EnumSet} containing the element. + * @throws NullPointerException if first is null. + */ + public static > EnumSet of(T first) + { + EnumSet r = new EnumSet() + { + public boolean add(T val) + { + if (store.get(val.ordinal())) + return false; + + store.set(val.ordinal()); + ++cardinality; + return true; + } + + public boolean addAll(Collection c) + { + boolean result = false; + if (c instanceof EnumSet) + { + EnumSet other = (EnumSet) c; + if (enumClass == other.enumClass) + { + store.or(other.store); + int save = cardinality; + cardinality = store.cardinality(); + result = save != cardinality; + } + } + else + { + for (T val : c) + { + if (add (val)) + result = true; + } + } + return result; + } + + public void clear() + { + store.clear(); + cardinality = 0; + } + + public boolean contains(Object o) + { + if (! (o instanceof Enum)) + return false; + + Enum e = (Enum) o; + if (e.getDeclaringClass() != enumClass) + return false; + + return store.get(e.ordinal()); + } + + public boolean containsAll(Collection c) + { + if (c instanceof EnumSet) + { + EnumSet other = (EnumSet) c; + if (enumClass == other.enumClass) + return store.containsAll(other.store); + + return false; + } + return super.containsAll(c); + } + + public Iterator iterator() + { + return new Iterator() + { + int next = -1; + int count = 0; + + public boolean hasNext() + { + return count < cardinality; + } + + public T next() + { + next = store.nextSetBit(next + 1); + ++count; + return enumClass.getEnumConstants()[next]; + } + + public void remove() + { + if (! store.get(next)) + { + store.clear(next); + --cardinality; + } + } + }; + } + + public boolean remove(Object o) + { + if (! (o instanceof Enum)) + return false; + + Enum e = (Enum) o; + if (e.getDeclaringClass() != enumClass) + return false; + + store.clear(e.ordinal()); + --cardinality; + return true; + } + + public boolean removeAll(Collection c) + { + if (c instanceof EnumSet) + { + EnumSet other = (EnumSet) c; + if (enumClass != other.enumClass) + return false; + + store.andNot(other.store); + int save = cardinality; + cardinality = store.cardinality(); + return save != cardinality; + } + return super.removeAll(c); + } + + public boolean retainAll(Collection c) + { + if (c instanceof EnumSet) + { + EnumSet other = (EnumSet) c; + if (enumClass != other.enumClass) + return false; + + store.and(other.store); + int save = cardinality; + cardinality = store.cardinality(); + return save != cardinality; + } + return super.retainAll(c); + } + + public int size() + { + return cardinality; + } + }; + + // initialize the class + r.enumClass = first.getDeclaringClass(); + r.store = new BitSet(r.enumClass.getEnumConstants().length); + + r.add(first); + return r; + } + + /** + * Creates a new {@link EnumSet} populated with the given two elements. + * + * @param first the first element to use to populate the new set. + * @param second the second element to use. + * @return an {@link EnumSet} containing the elements. + * @throws NullPointerException if any of the parameters are null. + */ + public static > EnumSet of(T first, T second) + { + EnumSet r = of(first); + r.add(second); + return r; + } + + /** + * Creates a new {@link EnumSet} populated with the given three elements. + * + * @param first the first element to use to populate the new set. + * @param second the second element to use. + * @param third the third element to use. + * @return an {@link EnumSet} containing the elements. + * @throws NullPointerException if any of the parameters are null. + */ + public static > EnumSet of(T first, T second, T third) + { + EnumSet r = of(first, second); + r.add(third); + return r; + } + + /** + * Creates a new {@link EnumSet} populated with the given four elements. + * + * @param first the first element to use to populate the new set. + * @param second the second element to use. + * @param third the third element to use. + * @param fourth the fourth element to use. + * @return an {@link EnumSet} containing the elements. + * @throws NullPointerException if any of the parameters are null. + */ + public static > EnumSet of(T first, T second, T third, + T fourth) + { + EnumSet r = of(first, second, third); + r.add(fourth); + return r; + } + + /** + * Creates a new {@link EnumSet} populated with the given five elements. + * + * @param first the first element to use to populate the new set. + * @param second the second element to use. + * @param third the third element to use. + * @param fourth the fourth element to use. + * @param fifth the fifth element to use. + * @return an {@link EnumSet} containing the elements. + * @throws NullPointerException if any of the parameters are null. + */ + public static > EnumSet of(T first, T second, T third, + T fourth, T fifth) + { + EnumSet r = of(first, second, third, fourth); + r.add(fifth); + return r; + } + + /** + * Creates a new {@link EnumSet} populated with the given elements. + * + * @param first the first element to use to populate the new set. + * @param rest the other elements to use. + * @return an {@link EnumSet} containing the elements. + * @throws NullPointerException if any of the parameters are null. + */ + public static > EnumSet of(T first, T... rest) + { + EnumSet r = noneOf(first.getDeclaringClass()); + r.add(first); + for (T val : rest) + r.add(val); + return r; + } + + /** + * Creates a new {@link EnumSet} using the enumeration constants + * starting from {@code from} and ending at {@code to} inclusive. + * The two may be the same, but they must be in the correct order. + * So giving the first constant twice would give a set with just that + * constant set, while supplying the first and second constant will give + * a set with those two elements. However, specifying the second as + * the {@code from} element followed by an earlier element as the + * {@code to} element will result in an error. + * + * @param from the element to start from. + * @param to the element to end at (may be the same as {@code from}. + * @return an {@link EnumSet} containing the specified range of elements. + * @throws NullPointerException if any of the parameters are null. + * @throws IllegalArgumentException if {@code first.compareTo(last) > 0}. + */ + public static > EnumSet range(T from, T to) + { + if (from.compareTo(to) > 0) + throw new IllegalArgumentException(); + Class type = from.getDeclaringClass(); + EnumSet r = noneOf(type); + + T[] values = type.getEnumConstants(); + // skip over values until start of range is found + int i = 0; + while (from != values[i]) + i++; + + // add values until end of range is found + while (to != values[i]) { + r.add(values[i]); + i++; + } + + // add end of range + r.add(to); + + return r; + } +} diff --git a/libjava/classpath/java/util/Enumeration.java b/libjava/classpath/java/util/Enumeration.java new file mode 100644 index 000000000..2aec31b6f --- /dev/null +++ b/libjava/classpath/java/util/Enumeration.java @@ -0,0 +1,82 @@ +/* Enumeration.java -- Interface for enumerating lists of objects + Copyright (C) 1998, 1999, 2001, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1. + * Status: Believed complete and correct + */ + +/** + * Interface for lists of objects that can be returned in sequence. Successive + * objects are obtained by the nextElement method. + *

        + * As of Java 1.2, the Iterator interface provides the same functionality, but + * with shorter method names and a new optional method to remove items from the + * list. If writing for 1.2, consider using Iterator instead. Enumerations over + * the new collections classes, for use with legacy APIs that require them, can + * be obtained by the enumeration method in class Collections. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Iterator + * @see Hashtable + * @see Vector + * @since 1.0 + * @status updated to 1.4 + */ +public interface Enumeration +{ + /** + * Tests whether there are elements remaining in the enumeration. + * + * @return true if there is at least one more element in the enumeration, + * that is, if the next call to nextElement will not throw a + * NoSuchElementException. + */ + boolean hasMoreElements(); + + /** + * Obtain the next element in the enumeration. + * + * @return the next element in the enumeration + * @throws NoSuchElementException if there are no more elements + */ + E nextElement(); +} diff --git a/libjava/classpath/java/util/EventListener.java b/libjava/classpath/java/util/EventListener.java new file mode 100644 index 000000000..c9a1795f9 --- /dev/null +++ b/libjava/classpath/java/util/EventListener.java @@ -0,0 +1,54 @@ +/* EventListener.java -- tagging interface for all event listeners + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Empty interface that is implemented by classes that need to receive + * events. Subinterfaces define methods that can be called to fire an + * event notification. Normally the name of these subinterfaces end in + * Listener and all method described by the subinterface + * take as argument an subclass of EventObject. + * + * @author Tom Tromey (tromey@cygnus.com) + * @see EventObject + * @status updated to 1.4 + */ +public interface EventListener +{ +} diff --git a/libjava/classpath/java/util/EventListenerProxy.java b/libjava/classpath/java/util/EventListenerProxy.java new file mode 100644 index 000000000..245c5ffb4 --- /dev/null +++ b/libjava/classpath/java/util/EventListenerProxy.java @@ -0,0 +1,75 @@ +/* EventListenerProxy.java -- abstract wrapper for event listeners + Copyright (C) 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An abstract wrapper for event listeners. This allows subclasses to + * attach additional parameters to an existing event listener to create + * a new one. Subclasses are expected to add methods to set and retrieve + * any attached properties. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.4 + * @status updated to 1.4 + */ +public abstract class EventListenerProxy implements EventListener +{ + /** The listener that this proxy wraps. */ + private final EventListener listener; + + /** + * Construct a proxy event listener, given an existing one to augment. + * + * @param listener the listener to wrap + */ + public EventListenerProxy(EventListener listener) + { + this.listener = listener; + } + + /** + * Return the wrapped event listener. + * + * @return the listener associated with this proxy + */ + public EventListener getListener() + { + return listener; + } +} // class EventListenerProxy diff --git a/libjava/classpath/java/util/EventObject.java b/libjava/classpath/java/util/EventObject.java new file mode 100644 index 000000000..7ced18aa4 --- /dev/null +++ b/libjava/classpath/java/util/EventObject.java @@ -0,0 +1,101 @@ +/* EventObject.java -- represents an event on an object + Copyright (C) 1999, 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * Represents Events fired by Objects. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see EventListener + * @since 1.1 + * @status updated to 1.4 + */ +public class EventObject implements Serializable +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5516075349620653480L; + + /** + * The source object; in other words, the object which this event takes + * place on. + */ + protected transient Object source; + + /** + * Constructs an EventObject with the specified source. + * + * @param source the source of the event + * @throws IllegalArgumentException if source is null (This is not + * specified, but matches the behavior of the JDK) + */ + public EventObject(Object source) + { + // This check for null is stupid, if you ask me, since source is + // protected and non-final, so a subclass can set it to null later on. + if (source == null) + throw new IllegalArgumentException(); + this.source = source; + } + + /** + * Returns the source of the event. + * + * @return the event source + */ + public Object getSource() + { + return source; + } + + /** + * Converts the event to a String. The format is not specified, but by + * observation, the JDK uses: + * getClass().getName() + "[source=" + source + "]";. + * + * @return String representation of the Event + */ + public String toString() + { + return getClass().getName() + "[source=" + source + "]"; + } +} // class EventObject diff --git a/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java b/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java new file mode 100644 index 000000000..b28ded187 --- /dev/null +++ b/libjava/classpath/java/util/FormatFlagsConversionMismatchException.java @@ -0,0 +1,111 @@ +/* FormatFlagsConversionMismatchException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the flags supplied to the {@link Formatter#format()} + * method of a {@link Formatter} contains a flag that does not match + * the conversion character specified for it. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class FormatFlagsConversionMismatchException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19120414L; + + /** + * The mismatching flag. + * + * @serial the mismatching flag. + */ + // Note: name fixed by serialization. + private String f; + + /** + * The conversion character which doesn't match the + * appropriate flag. + * + * @serial the conversion character which doesn't match its flag. + */ + // Note: name fixed by serialization. + private char c; + + /** + * Constructs a new FormatFlagsConversionMismatchException + * which specifies that the flag, f, does + * not match its appropriate conversion character, c. + * + * @param f the mismatching flag. + * @param c the conversion character which doesn't match its flag. + * @throws NullPointerException if f is null. + */ + public FormatFlagsConversionMismatchException(String f, char c) + { + super("Invalid flag " + f + " for conversion " + c); + if (f == null) + throw new + NullPointerException("Null flag value passed to constructor."); + this.f = f; + this.c = c; + } + + /** + * Returns the conversion character which doesn't + * match the flag. + * + * @return the conversion character. + */ + public char getConversion() + { + return c; + } + + /** + * Returns the mismatching flag. + * + * @return the mismatching flag. + */ + public String getFlags() + { + return f; + } +} diff --git a/libjava/classpath/java/util/Formattable.java b/libjava/classpath/java/util/Formattable.java new file mode 100644 index 000000000..6a83ed5d2 --- /dev/null +++ b/libjava/classpath/java/util/Formattable.java @@ -0,0 +1,92 @@ +/* Formattable.java -- Objects which can be passed to a Formatter + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + *

        + * The Formattable interface is used to provide customised + * formatting to arbitrary objects via the {@link Formatter}. The + * {@link #formatTo} method is called for Formattable + * objects used with the 's' conversion operator, allowing the object + * to provide its own formatting of its internal data. + *

        + *

        + * Thread safety is left up to the implementing class. Thus, + * {@link Formattable} objects are not guaranteed to be thread-safe, + * and users should make their own provisions for multiple thread access. + *

        + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public interface Formattable +{ + + /** + * Formats the object using the supplied formatter to the specification + * provided by the given flags, width and precision. + * + * @param formatter the formatter to use for formatting the object. + * The formatter gives access to the output stream + * and locale via {@link Formatter#out()} and + * {@link Formatter#locale()} respectively. + * @param flags a bit mask constructed from the flags in the + * {@link FormattableFlags} class. When no flags + * are set, the implementing class should use its + * defaults. + * @param width the minimum number of characters to include. + * A value of -1 indicates no minimum. The remaining + * space is padded with ' ' either on the left + * (the default) or right (if left justification is + * specified by the flags). + * @param precision the maximum number of characters to include. + * A value of -1 indicates no maximum. This value + * is applied prior to the minimum (the width). Thus, + * a value may meet the minimum width initially, but + * not when the width value is applied, due to + * characters being removed by the precision value. + * @throws IllegalFormatException if there is a problem with + * the syntax of the format + * specification or a mismatch + * between it and the arguments. + */ + public void formatTo(Formatter formatter, int flags, int width, + int precision); +} diff --git a/libjava/classpath/java/util/FormattableFlags.java b/libjava/classpath/java/util/FormattableFlags.java new file mode 100644 index 000000000..2c5e199ba --- /dev/null +++ b/libjava/classpath/java/util/FormattableFlags.java @@ -0,0 +1,123 @@ +/* FormattableFlags.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This class contains a set of flags used + * by the {@link Formattable#formatTo()} method. + * They are used to modify the output of the + * {@link Formattable}. The interpretation and + * validation of the flags is left to the + * particular {@link Formattable}. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class FormattableFlags +{ + + /** + * Requires the output to be left-justified. Any spaces + * required to meet the specified width will be added to + * the right of the output. The default output is + * right-justified, where spaces are added to the left. + * The output is as for the format specifier + * '-' ('\u002d'). + */ + public static final int LEFT_JUSTIFY = 1; + + /** + * Requires the output to be in uppercase. The output + * should be the same as the result from calling + * {@link String#toUpperCase(java.util.Locale)} with + * the formatting locale. The output is as for the + * format specifier '^' ('\u005e'). + */ + public static final int UPPERCASE = 2; + + /** + * Requires the use of an alternate form, as specified + * in the documentation of {@link Formattable}. + * The output is as for the format specifier + * '#' ('\u0023'). + */ + public static final int ALTERNATE = 4; + + // Used internally by Formatter. + // Changes here must be reflected in the FLAGS string there. + + /** + * Requires the output to always include a '+' sign. + * The output is as for the format specifier '+'. + */ + static final int PLUS = 8; + + /** + * Requires the output to include a leading space on + * positive value. The output is as for the format + * specifier ' '. + */ + static final int SPACE = 16; + + /** + * Requires the output to be zero-padded. The output + * is as for the format specifier '0'. + */ + static final int ZERO = 32; + + /** + * Requires the output to include locale-specific + * grouping operators. The output is as for the + * format specifier ','. + */ + static final int COMMA = 64; + + /** + * Requires the output to include negative numbers + * enclosed in parentheses. The output is as for + * the format specifier '('. + */ + static final int PAREN = 128; + + // Not instantiable. + private FormattableFlags() + { + } +} diff --git a/libjava/classpath/java/util/Formatter.java b/libjava/classpath/java/util/Formatter.java new file mode 100644 index 000000000..04ae8058d --- /dev/null +++ b/libjava/classpath/java/util/Formatter.java @@ -0,0 +1,1498 @@ +/* Formatter.java -- printf-style formatting + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.text.DateFormatSymbols; +import java.text.DecimalFormatSymbols; + +import gnu.classpath.SystemProperties; + +/** + *

        + * A Java formatter for printf-style format strings, + * as seen in the C programming language. This differs from the + * C interpretation of such strings by performing much stricter + * checking of format specifications and their corresponding + * arguments. While unknown conversions will be ignored in C, + * and invalid conversions will only produce compiler warnings, + * the Java version utilises a full range of run-time exceptions to + * handle these cases. The Java version is also more customisable + * by virtue of the provision of the {@link Formattable} interface, + * which allows an arbitrary class to be formatted by the formatter. + *

        + *

        + * The formatter is accessible by more convienient static methods. + * For example, streams now have appropriate format methods + * (the equivalent of fprintf) as do String + * objects (the equivalent of sprintf). + *

        + *

        + * Note: the formatter is not thread-safe. For + * multi-threaded access, external synchronization should be provided. + *

        + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public final class Formatter + implements Closeable, Flushable +{ + + /** + * The output of the formatter. + */ + private Appendable out; + + /** + * The locale used by the formatter. + */ + private Locale locale; + + /** + * Whether or not the formatter is closed. + */ + private boolean closed; + + /** + * The last I/O exception thrown by the output stream. + */ + private IOException ioException; + + // Some state used when actually formatting. + /** + * The format string. + */ + private String format; + + /** + * The current index into the string. + */ + private int index; + + /** + * The length of the format string. + */ + private int length; + + /** + * The formatting locale. + */ + private Locale fmtLocale; + + // Note that we include '-' twice. The flags are ordered to + // correspond to the values in FormattableFlags, and there is no + // flag (in the sense of this field used when parsing) for + // UPPERCASE; the second '-' serves as a placeholder. + /** + * A string used to index into the formattable flags. + */ + private static final String FLAGS = "--#+ 0,("; + + /** + * The system line separator. + */ + private static final String lineSeparator + = SystemProperties.getProperty("line.separator"); + + /** + * The type of numeric output format for a {@link BigDecimal}. + */ + public enum BigDecimalLayoutForm + { + DECIMAL_FLOAT, + SCIENTIFIC + } + + /** + * Constructs a new Formatter using the default + * locale and a {@link StringBuilder} as the output stream. + */ + public Formatter() + { + this(null, Locale.getDefault()); + } + + /** + * Constructs a new Formatter using the specified + * locale and a {@link StringBuilder} as the output stream. + * If the locale is null, then no localization + * is applied. + * + * @param loc the locale to use. + */ + public Formatter(Locale loc) + { + this(null, loc); + } + + /** + * Constructs a new Formatter using the default + * locale and the specified output stream. + * + * @param app the output stream to use. + */ + public Formatter(Appendable app) + { + this(app, Locale.getDefault()); + } + + /** + * Constructs a new Formatter using the specified + * locale and the specified output stream. If the locale is + * null, then no localization is applied. + * + * @param app the output stream to use. + * @param loc the locale to use. + */ + public Formatter(Appendable app, Locale loc) + { + this.out = app == null ? new StringBuilder() : app; + this.locale = loc; + } + + /** + * Constructs a new Formatter using the default + * locale and character set, with the specified file as the + * output stream. + * + * @param file the file to use for output. + * @throws FileNotFoundException if the file does not exist + * and can not be created. + * @throws SecurityException if a security manager is present + * and doesn't allow writing to the file. + */ + public Formatter(File file) + throws FileNotFoundException + { + this(new OutputStreamWriter(new FileOutputStream(file))); + } + + /** + * Constructs a new Formatter using the default + * locale, with the specified file as the output stream + * and the supplied character set. + * + * @param file the file to use for output. + * @param charset the character set to use for output. + * @throws FileNotFoundException if the file does not exist + * and can not be created. + * @throws SecurityException if a security manager is present + * and doesn't allow writing to the file. + * @throws UnsupportedEncodingException if the supplied character + * set is not supported. + */ + public Formatter(File file, String charset) + throws FileNotFoundException, UnsupportedEncodingException + { + this(file, charset, Locale.getDefault()); + } + + /** + * Constructs a new Formatter using the specified + * file as the output stream with the supplied character set + * and locale. If the locale is null, then no + * localization is applied. + * + * @param file the file to use for output. + * @param charset the character set to use for output. + * @param loc the locale to use. + * @throws FileNotFoundException if the file does not exist + * and can not be created. + * @throws SecurityException if a security manager is present + * and doesn't allow writing to the file. + * @throws UnsupportedEncodingException if the supplied character + * set is not supported. + */ + public Formatter(File file, String charset, Locale loc) + throws FileNotFoundException, UnsupportedEncodingException + { + this(new OutputStreamWriter(new FileOutputStream(file), charset), + loc); + } + + /** + * Constructs a new Formatter using the default + * locale and character set, with the specified output stream. + * + * @param out the output stream to use. + */ + public Formatter(OutputStream out) + { + this(new OutputStreamWriter(out)); + } + + /** + * Constructs a new Formatter using the default + * locale, with the specified file output stream and the + * supplied character set. + * + * @param out the output stream. + * @param charset the character set to use for output. + * @throws UnsupportedEncodingException if the supplied character + * set is not supported. + */ + public Formatter(OutputStream out, String charset) + throws UnsupportedEncodingException + { + this(out, charset, Locale.getDefault()); + } + + /** + * Constructs a new Formatter using the specified + * output stream with the supplied character set and locale. + * If the locale is null, then no localization is + * applied. + * + * @param out the output stream. + * @param charset the character set to use for output. + * @param loc the locale to use. + * @throws UnsupportedEncodingException if the supplied character + * set is not supported. + */ + public Formatter(OutputStream out, String charset, Locale loc) + throws UnsupportedEncodingException + { + this(new OutputStreamWriter(out, charset), loc); + } + + /** + * Constructs a new Formatter using the default + * locale with the specified output stream. The character + * set used is that of the output stream. + * + * @param out the output stream to use. + */ + public Formatter(PrintStream out) + { + this((Appendable) out); + } + + /** + * Constructs a new Formatter using the default + * locale and character set, with the specified file as the + * output stream. + * + * @param file the file to use for output. + * @throws FileNotFoundException if the file does not exist + * and can not be created. + * @throws SecurityException if a security manager is present + * and doesn't allow writing to the file. + */ + public Formatter(String file) throws FileNotFoundException + { + this(new OutputStreamWriter(new FileOutputStream(file))); + } + + /** + * Constructs a new Formatter using the default + * locale, with the specified file as the output stream + * and the supplied character set. + * + * @param file the file to use for output. + * @param charset the character set to use for output. + * @throws FileNotFoundException if the file does not exist + * and can not be created. + * @throws SecurityException if a security manager is present + * and doesn't allow writing to the file. + * @throws UnsupportedEncodingException if the supplied character + * set is not supported. + */ + public Formatter(String file, String charset) + throws FileNotFoundException, UnsupportedEncodingException + { + this(file, charset, Locale.getDefault()); + } + + /** + * Constructs a new Formatter using the specified + * file as the output stream with the supplied character set + * and locale. If the locale is null, then no + * localization is applied. + * + * @param file the file to use for output. + * @param charset the character set to use for output. + * @param loc the locale to use. + * @throws FileNotFoundException if the file does not exist + * and can not be created. + * @throws SecurityException if a security manager is present + * and doesn't allow writing to the file. + * @throws UnsupportedEncodingException if the supplied character + * set is not supported. + */ + public Formatter(String file, String charset, Locale loc) + throws FileNotFoundException, UnsupportedEncodingException + { + this(new OutputStreamWriter(new FileOutputStream(file), charset), + loc); + } + + /** + * Closes the formatter, so as to release used resources. + * If the underlying output stream supports the {@link Closeable} + * interface, then this is also closed. Attempts to use + * a formatter instance, via any method other than + * {@link #ioException()}, after closure results in a + * {@link FormatterClosedException}. + */ + public void close() + { + if (closed) + return; + try + { + if (out instanceof Closeable) + ((Closeable) out).close(); + } + catch (IOException _) + { + // FIXME: do we ignore these or do we set ioException? + // The docs seem to indicate that we should ignore. + } + closed = true; + } + + /** + * Flushes the formatter, writing any cached data to the output + * stream. If the underlying output stream supports the + * {@link Flushable} interface, it is also flushed. + * + * @throws FormatterClosedException if the formatter is closed. + */ + public void flush() + { + if (closed) + throw new FormatterClosedException(); + try + { + if (out instanceof Flushable) + ((Flushable) out).flush(); + } + catch (IOException _) + { + // FIXME: do we ignore these or do we set ioException? + // The docs seem to indicate that we should ignore. + } + } + + /** + * Return the name corresponding to a flag. + * + * @param flags the flag to return the name of. + * @return the name of the flag. + */ + private String getName(int flags) + { + // FIXME: do we want all the flags in here? + // Or should we redo how this is reported? + int bit = Integer.numberOfTrailingZeros(flags); + return FLAGS.substring(bit, bit + 1); + } + + /** + * Verify the flags passed to a conversion. + * + * @param flags the flags to verify. + * @param allowed the allowed flags mask. + * @param conversion the conversion character. + */ + private void checkFlags(int flags, int allowed, char conversion) + { + flags &= ~allowed; + if (flags != 0) + throw new FormatFlagsConversionMismatchException(getName(flags), + conversion); + } + + /** + * Throw an exception if a precision was specified. + * + * @param precision the precision value (-1 indicates not specified). + */ + private void noPrecision(int precision) + { + if (precision != -1) + throw new IllegalFormatPrecisionException(precision); + } + + /** + * Apply the numeric localization algorithm to a StringBuilder. + * + * @param builder the builder to apply to. + * @param flags the formatting flags to use. + * @param width the width of the numeric value. + * @param isNegative true if the value is negative. + */ + private void applyLocalization(CPStringBuilder builder, int flags, int width, + boolean isNegative) + { + DecimalFormatSymbols dfsyms; + if (fmtLocale == null) + dfsyms = new DecimalFormatSymbols(); + else + dfsyms = new DecimalFormatSymbols(fmtLocale); + + // First replace each digit. + char zeroDigit = dfsyms.getZeroDigit(); + int decimalOffset = -1; + for (int i = builder.length() - 1; i >= 0; --i) + { + char c = builder.charAt(i); + if (c >= '0' && c <= '9') + builder.setCharAt(i, (char) (c - '0' + zeroDigit)); + else if (c == '.') + { + assert decimalOffset == -1; + decimalOffset = i; + } + } + + // Localize the decimal separator. + if (decimalOffset != -1) + { + builder.deleteCharAt(decimalOffset); + builder.insert(decimalOffset, dfsyms.getDecimalSeparator()); + } + + // Insert the grouping separators. + if ((flags & FormattableFlags.COMMA) != 0) + { + char groupSeparator = dfsyms.getGroupingSeparator(); + int groupSize = 3; // FIXME + int offset = (decimalOffset == -1) ? builder.length() : decimalOffset; + // We use '>' because we don't want to insert a separator + // before the first digit. + for (int i = offset - groupSize; i > 0; i -= groupSize) + builder.insert(i, groupSeparator); + } + + if ((flags & FormattableFlags.ZERO) != 0) + { + // Zero fill. Note that according to the algorithm we do not + // insert grouping separators here. + for (int i = width - builder.length(); i > 0; --i) + builder.insert(0, zeroDigit); + } + + if (isNegative) + { + if ((flags & FormattableFlags.PAREN) != 0) + { + builder.insert(0, '('); + builder.append(')'); + } + else + builder.insert(0, '-'); + } + else if ((flags & FormattableFlags.PLUS) != 0) + builder.insert(0, '+'); + else if ((flags & FormattableFlags.SPACE) != 0) + builder.insert(0, ' '); + } + + /** + * A helper method that handles emitting a String after applying + * precision, width, justification, and upper case flags. + * + * @param arg the string to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @throws IOException if the output stream throws an I/O error. + */ + private void genericFormat(String arg, int flags, int width, int precision) + throws IOException + { + if ((flags & FormattableFlags.UPPERCASE) != 0) + { + if (fmtLocale == null) + arg = arg.toUpperCase(); + else + arg = arg.toUpperCase(fmtLocale); + } + + if (precision >= 0 && arg.length() > precision) + arg = arg.substring(0, precision); + + boolean leftJustify = (flags & FormattableFlags.LEFT_JUSTIFY) != 0; + if (leftJustify && width == -1) + throw new MissingFormatWidthException("fixme"); + if (! leftJustify && arg.length() < width) + { + for (int i = width - arg.length(); i > 0; --i) + out.append(' '); + } + out.append(arg); + if (leftJustify && arg.length() < width) + { + for (int i = width - arg.length(); i > 0; --i) + out.append(' '); + } + } + + /** + * Emit a boolean. + * + * @param arg the boolean to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void booleanFormat(Object arg, int flags, int width, int precision, + char conversion) + throws IOException + { + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + String result; + if (arg instanceof Boolean) + result = String.valueOf((Boolean) arg); + else + result = arg == null ? "false" : "true"; + genericFormat(result, flags, width, precision); + } + + /** + * Emit a hash code. + * + * @param arg the hash code to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void hashCodeFormat(Object arg, int flags, int width, int precision, + char conversion) + throws IOException + { + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + genericFormat(arg == null ? "null" : Integer.toHexString(arg.hashCode()), + flags, width, precision); + } + + /** + * Emit a String or Formattable conversion. + * + * @param arg the String or Formattable to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void stringFormat(Object arg, int flags, int width, int precision, + char conversion) + throws IOException + { + if (arg instanceof Formattable) + { + checkFlags(flags, + (FormattableFlags.LEFT_JUSTIFY + | FormattableFlags.UPPERCASE + | FormattableFlags.ALTERNATE), + conversion); + Formattable fmt = (Formattable) arg; + fmt.formatTo(this, flags, width, precision); + } + else + { + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + genericFormat(arg == null ? "null" : arg.toString(), flags, width, + precision); + } + } + + /** + * Emit a character. + * + * @param arg the character to emit. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void characterFormat(Object arg, int flags, int width, int precision, + char conversion) + throws IOException + { + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + noPrecision(precision); + + int theChar; + if (arg instanceof Character) + theChar = ((Character) arg).charValue(); + else if (arg instanceof Byte) + theChar = (char) (((Byte) arg).byteValue ()); + else if (arg instanceof Short) + theChar = (char) (((Short) arg).shortValue ()); + else if (arg instanceof Integer) + { + theChar = ((Integer) arg).intValue(); + if (! Character.isValidCodePoint(theChar)) + throw new IllegalFormatCodePointException(theChar); + } + else + throw new IllegalFormatConversionException(conversion, arg.getClass()); + String result = new String(Character.toChars(theChar)); + genericFormat(result, flags, width, precision); + } + + /** + * Emit a '%'. + * + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @throws IOException if the output stream throws an I/O error. + */ + private void percentFormat(int flags, int width, int precision) + throws IOException + { + checkFlags(flags, FormattableFlags.LEFT_JUSTIFY, '%'); + noPrecision(precision); + genericFormat("%", flags, width, precision); + } + + /** + * Emit a newline. + * + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @throws IOException if the output stream throws an I/O error. + */ + private void newLineFormat(int flags, int width, int precision) + throws IOException + { + checkFlags(flags, 0, 'n'); + noPrecision(precision); + if (width != -1) + throw new IllegalFormatWidthException(width); + genericFormat(lineSeparator, flags, width, precision); + } + + /** + * Helper method to do initial formatting and checking for integral + * conversions. + * + * @param arg the formatted argument. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param radix the radix of the number. + * @param conversion the conversion character. + * @return the result. + */ + private CPStringBuilder basicIntegralConversion(Object arg, int flags, + int width, int precision, + int radix, char conversion) + { + assert radix == 8 || radix == 10 || radix == 16; + noPrecision(precision); + + // Some error checking. + if ((flags & FormattableFlags.PLUS) != 0 + && (flags & FormattableFlags.SPACE) != 0) + throw new IllegalFormatFlagsException(getName(flags)); + + if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0 && width == -1) + throw new MissingFormatWidthException("fixme"); + + // Do the base translation of the value to a string. + String result; + int basicFlags = (FormattableFlags.LEFT_JUSTIFY + // We already handled any possible error when + // parsing. + | FormattableFlags.UPPERCASE + | FormattableFlags.ZERO); + if (radix == 10) + basicFlags |= (FormattableFlags.PLUS + | FormattableFlags.SPACE + | FormattableFlags.COMMA + | FormattableFlags.PAREN); + else + basicFlags |= FormattableFlags.ALTERNATE; + + if (arg instanceof BigInteger) + { + checkFlags(flags, + (basicFlags + | FormattableFlags.PLUS + | FormattableFlags.SPACE + | FormattableFlags.PAREN), + conversion); + BigInteger bi = (BigInteger) arg; + result = bi.toString(radix); + } + else if (arg instanceof Number + && ! (arg instanceof Float) + && ! (arg instanceof Double)) + { + checkFlags(flags, basicFlags, conversion); + long value = ((Number) arg).longValue (); + if (radix == 8) + result = Long.toOctalString(value); + else if (radix == 16) + result = Long.toHexString(value); + else + result = Long.toString(value); + } + else + throw new IllegalFormatConversionException(conversion, arg.getClass()); + + return new CPStringBuilder(result); + } + + /** + * Emit a hex or octal value. + * + * @param arg the hexadecimal or octal value. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param radix the radix of the number. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void hexOrOctalConversion(Object arg, int flags, int width, + int precision, int radix, + char conversion) + throws IOException + { + assert radix == 8 || radix == 16; + + CPStringBuilder builder = basicIntegralConversion(arg, flags, width, + precision, radix, + conversion); + int insertPoint = 0; + + // Insert the sign. + if (builder.charAt(0) == '-') + { + // Already inserted. Note that we don't insert a sign, since + // the only case where it is needed it BigInteger, and it has + // already been inserted by toString. + ++insertPoint; + } + else if ((flags & FormattableFlags.PLUS) != 0) + { + builder.insert(insertPoint, '+'); + ++insertPoint; + } + else if ((flags & FormattableFlags.SPACE) != 0) + { + builder.insert(insertPoint, ' '); + ++insertPoint; + } + + // Insert the radix prefix. + if ((flags & FormattableFlags.ALTERNATE) != 0) + { + builder.insert(insertPoint, radix == 8 ? "0" : "0x"); + insertPoint += radix == 8 ? 1 : 2; + } + + // Now justify the result. + int resultWidth = builder.length(); + if (resultWidth < width) + { + char fill = ((flags & FormattableFlags.ZERO) != 0) ? '0' : ' '; + if ((flags & FormattableFlags.LEFT_JUSTIFY) != 0) + { + // Left justify. + if (fill == ' ') + insertPoint = builder.length(); + } + else + { + // Right justify. Insert spaces before the radix prefix + // and sign. + insertPoint = 0; + } + while (resultWidth++ < width) + builder.insert(insertPoint, fill); + } + + String result = builder.toString(); + if ((flags & FormattableFlags.UPPERCASE) != 0) + { + if (fmtLocale == null) + result = result.toUpperCase(); + else + result = result.toUpperCase(fmtLocale); + } + + out.append(result); + } + + /** + * Emit a decimal value. + * + * @param arg the hexadecimal or octal value. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void decimalConversion(Object arg, int flags, int width, + int precision, char conversion) + throws IOException + { + CPStringBuilder builder = basicIntegralConversion(arg, flags, width, + precision, 10, + conversion); + boolean isNegative = false; + if (builder.charAt(0) == '-') + { + // Sign handling is done during localization. + builder.deleteCharAt(0); + isNegative = true; + } + + applyLocalization(builder, flags, width, isNegative); + genericFormat(builder.toString(), flags, width, precision); + } + + /** + * Emit a single date or time conversion to a StringBuilder. + * + * @param builder the builder to write to. + * @param cal the calendar to use in the conversion. + * @param conversion the formatting character to specify the type of data. + * @param syms the date formatting symbols. + */ + private void singleDateTimeConversion(CPStringBuilder builder, Calendar cal, + char conversion, + DateFormatSymbols syms) + { + int oldLen = builder.length(); + int digits = -1; + switch (conversion) + { + case 'H': + builder.append(cal.get(Calendar.HOUR_OF_DAY)); + digits = 2; + break; + case 'I': + builder.append(cal.get(Calendar.HOUR)); + digits = 2; + break; + case 'k': + builder.append(cal.get(Calendar.HOUR_OF_DAY)); + break; + case 'l': + builder.append(cal.get(Calendar.HOUR)); + break; + case 'M': + builder.append(cal.get(Calendar.MINUTE)); + digits = 2; + break; + case 'S': + builder.append(cal.get(Calendar.SECOND)); + digits = 2; + break; + case 'N': + // FIXME: nanosecond ... + digits = 9; + break; + case 'p': + { + int ampm = cal.get(Calendar.AM_PM); + builder.append(syms.getAmPmStrings()[ampm]); + } + break; + case 'z': + { + int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60); + builder.append(zone); + digits = 4; + // Skip the '-' sign. + if (zone < 0) + ++oldLen; + } + break; + case 'Z': + { + // FIXME: DST? + int zone = cal.get(Calendar.ZONE_OFFSET) / (1000 * 60 * 60); + String[][] zs = syms.getZoneStrings(); + builder.append(zs[zone + 12][1]); + } + break; + case 's': + { + long val = cal.getTime().getTime(); + builder.append(val / 1000); + } + break; + case 'Q': + { + long val = cal.getTime().getTime(); + builder.append(val); + } + break; + case 'B': + { + int month = cal.get(Calendar.MONTH); + builder.append(syms.getMonths()[month]); + } + break; + case 'b': + case 'h': + { + int month = cal.get(Calendar.MONTH); + builder.append(syms.getShortMonths()[month]); + } + break; + case 'A': + { + int day = cal.get(Calendar.DAY_OF_WEEK); + builder.append(syms.getWeekdays()[day]); + } + break; + case 'a': + { + int day = cal.get(Calendar.DAY_OF_WEEK); + builder.append(syms.getShortWeekdays()[day]); + } + break; + case 'C': + builder.append(cal.get(Calendar.YEAR) / 100); + digits = 2; + break; + case 'Y': + builder.append(cal.get(Calendar.YEAR)); + digits = 4; + break; + case 'y': + builder.append(cal.get(Calendar.YEAR) % 100); + digits = 2; + break; + case 'j': + builder.append(cal.get(Calendar.DAY_OF_YEAR)); + digits = 3; + break; + case 'm': + builder.append(cal.get(Calendar.MONTH) + 1); + digits = 2; + break; + case 'd': + builder.append(cal.get(Calendar.DAY_OF_MONTH)); + digits = 2; + break; + case 'e': + builder.append(cal.get(Calendar.DAY_OF_MONTH)); + break; + case 'R': + singleDateTimeConversion(builder, cal, 'H', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'M', syms); + break; + case 'T': + singleDateTimeConversion(builder, cal, 'H', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'M', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'S', syms); + break; + case 'r': + singleDateTimeConversion(builder, cal, 'I', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'M', syms); + builder.append(':'); + singleDateTimeConversion(builder, cal, 'S', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'p', syms); + break; + case 'D': + singleDateTimeConversion(builder, cal, 'm', syms); + builder.append('/'); + singleDateTimeConversion(builder, cal, 'd', syms); + builder.append('/'); + singleDateTimeConversion(builder, cal, 'y', syms); + break; + case 'F': + singleDateTimeConversion(builder, cal, 'Y', syms); + builder.append('-'); + singleDateTimeConversion(builder, cal, 'm', syms); + builder.append('-'); + singleDateTimeConversion(builder, cal, 'd', syms); + break; + case 'c': + singleDateTimeConversion(builder, cal, 'a', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'b', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'd', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'T', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'Z', syms); + builder.append(' '); + singleDateTimeConversion(builder, cal, 'Y', syms); + break; + default: + throw new UnknownFormatConversionException(String.valueOf(conversion)); + } + + if (digits > 0) + { + int newLen = builder.length(); + int delta = newLen - oldLen; + while (delta++ < digits) + builder.insert(oldLen, '0'); + } + } + + /** + * Emit a date or time value. + * + * @param arg the date or time value. + * @param flags the formatting flags to use. + * @param width the width to use. + * @param precision the precision to use. + * @param conversion the conversion character. + * @param subConversion the sub conversion character. + * @throws IOException if the output stream throws an I/O error. + */ + private void dateTimeConversion(Object arg, int flags, int width, + int precision, char conversion, + char subConversion) + throws IOException + { + noPrecision(precision); + checkFlags(flags, + FormattableFlags.LEFT_JUSTIFY | FormattableFlags.UPPERCASE, + conversion); + + Calendar cal; + if (arg instanceof Calendar) + cal = (Calendar) arg; + else + { + Date date; + if (arg instanceof Date) + date = (Date) arg; + else if (arg instanceof Long) + date = new Date(((Long) arg).longValue()); + else + throw new IllegalFormatConversionException(conversion, + arg.getClass()); + if (fmtLocale == null) + cal = Calendar.getInstance(); + else + cal = Calendar.getInstance(fmtLocale); + cal.setTime(date); + } + + // We could try to be more efficient by computing this lazily. + DateFormatSymbols syms; + if (fmtLocale == null) + syms = new DateFormatSymbols(); + else + syms = new DateFormatSymbols(fmtLocale); + + CPStringBuilder result = new CPStringBuilder(); + singleDateTimeConversion(result, cal, subConversion, syms); + + genericFormat(result.toString(), flags, width, precision); + } + + /** + * Advance the internal parsing index, and throw an exception + * on overrun. + * + * @throws IllegalArgumentException on overrun. + */ + private void advance() + { + ++index; + if (index >= length) + { + // FIXME: what exception here? + throw new IllegalArgumentException(); + } + } + + /** + * Parse an integer appearing in the format string. Will return -1 + * if no integer was found. + * + * @return the parsed integer. + */ + private int parseInt() + { + int start = index; + while (Character.isDigit(format.charAt(index))) + advance(); + if (start == index) + return -1; + return Integer.decode(format.substring(start, index)); + } + + /** + * Parse the argument index. Returns -1 if there was no index, 0 if + * we should re-use the previous index, and a positive integer to + * indicate an absolute index. + * + * @return the parsed argument index. + */ + private int parseArgumentIndex() + { + int result = -1; + int start = index; + if (format.charAt(index) == '<') + { + result = 0; + advance(); + } + else if (Character.isDigit(format.charAt(index))) + { + result = parseInt(); + if (format.charAt(index) == '$') + advance(); + else + { + // Reset. + index = start; + result = -1; + } + } + return result; + } + + /** + * Parse a set of flags and return a bit mask of values from + * FormattableFlags. Will throw an exception if a flag is + * duplicated. + * + * @return the parsed flags. + */ + private int parseFlags() + { + int value = 0; + int start = index; + while (true) + { + int x = FLAGS.indexOf(format.charAt(index)); + if (x == -1) + break; + int newValue = 1 << x; + if ((value & newValue) != 0) + throw new DuplicateFormatFlagsException(format.substring(start, + index + 1)); + value |= newValue; + advance(); + } + return value; + } + + /** + * Parse the width part of a format string. Returns -1 if no width + * was specified. + * + * @return the parsed width. + */ + private int parseWidth() + { + return parseInt(); + } + + /** + * If the current character is '.', parses the precision part of a + * format string. Returns -1 if no precision was specified. + * + * @return the parsed precision. + */ + private int parsePrecision() + { + if (format.charAt(index) != '.') + return -1; + advance(); + int precision = parseInt(); + if (precision == -1) + // FIXME + throw new IllegalArgumentException(); + return precision; + } + + /** + * Outputs a formatted string based on the supplied specification, + * fmt, and its arguments using the specified locale. + * The locale of the formatter does not change as a result; the + * specified locale is just used for this particular formatting + * operation. If the locale is null, then no + * localization is applied. + * + * @param loc the locale to use for this format. + * @param fmt the format specification. + * @param args the arguments to apply to the specification. + * @throws IllegalFormatException if there is a problem with + * the syntax of the format + * specification or a mismatch + * between it and the arguments. + * @throws FormatterClosedException if the formatter is closed. + */ + public Formatter format(Locale loc, String fmt, Object... args) + { + if (closed) + throw new FormatterClosedException(); + + // Note the arguments are indexed starting at 1. + int implicitArgumentIndex = 1; + int previousArgumentIndex = 0; + + try + { + fmtLocale = loc; + format = fmt; + length = format.length(); + for (index = 0; index < length; ++index) + { + char c = format.charAt(index); + if (c != '%') + { + out.append(c); + continue; + } + + int start = index; + advance(); + + // We do the needed post-processing of this later, when we + // determine whether an argument is actually needed by + // this conversion. + int argumentIndex = parseArgumentIndex(); + + int flags = parseFlags(); + int width = parseWidth(); + int precision = parsePrecision(); + char origConversion = format.charAt(index); + char conversion = origConversion; + if (Character.isUpperCase(conversion)) + { + flags |= FormattableFlags.UPPERCASE; + conversion = Character.toLowerCase(conversion); + } + + Object argument = null; + if (conversion == '%' || conversion == 'n') + { + if (argumentIndex != -1) + { + // FIXME: not sure about this. + throw new UnknownFormatConversionException("FIXME"); + } + } + else + { + if (argumentIndex == -1) + argumentIndex = implicitArgumentIndex++; + else if (argumentIndex == 0) + argumentIndex = previousArgumentIndex; + // Argument indices start at 1 but array indices at 0. + --argumentIndex; + if (argumentIndex < 0 || argumentIndex >= args.length) + throw new MissingFormatArgumentException(format.substring(start, index)); + argument = args[argumentIndex]; + } + + switch (conversion) + { + case 'b': + booleanFormat(argument, flags, width, precision, + origConversion); + break; + case 'h': + hashCodeFormat(argument, flags, width, precision, + origConversion); + break; + case 's': + stringFormat(argument, flags, width, precision, + origConversion); + break; + case 'c': + characterFormat(argument, flags, width, precision, + origConversion); + break; + case 'd': + checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'd'); + decimalConversion(argument, flags, width, precision, + origConversion); + break; + case 'o': + checkFlags(flags & FormattableFlags.UPPERCASE, 0, 'o'); + hexOrOctalConversion(argument, flags, width, precision, 8, + origConversion); + break; + case 'x': + hexOrOctalConversion(argument, flags, width, precision, 16, + origConversion); + case 'e': + // scientificNotationConversion(); + break; + case 'f': + // floatingDecimalConversion(); + break; + case 'g': + // smartFloatingConversion(); + break; + case 'a': + // hexFloatingConversion(); + break; + case 't': + advance(); + char subConversion = format.charAt(index); + dateTimeConversion(argument, flags, width, precision, + origConversion, subConversion); + break; + case '%': + percentFormat(flags, width, precision); + break; + case 'n': + newLineFormat(flags, width, precision); + break; + default: + throw new UnknownFormatConversionException(String.valueOf(origConversion)); + } + } + } + catch (IOException exc) + { + ioException = exc; + } + return this; + } + + /** + * Outputs a formatted string based on the supplied specification, + * fmt, and its arguments using the formatter's locale. + * + * @param format the format specification. + * @param args the arguments to apply to the specification. + * @throws IllegalFormatException if there is a problem with + * the syntax of the format + * specification or a mismatch + * between it and the arguments. + * @throws FormatterClosedException if the formatter is closed. + */ + public Formatter format(String format, Object... args) + { + return format(locale, format, args); + } + + /** + * Returns the last I/O exception thrown by the + * append() operation of the underlying + * output stream. + * + * @return the last I/O exception. + */ + public IOException ioException() + { + return ioException; + } + + /** + * Returns the locale used by this formatter. + * + * @return the formatter's locale. + * @throws FormatterClosedException if the formatter is closed. + */ + public Locale locale() + { + if (closed) + throw new FormatterClosedException(); + return locale; + } + + /** + * Returns the output stream used by this formatter. + * + * @return the formatter's output stream. + * @throws FormatterClosedException if the formatter is closed. + */ + public Appendable out() + { + if (closed) + throw new FormatterClosedException(); + return out; + } + + /** + * Returns the result of applying {@link Object#toString()} + * to the underlying output stream. The results returned + * depend on the particular {@link Appendable} being used. + * For example, a {@link StringBuilder} will return the + * formatted output but an I/O stream will not. + * + * @throws FormatterClosedException if the formatter is closed. + */ + public String toString() + { + if (closed) + throw new FormatterClosedException(); + return out.toString(); + } +} diff --git a/libjava/classpath/java/util/FormatterClosedException.java b/libjava/classpath/java/util/FormatterClosedException.java new file mode 100644 index 000000000..620684938 --- /dev/null +++ b/libjava/classpath/java/util/FormatterClosedException.java @@ -0,0 +1,60 @@ +/* FormatterClosedException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a method is called on a {@link Formatter} but + * it has already been closed. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class FormatterClosedException + extends IllegalStateException +{ + private static final long serialVersionUID = 18111216L; + + /** + * Constructs a new FormatterClosedException. + */ + public FormatterClosedException() + { + } +} diff --git a/libjava/classpath/java/util/GregorianCalendar.java b/libjava/classpath/java/util/GregorianCalendar.java new file mode 100644 index 000000000..b5d9e8c47 --- /dev/null +++ b/libjava/classpath/java/util/GregorianCalendar.java @@ -0,0 +1,1365 @@ +/* java.util.GregorianCalendar + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + + +/** + *

        + * This class represents the Gregorian calendar, that is used in most + * countries all over the world. It does also handle the Julian calendar + * for dates smaller than the date of the change to the Gregorian calendar. + * The Gregorian calendar differs from the Julian calendar by a different + * leap year rule (no leap year every 100 years, except if year is divisible + * by 400). + *

        + *

        + * This change date is different from country to country, and can be changed with + * setGregorianChange. The first countries to adopt the Gregorian + * calendar did so on the 15th of October, 1582. This date followed October + * the 4th, 1582 in the Julian calendar system. The non-existant days that were + * omitted when the change took place are interpreted as Gregorian dates. + *

        + *

        + * Prior to the changeover date, New Year's Day occurred on the 25th of March. + * However, this class always takes New Year's Day as being the 1st of January. + * Client code should manually adapt the year value, if required, for dates + * between January the 1st and March the 24th in years prior to the changeover. + *

        + *

        + * Any date infinitely forwards or backwards in time can be represented by + * this class. A proleptic calendar system is used, which allows + * future dates to be created via the existing rules. This allows meaningful + * and consistent dates to be produced for all years. However, dates are only + * historically accurate following March the 1st, 4AD when the Julian calendar + * system was adopted. Prior to this, leap year rules were applied erraticly. + *

        + *

        + * There are two eras available for the Gregorian calendar, namely BC and AD. + *

        + *

        + * Weeks are defined as a period of seven days, beginning on the first day + * of the week, as returned by getFirstDayOfWeek(), and ending + * on the day prior to this. + *

        + *

        + * The weeks of the year are numbered from 1 to a possible 53. The first week + * of the year is defined as the first week that contains at least the minimum + * number of days of the first week in the new year (retrieved via + * getMinimalDaysInFirstWeek()). All weeks after this are numbered + * from 2 onwards. + *

        + *

        + * For example, take the year 2004. It began on a Thursday. The first week + * of 2004 depends both on where a week begins and how long it must minimally + * last. Let's say that the week begins on a Monday and must have a minimum + * of 5 days. In this case, the first week begins on Monday, the 5th of January. + * The first 4 days (Thursday to Sunday) are not eligible, as they are too few + * to make up the minimum number of days of the first week which must be in + * the new year. If the minimum was lowered to 4 days, then the first week + * would instead begin on Monday, the 29th of December, 2003. This first week + * has 4 of its days in the new year, and is now eligible. + *

        + *

        + * The weeks of the month are numbered from 0 to a possible 6. The first week + * of the month (numbered 1) is a set of days, prior to the first day of the week, + * which number at least the minimum number of days in a week. Unlike the first + * week of the year, the first week of the month only uses days from that particular + * month. As a consequence, it may have a variable number of days (from the minimum + * number required up to a full week of 7) and it need not start on the first day of + * the week. It must, however, be following by the first day of the week, as this + * marks the beginning of week 2. Any days of the month which occur prior to the + * first week (because the first day of the week occurs before the minimum number + * of days is met) are seen as week 0. + *

        + *

        + * Again, we will take the example of the year 2004 to demonstrate this. September + * 2004 begins on a Wednesday. Taking our first day of the week as Monday, and the + * minimum length of the first week as 6, we find that week 1 runs from Monday, + * the 6th of September to Sunday the 12th. Prior to the 6th, there are only + * 5 days (Wednesday through to Sunday). This is too small a number to meet the + * minimum, so these are classed as being days in week 0. Week 2 begins on the + * 13th, and so on. This changes if we reduce the minimum to 5. In this case, + * week 1 is a truncated week from Wednesday the 1st to Sunday the 5th, and week + * 0 doesn't exist. The first seven day week is week 2, starting on the 6th. + *

        + *

        + * On using the clear() method, the Gregorian calendar returns + * to its default value of the 1st of January, 1970 AD 00:00:00 (the epoch). + * The day of the week is set to the correct day for that particular time. + * The day is also the first of the month, and the date is in week 0. + *

        + * + * @see Calendar + * @see TimeZone + * @see Calendar#getFirstDayOfWeek() + * @see Calendar#getMinimalDaysInFirstWeek() + */ +public class GregorianCalendar extends Calendar +{ + /** + * Constant representing the era BC (Before Christ). + */ + public static final int BC = 0; + + /** + * Constant representing the era AD (Anno Domini). + */ + public static final int AD = 1; + + /** + * The point at which the Gregorian calendar rules were used. + * This may be changed by using setGregorianChange; + * The default is midnight (UTC) on October 5, 1582 (Julian), + * or October 15, 1582 (Gregorian). + * + * @serial the changeover point from the Julian calendar + * system to the Gregorian. + */ + private long gregorianCutover = (new Date((24 * 60 * 60 * 1000L) * (((1582 * (365 * 4 + + 1)) / 4 + + (java.util.Calendar.OCTOBER * (31 + + 30 + 31 + 30 + 31) - 9) / 5 + 5) + - ((1970 * (365 * 4 + 1)) / 4 + 1 + - 13)))).getTime(); + + /** + * For compatability with Sun's JDK. + */ + static final long serialVersionUID = -8125100834729963327L; + + /** + * Days in the epoch. Relative Jan 1, year '0' which is not a leap year. + * (although there is no year zero, this does not matter.) + * This is consistent with the formula: + * = (year-1)*365L + ((year-1) >> 2) + * + * Plus the gregorian correction: + * Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.); + * For a correct julian date, the correction is -2 instead. + * + * The gregorian cutover in 1582 was 10 days, so by calculating the + * correction from year zero, we have 15 non-leap days (even centuries) + * minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects + * this to the correct number 10. + */ + private static final int EPOCH_DAYS = 719162; + + /** + * Constructs a new GregorianCalender representing the current + * time, using the default time zone and the default locale. + */ + public GregorianCalendar() + { + this(TimeZone.getDefault(), Locale.getDefault()); + } + + /** + * Constructs a new GregorianCalender representing the current + * time, using the specified time zone and the default locale. + * + * @param zone a time zone. + */ + public GregorianCalendar(TimeZone zone) + { + this(zone, Locale.getDefault()); + } + + /** + * Constructs a new GregorianCalender representing the current + * time, using the default time zone and the specified locale. + * + * @param locale a locale. + */ + public GregorianCalendar(Locale locale) + { + this(TimeZone.getDefault(), locale); + } + + /** + * Constructs a new GregorianCalender representing the current + * time with the given time zone and the given locale. + * + * @param zone a time zone. + * @param locale a locale. + */ + public GregorianCalendar(TimeZone zone, Locale locale) + { + this(zone, locale, false); + setTimeInMillis(System.currentTimeMillis()); + } + + /** + * Common constructor that all constructors should call. + * @param zone a time zone. + * @param locale a locale. + * @param unused unused parameter to make the signature differ from + * the public constructor (TimeZone, Locale). + */ + private GregorianCalendar(TimeZone zone, Locale locale, boolean unused) + { + super(zone, locale); + } + + /** + * Constructs a new GregorianCalendar representing midnight on the + * given date with the default time zone and locale. + * + * @param year corresponds to the YEAR time field. + * @param month corresponds to the MONTH time field. + * @param day corresponds to the DAY time field. + */ + public GregorianCalendar(int year, int month, int day) + { + this(TimeZone.getDefault(), Locale.getDefault(), false); + set(year, month, day); + } + + /** + * Constructs a new GregorianCalendar representing midnight on the + * given date with the default time zone and locale. + * + * @param year corresponds to the YEAR time field. + * @param month corresponds to the MONTH time field. + * @param day corresponds to the DAY time field. + * @param hour corresponds to the HOUR_OF_DAY time field. + * @param minute corresponds to the MINUTE time field. + */ + public GregorianCalendar(int year, int month, int day, int hour, int minute) + { + this(TimeZone.getDefault(), Locale.getDefault(), false); + set(year, month, day, hour, minute); + } + + /** + * Constructs a new GregorianCalendar representing midnight on the + * given date with the default time zone and locale. + * + * @param year corresponds to the YEAR time field. + * @param month corresponds to the MONTH time field. + * @param day corresponds to the DAY time field. + * @param hour corresponds to the HOUR_OF_DAY time field. + * @param minute corresponds to the MINUTE time field. + * @param second corresponds to the SECOND time field. + */ + public GregorianCalendar(int year, int month, int day, int hour, int minute, + int second) + { + this(TimeZone.getDefault(), Locale.getDefault(), false); + set(year, month, day, hour, minute, second); + } + + /** + * Sets the date of the switch from Julian dates to Gregorian dates. + * You can use new Date(Long.MAX_VALUE) to use a pure + * Julian calendar, or Long.MIN_VALUE for a pure Gregorian + * calendar. + * + * @param date the date of the change. + */ + public void setGregorianChange(Date date) + { + gregorianCutover = date.getTime(); + } + + /** + * Gets the date of the switch from Julian dates to Gregorian dates. + * + * @return the date of the change. + */ + public final Date getGregorianChange() + { + return new Date(gregorianCutover); + } + + /** + *

        + * Determines if the given year is a leap year. The result is + * undefined if the Gregorian change took place in 1800, so that + * the end of February is skipped, and that year is specified. + * (well...). + *

        + *

        + * To specify a year in the BC era, use a negative value calculated + * as 1 - y, where y is the required year in BC. So, 1 BC is 0, + * 2 BC is -1, 3 BC is -2, etc. + *

        + * + * @param year a year (use a negative value for BC). + * @return true, if the given year is a leap year, false otherwise. + */ + public boolean isLeapYear(int year) + { + // Only years divisible by 4 can be leap years + if ((year & 3) != 0) + return false; + + // Is the leap-day a Julian date? Then it's a leap year + if (! isGregorian(year, 31 + 29 - 1)) + return true; + + // Apply gregorian rules otherwise + return ((year % 100) != 0 || (year % 400) == 0); + } + + /** + * Retrieves the day of the week corresponding to the specified + * day of the specified year. + * + * @param year the year in which the dayOfYear occurs. + * @param dayOfYear the day of the year (an integer between 0 and + * and 366) + */ + private int getWeekDay(int year, int dayOfYear) + { + boolean greg = isGregorian(year, dayOfYear); + int day = (int) getLinearDay(year, dayOfYear, greg); + + // The epoch was a thursday. + int weekday = (day + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + return weekday; + } + + /** + * Returns the day of the week for the first day of a given month (0..11) + */ + private int getFirstDayOfMonth(int year, int month) + { + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + if (month > 11) + { + year += (month / 12); + month = month % 12; + } + + if (month < 0) + { + year += (int) month / 12; + month = month % 12; + if (month < 0) + { + month += 12; + year--; + } + } + + int dayOfYear = dayCount[month] + 1; + if (month > 1) + if (isLeapYear(year)) + dayOfYear++; + + boolean greg = isGregorian(year, dayOfYear); + int day = (int) getLinearDay(year, dayOfYear, greg); + + // The epoch was a thursday. + int weekday = (day + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + return weekday; + } + + /** + * Takes a year, and a (zero based) day of year and determines + * if it is gregorian or not. + */ + private boolean isGregorian(int year, int dayOfYear) + { + int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear + - EPOCH_DAYS; // gregorian days from 1 to epoch. + int gregFactor = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover); + } + + /** + * Check set fields for validity, without leniency. + * + * @throws IllegalArgumentException if a field is invalid + */ + private void nonLeniencyCheck() throws IllegalArgumentException + { + int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int year = fields[YEAR]; + int month = fields[MONTH]; + int leap = isLeapYear(year) ? 1 : 0; + + if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC) + throw new IllegalArgumentException("Illegal ERA."); + if (isSet[YEAR] && fields[YEAR] < 1) + throw new IllegalArgumentException("Illegal YEAR."); + if (isSet[MONTH] && (month < 0 || month > 11)) + throw new IllegalArgumentException("Illegal MONTH."); + if (isSet[WEEK_OF_YEAR]) + { + int daysInYear = 365 + leap; + daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week + int last = getFirstDayOfMonth(year, 11) + 4; + if (last > 7) + last -= 7; + daysInYear += 7 - last; + int weeks = daysInYear / 7; + if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks) + throw new IllegalArgumentException("Illegal WEEK_OF_YEAR."); + } + + if (isSet[WEEK_OF_MONTH]) + { + int weeks = (month == 1 && leap == 0) ? 5 : 6; + if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks) + throw new IllegalArgumentException("Illegal WEEK_OF_MONTH."); + } + + if (isSet[DAY_OF_MONTH]) + if (fields[DAY_OF_MONTH] < 1 + || fields[DAY_OF_MONTH] > month_days[month] + + ((month == 1) ? leap : 0)) + throw new IllegalArgumentException("Illegal DAY_OF_MONTH."); + + if (isSet[DAY_OF_YEAR] + && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap)) + throw new IllegalArgumentException("Illegal DAY_OF_YEAR."); + + if (isSet[DAY_OF_WEEK] + && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7)) + throw new IllegalArgumentException("Illegal DAY_OF_WEEK."); + + if (isSet[DAY_OF_WEEK_IN_MONTH]) + { + int weeks = (month == 1 && leap == 0) ? 4 : 5; + if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks + || fields[DAY_OF_WEEK_IN_MONTH] > weeks) + throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH."); + } + + if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) + throw new IllegalArgumentException("Illegal AM_PM."); + if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) + throw new IllegalArgumentException("Illegal HOUR."); + if (isSet[HOUR_OF_DAY] + && (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23)) + throw new IllegalArgumentException("Illegal HOUR_OF_DAY."); + if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59)) + throw new IllegalArgumentException("Illegal MINUTE."); + if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59)) + throw new IllegalArgumentException("Illegal SECOND."); + if (isSet[MILLISECOND] + && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999)) + throw new IllegalArgumentException("Illegal MILLISECOND."); + if (isSet[ZONE_OFFSET] + && (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L + || fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L)) + throw new IllegalArgumentException("Illegal ZONE_OFFSET."); + if (isSet[DST_OFFSET] + && (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L + || fields[DST_OFFSET] > 12 * 60 * 60 * 1000L)) + throw new IllegalArgumentException("Illegal DST_OFFSET."); + } + + /** + * Converts the time field values (fields) to + * milliseconds since the epoch UTC (time). + * + * @throws IllegalArgumentException if any calendar fields + * are invalid. + */ + protected synchronized void computeTime() + { + int millisInDay = 0; + int era = fields[ERA]; + int year = fields[YEAR]; + int month = fields[MONTH]; + int day = fields[DAY_OF_MONTH]; + + int minute = fields[MINUTE]; + int second = fields[SECOND]; + int millis = fields[MILLISECOND]; + int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int hour = 0; + + if (! isLenient()) + nonLeniencyCheck(); + + if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR])) + { + // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR + if (isSet[WEEK_OF_YEAR]) + { + int first = getFirstDayOfMonth(year, 0); + int offs = 1; + int daysInFirstWeek = getFirstDayOfWeek() - first; + if (daysInFirstWeek <= 0) + daysInFirstWeek += 7; + + if (daysInFirstWeek < getMinimalDaysInFirstWeek()) + offs += daysInFirstWeek; + else + offs -= 7 - daysInFirstWeek; + month = 0; + day = offs + 7 * (fields[WEEK_OF_YEAR] - 1); + offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek(); + + if (offs < 0) + offs += 7; + day += offs; + } + else + { + // 4: YEAR + DAY_OF_YEAR + month = 0; + day = fields[DAY_OF_YEAR]; + } + } + else + { + if (isSet[DAY_OF_WEEK]) + { + int first = getFirstDayOfMonth(year, month); + + // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK + if (isSet[DAY_OF_WEEK_IN_MONTH]) + { + if (fields[DAY_OF_WEEK_IN_MONTH] < 0) + { + month++; + first = getFirstDayOfMonth(year, month); + day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH]); + } + else + day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1); + + int offs = fields[DAY_OF_WEEK] - first; + if (offs < 0) + offs += 7; + day += offs; + } + else + { // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK + int offs = 1; + int daysInFirstWeek = getFirstDayOfWeek() - first; + if (daysInFirstWeek <= 0) + daysInFirstWeek += 7; + + if (daysInFirstWeek < getMinimalDaysInFirstWeek()) + offs += daysInFirstWeek; + else + offs -= 7 - daysInFirstWeek; + + day = offs + 7 * (fields[WEEK_OF_MONTH] - 1); + offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek(); + if (offs < 0) + offs += 7; + day += offs; + } + } + + // 1: YEAR + MONTH + DAY_OF_MONTH + } + if (era == BC && year > 0) + year = 1 - year; + + // rest of code assumes day/month/year set + // should negative BC years be AD? + // get the hour (but no check for validity) + if (isSet[HOUR]) + { + hour = fields[HOUR]; + if (fields[AM_PM] == PM) + hour += 12; + } + else + hour = fields[HOUR_OF_DAY]; + + // Read the era,year,month,day fields and convert as appropriate. + // Calculate number of milliseconds into the day + // This takes care of both h, m, s, ms over/underflows. + long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis; + day += allMillis / (24 * 60 * 60 * 1000L); + millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); + + if (month < 0) + { + year += (int) month / 12; + month = month % 12; + if (month < 0) + { + month += 12; + year--; + } + } + if (month > 11) + { + year += (month / 12); + month = month % 12; + } + + month_days[1] = isLeapYear(year) ? 29 : 28; + + while (day <= 0) + { + if (month == 0) + { + year--; + month_days[1] = isLeapYear(year) ? 29 : 28; + } + month = (month + 11) % 12; + day += month_days[month]; + } + while (day > month_days[month]) + { + day -= (month_days[month]); + month = (month + 1) % 12; + if (month == 0) + { + year++; + month_days[1] = isLeapYear(year) ? 29 : 28; + } + } + + // ok, by here we have valid day,month,year,era and millisinday + int dayOfYear = dayCount[month] + day - 1; // (day starts on 1) + if (isLeapYear(year) && month > 1) + dayOfYear++; + + int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear + - EPOCH_DAYS; // gregorian days from 1 to epoch. + int gregFactor = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover) + relativeDay += gregFactor; + else + relativeDay -= 2; + + time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay; + + // the epoch was a Thursday. + int weekday = (int) (relativeDay + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + fields[DAY_OF_WEEK] = weekday; + + // Time zone corrections. + TimeZone zone = getTimeZone(); + int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET] + : zone.getRawOffset(); + + int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET] + : (zone.getOffset((year < 0) ? BC : AD, + (year < 0) ? 1 - year + : year, + month, day, weekday, + millisInDay) + - zone.getRawOffset()); + + time -= rawOffset + dstOffset; + + isTimeSet = true; + } + + /** + * Get the linear day in days since the epoch, using the + * Julian or Gregorian calendar as specified. If you specify a + * nonpositive year it is interpreted as BC as following: 0 is 1 + * BC, -1 is 2 BC and so on. + * + * @param year the year of the date. + * @param dayOfYear the day of year of the date; 1 based. + * @param gregorian true, if we should use the Gregorian rules. + * @return the days since the epoch, may be negative. + */ + private long getLinearDay(int year, int dayOfYear, boolean gregorian) + { + // The 13 is the number of days, that were omitted in the Gregorian + // Calender until the epoch. + // We shift right by 2 instead of dividing by 4, to get correct + // results for negative years (and this is even more efficient). + long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1) + - EPOCH_DAYS; // gregorian days from 1 to epoch. + + if (gregorian) + { + // subtract the days that are missing in gregorian calendar + // with respect to julian calendar. + // + // Okay, here we rely on the fact that the gregorian + // calendar was introduced in the AD era. This doesn't work + // with negative years. + // + // The additional leap year factor accounts for the fact that + // a leap day is not seen on Jan 1 of the leap year. + int gregOffset = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + return julianDay + gregOffset; + } + else + julianDay -= 2; + return julianDay; + } + + /** + * Converts the given linear day into era, year, month, + * day_of_year, day_of_month, day_of_week, and writes the result + * into the fields array. + * + * @param day the linear day. + * @param gregorian true, if we should use Gregorian rules. + */ + private void calculateDay(int[] fields, long day, boolean gregorian) + { + // the epoch was a Thursday. + int weekday = (int) (day + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + fields[DAY_OF_WEEK] = weekday; + + // get a first approximation of the year. This may be one + // year too big. + int year = 1970 + + (int) (gregorian + ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L + + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L)); + if (day >= 0) + year++; + + long firstDayOfYear = getLinearDay(year, 1, gregorian); + + // Now look in which year day really lies. + if (day < firstDayOfYear) + { + year--; + firstDayOfYear = getLinearDay(year, 1, gregorian); + } + + day -= firstDayOfYear - 1; // day of year, one based. + + fields[DAY_OF_YEAR] = (int) day; + if (year <= 0) + { + fields[ERA] = BC; + fields[YEAR] = 1 - year; + } + else + { + fields[ERA] = AD; + fields[YEAR] = year; + } + + int leapday = isLeapYear(year) ? 1 : 0; + if (day <= 31 + 28 + leapday) + { + fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY + fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH]; + } + else + { + // A few more magic formulas + int scaledDay = ((int) day - leapday) * 5 + 8; + fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31); + fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1; + } + } + + /** + * Converts the milliseconds since the epoch UTC + * (time) to time fields + * (fields). + */ + protected synchronized void computeFields() + { + boolean gregorian = (time >= gregorianCutover); + + TimeZone zone = getTimeZone(); + fields[ZONE_OFFSET] = zone.getRawOffset(); + long localTime = time + fields[ZONE_OFFSET]; + + long day = localTime / (24 * 60 * 60 * 1000L); + int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L)); + + if (millisInDay < 0) + { + millisInDay += (24 * 60 * 60 * 1000); + day--; + } + + calculateDay(fields, day, gregorian); + fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR], + fields[MONTH], fields[DAY_OF_MONTH], + fields[DAY_OF_WEEK], millisInDay) + - fields[ZONE_OFFSET]; + + millisInDay += fields[DST_OFFSET]; + if (millisInDay >= 24 * 60 * 60 * 1000) + { + millisInDay -= 24 * 60 * 60 * 1000; + calculateDay(fields, ++day, gregorian); + } + + fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7; + + // which day of the week are we (0..6), relative to getFirstDayOfWeek + int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7; + + // which day of the week is the first of this month? + // nb 35 is the smallest multiple of 7 that ensures that + // the left hand side of the modulo operator is positive. + int relativeWeekdayOfFirst = (relativeWeekday - fields[DAY_OF_MONTH] + + 1 + 35) % 7; + + // which week of the month is the first of this month in? + int minDays = getMinimalDaysInFirstWeek(); + int weekOfFirst = ((7 - relativeWeekdayOfFirst) >= minDays) ? 1 : 0; + + // which week of the month is this day in? + fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] + + relativeWeekdayOfFirst - 1) / 7 + weekOfFirst; + + int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7; + + // Do the Correction: getMinimalDaysInFirstWeek() is always in the + // first week. + int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays) + - getFirstDayOfWeek()) % 7; + if (minDays - firstWeekday < 1) + weekOfYear++; + fields[WEEK_OF_YEAR] = weekOfYear; + + int hourOfDay = millisInDay / (60 * 60 * 1000); + fields[AM_PM] = (hourOfDay < 12) ? AM : PM; + int hour = hourOfDay % 12; + fields[HOUR] = hour; + fields[HOUR_OF_DAY] = hourOfDay; + millisInDay %= (60 * 60 * 1000); + fields[MINUTE] = millisInDay / (60 * 1000); + millisInDay %= (60 * 1000); + fields[SECOND] = millisInDay / (1000); + fields[MILLISECOND] = millisInDay % 1000; + + areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true; + } + + /** + * Return a hash code for this object, following the general contract + * specified by {@link Object#hashCode()}. + * @return the hash code + */ + public int hashCode() + { + int val = (int) ((gregorianCutover >>> 32) ^ (gregorianCutover & 0xffffffff)); + return super.hashCode() ^ val; + } + + /** + * Compares the given calendar with this. An object, o, is + * equivalent to this if it is also a GregorianCalendar + * with the same time since the epoch under the same conditions + * (same change date and same time zone). + * + * @param o the object to that we should compare. + * @return true, if the given object is a calendar, that represents + * the same time (but doesn't necessarily have the same fields). + * @throws IllegalArgumentException if one of the fields + * ZONE_OFFSET or DST_OFFSET is + * specified, if an unknown field is specified or if one + * of the calendar fields receives an illegal value when + * leniancy is not enabled. + */ + public boolean equals(Object o) + { + if (! (o instanceof GregorianCalendar)) + return false; + + GregorianCalendar cal = (GregorianCalendar) o; + return (cal.gregorianCutover == gregorianCutover + && super.equals(o)); + } + + /** + * Adds the specified amount of time to the given time field. The + * amount may be negative to subtract the time. If the field overflows + * it does what you expect: Jan, 25 + 10 Days is Feb, 4. + * @param field one of the time field constants. + * @param amount the amount of time to add. + * @exception IllegalArgumentException if field is + * ZONE_OFFSET, DST_OFFSET, or invalid; or + * if amount contains an out-of-range value and the calendar + * is not in lenient mode. + */ + public void add(int field, int amount) + { + switch (field) + { + case YEAR: + complete(); + fields[YEAR] += amount; + isTimeSet = false; + break; + case MONTH: + complete(); + int months = fields[MONTH] + amount; + fields[YEAR] += months / 12; + fields[MONTH] = months % 12; + if (fields[MONTH] < 0) + { + fields[MONTH] += 12; + fields[YEAR]--; + } + int maxDay = getActualMaximum(DAY_OF_MONTH); + if (fields[DAY_OF_MONTH] > maxDay) + fields[DAY_OF_MONTH] = maxDay; + set(YEAR, fields[YEAR]); + set(MONTH, fields[MONTH]); + break; + case DAY_OF_MONTH: + case DAY_OF_YEAR: + case DAY_OF_WEEK: + if (! isTimeSet) + computeTime(); + time += amount * (24 * 60 * 60 * 1000L); + areFieldsSet = false; + break; + case WEEK_OF_YEAR: + case WEEK_OF_MONTH: + case DAY_OF_WEEK_IN_MONTH: + if (! isTimeSet) + computeTime(); + time += amount * (7 * 24 * 60 * 60 * 1000L); + areFieldsSet = false; + break; + case AM_PM: + if (! isTimeSet) + computeTime(); + time += amount * (12 * 60 * 60 * 1000L); + areFieldsSet = false; + break; + case HOUR: + case HOUR_OF_DAY: + if (! isTimeSet) + computeTime(); + time += amount * (60 * 60 * 1000L); + areFieldsSet = false; + break; + case MINUTE: + if (! isTimeSet) + computeTime(); + time += amount * (60 * 1000L); + areFieldsSet = false; + break; + case SECOND: + if (! isTimeSet) + computeTime(); + time += amount * (1000L); + areFieldsSet = false; + break; + case MILLISECOND: + if (! isTimeSet) + computeTime(); + time += amount; + areFieldsSet = false; + break; + case ZONE_OFFSET: + case DST_OFFSET:default: + throw new IllegalArgumentException("Invalid or unknown field"); + } + } + + /** + * Rolls the specified time field up or down. This means add one + * to the specified field, but don't change the other fields. If + * the maximum for this field is reached, start over with the + * minimum value. + * + * Note: There may be situation, where the other + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically converted to July, 1. + * This requires lenient settings. + * + * @param field the time field. One of the time field constants. + * @param up the direction, true for up, false for down. + * @throws IllegalArgumentException if one of the fields + * ZONE_OFFSET or DST_OFFSET is + * specified, if an unknown field is specified or if one + * of the calendar fields receives an illegal value when + * leniancy is not enabled. + */ + public void roll(int field, boolean up) + { + roll(field, up ? 1 : -1); + } + + /** + * Checks that the fields are still within their legal bounds, + * following use of the roll() method. + * + * @param field the field to check. + * @param delta multipler for alterations to the time. + * @see #roll(int, boolean) + * @see #roll(int, int) + */ + private void cleanUpAfterRoll(int field, int delta) + { + switch (field) + { + case ERA: + case YEAR: + case MONTH: + // check that day of month is still in correct range + if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH)) + fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH); + isTimeSet = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + break; + case DAY_OF_MONTH: + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + time += delta * (24 * 60 * 60 * 1000L); + break; + case WEEK_OF_MONTH: + isSet[DAY_OF_MONTH] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + time += delta * (7 * 24 * 60 * 60 * 1000L); + break; + case DAY_OF_WEEK_IN_MONTH: + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + isSet[WEEK_OF_YEAR] = false; + time += delta * (7 * 24 * 60 * 60 * 1000L); + break; + case DAY_OF_YEAR: + isSet[MONTH] = false; + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_WEEK] = false; + isSet[WEEK_OF_YEAR] = false; + time += delta * (24 * 60 * 60 * 1000L); + break; + case WEEK_OF_YEAR: + isSet[MONTH] = false; + isSet[DAY_OF_MONTH] = false; + isSet[WEEK_OF_MONTH] = false; + isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[DAY_OF_YEAR] = false; + time += delta * (7 * 24 * 60 * 60 * 1000L); + break; + case AM_PM: + isSet[HOUR_OF_DAY] = false; + time += delta * (12 * 60 * 60 * 1000L); + break; + case HOUR: + isSet[HOUR_OF_DAY] = false; + time += delta * (60 * 60 * 1000L); + break; + case HOUR_OF_DAY: + isSet[HOUR] = false; + isSet[AM_PM] = false; + time += delta * (60 * 60 * 1000L); + break; + case MINUTE: + time += delta * (60 * 1000L); + break; + case SECOND: + time += delta * (1000L); + break; + case MILLISECOND: + time += delta; + break; + } + } + + /** + * Rolls the specified time field by the given amount. This means + * add amount to the specified field, but don't change the other + * fields. If the maximum for this field is reached, start over + * with the minimum value and vice versa for negative amounts. + * + * Note: There may be situation, where the other + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically corrected to June, 30. + * + * @param field the time field. One of the time field constants. + * @param amount the amount by which we should roll. + * @throws IllegalArgumentException if one of the fields + * ZONE_OFFSET or DST_OFFSET is + * specified, if an unknown field is specified or if one + * of the calendar fields receives an illegal value when + * leniancy is not enabled. + */ + public void roll(int field, int amount) + { + switch (field) + { + case DAY_OF_WEEK: + // day of week is special: it rolls automatically + add(field, amount); + return; + case ZONE_OFFSET: + case DST_OFFSET: + throw new IllegalArgumentException("Can't roll time zone"); + } + complete(); + int min = getActualMinimum(field); + int range = getActualMaximum(field) - min + 1; + int oldval = fields[field]; + int newval = (oldval - min + range + amount) % range + min; + if (newval < min) + newval += range; + fields[field] = newval; + cleanUpAfterRoll(field, newval - oldval); + } + + /** + * The minimum values for the calendar fields. + */ + private static final int[] minimums = + { + BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM, + 1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000), + 0 + }; + + /** + * The maximum values for the calendar fields. + */ + private static final int[] maximums = + { + AD, 5000000, 11, 53, 6, 31, 366, + SATURDAY, 5, PM, 12, 23, 59, 59, 999, + +(12 * 60 * 60 * 1000), + (12 * 60 * 60 * 1000) + }; + + /** + * Gets the smallest value that is allowed for the specified field. + * + * @param field one of the time field constants. + * @return the smallest value for the specified field. + */ + public int getMinimum(int field) + { + return minimums[field]; + } + + /** + * Gets the biggest value that is allowed for the specified field. + * + * @param field one of the time field constants. + * @return the biggest value. + */ + public int getMaximum(int field) + { + return maximums[field]; + } + + /** + * Gets the greatest minimum value that is allowed for the specified field. + * This is the largest value returned by the getActualMinimum(int) + * method. + * + * @param field the time field. One of the time field constants. + * @return the greatest minimum value. + * @see #getActualMinimum(int) + */ + public int getGreatestMinimum(int field) + { + if (field == WEEK_OF_YEAR) + return 1; + return minimums[field]; + } + + /** + * Gets the smallest maximum value that is allowed for the + * specified field. This is the smallest value returned + * by the getActualMaximum(int). For example, + * this is 28 for DAY_OF_MONTH (as all months have at least + * 28 days). + * + * @param field the time field. One of the time field constants. + * @return the least maximum value. + * @see #getActualMaximum(int) + * @since 1.2 + */ + public int getLeastMaximum(int field) + { + switch (field) + { + case WEEK_OF_YEAR: + return 52; + case DAY_OF_MONTH: + return 28; + case DAY_OF_YEAR: + return 365; + case DAY_OF_WEEK_IN_MONTH: + case WEEK_OF_MONTH: + return 4; + default: + return maximums[field]; + } + } + + /** + * Gets the actual minimum value that is allowed for the specified field. + * This value is dependent on the values of the other fields. Note that + * this calls complete() if not enough fields are set. This + * can have ugly side effects. The value given depends on the current + * time used by this instance. + * + * @param field the time field. One of the time field constants. + * @return the actual minimum value. + * @since 1.2 + */ + public int getActualMinimum(int field) + { + if (field == WEEK_OF_YEAR) + { + int min = getMinimalDaysInFirstWeek(); + if (min == 0) + return 1; + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) + complete(); + + int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; + int weekday = getWeekDay(year, min); + if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1) + return 1; + return 0; + } + return minimums[field]; + } + + /** + * Gets the actual maximum value that is allowed for the specified field. + * This value is dependent on the values of the other fields. Note that + * this calls complete() if not enough fields are set. This + * can have ugly side effects. The value given depends on the current time + * used by this instance; thus, leap years have a maximum day of month value of + * 29, rather than 28. + * + * @param field the time field. One of the time field constants. + * @return the actual maximum value. + */ + public int getActualMaximum(int field) + { + switch (field) + { + case WEEK_OF_YEAR: + { + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) + complete(); + + // This is wrong for the year that contains the gregorian change. + // I.e it gives the weeks in the julian year or in the gregorian + // year in that case. + int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; + int lastDay = isLeapYear(year) ? 366 : 365; + int weekday = getWeekDay(year, lastDay); + int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; + + int minimalDays = getMinimalDaysInFirstWeek(); + int firstWeekday = getWeekDay(year, minimalDays); + /* + * Is there a set of days at the beginning of the year, before the + * first day of the week, equal to or greater than the minimum number + * of days required in the first week? + */ + if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1) + return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */ + } + case DAY_OF_MONTH: + { + if (! areFieldsSet || ! isSet[MONTH]) + complete(); + int month = fields[MONTH]; + + // If you change this, you should also change + // SimpleTimeZone.getDaysInMonth(); + if (month == FEBRUARY) + { + if (! isSet[YEAR] || ! isSet[ERA]) + complete(); + int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; + return isLeapYear(year) ? 29 : 28; + } + else if (month < AUGUST) + return 31 - (month & 1); + else + return 30 + (month & 1); + } + case DAY_OF_YEAR: + { + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) + complete(); + int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; + return isLeapYear(year) ? 366 : 365; + } + case DAY_OF_WEEK_IN_MONTH: + { + // This is wrong for the month that contains the gregorian change. + int daysInMonth = getActualMaximum(DAY_OF_MONTH); + + // That's black magic, I know + return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7; + } + case WEEK_OF_MONTH: + { + int daysInMonth = getActualMaximum(DAY_OF_MONTH); + int weekday = (daysInMonth - fields[DAY_OF_MONTH] + + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY; + return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; + } + default: + return maximums[field]; + } + } +} diff --git a/libjava/classpath/java/util/HashMap.java b/libjava/classpath/java/util/HashMap.java new file mode 100644 index 000000000..55d81c620 --- /dev/null +++ b/libjava/classpath/java/util/HashMap.java @@ -0,0 +1,907 @@ +/* HashMap.java -- a class providing a basic hashtable data structure, + mapping Object --> Object + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +// NOTE: This implementation is very similar to that of Hashtable. If you fix +// a bug in here, chances are you should make a similar change to the Hashtable +// code. + +// NOTE: This implementation has some nasty coding style in order to +// support LinkedHashMap, which extends this. + +/** + * This class provides a hashtable-backed implementation of the + * Map interface. + *

        + * + * It uses a hash-bucket approach; that is, hash collisions are handled + * by linking the new node off of the pre-existing node (or list of + * nodes). In this manner, techniques such as linear probing (which + * can cause primary clustering) and rehashing (which does not fit very + * well with Java's method of precomputing hash codes) are avoided. + *

        + * + * Under ideal circumstances (no collisions), HashMap offers O(1) + * performance on most operations (containsValue() is, + * of course, O(n)). In the worst case (all keys map to the same + * hash code -- very unlikely), most operations are O(n). + *

        + * + * HashMap is part of the JDK1.2 Collections API. It differs from + * Hashtable in that it accepts the null key and null values, and it + * does not support "Enumeration views." Also, it is not synchronized; + * if you plan to use it in multiple threads, consider using:
        + * Map m = Collections.synchronizedMap(new HashMap(...)); + *

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * ConcurrentModificationException rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Jochen Hoenicke + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Object#hashCode() + * @see Collection + * @see Map + * @see TreeMap + * @see LinkedHashMap + * @see IdentityHashMap + * @see Hashtable + * @since 1.2 + * @status updated to 1.4 + */ +public class HashMap extends AbstractMap + implements Map, Cloneable, Serializable +{ + /** + * Default number of buckets. This is the value the JDK 1.3 uses. Some + * early documentation specified this value as 101. That is incorrect. + * Package visible for use by HashSet. + */ + static final int DEFAULT_CAPACITY = 11; + + /** + * The default load factor; this is explicitly specified by the spec. + * Package visible for use by HashSet. + */ + static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 362498820763181265L; + + /** + * The rounded product of the capacity and the load factor; when the number + * of elements exceeds the threshold, the HashMap calls + * rehash(). + * @serial the threshold for rehashing + */ + private int threshold; + + /** + * Load factor of this HashMap: used in computing the threshold. + * Package visible for use by HashSet. + * @serial the load factor + */ + final float loadFactor; + + /** + * Array containing the actual key-value mappings. + * Package visible for use by nested and subclasses. + */ + transient HashEntry[] buckets; + + /** + * Counts the number of modifications this HashMap has undergone, used + * by Iterators to know when to throw ConcurrentModificationExceptions. + * Package visible for use by nested and subclasses. + */ + transient int modCount; + + /** + * The size of this HashMap: denotes the number of key-value pairs. + * Package visible for use by nested and subclasses. + */ + transient int size; + + /** + * The cache for {@link #entrySet()}. + */ + private transient Set> entries; + + /** + * Class to represent an entry in the hash table. Holds a single key-value + * pair. Package visible for use by subclass. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class HashEntry extends AbstractMap.SimpleEntry + { + /** + * The next entry in the linked list. Package visible for use by subclass. + */ + HashEntry next; + + /** + * Simple constructor. + * @param key the key + * @param value the value + */ + HashEntry(K key, V value) + { + super(key, value); + } + + /** + * Called when this entry is accessed via {@link #put(Object, Object)}. + * This version does nothing, but in LinkedHashMap, it must do some + * bookkeeping for access-traversal mode. + */ + void access() + { + } + + /** + * Called when this entry is removed from the map. This version simply + * returns the value, but in LinkedHashMap, it must also do bookkeeping. + * + * @return the value of this key as it is removed + */ + V cleanup() + { + return value; + } + } + + /** + * Construct a new HashMap with the default capacity (11) and the default + * load factor (0.75). + */ + public HashMap() + { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new HashMap from the given Map, with initial capacity + * the greater of the size of m or the default of 11. + *

        + * + * Every element in Map m will be put into this new HashMap. + * + * @param m a Map whose key / value pairs will be put into the new HashMap. + * NOTE: key / value pairs are not cloned in this constructor. + * @throws NullPointerException if m is null + */ + public HashMap(Map m) + { + this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); + putAll(m); + } + + /** + * Construct a new HashMap with a specific inital capacity and + * default load factor of 0.75. + * + * @param initialCapacity the initial capacity of this HashMap (>=0) + * @throws IllegalArgumentException if (initialCapacity < 0) + */ + public HashMap(int initialCapacity) + { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new HashMap with a specific inital capacity and load factor. + * + * @param initialCapacity the initial capacity (>=0) + * @param loadFactor the load factor (> 0, not NaN) + * @throws IllegalArgumentException if (initialCapacity < 0) || + * ! (loadFactor > 0.0) + */ + public HashMap(int initialCapacity, float loadFactor) + { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Capacity: " + + initialCapacity); + if (! (loadFactor > 0)) // check for NaN too + throw new IllegalArgumentException("Illegal Load: " + loadFactor); + + if (initialCapacity == 0) + initialCapacity = 1; + buckets = (HashEntry[]) new HashEntry[initialCapacity]; + this.loadFactor = loadFactor; + threshold = (int) (initialCapacity * loadFactor); + } + + /** + * Returns the number of kay-value mappings currently in this Map. + * + * @return the size + */ + public int size() + { + return size; + } + + /** + * Returns true if there are no key-value mappings currently in this Map. + * + * @return size() == 0 + */ + public boolean isEmpty() + { + return size == 0; + } + + /** + * Return the value in this HashMap associated with the supplied key, + * or null if the key maps to nothing. NOTE: Since the value + * could also be null, you must use containsKey to see if this key + * actually maps to something. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public V get(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (equals(key, e.key)) + return e.value; + e = e.next; + } + return null; + } + + /** + * Returns true if the supplied object equals() a key + * in this HashMap. + * + * @param key the key to search for in this HashMap + * @return true if the key is in the table + * @see #containsValue(Object) + */ + public boolean containsKey(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (equals(key, e.key)) + return true; + e = e.next; + } + return false; + } + + /** + * Puts the supplied value into the Map, mapped by the supplied key. + * The value may be retrieved by any object which equals() + * this key. NOTE: Since the prior value could also be null, you must + * first use containsKey if you want to see if you are replacing the + * key's mapping. + * + * @param key the key used to locate the value + * @param value the value to be stored in the HashMap + * @return the prior mapping of the key, or null if there was none + * @see #get(Object) + * @see Object#equals(Object) + */ + public V put(K key, V value) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + + while (e != null) + { + if (equals(key, e.key)) + { + e.access(); // Must call this for bookkeeping in LinkedHashMap. + V r = e.value; + e.value = value; + return r; + } + else + e = e.next; + } + + // At this point, we know we need to add a new entry. + modCount++; + if (++size > threshold) + { + rehash(); + // Need a new hash value to suit the bigger table. + idx = hash(key); + } + + // LinkedHashMap cannot override put(), hence this call. + addEntry(key, value, idx, true); + return null; + } + + /** + * Copies all elements of the given map into this hashtable. If this table + * already has a mapping for a key, the new mapping replaces the current + * one. + * + * @param m the map to be hashed into this + */ + public void putAll(Map m) + { + final Map addMap = (Map) m; + final Iterator> it = addMap.entrySet().iterator(); + while (it.hasNext()) + { + final Map.Entry e = it.next(); + // Optimize in case the Entry is one of our own. + if (e instanceof AbstractMap.SimpleEntry) + { + AbstractMap.SimpleEntry entry + = (AbstractMap.SimpleEntry) e; + put(entry.key, entry.value); + } + else + put(e.getKey(), e.getValue()); + } + } + + /** + * Removes from the HashMap and returns the value which is mapped by the + * supplied key. If the key maps to nothing, then the HashMap remains + * unchanged, and null is returned. NOTE: Since the value + * could also be null, you must use containsKey to see if you are + * actually removing a mapping. + * + * @param key the key used to locate the value to remove + * @return whatever the key mapped to, if present + */ + public V remove(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + HashEntry last = null; + + while (e != null) + { + if (equals(key, e.key)) + { + modCount++; + if (last == null) + buckets[idx] = e.next; + else + last.next = e.next; + size--; + // Method call necessary for LinkedHashMap to work correctly. + return e.cleanup(); + } + last = e; + e = e.next; + } + return null; + } + + /** + * Clears the Map so it has no keys. This is O(1). + */ + public void clear() + { + if (size != 0) + { + modCount++; + Arrays.fill(buckets, null); + size = 0; + } + } + + /** + * Returns true if this HashMap contains a value o, such that + * o.equals(value). + * + * @param value the value to search for in this HashMap + * @return true if at least one key maps to the value + * @see #containsKey(Object) + */ + public boolean containsValue(Object value) + { + for (int i = buckets.length - 1; i >= 0; i--) + { + HashEntry e = buckets[i]; + while (e != null) + { + if (equals(value, e.value)) + return true; + e = e.next; + } + } + return false; + } + + /** + * Returns a shallow clone of this HashMap. The Map itself is cloned, + * but its contents are not. This is O(n). + * + * @return the clone + */ + public Object clone() + { + HashMap copy = null; + try + { + copy = (HashMap) super.clone(); + } + catch (CloneNotSupportedException x) + { + // This is impossible. + } + copy.buckets = (HashEntry[]) new HashEntry[buckets.length]; + copy.putAllInternal(this); + // Clear the entry cache. AbstractMap.clone() does the others. + copy.entries = null; + return copy; + } + + /** + * Returns a "set view" of this HashMap's keys. The set is backed by the + * HashMap, so changes in one show up in the other. The set supports + * element removal, but not element addition. + * + * @return a set view of the keys + * @see #values() + * @see #entrySet() + */ + public Set keySet() + { + if (keys == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overridden easily and efficiently. + keys = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + // Cannot create the iterator directly, because of LinkedHashMap. + return HashMap.this.iterator(KEYS); + } + + public void clear() + { + HashMap.this.clear(); + } + + public boolean contains(Object o) + { + return containsKey(o); + } + + public boolean remove(Object o) + { + // Test against the size of the HashMap to determine if anything + // really got removed. This is necessary because the return value + // of HashMap.remove() is ambiguous in the null case. + int oldsize = size; + HashMap.this.remove(o); + return oldsize != size; + } + }; + return keys; + } + + /** + * Returns a "collection view" (or "bag view") of this HashMap's values. + * The collection is backed by the HashMap, so changes in one show up + * in the other. The collection supports element removal, but not element + * addition. + * + * @return a bag view of the values + * @see #keySet() + * @see #entrySet() + */ + public Collection values() + { + if (values == null) + // We don't bother overriding many of the optional methods, as doing so + // wouldn't provide any significant performance advantage. + values = new AbstractCollection() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + // Cannot create the iterator directly, because of LinkedHashMap. + return HashMap.this.iterator(VALUES); + } + + public void clear() + { + HashMap.this.clear(); + } + }; + return values; + } + + /** + * Returns a "set view" of this HashMap's entries. The set is backed by + * the HashMap, so changes in one show up in the other. The set supports + * element removal, but not element addition.

        + * + * Note that the iterators for all three views, from keySet(), entrySet(), + * and values(), traverse the HashMap in the same sequence. + * + * @return a set view of the entries + * @see #keySet() + * @see #values() + * @see Map.Entry + */ + public Set> entrySet() + { + if (entries == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overridden easily and efficiently. + entries = new AbstractSet>() + { + public int size() + { + return size; + } + + public Iterator> iterator() + { + // Cannot create the iterator directly, because of LinkedHashMap. + return HashMap.this.iterator(ENTRIES); + } + + public void clear() + { + HashMap.this.clear(); + } + + public boolean contains(Object o) + { + return getEntry(o) != null; + } + + public boolean remove(Object o) + { + HashEntry e = getEntry(o); + if (e != null) + { + HashMap.this.remove(e.key); + return true; + } + return false; + } + }; + return entries; + } + + /** + * Helper method for put, that creates and adds a new Entry. This is + * overridden in LinkedHashMap for bookkeeping purposes. + * + * @param key the key of the new Entry + * @param value the value + * @param idx the index in buckets where the new Entry belongs + * @param callRemove whether to call the removeEldestEntry method + * @see #put(Object, Object) + */ + void addEntry(K key, V value, int idx, boolean callRemove) + { + HashEntry e = new HashEntry(key, value); + e.next = buckets[idx]; + buckets[idx] = e; + } + + /** + * Helper method for entrySet(), which matches both key and value + * simultaneously. + * + * @param o the entry to match + * @return the matching entry, if found, or null + * @see #entrySet() + */ + // Package visible, for use in nested classes. + final HashEntry getEntry(Object o) + { + if (! (o instanceof Map.Entry)) + return null; + Map.Entry me = (Map.Entry) o; + K key = me.getKey(); + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (equals(e.key, key)) + return equals(e.value, me.getValue()) ? e : null; + e = e.next; + } + return null; + } + + /** + * Helper method that returns an index in the buckets array for `key' + * based on its hashCode(). Package visible for use by subclasses. + * + * @param key the key + * @return the bucket number + */ + final int hash(Object key) + { + return key == null ? 0 : Math.abs(key.hashCode() % buckets.length); + } + + /** + * Generates a parameterized iterator. Must be overrideable, since + * LinkedHashMap iterates in a different order. + * + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + * @return the appropriate iterator + */ + Iterator iterator(int type) + { + // FIXME: bogus cast here. + return new HashIterator(type); + } + + /** + * A simplified, more efficient internal implementation of putAll(). clone() + * should not call putAll or put, in order to be compatible with the JDK + * implementation with respect to subclasses. + * + * @param m the map to initialize this from + */ + void putAllInternal(Map m) + { + final Map addMap = (Map) m; + final Iterator> it = addMap.entrySet().iterator(); + size = 0; + while (it.hasNext()) + { + final Map.Entry e = it.next(); + size++; + K key = e.getKey(); + int idx = hash(key); + addEntry(key, e.getValue(), idx, false); + } + } + + /** + * Increases the size of the HashMap and rehashes all keys to new + * array indices; this is called when the addition of a new value + * would cause size() > threshold. Note that the existing Entry + * objects are reused in the new hash table. + * + *

        This is not specified, but the new size is twice the current size + * plus one; this number is not always prime, unfortunately. + */ + private void rehash() + { + HashEntry[] oldBuckets = buckets; + + int newcapacity = (buckets.length * 2) + 1; + threshold = (int) (newcapacity * loadFactor); + buckets = (HashEntry[]) new HashEntry[newcapacity]; + + for (int i = oldBuckets.length - 1; i >= 0; i--) + { + HashEntry e = oldBuckets[i]; + while (e != null) + { + int idx = hash(e.key); + HashEntry dest = buckets[idx]; + HashEntry next = e.next; + e.next = buckets[idx]; + buckets[idx] = e; + e = next; + } + } + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the capacity(int) that is the length of the + * bucket array, the size(int) of the hash map + * are emitted first. They are followed by size entries, + * each consisting of a key (Object) and a value (Object). + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + // Write the threshold and loadFactor fields. + s.defaultWriteObject(); + + s.writeInt(buckets.length); + s.writeInt(size); + // Avoid creating a wasted Set by creating the iterator directly. + Iterator> it = iterator(ENTRIES); + while (it.hasNext()) + { + HashEntry entry = it.next(); + s.writeObject(entry.key); + s.writeObject(entry.value); + } + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the capacity(int) that is the length of the + * bucket array, the size(int) of the hash map + * are emitted first. They are followed by size entries, + * each consisting of a key (Object) and a value (Object). + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + // Read the threshold and loadFactor fields. + s.defaultReadObject(); + + // Read and use capacity, followed by key/value pairs. + buckets = (HashEntry[]) new HashEntry[s.readInt()]; + int len = s.readInt(); + size = len; + while (len-- > 0) + { + Object key = s.readObject(); + addEntry((K) key, (V) s.readObject(), hash(key), false); + } + } + + /** + * Iterate over HashMap's entries. + * This implementation is parameterized to give a sequential view of + * keys, values, or entries. + * + * @author Jon Zeppieri + */ + private final class HashIterator implements Iterator + { + /** + * The type of this Iterator: {@link #KEYS}, {@link #VALUES}, + * or {@link #ENTRIES}. + */ + private final int type; + /** + * The number of modifications to the backing HashMap that we know about. + */ + private int knownMod = modCount; + /** The number of elements remaining to be returned by next(). */ + private int count = size; + /** Current index in the physical hash table. */ + private int idx = buckets.length; + /** The last Entry returned by a next() call. */ + private HashEntry last; + /** + * The next entry that should be returned by next(). It is set to something + * if we're iterating through a bucket that contains multiple linked + * entries. It is null if next() needs to find a new bucket. + */ + private HashEntry next; + + /** + * Construct a new HashIterator with the supplied type. + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + */ + HashIterator(int type) + { + this.type = type; + } + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + */ + public boolean hasNext() + { + return count > 0; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the HashMap was modified + * @throws NoSuchElementException if there is none + */ + public T next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (count == 0) + throw new NoSuchElementException(); + count--; + HashEntry e = next; + + while (e == null) + e = buckets[--idx]; + + next = e.next; + last = e; + if (type == VALUES) + return (T) e.value; + if (type == KEYS) + return (T) e.key; + return (T) e; + } + + /** + * Removes from the backing HashMap the last element which was fetched + * with the next() method. + * @throws ConcurrentModificationException if the HashMap was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (last == null) + throw new IllegalStateException(); + + HashMap.this.remove(last.key); + last = null; + knownMod++; + } + } +} diff --git a/libjava/classpath/java/util/HashSet.java b/libjava/classpath/java/util/HashSet.java new file mode 100644 index 000000000..c08b6db5a --- /dev/null +++ b/libjava/classpath/java/util/HashSet.java @@ -0,0 +1,293 @@ +/* HashSet.java -- a class providing a HashMap-backed Set + Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class provides a HashMap-backed implementation of the Set interface. + *

        + * + * Most operations are O(1), assuming no hash collisions. In the worst + * case (where all hashes collide), operations are O(n). Setting the + * initial capacity too low will force many resizing operations, but + * setting the initial capacity too high (or loadfactor too low) leads + * to wasted memory and slower iteration. + *

        + * + * HashSet accepts the null key and null values. It is not synchronized, + * so if you need multi-threaded access, consider using:
        + * Set s = Collections.synchronizedSet(new HashSet(...)); + *

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Set + * @see TreeSet + * @see Collections#synchronizedSet(Set) + * @see HashMap + * @see LinkedHashSet + * @since 1.2 + * @status updated to 1.4 + */ +public class HashSet extends AbstractSet + implements Set, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = -5024744406713321676L; + + /** + * The HashMap which backs this Set. + */ + private transient HashMap map; + + /** + * Construct a new, empty HashSet whose backing HashMap has the default + * capacity (11) and loadFacor (0.75). + */ + public HashSet() + { + this(HashMap.DEFAULT_CAPACITY, HashMap.DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new, empty HashSet whose backing HashMap has the supplied + * capacity and the default load factor (0.75). + * + * @param initialCapacity the initial capacity of the backing HashMap + * @throws IllegalArgumentException if the capacity is negative + */ + public HashSet(int initialCapacity) + { + this(initialCapacity, HashMap.DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new, empty HashSet whose backing HashMap has the supplied + * capacity and load factor. + * + * @param initialCapacity the initial capacity of the backing HashMap + * @param loadFactor the load factor of the backing HashMap + * @throws IllegalArgumentException if either argument is negative, or + * if loadFactor is POSITIVE_INFINITY or NaN + */ + public HashSet(int initialCapacity, float loadFactor) + { + map = init(initialCapacity, loadFactor); + } + + /** + * Construct a new HashSet with the same elements as are in the supplied + * collection (eliminating any duplicates, of course). The backing storage + * has twice the size of the collection, or the default size of 11, + * whichever is greater; and the default load factor (0.75). + * + * @param c a collection of initial set elements + * @throws NullPointerException if c is null + */ + public HashSet(Collection c) + { + this(Math.max(2 * c.size(), HashMap.DEFAULT_CAPACITY)); + addAll(c); + } + + /** + * Adds the given Object to the set if it is not already in the Set. + * This set permits a null element. + * + * @param o the Object to add to this Set + * @return true if the set did not already contain o + */ + public boolean add(T o) + { + return map.put(o, "") == null; + } + + /** + * Empties this Set of all elements; this takes constant time. + */ + public void clear() + { + map.clear(); + } + + /** + * Returns a shallow copy of this Set. The Set itself is cloned; its + * elements are not. + * + * @return a shallow clone of the set + */ + public Object clone() + { + HashSet copy = null; + try + { + copy = (HashSet) super.clone(); + } + catch (CloneNotSupportedException x) + { + // Impossible to get here. + } + copy.map = (HashMap) map.clone(); + return copy; + } + + /** + * Returns true if the supplied element is in this Set. + * + * @param o the Object to look for + * @return true if it is in the set + */ + public boolean contains(Object o) + { + return map.containsKey(o); + } + + /** + * Returns true if this set has no elements in it. + * + * @return size() == 0. + */ + public boolean isEmpty() + { + return map.size == 0; + } + + /** + * Returns an Iterator over the elements of this Set, which visits the + * elements in no particular order. For this class, the Iterator allows + * removal of elements. The iterator is fail-fast, and will throw a + * ConcurrentModificationException if the set is modified externally. + * + * @return a set iterator + * @see ConcurrentModificationException + */ + public Iterator iterator() + { + // Avoid creating intermediate keySet() object by using non-public API. + return map.iterator(HashMap.KEYS); + } + + /** + * Removes the supplied Object from this Set if it is in the Set. + * + * @param o the object to remove + * @return true if an element was removed + */ + public boolean remove(Object o) + { + return (map.remove(o) != null); + } + + /** + * Returns the number of elements in this Set (its cardinality). + * + * @return the size of the set + */ + public int size() + { + return map.size; + } + + /** + * Helper method which initializes the backing Map. Overridden by + * LinkedHashSet for correct semantics. + * + * @param capacity the initial capacity + * @param load the initial load factor + * @return the backing HashMap + */ + HashMap init(int capacity, float load) + { + return new HashMap(capacity, load); + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the capacity (int) and loadFactor (float) + * of the backing store, followed by the set size (int), + * then a listing of its elements (Object) in no order + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + // Avoid creating intermediate keySet() object by using non-public API. + Iterator it = map.iterator(HashMap.KEYS); + s.writeInt(map.buckets.length); + s.writeFloat(map.loadFactor); + s.writeInt(map.size); + while (it.hasNext()) + s.writeObject(it.next()); + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the capacity (int) and loadFactor (float) + * of the backing store, followed by the set size (int), + * then a listing of its elements (Object) in no order + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + + map = init(s.readInt(), s.readFloat()); + for (int size = s.readInt(); size > 0; size--) + map.put((T) s.readObject(), ""); + } +} diff --git a/libjava/classpath/java/util/Hashtable.java b/libjava/classpath/java/util/Hashtable.java new file mode 100644 index 000000000..8f08e96ab --- /dev/null +++ b/libjava/classpath/java/util/Hashtable.java @@ -0,0 +1,1397 @@ +/* Hashtable.java -- a class providing a basic hashtable data structure, + mapping Object --> Object + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +// NOTE: This implementation is very similar to that of HashMap. If you fix +// a bug in here, chances are you should make a similar change to the HashMap +// code. + +/** + * A class which implements a hashtable data structure. + *

        + * + * This implementation of Hashtable uses a hash-bucket approach. That is: + * linear probing and rehashing is avoided; instead, each hashed value maps + * to a simple linked-list which, in the best case, only has one node. + * Assuming a large enough table, low enough load factor, and / or well + * implemented hashCode() methods, Hashtable should provide O(1) + * insertion, deletion, and searching of keys. Hashtable is O(n) in + * the worst case for all of these (if all keys hash to the same bucket). + *

        + * + * This is a JDK-1.2 compliant implementation of Hashtable. As such, it + * belongs, partially, to the Collections framework (in that it implements + * Map). For backwards compatibility, it inherits from the obsolete and + * utterly useless Dictionary class. + *

        + * + * Being a hybrid of old and new, Hashtable has methods which provide redundant + * capability, but with subtle and even crucial differences. + * For example, one can iterate over various aspects of a Hashtable with + * either an Iterator (which is the JDK-1.2 way of doing things) or with an + * Enumeration. The latter can end up in an undefined state if the Hashtable + * changes while the Enumeration is open. + *

        + * + * Unlike HashMap, Hashtable does not accept `null' as a key value. Also, + * all accesses are synchronized: in a single thread environment, this is + * expensive, but in a multi-thread environment, this saves you the effort + * of extra synchronization. However, the old-style enumerators are not + * synchronized, because they can lead to unspecified behavior even if + * they were synchronized. You have been warned. + *

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * ConcurrentModificationException rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Warren Levy + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see HashMap + * @see TreeMap + * @see IdentityHashMap + * @see LinkedHashMap + * @since 1.0 + * @status updated to 1.4 + */ +public class Hashtable extends Dictionary + implements Map, Cloneable, Serializable +{ + // WARNING: Hashtable is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** Default number of buckets. This is the value the JDK 1.3 uses. Some + * early documentation specified this value as 101. That is incorrect. + */ + private static final int DEFAULT_CAPACITY = 11; + + /** + * The default load factor; this is explicitly specified by the spec. + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 1421746759512286392L; + + /** + * The rounded product of the capacity and the load factor; when the number + * of elements exceeds the threshold, the Hashtable calls + * rehash(). + * @serial + */ + private int threshold; + + /** + * Load factor of this Hashtable: used in computing the threshold. + * @serial + */ + private final float loadFactor; + + /** + * Array containing the actual key-value mappings. + */ + // Package visible for use by nested classes. + transient HashEntry[] buckets; + + /** + * Counts the number of modifications this Hashtable has undergone, used + * by Iterators to know when to throw ConcurrentModificationExceptions. + */ + // Package visible for use by nested classes. + transient int modCount; + + /** + * The size of this Hashtable: denotes the number of key-value pairs. + */ + // Package visible for use by nested classes. + transient int size; + + /** + * The cache for {@link #keySet()}. + */ + private transient Set keys; + + /** + * The cache for {@link #values()}. + */ + private transient Collection values; + + /** + * The cache for {@link #entrySet()}. + */ + private transient Set> entries; + + /** + * Class to represent an entry in the hash table. Holds a single key-value + * pair. A Hashtable Entry is identical to a HashMap Entry, except that + * `null' is not allowed for keys and values. + */ + private static final class HashEntry + extends AbstractMap.SimpleEntry + { + /** The next entry in the linked list. */ + HashEntry next; + + /** + * Simple constructor. + * @param key the key, already guaranteed non-null + * @param value the value, already guaranteed non-null + */ + HashEntry(K key, V value) + { + super(key, value); + } + + /** + * Resets the value. + * @param newVal the new value + * @return the prior value + * @throws NullPointerException if newVal is null + */ + public V setValue(V newVal) + { + if (newVal == null) + throw new NullPointerException(); + return super.setValue(newVal); + } + } + + /** + * Construct a new Hashtable with the default capacity (11) and the default + * load factor (0.75). + */ + public Hashtable() + { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new Hashtable from the given Map, with initial capacity + * the greater of the size of m or the default of 11. + *

        + * + * Every element in Map m will be put into this new Hashtable. + * + * @param m a Map whose key / value pairs will be put into + * the new Hashtable. NOTE: key / value pairs + * are not cloned in this constructor. + * @throws NullPointerException if m is null, or if m contains a mapping + * to or from `null'. + * @since 1.2 + */ + public Hashtable(Map m) + { + this(Math.max(m.size() * 2, DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); + putAll(m); + } + + /** + * Construct a new Hashtable with a specific inital capacity and + * default load factor of 0.75. + * + * @param initialCapacity the initial capacity of this Hashtable (>= 0) + * @throws IllegalArgumentException if (initialCapacity < 0) + */ + public Hashtable(int initialCapacity) + { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Construct a new Hashtable with a specific initial capacity and + * load factor. + * + * @param initialCapacity the initial capacity (>= 0) + * @param loadFactor the load factor (> 0, not NaN) + * @throws IllegalArgumentException if (initialCapacity < 0) || + * ! (loadFactor > 0.0) + */ + public Hashtable(int initialCapacity, float loadFactor) + { + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Capacity: " + + initialCapacity); + if (! (loadFactor > 0)) // check for NaN too + throw new IllegalArgumentException("Illegal Load: " + loadFactor); + + if (initialCapacity == 0) + initialCapacity = 1; + buckets = (HashEntry[]) new HashEntry[initialCapacity]; + this.loadFactor = loadFactor; + threshold = (int) (initialCapacity * loadFactor); + } + + /** + * Returns the number of key-value mappings currently in this hashtable. + * @return the size + */ + public synchronized int size() + { + return size; + } + + /** + * Returns true if there are no key-value mappings currently in this table. + * @return size() == 0 + */ + public synchronized boolean isEmpty() + { + return size == 0; + } + + /** + * Return an enumeration of the keys of this table. There's no point + * in synchronizing this, as you have already been warned that the + * enumeration is not specified to be thread-safe. + * + * @return the keys + * @see #elements() + * @see #keySet() + */ + public Enumeration keys() + { + return new KeyEnumerator(); + } + + /** + * Return an enumeration of the values of this table. There's no point + * in synchronizing this, as you have already been warned that the + * enumeration is not specified to be thread-safe. + * + * @return the values + * @see #keys() + * @see #values() + */ + public Enumeration elements() + { + return new ValueEnumerator(); + } + + /** + * Returns true if this Hashtable contains a value o, + * such that o.equals(value). This is the same as + * containsValue(), and is O(n). + *

        + * + * @param value the value to search for in this Hashtable + * @return true if at least one key maps to the value + * @throws NullPointerException if value is null + * @see #containsValue(Object) + * @see #containsKey(Object) + */ + public synchronized boolean contains(Object value) + { + if (value == null) + throw new NullPointerException(); + + for (int i = buckets.length - 1; i >= 0; i--) + { + HashEntry e = buckets[i]; + while (e != null) + { + if (e.value.equals(value)) + return true; + e = e.next; + } + } + + return false; + } + + /** + * Returns true if this Hashtable contains a value o, such that + * o.equals(value). This is the new API for the old + * contains(). + * + * @param value the value to search for in this Hashtable + * @return true if at least one key maps to the value + * @see #contains(Object) + * @see #containsKey(Object) + * @throws NullPointerException if value is null + * @since 1.2 + */ + public boolean containsValue(Object value) + { + // Delegate to older method to make sure code overriding it continues + // to work. + return contains(value); + } + + /** + * Returns true if the supplied object equals() a key + * in this Hashtable. + * + * @param key the key to search for in this Hashtable + * @return true if the key is in the table + * @throws NullPointerException if key is null + * @see #containsValue(Object) + */ + public synchronized boolean containsKey(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (e.key.equals(key)) + return true; + e = e.next; + } + return false; + } + + /** + * Return the value in this Hashtable associated with the supplied key, + * or null if the key maps to nothing. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @throws NullPointerException if key is null + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public synchronized V get(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (e.key.equals(key)) + return e.value; + e = e.next; + } + return null; + } + + /** + * Puts the supplied value into the Map, mapped by the supplied key. + * Neither parameter may be null. The value may be retrieved by any + * object which equals() this key. + * + * @param key the key used to locate the value + * @param value the value to be stored in the table + * @return the prior mapping of the key, or null if there was none + * @throws NullPointerException if key or value is null + * @see #get(Object) + * @see Object#equals(Object) + */ + public synchronized V put(K key, V value) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + + // Check if value is null since it is not permitted. + if (value == null) + throw new NullPointerException(); + + while (e != null) + { + if (e.key.equals(key)) + { + // Bypass e.setValue, since we already know value is non-null. + V r = e.value; + e.value = value; + return r; + } + else + { + e = e.next; + } + } + + // At this point, we know we need to add a new entry. + modCount++; + if (++size > threshold) + { + rehash(); + // Need a new hash value to suit the bigger table. + idx = hash(key); + } + + e = new HashEntry(key, value); + + e.next = buckets[idx]; + buckets[idx] = e; + + return null; + } + + /** + * Removes from the table and returns the value which is mapped by the + * supplied key. If the key maps to nothing, then the table remains + * unchanged, and null is returned. + * + * @param key the key used to locate the value to remove + * @return whatever the key mapped to, if present + */ + public synchronized V remove(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + HashEntry last = null; + + while (e != null) + { + if (e.key.equals(key)) + { + modCount++; + if (last == null) + buckets[idx] = e.next; + else + last.next = e.next; + size--; + return e.value; + } + last = e; + e = e.next; + } + return null; + } + + /** + * Copies all elements of the given map into this hashtable. However, no + * mapping can contain null as key or value. If this table already has + * a mapping for a key, the new mapping replaces the current one. + * + * @param m the map to be hashed into this + * @throws NullPointerException if m is null, or contains null keys or values + */ + public synchronized void putAll(Map m) + { + final Map addMap = (Map) m; + final Iterator> it = addMap.entrySet().iterator(); + while (it.hasNext()) + { + final Map.Entry e = it.next(); + // Optimize in case the Entry is one of our own. + if (e instanceof AbstractMap.SimpleEntry) + { + AbstractMap.SimpleEntry entry + = (AbstractMap.SimpleEntry) e; + put(entry.key, entry.value); + } + else + { + put(e.getKey(), e.getValue()); + } + } + } + + /** + * Clears the hashtable so it has no keys. This is O(1). + */ + public synchronized void clear() + { + if (size > 0) + { + modCount++; + Arrays.fill(buckets, null); + size = 0; + } + } + + /** + * Returns a shallow clone of this Hashtable. The Map itself is cloned, + * but its contents are not. This is O(n). + * + * @return the clone + */ + public synchronized Object clone() + { + Hashtable copy = null; + try + { + copy = (Hashtable) super.clone(); + } + catch (CloneNotSupportedException x) + { + // This is impossible. + } + copy.buckets = (HashEntry[]) new HashEntry[buckets.length]; + copy.putAllInternal(this); + // Clear the caches. + copy.keys = null; + copy.values = null; + copy.entries = null; + return copy; + } + + /** + * Converts this Hashtable to a String, surrounded by braces, and with + * key/value pairs listed with an equals sign between, separated by a + * comma and space. For example, "{a=1, b=2}".

        + * + * NOTE: if the toString() method of any key or value + * throws an exception, this will fail for the same reason. + * + * @return the string representation + */ + public synchronized String toString() + { + // Since we are already synchronized, and entrySet().iterator() + // would repeatedly re-lock/release the monitor, we directly use the + // unsynchronized EntryIterator instead. + Iterator> entries = new EntryIterator(); + CPStringBuilder r = new CPStringBuilder("{"); + for (int pos = size; pos > 0; pos--) + { + r.append(entries.next()); + if (pos > 1) + r.append(", "); + } + r.append("}"); + return r.toString(); + } + + /** + * Returns a "set view" of this Hashtable's keys. The set is backed by + * the hashtable, so changes in one show up in the other. The set supports + * element removal, but not element addition. The set is properly + * synchronized on the original hashtable. Sun has not documented the + * proper interaction of null with this set, but has inconsistent behavior + * in the JDK. Therefore, in this implementation, contains, remove, + * containsAll, retainAll, removeAll, and equals just ignore a null key + * rather than throwing a {@link NullPointerException}. + * + * @return a set view of the keys + * @see #values() + * @see #entrySet() + * @since 1.2 + */ + public Set keySet() + { + if (keys == null) + { + // Create a synchronized AbstractSet with custom implementations of + // those methods that can be overridden easily and efficiently. + Set r = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new KeyIterator(); + } + + public void clear() + { + Hashtable.this.clear(); + } + + public boolean contains(Object o) + { + if (o == null) + return false; + return containsKey(o); + } + + public boolean remove(Object o) + { + return Hashtable.this.remove(o) != null; + } + }; + // We must specify the correct object to synchronize upon, hence the + // use of a non-public API + keys = new Collections.SynchronizedSet(this, r); + } + return keys; + } + + /** + * Returns a "collection view" (or "bag view") of this Hashtable's values. + * The collection is backed by the hashtable, so changes in one show up + * in the other. The collection supports element removal, but not element + * addition. The collection is properly synchronized on the original + * hashtable. Sun has not documented the proper interaction of null with + * this set, but has inconsistent behavior in the JDK. Therefore, in this + * implementation, contains, remove, containsAll, retainAll, removeAll, and + * equals just ignore a null value rather than throwing a + * {@link NullPointerException}. + * + * @return a bag view of the values + * @see #keySet() + * @see #entrySet() + * @since 1.2 + */ + public Collection values() + { + if (values == null) + { + // We don't bother overriding many of the optional methods, as doing so + // wouldn't provide any significant performance advantage. + Collection r = new AbstractCollection() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new ValueIterator(); + } + + public void clear() + { + Hashtable.this.clear(); + } + }; + // We must specify the correct object to synchronize upon, hence the + // use of a non-public API + values = new Collections.SynchronizedCollection(this, r); + } + return values; + } + + /** + * Returns a "set view" of this Hashtable's entries. The set is backed by + * the hashtable, so changes in one show up in the other. The set supports + * element removal, but not element addition. The set is properly + * synchronized on the original hashtable. Sun has not documented the + * proper interaction of null with this set, but has inconsistent behavior + * in the JDK. Therefore, in this implementation, contains, remove, + * containsAll, retainAll, removeAll, and equals just ignore a null entry, + * or an entry with a null key or value, rather than throwing a + * {@link NullPointerException}. However, calling entry.setValue(null) + * will fail. + *

        + * + * Note that the iterators for all three views, from keySet(), entrySet(), + * and values(), traverse the hashtable in the same sequence. + * + * @return a set view of the entries + * @see #keySet() + * @see #values() + * @see Map.Entry + * @since 1.2 + */ + public Set> entrySet() + { + if (entries == null) + { + // Create an AbstractSet with custom implementations of those methods + // that can be overridden easily and efficiently. + Set> r = new AbstractSet>() + { + public int size() + { + return size; + } + + public Iterator> iterator() + { + return new EntryIterator(); + } + + public void clear() + { + Hashtable.this.clear(); + } + + public boolean contains(Object o) + { + return getEntry(o) != null; + } + + public boolean remove(Object o) + { + HashEntry e = getEntry(o); + if (e != null) + { + Hashtable.this.remove(e.key); + return true; + } + return false; + } + }; + // We must specify the correct object to synchronize upon, hence the + // use of a non-public API + entries = new Collections.SynchronizedSet>(this, r); + } + return entries; + } + + /** + * Returns true if this Hashtable equals the supplied Object o. + * As specified by Map, this is: + * + * (o instanceof Map) && entrySet().equals(((Map) o).entrySet()); + * + * + * @param o the object to compare to + * @return true if o is an equal map + * @since 1.2 + */ + public boolean equals(Object o) + { + // no need to synchronize, entrySet().equals() does that. + if (o == this) + return true; + if (!(o instanceof Map)) + return false; + + return entrySet().equals(((Map) o).entrySet()); + } + + /** + * Returns the hashCode for this Hashtable. As specified by Map, this is + * the sum of the hashCodes of all of its Map.Entry objects + * + * @return the sum of the hashcodes of the entries + * @since 1.2 + */ + public synchronized int hashCode() + { + // Since we are already synchronized, and entrySet().iterator() + // would repeatedly re-lock/release the monitor, we directly use the + // unsynchronized EntryIterator instead. + Iterator> itr = new EntryIterator(); + int hashcode = 0; + for (int pos = size; pos > 0; pos--) + hashcode += itr.next().hashCode(); + + return hashcode; + } + + /** + * Helper method that returns an index in the buckets array for `key' + * based on its hashCode(). + * + * @param key the key + * @return the bucket number + * @throws NullPointerException if key is null + */ + private int hash(Object key) + { + // Note: Inline Math.abs here, for less method overhead, and to avoid + // a bootstrap dependency, since Math relies on native methods. + int hash = key.hashCode() % buckets.length; + return hash < 0 ? -hash : hash; + } + + /** + * Helper method for entrySet(), which matches both key and value + * simultaneously. Ignores null, as mentioned in entrySet(). + * + * @param o the entry to match + * @return the matching entry, if found, or null + * @see #entrySet() + */ + // Package visible, for use in nested classes. + HashEntry getEntry(Object o) + { + if (! (o instanceof Map.Entry)) + return null; + K key = ((Map.Entry) o).getKey(); + if (key == null) + return null; + + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (e.equals(o)) + return e; + e = e.next; + } + return null; + } + + /** + * A simplified, more efficient internal implementation of putAll(). clone() + * should not call putAll or put, in order to be compatible with the JDK + * implementation with respect to subclasses. + * + * @param m the map to initialize this from + */ + void putAllInternal(Map m) + { + final Map addMap = (Map) m; + final Iterator> it = addMap.entrySet().iterator(); + size = 0; + while (it.hasNext()) + { + final Map.Entry e = it.next(); + size++; + K key = e.getKey(); + int idx = hash(key); + HashEntry he = new HashEntry(key, e.getValue()); + he.next = buckets[idx]; + buckets[idx] = he; + } + } + + /** + * Increases the size of the Hashtable and rehashes all keys to new array + * indices; this is called when the addition of a new value would cause + * size() > threshold. Note that the existing Entry objects are reused in + * the new hash table. + *

        + * + * This is not specified, but the new size is twice the current size plus + * one; this number is not always prime, unfortunately. This implementation + * is not synchronized, as it is only invoked from synchronized methods. + */ + protected void rehash() + { + HashEntry[] oldBuckets = buckets; + + int newcapacity = (buckets.length * 2) + 1; + threshold = (int) (newcapacity * loadFactor); + buckets = (HashEntry[]) new HashEntry[newcapacity]; + + for (int i = oldBuckets.length - 1; i >= 0; i--) + { + HashEntry e = oldBuckets[i]; + while (e != null) + { + int idx = hash(e.key); + HashEntry dest = buckets[idx]; + + if (dest != null) + { + HashEntry next = dest.next; + while (next != null) + { + dest = next; + next = dest.next; + } + dest.next = e; + } + else + { + buckets[idx] = e; + } + + HashEntry next = e.next; + e.next = null; + e = next; + } + } + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the capacity (int) that is the length of the + * bucket array, the size (int) of the hash map + * are emitted first. They are followed by size entries, + * each consisting of a key (Object) and a value (Object). + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + // Write the threshold and loadFactor fields. + s.defaultWriteObject(); + + s.writeInt(buckets.length); + s.writeInt(size); + // Since we are already synchronized, and entrySet().iterator() + // would repeatedly re-lock/release the monitor, we directly use the + // unsynchronized EntryIterator instead. + Iterator> it = new EntryIterator(); + while (it.hasNext()) + { + HashEntry entry = (HashEntry) it.next(); + s.writeObject(entry.key); + s.writeObject(entry.value); + } + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the capacity (int) that is the length of the + * bucket array, the size (int) of the hash map + * are emitted first. They are followed by size entries, + * each consisting of a key (Object) and a value (Object). + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + // Read the threshold and loadFactor fields. + s.defaultReadObject(); + + // Read and use capacity. + buckets = (HashEntry[]) new HashEntry[s.readInt()]; + int len = s.readInt(); + + // Read and use key/value pairs. + // TODO: should we be defensive programmers, and check for illegal nulls? + while (--len >= 0) + put((K) s.readObject(), (V) s.readObject()); + } + + /** + * A class which implements the Iterator interface and is used for + * iterating over Hashtables. + * This implementation iterates entries. Subclasses are used to + * iterate key and values. It also allows the removal of elements, + * as per the Javasoft spec. Note that it is not synchronized; this + * is a performance enhancer since it is never exposed externally + * and is only used within synchronized blocks above. + * + * @author Jon Zeppieri + * @author Fridjof Siebert + */ + private class EntryIterator + implements Iterator> + { + /** + * The number of modifications to the backing Hashtable that we know about. + */ + int knownMod = modCount; + /** The number of elements remaining to be returned by next(). */ + int count = size; + /** Current index in the physical hash table. */ + int idx = buckets.length; + /** The last Entry returned by a next() call. */ + HashEntry last; + /** + * The next entry that should be returned by next(). It is set to something + * if we're iterating through a bucket that contains multiple linked + * entries. It is null if next() needs to find a new bucket. + */ + HashEntry next; + + /** + * Construct a new EntryIterator + */ + EntryIterator() + { + } + + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + */ + public boolean hasNext() + { + return count > 0; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the hashtable was modified + * @throws NoSuchElementException if there is none + */ + public Map.Entry next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (count == 0) + throw new NoSuchElementException(); + count--; + HashEntry e = next; + + while (e == null) + if (idx <= 0) + return null; + else + e = buckets[--idx]; + + next = e.next; + last = e; + return e; + } + + /** + * Removes from the backing Hashtable the last element which was fetched + * with the next() method. + * @throws ConcurrentModificationException if the hashtable was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (last == null) + throw new IllegalStateException(); + + Hashtable.this.remove(last.key); + last = null; + knownMod++; + } + } // class EntryIterator + + /** + * A class which implements the Iterator interface and is used for + * iterating over keys in Hashtables. This class uses an + * EntryIterator to obtain the keys of each entry. + * + * @author Fridtjof Siebert + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private class KeyIterator + implements Iterator + { + + /** + * This entry iterator is used for most operations. Only + * next() gives a different result, by returning just + * the key rather than the whole element. + */ + private final EntryIterator iterator; + + /** + * Construct a new KeyIterator + */ + KeyIterator() + { + iterator = new EntryIterator(); + } + + + /** + * Returns true if the entry iterator has more elements. + * + * @return true if there are more elements + * @throws ConcurrentModificationException if the hashtable was modified + */ + public boolean hasNext() + { + return iterator.hasNext(); + } + + /** + * Returns the next element in the Iterator's sequential view. + * + * @return the next element + * + * @throws ConcurrentModificationException if the hashtable was modified + * @throws NoSuchElementException if there is none + */ + public K next() + { + return ((HashEntry) iterator.next()).key; + } + + /** + * Removes the last element used by the next() method + * using the entry iterator. + * + * @throws ConcurrentModificationException if the hashtable was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + iterator.remove(); + } + } // class KeyIterator + + /** + * A class which implements the Iterator interface and is used for + * iterating over values in Hashtables. This class uses an + * EntryIterator to obtain the values of each entry. + * + * @author Fridtjof Siebert + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private class ValueIterator + implements Iterator + { + + /** + * This entry iterator is used for most operations. Only + * next() gives a different result, by returning just + * the value rather than the whole element. + */ + private final EntryIterator iterator; + + /** + * Construct a new KeyIterator + */ + ValueIterator() + { + iterator = new EntryIterator(); + } + + + /** + * Returns true if the entry iterator has more elements. + * + * @return true if there are more elements + * @throws ConcurrentModificationException if the hashtable was modified + */ + public boolean hasNext() + { + return iterator.hasNext(); + } + + /** + * Returns the value of the next element in the iterator's sequential view. + * + * @return the next value + * + * @throws ConcurrentModificationException if the hashtable was modified + * @throws NoSuchElementException if there is none + */ + public V next() + { + return ((HashEntry) iterator.next()).value; + } + + /** + * Removes the last element used by the next() method + * using the entry iterator. + * + * @throws ConcurrentModificationException if the hashtable was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + iterator.remove(); + } + + } // class ValueIterator + + /** + * Enumeration view of the entries in this Hashtable, providing + * sequential access to its elements. + * + * NOTE: Enumeration is not safe if new elements are put in the table + * as this could cause a rehash and we'd completely lose our place. Even + * without a rehash, it is undetermined if a new element added would + * appear in the enumeration. The spec says nothing about this, but + * the "Java Class Libraries" book implies that modifications to the + * hashtable during enumeration causes indeterminate results. Don't do it! + * + * @author Jon Zeppieri + * @author Fridjof Siebert + */ + private class EntryEnumerator + implements Enumeration> + { + /** The number of elements remaining to be returned by next(). */ + int count = size; + /** Current index in the physical hash table. */ + int idx = buckets.length; + /** + * Entry which will be returned by the next nextElement() call. It is + * set if we are iterating through a bucket with multiple entries, or null + * if we must look in the next bucket. + */ + HashEntry next; + + /** + * Construct the enumeration. + */ + EntryEnumerator() + { + // Nothing to do here. + } + + /** + * Checks whether more elements remain in the enumeration. + * @return true if nextElement() will not fail. + */ + public boolean hasMoreElements() + { + return count > 0; + } + + /** + * Returns the next element. + * @return the next element + * @throws NoSuchElementException if there is none. + */ + public Map.Entry nextElement() + { + if (count == 0) + throw new NoSuchElementException("Hashtable Enumerator"); + count--; + HashEntry e = next; + + while (e == null) + if (idx <= 0) + return null; + else + e = buckets[--idx]; + + next = e.next; + return e; + } + } // class EntryEnumerator + + + /** + * Enumeration view of this Hashtable, providing sequential access to its + * elements. + * + * NOTE: Enumeration is not safe if new elements are put in the table + * as this could cause a rehash and we'd completely lose our place. Even + * without a rehash, it is undetermined if a new element added would + * appear in the enumeration. The spec says nothing about this, but + * the "Java Class Libraries" book implies that modifications to the + * hashtable during enumeration causes indeterminate results. Don't do it! + * + * @author Jon Zeppieri + * @author Fridjof Siebert + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private final class KeyEnumerator + implements Enumeration + { + /** + * This entry enumerator is used for most operations. Only + * nextElement() gives a different result, by returning just + * the key rather than the whole element. + */ + private final EntryEnumerator enumerator; + + /** + * Construct a new KeyEnumerator + */ + KeyEnumerator() + { + enumerator = new EntryEnumerator(); + } + + + /** + * Returns true if the entry enumerator has more elements. + * + * @return true if there are more elements + * @throws ConcurrentModificationException if the hashtable was modified + */ + public boolean hasMoreElements() + { + return enumerator.hasMoreElements(); + } + + /** + * Returns the next element. + * @return the next element + * @throws NoSuchElementException if there is none. + */ + public K nextElement() + { + HashEntry entry = (HashEntry) enumerator.nextElement(); + K retVal = null; + if (entry != null) + retVal = entry.key; + return retVal; + } + } // class KeyEnumerator + + + /** + * Enumeration view of this Hashtable, providing sequential access to its + * values. + * + * NOTE: Enumeration is not safe if new elements are put in the table + * as this could cause a rehash and we'd completely lose our place. Even + * without a rehash, it is undetermined if a new element added would + * appear in the enumeration. The spec says nothing about this, but + * the "Java Class Libraries" book implies that modifications to the + * hashtable during enumeration causes indeterminate results. Don't do it! + * + * @author Jon Zeppieri + * @author Fridjof Siebert + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private final class ValueEnumerator + implements Enumeration + { + /** + * This entry enumerator is used for most operations. Only + * nextElement() gives a different result, by returning just + * the value rather than the whole element. + */ + private final EntryEnumerator enumerator; + + /** + * Construct a new ValueEnumerator + */ + ValueEnumerator() + { + enumerator = new EntryEnumerator(); + } + + + /** + * Returns true if the entry enumerator has more elements. + * + * @return true if there are more elements + * @throws ConcurrentModificationException if the hashtable was modified + */ + public boolean hasMoreElements() + { + return enumerator.hasMoreElements(); + } + + /** + * Returns the next element. + * @return the next element + * @throws NoSuchElementException if there is none. + */ + public V nextElement() + { + HashEntry entry = (HashEntry) enumerator.nextElement(); + V retVal = null; + if (entry != null) + retVal = entry.value; + return retVal; + } + } // class ValueEnumerator + +} // class Hashtable diff --git a/libjava/classpath/java/util/IdentityHashMap.java b/libjava/classpath/java/util/IdentityHashMap.java new file mode 100644 index 000000000..89a75259e --- /dev/null +++ b/libjava/classpath/java/util/IdentityHashMap.java @@ -0,0 +1,990 @@ +/* IdentityHashMap.java -- a class providing a hashtable data structure, + mapping Object --> Object, which uses object identity for hashing. + Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class provides a hashtable-backed implementation of the + * Map interface, but uses object identity to do its hashing. In fact, + * it uses object identity for comparing values, as well. It uses a + * linear-probe hash table, which may have faster performance + * than the chaining employed by HashMap. + *

        + * + * WARNING: This is not a general purpose map. Because it uses + * System.identityHashCode and ==, instead of hashCode and equals, for + * comparison, it violated Map's general contract, and may cause + * undefined behavior when compared to other maps which are not + * IdentityHashMaps. This is designed only for the rare cases when + * identity semantics are needed. An example use is + * topology-preserving graph transformations, such as deep cloning, + * or as proxy object mapping such as in debugging. + *

        + * + * This map permits null keys and values, and does not + * guarantee that elements will stay in the same order over time. The + * basic operations (get and put) take + * constant time, provided System.identityHashCode is decent. You can + * tune the behavior by specifying the expected maximum size. As more + * elements are added, the map may need to allocate a larger table, + * which can be expensive. + *

        + * + * This implementation is unsynchronized. If you want multi-thread + * access to be consistent, you must synchronize it, perhaps by using + * Collections.synchronizedMap(new IdentityHashMap(...));. + * The iterators are fail-fast, meaning that a structural modification + * made to the map outside of an iterator's remove method cause the + * iterator, and in the case of the entrySet, the Map.Entry, to + * fail with a {@link ConcurrentModificationException}. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see System#identityHashCode(Object) + * @see Collection + * @see Map + * @see HashMap + * @see TreeMap + * @see LinkedHashMap + * @see WeakHashMap + * @since 1.4 + * @status updated to 1.4 + */ +public class IdentityHashMap extends AbstractMap + implements Map, Serializable, Cloneable +{ + /** The default capacity. */ + private static final int DEFAULT_CAPACITY = 21; + + /** + * This object is used to mark a slot whose key or value is 'null'. + * This is more efficient than using a special value to mark an empty + * slot, because null entries are rare, empty slots are common, and + * the JVM will clear new arrays for us. + * Package visible for use by nested classes. + */ + static final Object nullslot = new Object(); + + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 8188218128353913216L; + + /** + * The number of mappings in the table. Package visible for use by nested + * classes. + * @serial + */ + int size; + + /** + * The table itself. Package visible for use by nested classes. + */ + transient Object[] table; + + /** + * The number of structural modifications made so far. Package visible for + * use by nested classes. + */ + transient int modCount; + + /** + * The cache for {@link #entrySet()}. + */ + private transient Set> entries; + + /** + * The threshold for rehashing, which is 75% of (table.length / 2). + */ + private transient int threshold; + + /** + * Create a new IdentityHashMap with the default capacity (21 entries). + */ + public IdentityHashMap() + { + this(DEFAULT_CAPACITY); + } + + /** + * Create a new IdentityHashMap with the indicated number of + * entries. If the number of elements added to this hash map + * exceeds this maximum, the map will grow itself; however, that + * incurs a performance penalty. + * + * @param max initial size + * @throws IllegalArgumentException if max is negative + */ + public IdentityHashMap(int max) + { + if (max < 0) + throw new IllegalArgumentException(); + // Need at least two slots, or hash() will break. + if (max < 2) + max = 2; + table = new Object[max << 1]; + threshold = (max >> 2) * 3; + } + + /** + * Create a new IdentityHashMap whose contents are taken from the + * given Map. + * + * @param m The map whose elements are to be put in this map + * @throws NullPointerException if m is null + */ + public IdentityHashMap(Map m) + { + this(Math.max(m.size() << 1, DEFAULT_CAPACITY)); + putAll(m); + } + + /** + * Remove all mappings from this map. + */ + public void clear() + { + if (size != 0) + { + modCount++; + Arrays.fill(table, null); + size = 0; + } + } + + /** + * Creates a shallow copy where keys and values are not cloned. + */ + public Object clone() + { + try + { + IdentityHashMap copy = (IdentityHashMap) super.clone(); + copy.table = (Object[]) table.clone(); + copy.entries = null; // invalidate the cache + return copy; + } + catch (CloneNotSupportedException e) + { + // Can't happen. + return null; + } + } + + /** + * Tests whether the specified key is in this map. Unlike normal Maps, + * this test uses entry == key instead of + * entry == null ? key == null : entry.equals(key). + * + * @param key the key to look for + * @return true if the key is contained in the map + * @see #containsValue(Object) + * @see #get(Object) + */ + public boolean containsKey(Object key) + { + key = xform(key); + return key == table[hash(key)]; + } + + /** + * Returns true if this HashMap contains the value. Unlike normal maps, + * this test uses entry == value instead of + * entry == null ? value == null : entry.equals(value). + * + * @param value the value to search for in this HashMap + * @return true if at least one key maps to the value + * @see #containsKey(Object) + */ + public boolean containsValue(Object value) + { + value = xform(value); + for (int i = table.length - 1; i > 0; i -= 2) + if (table[i] == value) + return true; + return false; + } + + /** + * Returns a "set view" of this Map's entries. The set is backed by + * the Map, so changes in one show up in the other. The set supports + * element removal, but not element addition. + *

        + * + * The semantics of this set, and of its contained entries, are + * different from the contract of Set and Map.Entry in order to make + * IdentityHashMap work. This means that while you can compare these + * objects between IdentityHashMaps, comparing them with regular sets + * or entries is likely to have undefined behavior. The entries + * in this set are reference-based, rather than the normal object + * equality. Therefore, e1.equals(e2) returns + * e1.getKey() == e2.getKey() && e1.getValue() == e2.getValue(), + * and e.hashCode() returns + * System.identityHashCode(e.getKey()) ^ + * System.identityHashCode(e.getValue()). + *

        + * + * Note that the iterators for all three views, from keySet(), entrySet(), + * and values(), traverse the Map in the same sequence. + * + * @return a set view of the entries + * @see #keySet() + * @see #values() + * @see Map.Entry + */ + public Set> entrySet() + { + if (entries == null) + entries = new AbstractSet>() + { + public int size() + { + return size; + } + + public Iterator> iterator() + { + return new IdentityIterator>(ENTRIES); + } + + public void clear() + { + IdentityHashMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry m = (Map.Entry) o; + Object value = xform(m.getValue()); + Object key = xform(m.getKey()); + return value == table[hash(key) + 1]; + } + + public int hashCode() + { + return IdentityHashMap.this.hashCode(); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Object key = xform(((Map.Entry) o).getKey()); + int h = hash(key); + if (table[h] == key) + { + size--; + modCount++; + IdentityHashMap.this.removeAtIndex(h); + return true; + } + return false; + } + }; + return entries; + } + + /** + * Compares two maps for equality. This returns true only if both maps + * have the same reference-identity comparisons. While this returns + * this.entrySet().equals(m.entrySet()) as specified by Map, + * this will not work with normal maps, since the entry set compares + * with == instead of .equals. + * + * @param o the object to compare to + * @return true if it is equal + */ + public boolean equals(Object o) + { + // Why did Sun specify this one? The superclass does the right thing. + return super.equals(o); + } + + /** + * Return the value in this Map associated with the supplied key, or + * null if the key maps to nothing. + * + *

        NOTE: Since the value could also be null, you must use + * containsKey to see if this key actually maps to something. + * Unlike normal maps, this tests for the key with entry == + * key instead of entry == null ? key == null : + * entry.equals(key). + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public V get(Object key) + { + key = xform(key); + int h = hash(key); + return (V) (table[h] == key ? unxform(table[h + 1]) : null); + } + + /** + * Returns the hashcode of this map. This guarantees that two + * IdentityHashMaps that compare with equals() will have the same hash code, + * but may break with comparison to normal maps since it uses + * System.identityHashCode() instead of hashCode(). + * + * @return the hash code + */ + public int hashCode() + { + int hash = 0; + for (int i = table.length - 2; i >= 0; i -= 2) + { + Object key = table[i]; + if (key == null) + continue; + // FIXME: this is a lame computation. + hash += (System.identityHashCode(unxform(key)) + ^ System.identityHashCode(unxform(table[i + 1]))); + } + return hash; + } + + /** + * Returns true if there are no key-value mappings currently in this Map + * @return size() == 0 + */ + public boolean isEmpty() + { + return size == 0; + } + + /** + * Returns a "set view" of this Map's keys. The set is backed by the + * Map, so changes in one show up in the other. The set supports + * element removal, but not element addition. + *

        + * + * The semantics of this set are different from the contract of Set + * in order to make IdentityHashMap work. This means that while you can + * compare these objects between IdentityHashMaps, comparing them with + * regular sets is likely to have undefined behavior. The hashCode + * of the set is the sum of the identity hash codes, instead of the + * regular hashCodes, and equality is determined by reference instead + * of by the equals method. + *

        + * + * @return a set view of the keys + * @see #values() + * @see #entrySet() + */ + public Set keySet() + { + if (keys == null) + keys = new AbstractSet() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new IdentityIterator(KEYS); + } + + public void clear() + { + IdentityHashMap.this.clear(); + } + + public boolean contains(Object o) + { + return containsKey(o); + } + + public int hashCode() + { + int hash = 0; + for (int i = table.length - 2; i >= 0; i -= 2) + { + Object key = table[i]; + if (key == null) + continue; + hash += System.identityHashCode(unxform(key)); + } + return hash; + } + + public boolean remove(Object o) + { + o = xform(o); + int h = hash(o); + if (table[h] == o) + { + size--; + modCount++; + removeAtIndex(h); + return true; + } + return false; + } + }; + return keys; + } + + /** + * Puts the supplied value into the Map, mapped by the supplied key. + * The value may be retrieved by any object which equals() + * this key. NOTE: Since the prior value could also be null, you must + * first use containsKey if you want to see if you are replacing the + * key's mapping. Unlike normal maps, this tests for the key + * with entry == key instead of + * entry == null ? key == null : entry.equals(key). + * + * @param key the key used to locate the value + * @param value the value to be stored in the HashMap + * @return the prior mapping of the key, or null if there was none + * @see #get(Object) + */ + public V put(K key, V value) + { + key = (K) xform(key); + value = (V) xform(value); + + // We don't want to rehash if we're overwriting an existing slot. + int h = hash(key); + if (table[h] == key) + { + V r = (V) unxform(table[h + 1]); + table[h + 1] = value; + return r; + } + + // Rehash if the load factor is too high. + if (size > threshold) + { + Object[] old = table; + // This isn't necessarily prime, but it is an odd number of key/value + // slots, which has a higher probability of fewer collisions. + table = new Object[(old.length * 2) + 2]; + size = 0; + threshold = (table.length >>> 3) * 3; + + for (int i = old.length - 2; i >= 0; i -= 2) + { + K oldkey = (K) old[i]; + if (oldkey != null) + { + h = hash(oldkey); + table[h] = oldkey; + table[h + 1] = old[i + 1]; + ++size; + // No need to update modCount here, we'll do it + // just after the loop. + } + } + + // Now that we've resize, recompute the hash value. + h = hash(key); + } + + // At this point, we add a new mapping. + modCount++; + size++; + table[h] = key; + table[h + 1] = value; + return null; + } + + /** + * Copies all of the mappings from the specified map to this. If a key + * is already in this map, its value is replaced. + * + * @param m the map to copy + * @throws NullPointerException if m is null + */ + public void putAll(Map m) + { + // Why did Sun specify this one? The superclass does the right thing. + super.putAll(m); + } + + /** + * Remove the element at index and update the table to compensate. + * This is package-private for use by inner classes. + * @param i index of the removed element + */ + final void removeAtIndex(int i) + { + // This is Algorithm R from Knuth, section 6.4. + // Variable names are taken directly from the text. + while (true) + { + table[i] = null; + table[i + 1] = null; + int j = i; + int r; + do + { + i -= 2; + if (i < 0) + i = table.length - 2; + Object key = table[i]; + if (key == null) + return; + r = Math.abs(System.identityHashCode(key) + % (table.length >> 1)) << 1; + } + while ((i <= r && r < j) + || (r < j && j < i) + || (j < i && i <= r)); + table[j] = table[i]; + table[j + 1] = table[i + 1]; + } + } + + /** + * Removes from the HashMap and returns the value which is mapped by + * the supplied key. If the key maps to nothing, then the HashMap + * remains unchanged, and null is returned. + * + * NOTE: Since the value could also be null, you must use + * containsKey to see if you are actually removing a mapping. + * Unlike normal maps, this tests for the key with entry == + * key instead of entry == null ? key == null : + * entry.equals(key). + * + * @param key the key used to locate the value to remove + * @return whatever the key mapped to, if present + */ + public V remove(Object key) + { + key = xform(key); + int h = hash(key); + if (table[h] == key) + { + modCount++; + size--; + Object r = unxform(table[h + 1]); + removeAtIndex(h); + return (V) r; + } + return null; + } + + /** + * Returns the number of kay-value mappings currently in this Map + * @return the size + */ + public int size() + { + return size; + } + + /** + * Returns a "collection view" (or "bag view") of this Map's values. + * The collection is backed by the Map, so changes in one show up + * in the other. The collection supports element removal, but not element + * addition. + *

        + * + * The semantics of this set are different from the contract of + * Collection in order to make IdentityHashMap work. This means that + * while you can compare these objects between IdentityHashMaps, comparing + * them with regular sets is likely to have undefined behavior. + * Likewise, contains and remove go by == instead of equals(). + *

        + * + * @return a bag view of the values + * @see #keySet() + * @see #entrySet() + */ + public Collection values() + { + if (values == null) + values = new AbstractCollection() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new IdentityIterator(VALUES); + } + + public void clear() + { + IdentityHashMap.this.clear(); + } + + public boolean remove(Object o) + { + o = xform(o); + // This approach may look strange, but it is ok. + for (int i = table.length - 1; i > 0; i -= 2) + if (table[i] == o) + { + modCount++; + size--; + IdentityHashMap.this.removeAtIndex(i - 1); + return true; + } + return false; + } + }; + return values; + } + + /** + * Transform a reference from its external form to its internal form. + * This is package-private for use by inner classes. + */ + final Object xform(Object o) + { + if (o == null) + o = nullslot; + return o; + } + + /** + * Transform a reference from its internal form to its external form. + * This is package-private for use by inner classes. + */ + final Object unxform(Object o) + { + if (o == nullslot) + o = null; + return o; + } + + /** + * Helper method which computes the hash code, then traverses the table + * until it finds the key, or the spot where the key would go. the key + * must already be in its internal form. + * + * @param key the key to check + * @return the index where the key belongs + * @see #IdentityHashMap(int) + * @see #put(Object, Object) + */ + // Package visible for use by nested classes. + final int hash(Object key) + { + int h = Math.abs(System.identityHashCode(key) % (table.length >> 1)) << 1; + + while (true) + { + // By requiring at least 2 key/value slots, and rehashing at 75% + // capacity, we guarantee that there will always be either an empty + // slot somewhere in the table. + if (table[h] == key || table[h] == null) + return h; + // We use linear probing as it is friendlier to the cache and + // it lets us efficiently remove entries. + h -= 2; + if (h < 0) + h = table.length - 2; + } + } + + /** + * This class allows parameterized iteration over IdentityHashMaps. Based + * on its construction, it returns the key or value of a mapping, or + * creates the appropriate Map.Entry object with the correct fail-fast + * semantics and identity comparisons. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Eric Blake (ebb9@email.byu.edu) + */ + private class IdentityIterator implements Iterator + { + /** + * The type of this Iterator: {@link #KEYS}, {@link #VALUES}, + * or {@link #ENTRIES}. + */ + final int type; + /** The number of modifications to the backing Map that we know about. */ + int knownMod = modCount; + /** The number of elements remaining to be returned by next(). */ + int count = size; + /** Location in the table. */ + int loc = table.length; + + /** + * Construct a new Iterator with the supplied type. + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + */ + IdentityIterator(int type) + { + this.type = type; + } + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + */ + public boolean hasNext() + { + return count > 0; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the Map was modified + * @throws NoSuchElementException if there is none + */ + public I next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (count == 0) + throw new NoSuchElementException(); + count--; + + Object key; + do + { + loc -= 2; + key = table[loc]; + } + while (key == null); + + return (I) (type == KEYS ? unxform(key) + : (type == VALUES ? unxform(table[loc + 1]) + : new IdentityEntry(loc))); + } + + /** + * Removes from the backing Map the last element which was fetched + * with the next() method. + * + * @throws ConcurrentModificationException if the Map was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (loc == table.length) + throw new IllegalStateException(); + modCount++; + size--; + removeAtIndex(loc); + knownMod++; + } + } // class IdentityIterator + + /** + * This class provides Map.Entry objects for IdentityHashMaps. The entry + * is fail-fast, and will throw a ConcurrentModificationException if + * the underlying map is modified, or if remove is called on the iterator + * that generated this object. It is identity based, so it violates + * the general contract of Map.Entry, and is probably unsuitable for + * comparison to normal maps; but it works among other IdentityHashMaps. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private final class IdentityEntry implements Map.Entry + { + /** The location of this entry. */ + final int loc; + /** The number of modifications to the backing Map that we know about. */ + final int knownMod = modCount; + + /** + * Constructs the Entry. + * + * @param loc the location of this entry in table + */ + IdentityEntry(int loc) + { + this.loc = loc; + } + + /** + * Compares the specified object with this entry, using identity + * semantics. Note that this can lead to undefined results with + * Entry objects created by normal maps. + * + * @param o the object to compare + * @return true if it is equal + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public boolean equals(Object o) + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (! (o instanceof Map.Entry)) + return false; + Map.Entry e = (Map.Entry) o; + return table[loc] == xform(e.getKey()) + && table[loc + 1] == xform(e.getValue()); + } + + /** + * Returns the key of this entry. + * + * @return the key + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public EK getKey() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return (EK) unxform(table[loc]); + } + + /** + * Returns the value of this entry. + * + * @return the value + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public EV getValue() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return (EV) unxform(table[loc + 1]); + } + + /** + * Returns the hashcode of the entry, using identity semantics. + * Note that this can lead to undefined results with Entry objects + * created by normal maps. + * + * @return the hash code + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public int hashCode() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return (System.identityHashCode(unxform(table[loc])) + ^ System.identityHashCode(unxform(table[loc + 1]))); + } + + /** + * Replaces the value of this mapping, and returns the old value. + * + * @param value the new value + * @return the old value + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public EV setValue(EV value) + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + EV r = (EV) unxform(table[loc + 1]); + table[loc + 1] = xform(value); + return r; + } + + /** + * This provides a string representation of the entry. It is of the form + * "key=value", where string concatenation is used on key and value. + * + * @return the string representation + * @throws ConcurrentModificationException if the entry was invalidated + * by modifying the Map or calling Iterator.remove() + */ + public String toString() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + return unxform(table[loc]) + "=" + unxform(table[loc + 1]); + } + } // class IdentityEntry + + /** + * Reads the object from a serial stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData expects the size (int), followed by that many key (Object) + * and value (Object) pairs, with the pairs in no particular + * order + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + + int num = s.readInt(); + table = new Object[Math.max(num << 1, DEFAULT_CAPACITY) << 1]; + // Read key/value pairs. + while (--num >= 0) + put((K) s.readObject(), (V) s.readObject()); + } + + /** + * Writes the object to a serial stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData outputs the size (int), followed by that many key (Object) + * and value (Object) pairs, with the pairs in no particular + * order + */ + private void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + s.writeInt(size); + for (int i = table.length - 2; i >= 0; i -= 2) + { + Object key = table[i]; + if (key != null) + { + s.writeObject(unxform(key)); + s.writeObject(unxform(table[i + 1])); + } + } + } +} diff --git a/libjava/classpath/java/util/IllegalFormatCodePointException.java b/libjava/classpath/java/util/IllegalFormatCodePointException.java new file mode 100644 index 000000000..f6d5c2fef --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatCodePointException.java @@ -0,0 +1,85 @@ +/* IllegalFormatCodePointException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a {@link Formatter} receives a character with an + * invalid Unicode codepoint, as defined by + * {@link Character#isValidCodePoint(int)}. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatCodePointException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19080630L; + + /** + * The character which is an invalid Unicode code point. + * + * @serial the invalid character. + */ + // Note: name fixed by serialization. + int c; + + /** + * Constructs a new IllegalFormatCodePointException + * which specifies that the character, c, passed to + * a {@link Formatter} is an invalid Unicode code point. + * + * @param c the invalid character. + */ + public IllegalFormatCodePointException(int c) + { + super("An invalid Unicode code point was supplied."); + this.c = c; + } + + /** + * Returns the invalid character. + * + * @return the invalid character. + */ + public int getCodePoint() + { + return c; + } +} diff --git a/libjava/classpath/java/util/IllegalFormatConversionException.java b/libjava/classpath/java/util/IllegalFormatConversionException.java new file mode 100644 index 000000000..58aa91833 --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatConversionException.java @@ -0,0 +1,110 @@ +/* IllegalFormatConversionException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the type of an argument supplied to the + * {@link Formatter#format()} method of a {@link Formatter} + * does not match the conversion character specified for it. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatConversionException + extends IllegalFormatException +{ + private static final long serialVersionUID = 17000126L; + + /** + * The conversion character which doesn't match + * the type of the argument. + * + * @serial the conversion character. + */ + // Note: name fixed by serialization. + char c; + + /** + * The type of the mismatching argument. + * + * @serial the mismatching argument type. + */ + // Note: name fixed by serialization. + Class arg; + + /** + * Constructs a new IllegalFormatConversionException + * which specifies that the argument of type arg does + * not match the conversion character, c. + * + * @param c the conversion character. + * @param arg the type which doesn't match the conversion character. + * @throws NullPointerException if arg is null. + */ + public IllegalFormatConversionException(char c, Class arg) + { + super("The type, " + arg + ", is invalid for the conversion character, " + + c + "."); + if (arg == null) + throw new NullPointerException("The supplied type was null."); + this.c = c; + this.arg = arg; + } + + /** + * Returns the conversion character. + * + * @return the conversion character. + */ + public char getConversion() + { + return c; + } + + /** + * Returns the type of the mismatched argument. + * + * @return the type of the mismatched argument. + */ + public Class getArgumentClass() + { + return arg; + } +} diff --git a/libjava/classpath/java/util/IllegalFormatException.java b/libjava/classpath/java/util/IllegalFormatException.java new file mode 100644 index 000000000..b5c740a8f --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatException.java @@ -0,0 +1,75 @@ +/* IllegalFormatException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A general exception thrown when a format string is supplied + * to a {@link Formatter} that contains either invalid syntax + * or a mismatch between the format specification and the + * supplied arguments. This class is never instantiated; + * instead one of its subclasses is used to throw a more + * specific exception. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatException + extends IllegalArgumentException +{ + private static final long serialVersionUID = 18830826L; + + /** + * Constructs a new IllegalFormatException. + */ + IllegalFormatException() + { + } + + /** + * Constructs a new IllegalFormatException + * with the specified message. + * + * @param msg the error message for this exception. + */ + IllegalFormatException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/util/IllegalFormatFlagsException.java b/libjava/classpath/java/util/IllegalFormatFlagsException.java new file mode 100644 index 000000000..b53bfa50a --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatFlagsException.java @@ -0,0 +1,86 @@ +/* IllegalFormatFlagsException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the flags supplied to the {@link Formatter#format()} + * method of a {@link Formatter} form an illegal combination. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatFlagsException + extends IllegalFormatException +{ + private static final long serialVersionUID = 790824L; + + /** + * The set of flags which forms an illegal combination. + * + * @serial the illegal set of flags. + */ + // Note: name fixed by serialization. + private String flags; + + /** + * Constructs a new IllegalFormatFlagsException + * for the specified flags. + * + * @param flags the illegal set of flags. + * @throws NullPointerException if flags is null. + */ + public IllegalFormatFlagsException(String flags) + { + super("An illegal set of flags, " + flags + ", was supplied."); + if (flags == null) + throw new NullPointerException("The supplied flags are null."); + this.flags = flags; + } + + /** + * Returns the illegal flags. + * + * @return the illegal flags. + */ + public String getFlags() + { + return flags; + } +} diff --git a/libjava/classpath/java/util/IllegalFormatPrecisionException.java b/libjava/classpath/java/util/IllegalFormatPrecisionException.java new file mode 100644 index 000000000..a216dc185 --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatPrecisionException.java @@ -0,0 +1,85 @@ +/* IllegalFormatPrecisionException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the specified precision for a {@link Formatter} + * argument is illegal. This may be because the number is + * a negative number (other than -1), the argument does not + * accept a precision or for some other reason. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatPrecisionException + extends IllegalFormatException +{ + private static final long serialVersionUID = 18711008L; + + /** + * The illegal precision value. + * + * @serial the illegal precision. + */ + // Note: name fixed by serialization. + private int p; + + /** + * Constructs a new IllegalFormatPrecisionException + * for the precision, p. + * + * @param p the illegal precision. + */ + public IllegalFormatPrecisionException(int p) + { + super("The precision, " + p + ", is illegal."); + this.p = p; + } + + /** + * Returns the illegal precision. + * + * @return the illegal precision. + */ + public int getPrecision() + { + return p; + } +} diff --git a/libjava/classpath/java/util/IllegalFormatWidthException.java b/libjava/classpath/java/util/IllegalFormatWidthException.java new file mode 100644 index 000000000..7f2a6257e --- /dev/null +++ b/libjava/classpath/java/util/IllegalFormatWidthException.java @@ -0,0 +1,84 @@ +/* IllegalFormatWidthException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the specified width for a {@link Formatter} + * argument is illegal. This may be because the number is + * a negative number (other than -1) or for some other reason. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class IllegalFormatWidthException + extends IllegalFormatException +{ + private static final long serialVersionUID = 16660902L; + + /** + * The illegal width value. + * + * @serial the illegal width. + */ + // Note: name fixed by serialization. + private int w; + + /** + * Constructs a new IllegalFormatWidthException + * with the specified width, w. + * + * @param w the illegal width. + */ + public IllegalFormatWidthException(int w) + { + super("The width, " + w + ", is illegal."); + this.w = w; + } + + /** + * Returns the illegal width. + * + * @return the illegal width. + */ + public int getWidth() + { + return w; + } +} diff --git a/libjava/classpath/java/util/InputMismatchException.java b/libjava/classpath/java/util/InputMismatchException.java new file mode 100644 index 000000000..ee29e4653 --- /dev/null +++ b/libjava/classpath/java/util/InputMismatchException.java @@ -0,0 +1,73 @@ +/* InputMismatchException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a {@link Scanner} instance encounters a mismatch + * between the input data and the pattern it is trying to match it + * against. This could be because the input data represents an + * out-of-range value for the type the pattern represents, or simply + * because the data does not fit that particular type. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class InputMismatchException + extends NoSuchElementException +{ + /** + * Constructs a new InputMismatchException + * with a null message. + */ + public InputMismatchException() + { + } + + /** + * Constructs a new InputMismatchException + * with the supplied error message. + * + * @param s the error message to report back to the user. + */ + public InputMismatchException(String s) + { + super(s); + } +} diff --git a/libjava/classpath/java/util/InvalidPropertiesFormatException.java b/libjava/classpath/java/util/InvalidPropertiesFormatException.java new file mode 100644 index 000000000..aaa6c4eb4 --- /dev/null +++ b/libjava/classpath/java/util/InvalidPropertiesFormatException.java @@ -0,0 +1,72 @@ +/* InvalidPropertiesFormatException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** @since 1.5 */ +public class InvalidPropertiesFormatException extends IOException +{ + // This class won't serialize, but we have a UID to placate the compiler. + private static final long serialVersionUID = 7763056076009360219L; + + public InvalidPropertiesFormatException(String message) + { + super(message); + } + + public InvalidPropertiesFormatException(Throwable cause) + { + super(); + initCause(cause); + } + + private void writeObject(ObjectOutputStream out) throws IOException + { + throw new NotSerializableException("objects of this type are not serializable"); + } + + private void readObject(ObjectInputStream in) throws IOException + { + throw new NotSerializableException("objects of this type are not serializable"); + } +} diff --git a/libjava/classpath/java/util/Iterator.java b/libjava/classpath/java/util/Iterator.java new file mode 100644 index 000000000..41111a52d --- /dev/null +++ b/libjava/classpath/java/util/Iterator.java @@ -0,0 +1,87 @@ +/* Iterator.java -- Interface for iterating over collections + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An object which iterates over a collection. An Iterator is used to return + * the items once only, in sequence, by successive calls to the next method. + * It is also possible to remove elements from the underlying collection by + * using the optional remove method. Iterator is intended as a replacement + * for the Enumeration interface of previous versions of Java, which did not + * have the remove method and had less conveniently named methods. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see ListIterator + * @see Enumeration + * @since 1.2 + * @status updated to 1.4 + */ +public interface Iterator +{ + /** + * Tests whether there are elements remaining in the collection. In other + * words, calling next() will not throw an exception. + * + * @return true if there is at least one more element in the collection + */ + boolean hasNext(); + + /** + * Obtain the next element in the collection. + * + * @return the next element in the collection + * @throws NoSuchElementException if there are no more elements + */ + E next(); + + /** + * Remove from the underlying collection the last element returned by next + * (optional operation). This method can be called only once after each + * call to next(). It does not affect what will be returned + * by subsequent calls to next. + * + * @throws IllegalStateException if next has not yet been called or remove + * has already been called since the last call to next. + * @throws UnsupportedOperationException if this Iterator does not support + * the remove operation. + */ + void remove(); +} diff --git a/libjava/classpath/java/util/LinkedHashMap.java b/libjava/classpath/java/util/LinkedHashMap.java new file mode 100644 index 000000000..7701d7763 --- /dev/null +++ b/libjava/classpath/java/util/LinkedHashMap.java @@ -0,0 +1,500 @@ +/* LinkedHashMap.java -- a class providing hashtable data structure, + mapping Object --> Object, with linked list traversal + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This class provides a hashtable-backed implementation of the + * Map interface, with predictable traversal order. + *

        + * + * It uses a hash-bucket approach; that is, hash collisions are handled + * by linking the new node off of the pre-existing node (or list of + * nodes). In this manner, techniques such as linear probing (which + * can cause primary clustering) and rehashing (which does not fit very + * well with Java's method of precomputing hash codes) are avoided. In + * addition, this maintains a doubly-linked list which tracks either + * insertion or access order. + *

        + * + * In insertion order, calling put adds the key to the end of + * traversal, unless the key was already in the map; changing traversal order + * requires removing and reinserting a key. On the other hand, in access + * order, all calls to put and get cause the + * accessed key to move to the end of the traversal list. Note that any + * accesses to the map's contents via its collection views and iterators do + * not affect the map's traversal order, since the collection views do not + * call put or get. + *

        + * + * One of the nice features of tracking insertion order is that you can + * copy a hashtable, and regardless of the implementation of the original, + * produce the same results when iterating over the copy. This is possible + * without needing the overhead of TreeMap. + *

        + * + * When using this {@link #LinkedHashMap(int, float, boolean) constructor}, + * you can build an access-order mapping. This can be used to implement LRU + * caches, for example. By overriding {@link #removeEldestEntry(Map.Entry)}, + * you can also control the removal of the oldest entry, and thereby do + * things like keep the map at a fixed size. + *

        + * + * Under ideal circumstances (no collisions), LinkedHashMap offers O(1) + * performance on most operations (containsValue() is, + * of course, O(n)). In the worst case (all keys map to the same + * hash code -- very unlikely), most operations are O(n). Traversal is + * faster than in HashMap (proportional to the map size, and not the space + * allocated for the map), but other operations may be slower because of the + * overhead of the maintaining the traversal order list. + *

        + * + * LinkedHashMap accepts the null key and null values. It is not + * synchronized, so if you need multi-threaded access, consider using:
        + * Map m = Collections.synchronizedMap(new LinkedHashMap(...)); + *

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Object#hashCode() + * @see Collection + * @see Map + * @see HashMap + * @see TreeMap + * @see Hashtable + * @since 1.4 + * @status updated to 1.4 + */ +public class LinkedHashMap extends HashMap +{ + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3801124242820219131L; + + /** + * The oldest Entry to begin iteration at. + */ + transient LinkedHashEntry root; + + /** + * The iteration order of this linked hash map: true for + * access-order, false for insertion-order. + * + * @serial true for access order traversal + */ + final boolean accessOrder; + + /** + * Class to represent an entry in the hash table. Holds a single key-value + * pair and the doubly-linked insertion order list. + */ + class LinkedHashEntry extends HashEntry + { + /** + * The predecessor in the iteration list. If this entry is the root + * (eldest), pred points to the newest entry. + */ + LinkedHashEntry pred; + + /** The successor in the iteration list, null if this is the newest. */ + LinkedHashEntry succ; + + /** + * Simple constructor. + * + * @param key the key + * @param value the value + */ + LinkedHashEntry(K key, V value) + { + super(key, value); + if (root == null) + { + root = this; + pred = this; + } + else + { + pred = root.pred; + pred.succ = this; + root.pred = this; + } + } + + /** + * Called when this entry is accessed via put or get. This version does + * the necessary bookkeeping to keep the doubly-linked list in order, + * after moving this element to the newest position in access order. + */ + void access() + { + if (accessOrder && succ != null) + { + modCount++; + if (this == root) + { + root = succ; + pred.succ = this; + succ = null; + } + else + { + pred.succ = succ; + succ.pred = pred; + succ = null; + pred = root.pred; + pred.succ = this; + root.pred = this; + } + } + } + + /** + * Called when this entry is removed from the map. This version does + * the necessary bookkeeping to keep the doubly-linked list in order. + * + * @return the value of this key as it is removed + */ + V cleanup() + { + if (this == root) + { + root = succ; + if (succ != null) + succ.pred = pred; + } + else if (succ == null) + { + pred.succ = null; + root.pred = pred; + } + else + { + pred.succ = succ; + succ.pred = pred; + } + return value; + } + } // class LinkedHashEntry + + /** + * Construct a new insertion-ordered LinkedHashMap with the default + * capacity (11) and the default load factor (0.75). + */ + public LinkedHashMap() + { + super(); + accessOrder = false; + } + + /** + * Construct a new insertion-ordered LinkedHashMap from the given Map, + * with initial capacity the greater of the size of m or + * the default of 11. + *

        + * + * Every element in Map m will be put into this new HashMap, in the + * order of m's iterator. + * + * @param m a Map whose key / value pairs will be put into + * the new HashMap. NOTE: key / value pairs + * are not cloned in this constructor. + * @throws NullPointerException if m is null + */ + public LinkedHashMap(Map m) + { + super(m); + accessOrder = false; + } + + /** + * Construct a new insertion-ordered LinkedHashMap with a specific + * inital capacity and default load factor of 0.75. + * + * @param initialCapacity the initial capacity of this HashMap (>= 0) + * @throws IllegalArgumentException if (initialCapacity < 0) + */ + public LinkedHashMap(int initialCapacity) + { + super(initialCapacity); + accessOrder = false; + } + + /** + * Construct a new insertion-orderd LinkedHashMap with a specific + * inital capacity and load factor. + * + * @param initialCapacity the initial capacity (>= 0) + * @param loadFactor the load factor (> 0, not NaN) + * @throws IllegalArgumentException if (initialCapacity < 0) || + * ! (loadFactor > 0.0) + */ + public LinkedHashMap(int initialCapacity, float loadFactor) + { + super(initialCapacity, loadFactor); + accessOrder = false; + } + + /** + * Construct a new LinkedHashMap with a specific inital capacity, load + * factor, and ordering mode. + * + * @param initialCapacity the initial capacity (>=0) + * @param loadFactor the load factor (>0, not NaN) + * @param accessOrder true for access-order, false for insertion-order + * @throws IllegalArgumentException if (initialCapacity < 0) || + * ! (loadFactor > 0.0) + */ + public LinkedHashMap(int initialCapacity, float loadFactor, + boolean accessOrder) + { + super(initialCapacity, loadFactor); + this.accessOrder = accessOrder; + } + + /** + * Clears the Map so it has no keys. This is O(1). + */ + public void clear() + { + super.clear(); + root = null; + } + + /** + * Returns true if this HashMap contains a value + * o, such that o.equals(value). + * + * @param value the value to search for in this HashMap + * @return true if at least one key maps to the value + */ + public boolean containsValue(Object value) + { + LinkedHashEntry e = root; + while (e != null) + { + if (equals(value, e.value)) + return true; + e = e.succ; + } + return false; + } + + /** + * Return the value in this Map associated with the supplied key, + * or null if the key maps to nothing. If this is an + * access-ordered Map and the key is found, this performs structural + * modification, moving the key to the newest end of the list. NOTE: + * Since the value could also be null, you must use containsKey to + * see if this key actually maps to something. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public V get(Object key) + { + int idx = hash(key); + HashEntry e = buckets[idx]; + while (e != null) + { + if (equals(key, e.key)) + { + e.access(); + return e.value; + } + e = e.next; + } + return null; + } + + /** + * Returns true if this map should remove the eldest entry. + * This method is invoked by all calls to put and + * putAll which place a new entry in the map, providing + * the implementer an opportunity to remove the eldest entry any time + * a new one is added. This can be used to save memory usage of the + * hashtable, as well as emulating a cache, by deleting stale entries. + *

        + * + * For example, to keep the Map limited to 100 entries, override as follows: + *

        +   * private static final int MAX_ENTRIES = 100;
        +   * protected boolean removeEldestEntry(Map.Entry eldest)
        +   * {
        +   *   return size() > MAX_ENTRIES;
        +   * }
        +   * 

        + * + * Typically, this method does not modify the map, but just uses the + * return value as an indication to put whether to proceed. + * However, if you override it to modify the map, you must return false + * (indicating that put should leave the modified map alone), + * or you face unspecified behavior. Remember that in access-order mode, + * even calling get is a structural modification, but using + * the collections views (such as keySet) is not. + *

        + * + * This method is called after the eldest entry has been inserted, so + * if put was called on a previously empty map, the eldest + * entry is the one you just put in! The default implementation just + * returns false, so that this map always behaves like + * a normal one with unbounded growth. + * + * @param eldest the eldest element which would be removed if this + * returns true. For an access-order map, this is the least + * recently accessed; for an insertion-order map, this is the + * earliest element inserted. + * @return true if eldest should be removed + */ + protected boolean removeEldestEntry(Map.Entry eldest) + { + return false; + } + + /** + * Helper method called by put, which creates and adds a + * new Entry, followed by performing bookkeeping (like removeEldestEntry). + * + * @param key the key of the new Entry + * @param value the value + * @param idx the index in buckets where the new Entry belongs + * @param callRemove whether to call the removeEldestEntry method + * @see #put(Object, Object) + * @see #removeEldestEntry(Map.Entry) + * @see LinkedHashEntry#LinkedHashEntry(Object, Object) + */ + void addEntry(K key, V value, int idx, boolean callRemove) + { + LinkedHashEntry e = new LinkedHashEntry(key, value); + e.next = buckets[idx]; + buckets[idx] = e; + if (callRemove && removeEldestEntry(root)) + remove(root.key); + } + + /** + * Helper method, called by clone() to reset the doubly-linked list. + * + * @param m the map to add entries from + * @see #clone() + */ + void putAllInternal(Map m) + { + root = null; + super.putAllInternal(m); + } + + /** + * Generates a parameterized iterator. This allows traversal to follow + * the doubly-linked list instead of the random bin order of HashMap. + * + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + * @return the appropriate iterator + */ + Iterator iterator(final int type) + { + return new Iterator() + { + /** The current Entry. */ + LinkedHashEntry current = root; + + /** The previous Entry returned by next(). */ + LinkedHashEntry last; + + /** The number of known modifications to the backing Map. */ + int knownMod = modCount; + + /** + * Returns true if the Iterator has more elements. + * + * @return true if there are more elements + */ + public boolean hasNext() + { + return current != null; + } + + /** + * Returns the next element in the Iterator's sequential view. + * + * @return the next element + * @throws ConcurrentModificationException if the HashMap was modified + * @throws NoSuchElementException if there is none + */ + public Object next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (current == null) + throw new NoSuchElementException(); + last = current; + current = current.succ; + return type == VALUES ? last.value : type == KEYS ? last.key : last; + } + + /** + * Removes from the backing HashMap the last element which was fetched + * with the next() method. + * + * @throws ConcurrentModificationException if the HashMap was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (last == null) + throw new IllegalStateException(); + LinkedHashMap.this.remove(last.key); + last = null; + knownMod++; + } + }; + } +} // class LinkedHashMap diff --git a/libjava/classpath/java/util/LinkedHashSet.java b/libjava/classpath/java/util/LinkedHashSet.java new file mode 100644 index 000000000..2caebaf93 --- /dev/null +++ b/libjava/classpath/java/util/LinkedHashSet.java @@ -0,0 +1,159 @@ +/* LinkedHashSet.java -- a set backed by a LinkedHashMap, for linked + list traversal. + Copyright (C) 2001, 2004, 2005, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * This class provides a hashtable-backed implementation of the + * Set interface, with predictable traversal order. + *

        + * + * It uses a hash-bucket approach; that is, hash collisions are handled + * by linking the new node off of the pre-existing node (or list of + * nodes). In this manner, techniques such as linear probing (which + * can cause primary clustering) and rehashing (which does not fit very + * well with Java's method of precomputing hash codes) are avoided. In + * addition, this maintains a doubly-linked list which tracks insertion + * order. Note that the insertion order is not modified if an + * add simply reinserts an element in the set. + *

        + * + * One of the nice features of tracking insertion order is that you can + * copy a set, and regardless of the implementation of the original, + * produce the same results when iterating over the copy. This is possible + * without needing the overhead of TreeSet. + *

        + * + * Under ideal circumstances (no collisions), LinkedHashSet offers O(1) + * performance on most operations. In the worst case (all elements map + * to the same hash code -- very unlikely), most operations are O(n). + *

        + * + * LinkedHashSet accepts the null entry. It is not synchronized, so if + * you need multi-threaded access, consider using:
        + * Set s = Collections.synchronizedSet(new LinkedHashSet(...)); + *

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see Object#hashCode() + * @see Collection + * @see Set + * @see HashSet + * @see TreeSet + * @see Collections#synchronizedSet(Set) + * @since 1.4 + * @status updated to 1.4 + */ +public class LinkedHashSet extends HashSet + implements Set, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -2851667679971038690L; + + /** + * Construct a new, empty HashSet whose backing HashMap has the default + * capacity (11) and loadFactor (0.75). + */ + public LinkedHashSet() + { + super(); + } + + /** + * Construct a new, empty HashSet whose backing HashMap has the supplied + * capacity and the default load factor (0.75). + * + * @param initialCapacity the initial capacity of the backing HashMap + * @throws IllegalArgumentException if the capacity is negative + */ + public LinkedHashSet(int initialCapacity) + { + super(initialCapacity); + } + + /** + * Construct a new, empty HashSet whose backing HashMap has the supplied + * capacity and load factor. + * + * @param initialCapacity the initial capacity of the backing HashMap + * @param loadFactor the load factor of the backing HashMap + * @throws IllegalArgumentException if either argument is negative, or + * if loadFactor is POSITIVE_INFINITY or NaN + */ + public LinkedHashSet(int initialCapacity, float loadFactor) + { + super(initialCapacity, loadFactor); + } + + /** + * Construct a new HashSet with the same elements as are in the supplied + * collection (eliminating any duplicates, of course). The backing storage + * has twice the size of the collection, or the default size of 11, + * whichever is greater; and the default load factor (0.75). + * + * @param c a collection of initial set elements + * @throws NullPointerException if c is null + */ + public LinkedHashSet(Collection c) + { + super(c); + } + + /** + * Helper method which initializes the backing Map. + * + * @param capacity the initial capacity + * @param load the initial load factor + * @return the backing HashMap + */ + HashMap init(int capacity, float load) + { + return new LinkedHashMap(capacity, load); + } +} diff --git a/libjava/classpath/java/util/LinkedList.java b/libjava/classpath/java/util/LinkedList.java new file mode 100644 index 000000000..813d099da --- /dev/null +++ b/libjava/classpath/java/util/LinkedList.java @@ -0,0 +1,1260 @@ +/* LinkedList.java -- Linked list implementation of the List interface + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * Linked list implementation of the List interface. In addition to the + * methods of the List interface, this class provides access to the first + * and last list elements in O(1) time for easy stack, queue, or double-ended + * queue (deque) creation. The list is doubly-linked, with traversal to a + * given index starting from the end closest to the element.

        + * + * LinkedList is not synchronized, so if you need multi-threaded access, + * consider using:
        + * List l = Collections.synchronizedList(new LinkedList(...)); + *

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * {@link ConcurrentModificationException} rather than exhibit + * non-deterministic behavior. + * + * @author Original author unknown + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see List + * @see ArrayList + * @see Vector + * @see Collections#synchronizedList(List) + * @since 1.2 + * @status Complete to 1.6 + */ +public class LinkedList extends AbstractSequentialList + implements List, Deque, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 876323262645176354L; + + /** + * The first element in the list. + */ + transient Entry first; + + /** + * The last element in the list. + */ + transient Entry last; + + /** + * The current length of the list. + */ + transient int size = 0; + + /** + * Class to represent an entry in the list. Holds a single element. + */ + private static final class Entry + { + /** The element in the list. */ + T data; + + /** The next list entry, null if this is last. */ + Entry next; + + /** The previous list entry, null if this is first. */ + Entry previous; + + /** + * Construct an entry. + * @param data the list element + */ + Entry(T data) + { + this.data = data; + } + } // class Entry + + /** + * Obtain the Entry at a given position in a list. This method of course + * takes linear time, but it is intelligent enough to take the shorter of the + * paths to get to the Entry required. This implies that the first or last + * entry in the list is obtained in constant time, which is a very desirable + * property. + * For speed and flexibility, range checking is not done in this method: + * Incorrect values will be returned if (n < 0) or (n >= size). + * + * @param n the number of the entry to get + * @return the entry at position n + */ + // Package visible for use in nested classes. + Entry getEntry(int n) + { + Entry e; + if (n < size / 2) + { + e = first; + // n less than size/2, iterate from start + while (n-- > 0) + e = e.next; + } + else + { + e = last; + // n greater than size/2, iterate from end + while (++n < size) + e = e.previous; + } + return e; + } + + /** + * Remove an entry from the list. This will adjust size and deal with + * `first' and `last' appropriatly. + * + * @param e the entry to remove + */ + // Package visible for use in nested classes. + void removeEntry(Entry e) + { + modCount++; + size--; + if (size == 0) + first = last = null; + else + { + if (e == first) + { + first = e.next; + e.next.previous = null; + } + else if (e == last) + { + last = e.previous; + e.previous.next = null; + } + else + { + e.next.previous = e.previous; + e.previous.next = e.next; + } + } + } + + /** + * Checks that the index is in the range of possible elements (inclusive). + * + * @param index the index to check + * @throws IndexOutOfBoundsException if index < 0 || index > size + */ + private void checkBoundsInclusive(int index) + { + if (index < 0 || index > size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size); + } + + /** + * Checks that the index is in the range of existing elements (exclusive). + * + * @param index the index to check + * @throws IndexOutOfBoundsException if index < 0 || index >= size + */ + private void checkBoundsExclusive(int index) + { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size); + } + + /** + * Create an empty linked list. + */ + public LinkedList() + { + } + + /** + * Create a linked list containing the elements, in order, of a given + * collection. + * + * @param c the collection to populate this list from + * @throws NullPointerException if c is null + */ + public LinkedList(Collection c) + { + addAll(c); + } + + /** + * Returns the first element in the list. + * + * @return the first list element + * @throws NoSuchElementException if the list is empty + */ + public T getFirst() + { + if (size == 0) + throw new NoSuchElementException(); + return first.data; + } + + /** + * Returns the last element in the list. + * + * @return the last list element + * @throws NoSuchElementException if the list is empty + */ + public T getLast() + { + if (size == 0) + throw new NoSuchElementException(); + return last.data; + } + + /** + * Remove and return the first element in the list. + * + * @return the former first element in the list + * @throws NoSuchElementException if the list is empty + */ + public T removeFirst() + { + if (size == 0) + throw new NoSuchElementException(); + modCount++; + size--; + T r = first.data; + + if (first.next != null) + first.next.previous = null; + else + last = null; + + first = first.next; + + return r; + } + + /** + * Remove and return the last element in the list. + * + * @return the former last element in the list + * @throws NoSuchElementException if the list is empty + */ + public T removeLast() + { + if (size == 0) + throw new NoSuchElementException(); + modCount++; + size--; + T r = last.data; + + if (last.previous != null) + last.previous.next = null; + else + first = null; + + last = last.previous; + + return r; + } + + /** + * Insert an element at the first of the list. + * + * @param o the element to insert + */ + public void addFirst(T o) + { + Entry e = new Entry(o); + + modCount++; + if (size == 0) + first = last = e; + else + { + e.next = first; + first.previous = e; + first = e; + } + size++; + } + + /** + * Insert an element at the last of the list. + * + * @param o the element to insert + */ + public void addLast(T o) + { + addLastEntry(new Entry(o)); + } + + /** + * Inserts an element at the end of the list. + * + * @param e the entry to add + */ + private void addLastEntry(Entry e) + { + modCount++; + if (size == 0) + first = last = e; + else + { + e.previous = last; + last.next = e; + last = e; + } + size++; + } + + /** + * Returns true if the list contains the given object. Comparison is done by + * o == null ? e = null : o.equals(e). + * + * @param o the element to look for + * @return true if it is found + */ + public boolean contains(Object o) + { + Entry e = first; + while (e != null) + { + if (equals(o, e.data)) + return true; + e = e.next; + } + return false; + } + + /** + * Returns the size of the list. + * + * @return the list size + */ + public int size() + { + return size; + } + + /** + * Adds an element to the end of the list. + * + * @param o the entry to add + * @return true, as it always succeeds + */ + public boolean add(T o) + { + addLastEntry(new Entry(o)); + return true; + } + + /** + * Removes the entry at the lowest index in the list that matches the given + * object, comparing by o == null ? e = null : o.equals(e). + * + * @param o the object to remove + * @return true if an instance of the object was removed + */ + public boolean remove(Object o) + { + Entry e = first; + while (e != null) + { + if (equals(o, e.data)) + { + removeEntry(e); + return true; + } + e = e.next; + } + return false; + } + + /** + * Append the elements of the collection in iteration order to the end of + * this list. If this list is modified externally (for example, if this + * list is the collection), behavior is unspecified. + * + * @param c the collection to append + * @return true if the list was modified + * @throws NullPointerException if c is null + */ + public boolean addAll(Collection c) + { + return addAll(size, c); + } + + /** + * Insert the elements of the collection in iteration order at the given + * index of this list. If this list is modified externally (for example, + * if this list is the collection), behavior is unspecified. + * + * @param c the collection to append + * @return true if the list was modified + * @throws NullPointerException if c is null + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public boolean addAll(int index, Collection c) + { + checkBoundsInclusive(index); + int csize = c.size(); + + if (csize == 0) + return false; + + Iterator itr = c.iterator(); + + // Get the entries just before and after index. If index is at the start + // of the list, BEFORE is null. If index is at the end of the list, AFTER + // is null. If the list is empty, both are null. + Entry after = null; + Entry before = null; + if (index != size) + { + after = getEntry(index); + before = after.previous; + } + else + before = last; + + // Create the first new entry. We do not yet set the link from `before' + // to the first entry, in order to deal with the case where (c == this). + // [Actually, we don't have to handle this case to fufill the + // contract for addAll(), but Sun's implementation appears to.] + Entry e = new Entry(itr.next()); + e.previous = before; + Entry prev = e; + Entry firstNew = e; + + // Create and link all the remaining entries. + for (int pos = 1; pos < csize; pos++) + { + e = new Entry(itr.next()); + e.previous = prev; + prev.next = e; + prev = e; + } + + // Link the new chain of entries into the list. + modCount++; + size += csize; + prev.next = after; + if (after != null) + after.previous = e; + else + last = e; + + if (before != null) + before.next = firstNew; + else + first = firstNew; + return true; + } + + /** + * Remove all elements from this list. + */ + public void clear() + { + if (size > 0) + { + modCount++; + first = null; + last = null; + size = 0; + } + } + + /** + * Return the element at index. + * + * @param index the place to look + * @return the element at index + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public T get(int index) + { + checkBoundsExclusive(index); + return getEntry(index).data; + } + + /** + * Replace the element at the given location in the list. + * + * @param index which index to change + * @param o the new element + * @return the prior element + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public T set(int index, T o) + { + checkBoundsExclusive(index); + Entry e = getEntry(index); + T old = e.data; + e.data = o; + return old; + } + + /** + * Inserts an element in the given position in the list. + * + * @param index where to insert the element + * @param o the element to insert + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public void add(int index, T o) + { + checkBoundsInclusive(index); + Entry e = new Entry(o); + + if (index < size) + { + modCount++; + Entry after = getEntry(index); + e.next = after; + e.previous = after.previous; + if (after.previous == null) + first = e; + else + after.previous.next = e; + after.previous = e; + size++; + } + else + addLastEntry(e); + } + + /** + * Removes the element at the given position from the list. + * + * @param index the location of the element to remove + * @return the removed element + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public T remove(int index) + { + checkBoundsExclusive(index); + Entry e = getEntry(index); + removeEntry(e); + return e.data; + } + + /** + * Returns the first index where the element is located in the list, or -1. + * + * @param o the element to look for + * @return its position, or -1 if not found + */ + public int indexOf(Object o) + { + int index = 0; + Entry e = first; + while (e != null) + { + if (equals(o, e.data)) + return index; + index++; + e = e.next; + } + return -1; + } + + /** + * Returns the last index where the element is located in the list, or -1. + * + * @param o the element to look for + * @return its position, or -1 if not found + */ + public int lastIndexOf(Object o) + { + int index = size - 1; + Entry e = last; + while (e != null) + { + if (equals(o, e.data)) + return index; + index--; + e = e.previous; + } + return -1; + } + + /** + * Obtain a ListIterator over this list, starting at a given index. The + * ListIterator returned by this method supports the add, remove and set + * methods. + * + * @param index the index of the element to be returned by the first call to + * next(), or size() to be initially positioned at the end of the list + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + checkBoundsInclusive(index); + return new LinkedListItr(index); + } + + /** + * Create a shallow copy of this LinkedList (the elements are not cloned). + * + * @return an object of the same class as this object, containing the + * same elements in the same order + */ + public Object clone() + { + LinkedList copy = null; + try + { + copy = (LinkedList) super.clone(); + } + catch (CloneNotSupportedException ex) + { + } + copy.clear(); + copy.addAll(this); + return copy; + } + + /** + * Returns an array which contains the elements of the list in order. + * + * @return an array containing the list elements + */ + public Object[] toArray() + { + Object[] array = new Object[size]; + Entry e = first; + for (int i = 0; i < size; i++) + { + array[i] = e.data; + e = e.next; + } + return array; + } + + /** + * Returns an Array whose component type is the runtime component type of + * the passed-in Array. The returned Array is populated with all of the + * elements in this LinkedList. If the passed-in Array is not large enough + * to store all of the elements in this List, a new Array will be created + * and returned; if the passed-in Array is larger than the size + * of this List, then size() index will be set to null. + * + * @param a the passed-in Array + * @return an array representation of this list + * @throws ArrayStoreException if the runtime type of a does not allow + * an element in this list + * @throws NullPointerException if a is null + */ + public S[] toArray(S[] a) + { + if (a.length < size) + a = (S[]) Array.newInstance(a.getClass().getComponentType(), size); + else if (a.length > size) + a[size] = null; + Entry e = first; + for (int i = 0; i < size; i++) + { + a[i] = (S) e.data; + e = e.next; + } + return a; + } + + /** + * Adds the specified element to the end of the list. + * + * @param value the value to add. + * @return true. + * @since 1.5 + */ + public boolean offer(T value) + { + return add(value); + } + + /** + * Returns the first element of the list without removing + * it. + * + * @return the first element of the list. + * @throws NoSuchElementException if the list is empty. + * @since 1.5 + */ + public T element() + { + return getFirst(); + } + + /** + * Returns the first element of the list without removing + * it. + * + * @return the first element of the list, or null + * if the list is empty. + * @since 1.5 + */ + public T peek() + { + if (size == 0) + return null; + return getFirst(); + } + + /** + * Removes and returns the first element of the list. + * + * @return the first element of the list, or null + * if the list is empty. + * @since 1.5 + */ + public T poll() + { + if (size == 0) + return null; + return removeFirst(); + } + + /** + * Removes and returns the first element of the list. + * + * @return the first element of the list. + * @throws NoSuchElementException if the list is empty. + * @since 1.5 + */ + public T remove() + { + return removeFirst(); + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the size of the list (int), followed by all the elements + * (Object) in proper order + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + s.writeInt(size); + Entry e = first; + while (e != null) + { + s.writeObject(e.data); + e = e.next; + } + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the size of the list (int), followed by all the elements + * (Object) in proper order + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + int i = s.readInt(); + while (--i >= 0) + addLastEntry(new Entry((T) s.readObject())); + } + + /** + * A ListIterator over the list. This class keeps track of its + * position in the list and the two list entries it is between. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + */ + private final class LinkedListItr + implements ListIterator + { + /** Number of modifications we know about. */ + private int knownMod = modCount; + + /** Entry that will be returned by next(). */ + private Entry next; + + /** Entry that will be returned by previous(). */ + private Entry previous; + + /** Entry that will be affected by remove() or set(). */ + private Entry lastReturned; + + /** Index of `next'. */ + private int position; + + /** + * Initialize the iterator. + * + * @param index the initial index + */ + LinkedListItr(int index) + { + if (index == size) + { + next = null; + previous = (Entry) last; + } + else + { + next = (Entry) getEntry(index); + previous = next.previous; + } + position = index; + } + + /** + * Checks for iterator consistency. + * + * @throws ConcurrentModificationException if the list was modified + */ + private void checkMod() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Returns the index of the next element. + * + * @return the next index + */ + public int nextIndex() + { + return position; + } + + /** + * Returns the index of the previous element. + * + * @return the previous index + */ + public int previousIndex() + { + return position - 1; + } + + /** + * Returns true if more elements exist via next. + * + * @return true if next will succeed + */ + public boolean hasNext() + { + return (next != null); + } + + /** + * Returns true if more elements exist via previous. + * + * @return true if previous will succeed + */ + public boolean hasPrevious() + { + return (previous != null); + } + + /** + * Returns the next element. + * + * @return the next element + * @throws ConcurrentModificationException if the list was modified + * @throws NoSuchElementException if there is no next + */ + public I next() + { + checkMod(); + if (next == null) + throw new NoSuchElementException(); + position++; + lastReturned = previous = next; + next = lastReturned.next; + return lastReturned.data; + } + + /** + * Returns the previous element. + * + * @return the previous element + * @throws ConcurrentModificationException if the list was modified + * @throws NoSuchElementException if there is no previous + */ + public I previous() + { + checkMod(); + if (previous == null) + throw new NoSuchElementException(); + position--; + lastReturned = next = previous; + previous = lastReturned.previous; + return lastReturned.data; + } + + /** + * Remove the most recently returned element from the list. + * + * @throws ConcurrentModificationException if the list was modified + * @throws IllegalStateException if there was no last element + */ + public void remove() + { + checkMod(); + if (lastReturned == null) + throw new IllegalStateException(); + + // Adjust the position to before the removed element, if the element + // being removed is behind the cursor. + if (lastReturned == previous) + position--; + + next = lastReturned.next; + previous = lastReturned.previous; + removeEntry((Entry) lastReturned); + knownMod++; + + lastReturned = null; + } + + /** + * Adds an element between the previous and next, and advance to the next. + * + * @param o the element to add + * @throws ConcurrentModificationException if the list was modified + */ + public void add(I o) + { + checkMod(); + modCount++; + knownMod++; + size++; + position++; + Entry e = new Entry(o); + e.previous = previous; + e.next = next; + + if (previous != null) + previous.next = e; + else + first = (Entry) e; + + if (next != null) + next.previous = e; + else + last = (Entry) e; + + previous = e; + lastReturned = null; + } + + /** + * Changes the contents of the element most recently returned. + * + * @param o the new element + * @throws ConcurrentModificationException if the list was modified + * @throws IllegalStateException if there was no last element + */ + public void set(I o) + { + checkMod(); + if (lastReturned == null) + throw new IllegalStateException(); + lastReturned.data = o; + } + } // class LinkedListItr + + /** + * Obtain an Iterator over this list in reverse sequential order. + * + * @return an Iterator over the elements of the list in + * reverse order. + * @since 1.6 + */ + public Iterator descendingIterator() + { + return new Iterator() + { + /** Number of modifications we know about. */ + private int knownMod = modCount; + + /** Entry that will be returned by next(). */ + private Entry next = last; + + /** Entry that will be affected by remove() or set(). */ + private Entry lastReturned; + + /** Index of `next'. */ + private int position = size() - 1; + + // This will get inlined, since it is private. + /** + * Checks for modifications made to the list from + * elsewhere while iteration is in progress. + * + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + private void checkMod() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + } + + /** + * Tests to see if there are any more objects to + * return. + * + * @return true if the start of the list has not yet been + * reached. + */ + public boolean hasNext() + { + return next != null; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are + * no more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public T next() + { + checkMod(); + if (next == null) + throw new NoSuchElementException(); + --position; + lastReturned = next; + next = lastReturned.previous; + return lastReturned.data; + } + + /** + * Removes the last object retrieved by next() + * from the list, if the list supports object removal. + * + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + */ + public void remove() + { + checkMod(); + if (lastReturned == null) + throw new IllegalStateException(); + removeEntry(lastReturned); + lastReturned = null; + ++knownMod; + } + }; + } + + /** + * Inserts the specified element at the front of the list. + * + * @param value the element to insert. + * @return true. + * @since 1.6 + */ + public boolean offerFirst(T value) + { + addFirst(value); + return true; + } + + /** + * Inserts the specified element at the end of the list. + * + * @param value the element to insert. + * @return true. + * @since 1.6 + */ + public boolean offerLast(T value) + { + return add(value); + } + + /** + * Returns the first element of the list without removing + * it. + * + * @return the first element of the list, or null + * if the list is empty. + * @since 1.6 + */ + public T peekFirst() + { + return peek(); + } + + /** + * Returns the last element of the list without removing + * it. + * + * @return the last element of the list, or null + * if the list is empty. + * @since 1.6 + */ + public T peekLast() + { + if (size == 0) + return null; + return getLast(); + } + + /** + * Removes and returns the first element of the list. + * + * @return the first element of the list, or null + * if the list is empty. + * @since 1.6 + */ + public T pollFirst() + { + return poll(); + } + + /** + * Removes and returns the last element of the list. + * + * @return the last element of the list, or null + * if the list is empty. + * @since 1.6 + */ + public T pollLast() + { + if (size == 0) + return null; + return removeLast(); + } + + /** + * Pops an element from the stack by removing and returning + * the first element in the list. This is equivalent to + * calling {@link #removeFirst()}. + * + * @return the top of the stack, which is the first element + * of the list. + * @throws NoSuchElementException if the list is empty. + * @since 1.6 + * @see #removeFirst() + */ + public T pop() + { + return removeFirst(); + } + + /** + * Pushes an element on to the stack by adding it to the + * front of the list. This is equivalent to calling + * {@link #addFirst(T)}. + * + * @param value the element to push on to the stack. + * @throws NoSuchElementException if the list is empty. + * @since 1.6 + * @see #addFirst(T) + */ + public void push(T value) + { + addFirst(value); + } + + /** + * Removes the first occurrence of the specified element + * from the list, when traversing the list from head to + * tail. The list is unchanged if the element is not found. + * This is equivalent to calling {@link #remove(Object)}. + * + * @param o the element to remove. + * @return true if an instance of the object was removed. + * @since 1.6 + */ + public boolean removeFirstOccurrence(Object o) + { + return remove(o); + } + + /** + * Removes the last occurrence of the specified element + * from the list, when traversing the list from head to + * tail. The list is unchanged if the element is not found. + * + * @param o the element to remove. + * @return true if an instance of the object was removed. + * @since 1.6 + */ + public boolean removeLastOccurrence(Object o) + { + Entry e = last; + while (e != null) + { + if (equals(o, e.data)) + { + removeEntry(e); + return true; + } + e = e.previous; + } + return false; + } + +} diff --git a/libjava/classpath/java/util/List.java b/libjava/classpath/java/util/List.java new file mode 100644 index 000000000..3a437a5a2 --- /dev/null +++ b/libjava/classpath/java/util/List.java @@ -0,0 +1,451 @@ +/* List.java -- An ordered collection which allows indexed access + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An ordered collection (also known as a list). This collection allows + * access to elements by position, as well as control on where elements + * are inserted. Unlike sets, duplicate elements are permitted by this + * general contract (if a subclass forbids duplicates, this should be + * documented). + *

        + * + * List places additional requirements on iterator, + * add, remove, equals, and + * hashCode, in addition to requiring more methods. List + * indexing is 0-based (like arrays), although some implementations may + * require time proportional to the index to obtain an arbitrary element. + * The List interface is incompatible with Set; you cannot implement both + * simultaneously. + *

        + * + * Lists also provide a ListIterator which allows bidirectional + * traversal and other features atop regular iterators. Lists can be + * searched for arbitrary elements, and allow easy insertion and removal + * of multiple elements in one method call. + *

        + * + * Note: While lists may contain themselves as elements, this leads to + * undefined (usually infinite recursive) behavior for some methods like + * hashCode or equals. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Set + * @see ArrayList + * @see LinkedList + * @see Vector + * @see Arrays#asList(Object[]) + * @see Collections#nCopies(int, Object) + * @see Collections#EMPTY_LIST + * @see AbstractList + * @see AbstractSequentialList + * @since 1.2 + * @status updated to 1.4 + */ +public interface List extends Collection +{ + /** + * Insert an element into the list at a given position (optional operation). + * This shifts all existing elements from that position to the end one + * index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + void add(int index, E o); + + /** + * Add an element to the end of the list (optional operation). If the list + * imposes restraints on what can be inserted, such as no null elements, + * this should be documented. + * + * @param o the object to add + * @return true, as defined by Collection for a modified list + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + boolean add(E o); + + /** + * Insert the contents of a collection into the list at a given position + * (optional operation). Shift all elements at that position to the right + * by the number of elements inserted. This operation is undefined if + * this list is modified during the operation (for example, if you try + * to insert a list into itself). + * + * @param index the location to insert the collection + * @param c the collection to insert + * @return true if the list was modified by this action, that is, if c is + * non-empty + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if some element of c is null and this list + * doesn't support the addition of null values. + * @throws NullPointerException if the specified collection is null + * @see #add(int, Object) + */ + boolean addAll(int index, Collection c); + + /** + * Add the contents of a collection to the end of the list (optional + * operation). This operation is undefined if this list is modified + * during the operation (for example, if you try to insert a list into + * itself). + * + * @param c the collection to add + * @return true if the list was modified by this action, that is, if c is + * non-empty + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + * @throws NullPointerException if some element of c is null and this list + * doesn't support the addition of null values. + * @see #add(Object) + */ + boolean addAll(Collection c); + + /** + * Clear the list, such that a subsequent call to isEmpty() would return + * true (optional operation). + * + * @throws UnsupportedOperationException if this list does not support the + * clear operation + */ + void clear(); + + /** + * Test whether this list contains a given object as one of its elements. + * This is defined as the existence of an element e such that + * o == null ? e == null : o.equals(e). + * + * @param o the element to look for + * @return true if this list contains the element + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws NullPointerException if o is null and the list doesn't + * support null values. + */ + boolean contains(Object o); + + /** + * Test whether this list contains every element in a given collection. + * + * @param c the collection to test for + * @return true if for every element o in c, contains(o) would return true + * @throws NullPointerException if the collection is null + * @throws ClassCastException if the type of any element in c is not a valid + * type for this list. + * @throws NullPointerException if some element of c is null and this + * list does not support null values. + * @see #contains(Object) + */ + boolean containsAll(Collection c); + + /** + * Test whether this list is equal to another object. A List is defined to be + * equal to an object if and only if that object is also a List, and the two + * lists have the same sequence. Two lists l1 and l2 are equal if and only + * if l1.size() == l2.size(), and for every integer n between 0 + * and l1.size() - 1 inclusive, l1.get(n) == null ? + * l2.get(n) == null : l1.get(n).equals(l2.get(n)). + * + * @param o the object to test for equality with this list + * @return true if o is equal to this list + * @see Object#equals(Object) + * @see #hashCode() + */ + boolean equals(Object o); + + /** + * Get the element at a given index in this list. + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + E get(int index); + + /** + * Obtains a hash code for this list. In order to obey the general + * contract of the hashCode method of class Object, this value is + * calculated as follows: + * +

        hashCode = 1;
        +Iterator i = list.iterator();
        +while (i.hasNext())
        +{
        +  Object obj = i.next();
        +  hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
        +}
        + * + *

        This ensures that the general contract of Object.hashCode() + * is adhered to. + * + * @return the hash code of this list + * @see Object#hashCode() + * @see #equals(Object) + */ + int hashCode(); + + /** + * Obtain the first index at which a given object is to be found in this + * list. + * + * @param o the object to search for + * @return the least integer n such that o == null ? get(n) == null : + * o.equals(get(n)), or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + int indexOf(Object o); + + /** + * Test whether this list is empty, that is, if size() == 0. + * + * @return true if this list contains no elements + */ + boolean isEmpty(); + + /** + * Obtain an Iterator over this list, whose sequence is the list order. + * + * @return an Iterator over the elements of this list, in order + */ + Iterator iterator(); + + /** + * Obtain the last index at which a given object is to be found in this + * list. + * + * @return the greatest integer n such that o == null ? get(n) == null + * : o.equals(get(n)), or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + int lastIndexOf(Object o); + + /** + * Obtain a ListIterator over this list, starting at the beginning. + * + * @return a ListIterator over the elements of this list, in order, starting + * at the beginning + */ + ListIterator listIterator(); + + /** + * Obtain a ListIterator over this list, starting at a given position. + * A first call to next() would return the same as get(index), and a + * first call to previous() would return the same as get(index - 1). + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from + * @return a ListIterator over the elements of this list, in order, starting + * at index + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + ListIterator listIterator(int index); + + /** + * Remove the element at a given position in this list (optional operation). + * Shifts all remaining elements to the left to fill the gap. + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + E remove(int index); + + /** + * Remove the first occurence of an object from this list (optional + * operation). That is, remove the first element e such that + * o == null ? e == null : o.equals(e). + * + * @param o the object to remove + * @return true if the list changed as a result of this call, that is, if + * the list contained at least one occurrence of o + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support removing null values. + */ + boolean remove(Object o); + + /** + * Remove all elements of a given collection from this list (optional + * operation). That is, remove every element e such that c.contains(e). + * + * @param c the collection to filter out + * @return true if this list was modified as a result of this call + * @throws UnsupportedOperationException if this list does not support the + * removeAll operation + * @throws NullPointerException if the collection is null + * @throws ClassCastException if the type of any element in c is not a valid + * type for this list. + * @throws NullPointerException if some element of c is null and this + * list does not support removing null values. + * @see #remove(Object) + * @see #contains(Object) + */ + boolean removeAll(Collection c); + + /** + * Remove all elements of this list that are not contained in a given + * collection (optional operation). That is, remove every element e such + * that !c.contains(e). + * + * @param c the collection to retain + * @return true if this list was modified as a result of this call + * @throws UnsupportedOperationException if this list does not support the + * retainAll operation + * @throws NullPointerException if the collection is null + * @throws ClassCastException if the type of any element in c is not a valid + * type for this list. + * @throws NullPointerException if some element of c is null and this + * list does not support retaining null values. + * @see #remove(Object) + * @see #contains(Object) + */ + boolean retainAll(Collection c); + + /** + * Replace an element of this list with another object (optional operation). + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + E set(int index, E o); + + /** + * Get the number of elements in this list. If the list contains more + * than Integer.MAX_VALUE elements, return Integer.MAX_VALUE. + * + * @return the number of elements in the list + */ + int size(); + + /** + * Obtain a List view of a subsection of this list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex + */ + List subList(int fromIndex, int toIndex); + + /** + * Copy the current contents of this list into an array. + * + * @return an array of type Object[] and length equal to the length of this + * list, containing the elements currently in this list, in order + */ + Object[] toArray(); + + /** + * Copy the current contents of this list into an array. If the array passed + * as an argument has length less than that of this list, an array of the + * same run-time type as a, and length equal to the length of this list, is + * allocated using Reflection. Otherwise, a itself is used. The elements of + * this list are copied into it, and if there is space in the array, the + * following element is set to null. The resultant array is returned. + * Note: The fact that the following element is set to null is only useful + * if it is known that this list does not contain any null elements. + * + * @param a the array to copy this list into + * @return an array containing the elements currently in this list, in + * order + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a + * @throws NullPointerException if the specified array is null + */ + T[] toArray(T[] a); +} diff --git a/libjava/classpath/java/util/ListIterator.java b/libjava/classpath/java/util/ListIterator.java new file mode 100644 index 000000000..9b74528c5 --- /dev/null +++ b/libjava/classpath/java/util/ListIterator.java @@ -0,0 +1,170 @@ +/* ListIterator.java -- Extended Iterator for iterating over ordered lists + Copyright (C) 1998, 1999, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An extended version of Iterator to support the extra features of Lists. The + * elements may be accessed in forward or reverse order, elements may be + * replaced as well as removed, and new elements may be inserted, during the + * traversal of the list. + *

        + * + * A list with n elements provides n+1 iterator positions (the front, the end, + * or between two elements). Note that remove and set + * operate on the last element returned, whether it was by next + * or previous. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see Iterator + * @see Enumeration + * @since 1.2 + * @status updated to 1.4 + */ +public interface ListIterator extends Iterator +{ + /** + * Tests whether there are elements remaining in the list in the forward + * direction. In other words, next() will not fail with a + * NoSuchElementException. + * + * @return true if the list continues in the forward direction + */ + boolean hasNext(); + + /** + * Tests whether there are elements remaining in the list in the reverse + * direction. In other words, previous() will not fail with a + * NoSuchElementException. + * + * @return true if the list continues in the reverse direction + */ + boolean hasPrevious(); + + /** + * Obtain the next element in the list in the forward direction. Repeated + * calls to next may be used to iterate over the entire list, or calls to + * next and previous may be used together to go forwards and backwards. + * Alternating calls to next and previous will return the same element. + * + * @return the next element in the list in the forward direction + * @throws NoSuchElementException if there are no more elements + */ + E next(); + + /** + * Obtain the next element in the list in the reverse direction. Repeated + * calls to previous may be used to iterate backwards over the entire list, + * or calls to next and previous may be used together to go forwards and + * backwards. Alternating calls to next and previous will return the same + * element. + * + * @return the next element in the list in the reverse direction + * @throws NoSuchElementException if there are no more elements + */ + E previous(); + + /** + * Find the index of the element that would be returned by a call to next. + * If hasNext() returns false, this returns the list size. + * + * @return the index of the element that would be returned by next() + */ + int nextIndex(); + + /** + * Find the index of the element that would be returned by a call to + * previous. If hasPrevious() returns false, this returns -1. + * + * @return the index of the element that would be returned by previous() + */ + int previousIndex(); + + /** + * Insert an element into the list at the current position of the iterator + * (optional operation). The element is inserted in between the element that + * would be returned by previous and the element that would be returned by + * next. After the insertion, a subsequent call to next is unaffected, but + * a call to previous returns the item that was added. The values returned + * by nextIndex() and previousIndex() are incremented. + * + * @param o the object to insert into the list + * @throws ClassCastException if the object is of a type which cannot be added + * to this list. + * @throws IllegalArgumentException if some other aspect of the object stops + * it being added to this list. + * @throws UnsupportedOperationException if this ListIterator does not + * support the add operation. + */ + void add(E o); + + /** + * Remove from the list the element last returned by a call to next or + * previous (optional operation). This method may only be called if neither + * add nor remove have been called since the last call to next or previous. + * + * @throws IllegalStateException if neither next or previous have been + * called, or if add or remove has been called since the last call + * to next or previous + * @throws UnsupportedOperationException if this ListIterator does not + * support the remove operation + */ + void remove(); + + /** + * Replace the element last returned by a call to next or previous with a + * given object (optional operation). This method may only be called if + * neither add nor remove have been called since the last call to next or + * previous. + * + * @param o the object to replace the element with + * @throws ClassCastException the object is of a type which cannot be added + * to this list + * @throws IllegalArgumentException some other aspect of the object stops + * it being added to this list + * @throws IllegalStateException if neither next or previous have been + * called, or if add or remove has been called since the last call + * to next or previous + * @throws UnsupportedOperationException if this ListIterator does not + * support the set operation + */ + void set(E o); +} diff --git a/libjava/classpath/java/util/ListResourceBundle.java b/libjava/classpath/java/util/ListResourceBundle.java new file mode 100644 index 000000000..2e48a22b5 --- /dev/null +++ b/libjava/classpath/java/util/ListResourceBundle.java @@ -0,0 +1,140 @@ +/* ListResourceBundle -- a resource bundle build around a list + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A ListResouceBundle provides an easy way, to create your own + * resource bundle. It is an abstract class that you can subclass. You should + * then overwrite the getContents method, that provides a key/value list. + * + *

        The key/value list is a two dimensional list of Object. The first + * dimension ranges over the resources. The second dimension ranges from + * zero (key) to one (value). The keys must be of type String, and they are + * case-sensitive. For example: + * +

        public class MyResources
        +  extends ListResourceBundle
        +{
        +  public Object[][] getContents()
        +  {
        +    return contents;
        +  }
        +
        +  static final Object[][] contents =
        +  {
        +    // LOCALIZED STRINGS
        +    {"s1", "The disk \"{1}\" contains {0}."},  // MessageFormat pattern
        +    {"s2", "1"},                       // location of {0} in pattern
        +    {"s3", "My Disk"},                 // sample disk name
        +    {"s4", "no files"},                // first ChoiceFormat choice
        +    {"s5", "one file"},                // second ChoiceFormat choice
        +    {"s6", "{0,number} files"}         // third ChoiceFormat choice
        +    {"s7", "3 Mar 96"},                // sample date
        +    {"s8", new Dimension(1,5)}         // real object, not just string
        +    // END OF LOCALIZED MATERIAL
        +  };
        +}
        + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @see Locale + * @see PropertyResourceBundle + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ListResourceBundle extends ResourceBundle +{ + /** + * The constructor. It does nothing special. + */ + public ListResourceBundle() + { + } + + /** + * Gets a resource for a given key. This is called by getObject. + * + * @param key the key of the resource + * @return the resource for the key, or null if it doesn't exist + */ + public final Object handleGetObject(String key) + { + Object[][] contents = getContents(); + int i = contents.length; + while (--i >= 0) + if (key.equals(contents[i][0])) + return contents[i][1]; + return null; + } + + /** + * This method should return all keys for which a resource exists. + * + * @return an enumeration of the keys + */ + public Enumeration getKeys() + { + // We make a new Set that holds all the keys, then return an enumeration + // for that. This prevents modifications from ruining the enumeration, + // as well as ignoring duplicates. + final Object[][] contents = getContents(); + Set s = new HashSet(); + int i = contents.length; + while (--i >= 0) + s.add((String) contents[i][0]); + ResourceBundle bundle = parent; + // Eliminate tail recursion. + while (bundle != null) + { + Enumeration e = bundle.getKeys(); + while (e.hasMoreElements()) + s.add(e.nextElement()); + bundle = bundle.parent; + } + return Collections.enumeration(s); + } + + /** + * Gets the key/value list. You must override this method, and should not + * provide duplicate keys or null entries. + * + * @return a two dimensional list of String key / Object resouce pairs + */ + protected abstract Object[][] getContents(); +} // class ListResourceBundle diff --git a/libjava/classpath/java/util/Locale.java b/libjava/classpath/java/util/Locale.java new file mode 100644 index 000000000..ef111737d --- /dev/null +++ b/libjava/classpath/java/util/Locale.java @@ -0,0 +1,1029 @@ +/* Locale.java -- i18n locales + Copyright (C) 1998, 1999, 2001, 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.classpath.SystemProperties; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.locale.LocaleHelper; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import java.util.spi.LocaleNameProvider; + +/** + * Locales represent a specific country and culture. Classes which can be + * passed a Locale object tailor their information for a given locale. For + * instance, currency number formatting is handled differently for the USA + * and France. + * + *

        Locales are made up of a language code, a country code, and an optional + * set of variant strings. Language codes are represented by + * + * ISO 639:1988 w/ additions from ISO 639/RA Newsletter No. 1/1989 + * and a decision of the Advisory Committee of ISO/TC39 on August 8, 1997. + * + *

        Country codes are represented by + * + * ISO 3166. Variant strings are vendor and browser specific. Standard + * variant strings include "POSIX" for POSIX, "WIN" for MS-Windows, and + * "MAC" for Macintosh. When there is more than one variant string, they must + * be separated by an underscore (U+005F). + * + *

        The default locale is determined by the values of the system properties + * user.language, user.country (or user.region), and user.variant, defaulting + * to "en_US". Note that the locale does NOT contain the conversion and + * formatting capabilities (for that, use ResourceBundle and java.text). + * Rather, it is an immutable tag object for identifying a given locale, which + * is referenced by these other classes when they must make locale-dependent + * decisions. + * + * @see ResourceBundle + * @see java.text.Format + * @see java.text.NumberFormat + * @see java.text.Collator + * @author Jochen Hoenicke + * @author Paul Fisher + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.1 + * @status updated to 1.4 + */ +public final class Locale implements Serializable, Cloneable +{ + /** Locale which represents the English language. */ + public static final Locale ENGLISH = getLocale("en"); + + /** Locale which represents the French language. */ + public static final Locale FRENCH = getLocale("fr"); + + /** Locale which represents the German language. */ + public static final Locale GERMAN = getLocale("de"); + + /** Locale which represents the Italian language. */ + public static final Locale ITALIAN = getLocale("it"); + + /** Locale which represents the Japanese language. */ + public static final Locale JAPANESE = getLocale("ja"); + + /** Locale which represents the Korean language. */ + public static final Locale KOREAN = getLocale("ko"); + + /** Locale which represents the Chinese language. */ + public static final Locale CHINESE = getLocale("zh"); + + /** Locale which represents the Chinese language as used in China. */ + public static final Locale SIMPLIFIED_CHINESE = getLocale("zh", "CN"); + + /** + * Locale which represents the Chinese language as used in Taiwan. + * Same as TAIWAN Locale. + */ + public static final Locale TRADITIONAL_CHINESE = getLocale("zh", "TW"); + + /** Locale which represents France. */ + public static final Locale FRANCE = getLocale("fr", "FR"); + + /** Locale which represents Germany. */ + public static final Locale GERMANY = getLocale("de", "DE"); + + /** Locale which represents Italy. */ + public static final Locale ITALY = getLocale("it", "IT"); + + /** Locale which represents Japan. */ + public static final Locale JAPAN = getLocale("ja", "JP"); + + /** Locale which represents Korea. */ + public static final Locale KOREA = getLocale("ko", "KR"); + + /** + * Locale which represents China. + * Same as SIMPLIFIED_CHINESE Locale. + */ + public static final Locale CHINA = SIMPLIFIED_CHINESE; + + /** + * Locale which represents the People's Republic of China. + * Same as CHINA Locale. + */ + public static final Locale PRC = CHINA; + + /** + * Locale which represents Taiwan. + * Same as TRADITIONAL_CHINESE Locale. + */ + public static final Locale TAIWAN = TRADITIONAL_CHINESE; + + /** Locale which represents the United Kingdom. */ + public static final Locale UK = getLocale("en", "GB"); + + /** Locale which represents the United States. */ + public static final Locale US = getLocale("en", "US"); + + /** Locale which represents the English speaking portion of Canada. */ + public static final Locale CANADA = getLocale("en", "CA"); + + /** Locale which represents the French speaking portion of Canada. */ + public static final Locale CANADA_FRENCH = getLocale("fr", "CA"); + + /** The root locale, used as the base case in lookups by + * locale-sensitive operations. + */ + public static final Locale ROOT = new Locale("","",""); + + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 9149081749638150636L; + + /** + * The language code, as returned by getLanguage(). + * + * @serial the languange, possibly "" + */ + private final String language; + + /** + * The country code, as returned by getCountry(). + * + * @serial the country, possibly "" + */ + private final String country; + + /** + * The variant code, as returned by getVariant(). + * + * @serial the variant, possibly "" + */ + private final String variant; + + /** + * This is the cached hashcode. When writing to stream, we write -1. + * + * @serial should be -1 in serial streams + */ + private int hashcode; + + /** + * Array storing all available locales. + */ + private static transient Locale[] availableLocales; + + /** + * Locale cache. Only created locale objects are stored. + * Contains all supported locales when getAvailableLocales() + * got called. + */ + private static transient HashMap localeMap; + + /** + * The default locale. Except for during bootstrapping, this should never be + * null. Note the logic in the main constructor, to detect when + * bootstrapping has completed. + */ + private static Locale defaultLocale; + + static { + String language = SystemProperties.getProperty("user.language", "en"); + String country = SystemProperties.getProperty("user.country", "US"); + String region = SystemProperties.getProperty("user.region", null); + String variant = SystemProperties.getProperty("user.variant", ""); + + defaultLocale = getLocale(language, + (region != null) ? region : country, + variant); + } + + /** + * Array storing all the available two-letter ISO639 languages. + */ + private static transient String[] languageCache; + + /** + * Array storing all the available two-letter ISO3166 country codes. + */ + private static transient String[] countryCache; + + /** + * Retrieves the locale with the specified language from the cache. + * + * @param language the language of the locale to retrieve. + * @return the locale. + */ + private static Locale getLocale(String language) + { + return getLocale(language, "", ""); + } + + /** + * Retrieves the locale with the specified language and country + * from the cache. + * + * @param language the language of the locale to retrieve. + * @param country the country of the locale to retrieve. + * @return the locale. + */ + private static Locale getLocale(String language, String country) + { + return getLocale(language, country, ""); + } + + /** + * Retrieves the locale with the specified language, country + * and variant from the cache. + * + * @param language the language of the locale to retrieve. + * @param country the country of the locale to retrieve. + * @param variant the variant of the locale to retrieve. + * @return the locale. + */ + private static Locale getLocale(String language, String country, String variant) + { + if (localeMap == null) + localeMap = new HashMap(256); + + String name = language + "_" + country + "_" + variant; + Locale locale = (Locale) localeMap.get(name); + + if (locale == null) + { + locale = new Locale(language, country, variant); + localeMap.put(name, locale); + } + + return locale; + } + + /** + * Convert new iso639 codes to the old ones. + * + * @param language the language to check + * @return the appropriate code + */ + private String convertLanguage(String language) + { + if (language.equals("")) + return language; + language = language.toLowerCase(); + int index = "he,id,yi".indexOf(language); + if (index != -1) + return "iw,in,ji".substring(index, index + 2); + return language; + } + + /** + * Creates a new locale for the given language and country. + * + * @param language lowercase two-letter ISO-639 A2 language code + * @param country uppercase two-letter ISO-3166 A2 contry code + * @param variant vendor and browser specific + * @throws NullPointerException if any argument is null + */ + public Locale(String language, String country, String variant) + { + // During bootstrap, we already know the strings being passed in are + // the correct capitalization, and not null. We can't call + // String.toUpperCase during this time, since that depends on the + // default locale. + if (defaultLocale != null) + { + language = convertLanguage(language); + country = country.toUpperCase(); + } + this.language = language.intern(); + this.country = country.intern(); + this.variant = variant.intern(); + hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + } + + /** + * Creates a new locale for the given language and country. + * + * @param language lowercase two-letter ISO-639 A2 language code + * @param country uppercase two-letter ISO-3166 A2 country code + * @throws NullPointerException if either argument is null + */ + public Locale(String language, String country) + { + this(language, country, ""); + } + + /** + * Creates a new locale for a language. + * + * @param language lowercase two-letter ISO-639 A2 language code + * @throws NullPointerException if either argument is null + * @since 1.4 + */ + public Locale(String language) + { + this(language, "", ""); + } + + /** + * Returns the default Locale. The default locale is generally once set + * on start up and then never changed. Normally you should use this locale + * for everywhere you need a locale. The initial setting matches the + * default locale, the user has chosen. + * + * @return the default locale for this virtual machine + */ + public static Locale getDefault() + { + return defaultLocale; + } + + /** + * Changes the default locale. Normally only called on program start up. + * Note that this doesn't change the locale for other programs. This has + * a security check, + * PropertyPermission("user.language", "write"), because of + * its potential impact to running code. + * + * @param newLocale the new default locale + * @throws NullPointerException if newLocale is null + * @throws SecurityException if permission is denied + */ + public static void setDefault(Locale newLocale) + { + if (newLocale == null) + throw new NullPointerException(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new PropertyPermission("user.language", "write")); + defaultLocale = newLocale; + } + + /** + * Returns the list of available locales. + * + * @return the installed locales + */ + public static synchronized Locale[] getAvailableLocales() + { + if (availableLocales == null) + { + int len = LocaleHelper.getLocaleCount(); + availableLocales = new Locale[len]; + + for (int i = 0; i < len; i++) + { + String language; + String country = ""; + String variant = ""; + String name = LocaleHelper.getLocaleName(i); + + language = name.substring(0, 2); + + if (name.length() > 2) + country = name.substring(3); + + int index = country.indexOf("_"); + if (index > 0) + { + variant = country.substring(index + 1); + country = country.substring(0, index - 1); + } + + availableLocales[i] = getLocale(language, country, variant); + } + } + + return (Locale[]) availableLocales.clone(); + } + + /** + * Returns a list of all 2-letter uppercase country codes as defined + * in ISO 3166. + * + * @return a list of acceptable country codes + */ + public static String[] getISOCountries() + { + if (countryCache == null) + { + countryCache = getISOStrings("territories"); + } + + return (String[]) countryCache.clone(); + } + + /** + * Returns a list of all 2-letter lowercase language codes as defined + * in ISO 639 (both old and new variant). + * + * @return a list of acceptable language codes + */ + public static String[] getISOLanguages() + { + if (languageCache == null) + { + languageCache = getISOStrings("languages"); + } + return (String[]) languageCache.clone(); + } + + /** + * Returns the set of keys from the specified resource hashtable, filtered + * so that only two letter strings are returned. + * + * @param tableName the name of the table from which to retrieve the keys. + * @return an array of two-letter strings. + */ + private static String[] getISOStrings(String tableName) + { + int count = 0; + ResourceBundle bundle = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation"); + Enumeration e = bundle.getKeys(); + ArrayList tempList = new ArrayList(); + + while (e.hasMoreElements()) + { + String key = (String) e.nextElement(); + + if (key.startsWith(tableName + ".")) + { + String str = key.substring(tableName.length() + 1); + + if (str.length() == 2 + && Character.isLetter(str.charAt(0)) + && Character.isLetter(str.charAt(1))) + { + tempList.add(str); + ++count; + } + } + } + + String[] strings = new String[count]; + + for (int a = 0; a < count; ++a) + strings[a] = (String) tempList.get(a); + + return strings; + } + + /** + * Returns the language code of this locale. Some language codes have changed + * as ISO 639 has evolved; this returns the old name, even if you built + * the locale with the new one. + * + * @return language code portion of this locale, or an empty String + */ + public String getLanguage() + { + return language; + } + + /** + * Returns the country code of this locale. + * + * @return country code portion of this locale, or an empty String + */ + public String getCountry() + { + return country; + } + + /** + * Returns the variant code of this locale. + * + * @return the variant code portion of this locale, or an empty String + */ + public String getVariant() + { + return variant; + } + + /** + * Gets the string representation of the current locale. This consists of + * the language, the country, and the variant, separated by an underscore. + * The variant is listed only if there is a language or country. Examples: + * "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC". + * + * @return the string representation of this Locale + * @see #getDisplayName() + */ + public String toString() + { + if (language.length() == 0 && country.length() == 0) + return ""; + else if (country.length() == 0 && variant.length() == 0) + return language; + CPStringBuilder result = new CPStringBuilder(language); + result.append('_').append(country); + if (variant.length() != 0) + result.append('_').append(variant); + return result.toString(); + } + + /** + * Returns the three-letter ISO language abbrevation of this locale. + * + * @throws MissingResourceException if the three-letter code is not known + */ + public String getISO3Language() + { + // We know all strings are interned so we can use '==' for better performance. + if (language == "") + return ""; + int index + = ("aa,ab,af,am,ar,as,ay,az,ba,be,bg,bh,bi,bn,bo,br,ca,co,cs,cy,da," + + "de,dz,el,en,eo,es,et,eu,fa,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,ha,iw," + + "hi,hr,hu,hy,ia,in,ie,ik,in,is,it,iu,iw,ja,ji,jw,ka,kk,kl,km,kn," + + "ko,ks,ku,ky,la,ln,lo,lt,lv,mg,mi,mk,ml,mn,mo,mr,ms,mt,my,na,ne," + + "nl,no,oc,om,or,pa,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sd,sg,sh,si,sk," + + "sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr," + + "ts,tt,tw,ug,uk,ur,uz,vi,vo,wo,xh,ji,yo,za,zh,zu") + .indexOf(language); + + if (index % 3 != 0 || language.length() != 2) + throw new MissingResourceException + ("Can't find ISO3 language for " + language, + "java.util.Locale", language); + + // Don't read this aloud. These are the three letter language codes. + return + ("aarabkaframharaasmaymazebakbelbulbihbisbenbodbrecatcoscescymdandeu" + + "dzoellengepospaesteusfasfinfijfaofrafrygaigdhglggrngujhauhebhinhrv" + + "hunhyeinaindileipkindislitaikuhebjpnyidjawkatkazkalkhmkankorkaskur" + + "kirlatlinlaolitlavmlgmrimkdmalmonmolmarmsamltmyanaunepnldnorociorm" + + "oripanpolpusporquerohrunronruskinsansndsagsrpsinslkslvsmosnasomsqi" + + "srpsswsotsunsweswatamteltgkthatirtuktgltsntonturtsotattwiuigukrurd" + + "uzbvievolwolxhoyidyorzhazhozul") + .substring(index, index + 3); + } + + /** + * Returns the three-letter ISO country abbrevation of the locale. + * + * @throws MissingResourceException if the three-letter code is not known + */ + public String getISO3Country() + { + // We know all strings are interned so we can use '==' for better performance. + if (country == "") + return ""; + int index + = ("AD,AE,AF,AG,AI,AL,AM,AN,AO,AQ,AR,AS,AT,AU,AW,AZ,BA,BB,BD,BE,BF," + + "BG,BH,BI,BJ,BM,BN,BO,BR,BS,BT,BV,BW,BY,BZ,CA,CC,CF,CG,CH,CI,CK," + + "CL,CM,CN,CO,CR,CU,CV,CX,CY,CZ,DE,DJ,DK,DM,DO,DZ,EC,EE,EG,EH,ER," + + "ES,ET,FI,FJ,FK,FM,FO,FR,FX,GA,GB,GD,GE,GF,GH,GI,GL,GM,GN,GP,GQ," + + "GR,GS,GT,GU,GW,GY,HK,HM,HN,HR,HT,HU,ID,IE,IL,IN,IO,IQ,IR,IS,IT," + + "JM,JO,JP,KE,KG,KH,KI,KM,KN,KP,KR,KW,KY,KZ,LA,LB,LC,LI,LK,LR,LS," + + "LT,LU,LV,LY,MA,MC,MD,MG,MH,MK,ML,MM,MN,MO,MP,MQ,MR,MS,MT,MU,MV," + + "MW,MX,MY,MZ,NA,NC,NE,NF,NG,NI,NL,NO,NP,NR,NU,NZ,OM,PA,PE,PF,PG," + + "PH,PK,PL,PM,PN,PR,PT,PW,PY,QA,RE,RO,RU,RW,SA,SB,SC,SD,SE,SG,SH," + + "SI,SJ,SK,SL,SM,SN,SO,SR,ST,SV,SY,SZ,TC,TD,TF,TG,TH,TJ,TK,TM,TN," + + "TO,TP,TR,TT,TV,TW,TZ,UA,UG,UM,US,UY,UZ,VA,VC,VE,VG,VI,VN,VU,WF," + + "WS,YE,YT,YU,ZA,ZM,ZR,ZW") + .indexOf(country); + + if (index % 3 != 0 || country.length() != 2) + throw new MissingResourceException + ("Can't find ISO3 country for " + country, + "java.util.Locale", country); + + // Don't read this aloud. These are the three letter country codes. + return + ("ANDAREAFGATGAIAALBARMANTAGOATAARGASMAUTAUSABWAZEBIHBRBBGDBELBFABGR" + + "BHRBDIBENBMUBRNBOLBRABHSBTNBVTBWABLRBLZCANCCKCAFCOGCHECIVCOKCHLCMR" + + "CHNCOLCRICUBCPVCXRCYPCZEDEUDJIDNKDMADOMDZAECUESTEGYESHERIESPETHFIN" + + "FJIFLKFSMFROFRAFXXGABGBRGRDGEOGUFGHAGIBGRLGMBGINGLPGNQGRCSGSGTMGUM" + + "GNBGUYHKGHMDHNDHRVHTIHUNIDNIRLISRINDIOTIRQIRNISLITAJAMJORJPNKENKGZ" + + "KHMKIRCOMKNAPRKKORKWTCYMKAZLAOLBNLCALIELKALBRLSOLTULUXLVALBYMARMCO" + + "MDAMDGMHLMKDMLIMMRMNGMACMNPMTQMRTMSRMLTMUSMDVMWIMEXMYSMOZNAMNCLNER" + + "NFKNGANICNLDNORNPLNRUNIUNZLOMNPANPERPYFPNGPHLPAKPOLSPMPCNPRIPRTPLW" + + "PRYQATREUROMRUSRWASAUSLBSYCSDNSWESGPSHNSVNSJMSVKSLESMRSENSOMSURSTP" + + "SLVSYRSWZTCATCDATFTGOTHATJKTKLTKMTUNTONTMPTURTTOTUVTWNTZAUKRUGAUMI" + + "USAURYUZBVATVCTVENVGBVIRVNMVUTWLFWSMYEMMYTYUGZAFZMBZARZWE") + .substring(index, index + 3); + } + + /** + * Gets the country name suitable for display to the user, formatted + * for the default locale. This has the same effect as + *

        +   * getDisplayLanguage(Locale.getDefault());
        +   * 
        + * + * @return the language name of this locale localized to the default locale, + * with the ISO code as backup + */ + public String getDisplayLanguage() + { + return getDisplayLanguage(defaultLocale); + } + + /** + *

        + * Gets the name of the language specified by this locale, in a form suitable + * for display to the user. If possible, the display name will be localized + * to the specified locale. For example, if the locale instance is + * Locale.GERMANY, and the specified locale is Locale.UK, + * the result would be 'German'. Using the German locale would instead give + * 'Deutsch'. If the display name can not be localized to the supplied + * locale, it will fall back on other output in the following order: + *

        + *
          + *
        • the display name in the default locale
        • + *
        • the display name in English
        • + *
        • the ISO code
        • + *
        + *

        + * If the language is unspecified by this locale, then the empty string is + * returned. + *

        + * + * @param inLocale the locale to use for formatting the display string. + * @return the language name of this locale localized to the given locale, + * with the default locale, English and the ISO code as backups. + * @throws NullPointerException if the supplied locale is null. + */ + public String getDisplayLanguage(Locale inLocale) + { + if (language.isEmpty()) + return ""; + try + { + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + inLocale, + ClassLoader.getSystemClassLoader()); + + return res.getString("languages." + language); + } + catch (MissingResourceException e) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (LocaleNameProvider p : + ServiceLoader.load(LocaleNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(inLocale)) + { + String locLang = p.getDisplayLanguage(language, + inLocale); + if (locLang != null) + return locLang; + break; + } + } + } + if (inLocale.equals(Locale.ROOT)) // Base case + return language; + return getDisplayLanguage(LocaleHelper.getFallbackLocale(inLocale)); + } + + /** + * Returns the country name of this locale localized to the + * default locale. If the localized is not found, the ISO code + * is returned. This has the same effect as + *
        +   * getDisplayCountry(Locale.getDefault());
        +   * 
        + * + * @return the country name of this locale localized to the given locale, + * with the ISO code as backup + */ + public String getDisplayCountry() + { + return getDisplayCountry(defaultLocale); + } + + /** + *

        + * Gets the name of the country specified by this locale, in a form suitable + * for display to the user. If possible, the display name will be localized + * to the specified locale. For example, if the locale instance is + * Locale.GERMANY, and the specified locale is Locale.UK, + * the result would be 'Germany'. Using the German locale would instead give + * 'Deutschland'. If the display name can not be localized to the supplied + * locale, it will fall back on other output in the following order: + *

        + *
          + *
        • the display name in the default locale
        • + *
        • the display name in English
        • + *
        • the ISO code
        • + *
        + *

        + * If the country is unspecified by this locale, then the empty string is + * returned. + *

        + * + * @param inLocale the locale to use for formatting the display string. + * @return the country name of this locale localized to the given locale, + * with the default locale, English and the ISO code as backups. + * @throws NullPointerException if the supplied locale is null. + */ + public String getDisplayCountry(Locale inLocale) + { + if (country.isEmpty()) + return ""; + try + { + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + inLocale, + ClassLoader.getSystemClassLoader()); + + return res.getString("territories." + country); + } + catch (MissingResourceException e) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (LocaleNameProvider p : + ServiceLoader.load(LocaleNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(inLocale)) + { + String locCountry = p.getDisplayCountry(country, + inLocale); + if (locCountry != null) + return locCountry; + break; + } + } + } + if (inLocale.equals(Locale.ROOT)) // Base case + return country; + return getDisplayCountry(LocaleHelper.getFallbackLocale(inLocale)); + } + + /** + * Returns the variant name of this locale localized to the + * default locale. If the localized is not found, the variant code + * itself is returned. This has the same effect as + *
        +   * getDisplayVariant(Locale.getDefault());
        +   * 
        + * + * @return the variant code of this locale localized to the given locale, + * with the ISO code as backup + */ + public String getDisplayVariant() + { + return getDisplayVariant(defaultLocale); + } + + + /** + *

        + * Gets the name of the variant specified by this locale, in a form suitable + * for display to the user. If possible, the display name will be localized + * to the specified locale. For example, if the locale instance is a revised + * variant, and the specified locale is Locale.UK, the result + * would be 'REVISED'. Using the German locale would instead give + * 'Revidiert'. If the display name can not be localized to the supplied + * locale, it will fall back on other output in the following order: + *

        + *
          + *
        • the display name in the default locale
        • + *
        • the display name in English
        • + *
        • the ISO code
        • + *
        + *

        + * If the variant is unspecified by this locale, then the empty string is + * returned. + *

        + * + * @param inLocale the locale to use for formatting the display string. + * @return the variant name of this locale localized to the given locale, + * with the default locale, English and the ISO code as backups. + * @throws NullPointerException if the supplied locale is null. + */ + public String getDisplayVariant(Locale inLocale) + { + if (variant.isEmpty()) + return ""; + try + { + ResourceBundle res = + ResourceBundle.getBundle("gnu.java.locale.LocaleInformation", + inLocale, + ClassLoader.getSystemClassLoader()); + + return res.getString("variants." + variant); + } + catch (MissingResourceException e) + { + /* This means runtime support for the locale + * is not available, so we check providers. */ + } + for (LocaleNameProvider p : + ServiceLoader.load(LocaleNameProvider.class)) + { + for (Locale loc : p.getAvailableLocales()) + { + if (loc.equals(inLocale)) + { + String locVar = p.getDisplayVariant(variant, + inLocale); + if (locVar != null) + return locVar; + break; + } + } + } + if (inLocale.equals(Locale.ROOT)) // Base case + return country; + return getDisplayVariant(LocaleHelper.getFallbackLocale(inLocale)); + } + + /** + * Gets all local components suitable for display to the user, formatted + * for the default locale. For the language component, getDisplayLanguage + * is called. For the country component, getDisplayCountry is called. + * For the variant set component, getDisplayVariant is called. + * + *

        The returned String will be one of the following forms:
        + *

        +   * language (country, variant)
        +   * language (country)
        +   * language (variant)
        +   * country (variant)
        +   * language
        +   * country
        +   * variant
        +   * 
        + * + * @return String version of this locale, suitable for display to the user + */ + public String getDisplayName() + { + return getDisplayName(defaultLocale); + } + + /** + * Gets all local components suitable for display to the user, formatted + * for a specified locale. For the language component, + * getDisplayLanguage(Locale) is called. For the country component, + * getDisplayCountry(Locale) is called. For the variant set component, + * getDisplayVariant(Locale) is called. + * + *

        The returned String will be one of the following forms:
        + *

        +   * language (country, variant)
        +   * language (country)
        +   * language (variant)
        +   * country (variant)
        +   * language
        +   * country
        +   * variant
        +   * 
        + * + * @param locale locale to use for formatting + * @return String version of this locale, suitable for display to the user + */ + public String getDisplayName(Locale locale) + { + CPStringBuilder result = new CPStringBuilder(); + int count = 0; + String[] delimiters = {"", " (", ","}; + if (language.length() != 0) + { + result.append(delimiters[count++]); + result.append(getDisplayLanguage(locale)); + } + if (country.length() != 0) + { + result.append(delimiters[count++]); + result.append(getDisplayCountry(locale)); + } + if (variant.length() != 0) + { + result.append(delimiters[count++]); + result.append(getDisplayVariant(locale)); + } + if (count > 1) + result.append(")"); + return result.toString(); + } + + /** + * Does the same as Object.clone() but does not throw + * a CloneNotSupportedException. Why anyone would + * use this method is a secret to me, since this class is immutable. + * + * @return the clone + */ + public Object clone() + { + // This class is final, so no need to use native super.clone(). + return new Locale(language, country, variant); + } + + /** + * Return the hash code for this locale. The hashcode is the logical + * xor of the hash codes of the language, the country and the variant. + * The hash code is precomputed, since Locales are often + * used in hash tables. + * + * @return the hashcode + */ + public int hashCode() + { + return hashcode; + } + + /** + * Compares two locales. To be equal, obj must be a Locale with the same + * language, country, and variant code. + * + * @param obj the other locale + * @return true if obj is equal to this + */ + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (! (obj instanceof Locale)) + return false; + Locale l = (Locale) obj; + + 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 + { + ObjectOutputStream.PutField fields = s.putFields(); + fields.put("hashcode", -1); + s.defaultWriteObject(); + } + + /** + * Reads a locale from the input stream. + * + * @param s the stream to read from + * @throws IOException if reading fails + * @throws ClassNotFoundException if reading fails + * @serialData the hashCode is always invalid and must be recomputed + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + } +} // class Locale diff --git a/libjava/classpath/java/util/Map.java b/libjava/classpath/java/util/Map.java new file mode 100644 index 000000000..1bcb1b2f0 --- /dev/null +++ b/libjava/classpath/java/util/Map.java @@ -0,0 +1,338 @@ +/* Map.java: interface Map -- An object that maps keys to values + interface Map.Entry -- an Entry in a Map + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * An object that maps keys onto values. Keys cannot be duplicated. This + * interface replaces the obsolete {@link Dictionary} abstract class. + *

        + * + * The map has three collection views, which are backed by the map + * (modifications on one show up on the other): a set of keys, a collection + * of values, and a set of key-value mappings. Some maps have a guaranteed + * order, but not all do. + *

        + * + * Note: Be careful about using mutable keys. Behavior is unspecified if + * a key's comparison behavior is changed after the fact. As a corollary + * to this rule, don't use a Map as one of its own keys or values, as it makes + * hashCode and equals have undefined behavior. + *

        + * + * All maps are recommended to provide a no argument constructor, which builds + * an empty map, and one that accepts a Map parameter and copies the mappings + * (usually by putAll), to create an equivalent map. Unfortunately, Java + * cannot enforce these suggestions. + *

        + * + * The map may be unmodifiable, in which case unsupported operations will + * throw an UnsupportedOperationException. Note that some operations may be + * safe, such as putAll(m) where m is empty, even if the operation would + * normally fail with a non-empty argument. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see HashMap + * @see TreeMap + * @see Hashtable + * @see SortedMap + * @see Collection + * @see Set + * @since 1.2 + * @status updated to 1.4 + */ +public interface Map +{ + /** + * Remove all entries from this Map (optional operation). + * + * @throws UnsupportedOperationException if clear is not supported + */ + void clear(); + + /** + * Returns true if this contains a mapping for the given key. + * + * @param key the key to search for + * @return true if the map contains the key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if key is null but the map + * does not permit null keys + */ + boolean containsKey(Object key); + + /** + * Returns true if this contains at least one mapping with the given value. + * In other words, returns true if a value v exists where + * (value == null ? v == null : value.equals(v)). This usually + * requires linear time. + * + * @param value the value to search for + * @return true if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + boolean containsValue(Object value); + + /** + * Returns a set view of the mappings in this Map. Each element in the + * set is a Map.Entry. The set is backed by the map, so that changes in + * one show up in the other. Modifications made while an iterator is + * in progress cause undefined behavior. If the set supports removal, + * these methods remove the underlying mapping from the map: + * Iterator.remove, Set.remove, + * removeAll, retainAll, and clear. + * Element addition, via add or addAll, is + * not supported via this set. + * + * @return the set view of all mapping entries + * @see Map.Entry + */ + Set> entrySet(); + + /** + * Compares the specified object with this map for equality. Returns + * true if the other object is a Map with the same mappings, + * that is,
        + * o instanceof Map && entrySet().equals(((Map) o).entrySet(); + * This allows comparison of maps, regardless of implementation. + * + * @param o the object to be compared + * @return true if the object equals this map + * @see Set#equals(Object) + */ + boolean equals(Object o); + + /** + * Returns the value mapped by the given key. Returns null if + * there is no mapping. However, in Maps that accept null values, you + * must rely on containsKey to determine if a mapping exists. + * + * @param key the key to look up + * @return the value associated with the key, or null if key not in map + * @throws ClassCastException if the key is an inappropriate type + * @throws NullPointerException if this map does not accept null keys + * @see #containsKey(Object) + */ + V get(Object key); + + /** + * Associates the given key to the given value (optional operation). If the + * map already contains the key, its value is replaced. Be aware that in + * a map that permits null values, a null return does not + * always imply that the mapping was created. + * + * @param key the key to map + * @param value the value to be mapped + * @return the previous value of the key, or null if there was no mapping + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the key or value is of the wrong type + * @throws IllegalArgumentException if something about this key or value + * prevents it from existing in this map + * @throws NullPointerException if either the key or the value is null, + * and the map forbids null keys or values + * @see #containsKey(Object) + */ + V put(K key, V value); + + /** + * Returns the hash code for this map. This is the sum of all hashcodes + * for each Map.Entry object in entrySet. This allows comparison of maps, + * regardless of implementation, and satisfies the contract of + * Object.hashCode. + * + * @return the hash code + * @see Map.Entry#hashCode() + */ + int hashCode(); + + /** + * Returns true if the map contains no mappings. + * + * @return true if the map is empty + */ + boolean isEmpty(); + + /** + * Returns a set view of the keys in this Map. The set is backed by the + * map, so that changes in one show up in the other. Modifications made + * while an iterator is in progress cause undefined behavior. If the set + * supports removal, these methods remove the underlying mapping from + * the map: Iterator.remove, Set.remove, + * removeAll, retainAll, and clear. + * Element addition, via add or addAll, is + * not supported via this set. + * + * @return the set view of all keys + */ + Set keySet(); + + /** + * Copies all entries of the given map to this one (optional operation). If + * the map already contains a key, its value is replaced. + * + * @param m the mapping to load into this map + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if a key or value is of the wrong type + * @throws IllegalArgumentException if something about a key or value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null keys or values, or + * if m is null. + * @see #put(Object, Object) + */ + void putAll(Map m); + + /** + * Removes the mapping for this key if present (optional operation). If + * the key is not present, this returns null. Note that maps which permit + * null values may also return null if the key was removed. + * + * @param key the key to remove + * @return the value the key mapped to, or null if not present. + * @throws UnsupportedOperationException if deletion is unsupported + * @throws NullPointerException if the key is null and this map doesn't + * support null keys. + * @throws ClassCastException if the type of the key is not a valid type + * for this map. + */ + V remove(Object o); + + /** + * Returns the number of key-value mappings in the map. If there are more + * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. + * + * @return the number of mappings + */ + int size(); + + /** + * Returns a collection (or bag) view of the values in this Map. The + * collection is backed by the map, so that changes in one show up in + * the other. Modifications made while an iterator is in progress cause + * undefined behavior. If the collection supports removal, these methods + * remove the underlying mapping from the map: Iterator.remove, + * Collection.remove, removeAll, + * retainAll, and clear. Element addition, via + * add or addAll, is not supported via this + * collection. + * + * @return the collection view of all values + */ + Collection values(); + + /** + * A map entry (key-value pair). The Map.entrySet() method returns a set + * view of these objects; there is no other valid way to come across them. + * These objects are only valid for the duration of an iteration; in other + * words, if you mess with one after modifying the map, you are asking + * for undefined behavior. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see Map#entrySet() + * @since 1.2 + * @status updated to 1.4 + */ + interface Entry + { + /** + * Get the key corresponding to this entry. + * + * @return the key + */ + K getKey(); + + /** + * Get the value corresponding to this entry. If you already called + * Iterator.remove(), this is undefined. + * + * @return the value + */ + V getValue(); + + /** + * Replaces the value with the specified object (optional operation). + * This writes through to the map, and is undefined if you already + * called Iterator.remove(). + * + * @param value the new value to store + * @return the old value + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the value is of the wrong type + * @throws IllegalArgumentException if something about the value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null values + */ + V setValue(V value); + + + /** + * Returns the hash code of the entry. This is defined as the + * exclusive-or of the hashcodes of the key and value (using 0 for + * null). In other words, this must be: + * +

        (getKey() == null ? 0 : getKey().hashCode())
        +^ (getValue() == null ? 0 : getValue().hashCode())
        + * + * @return the hash code + */ + int hashCode(); + + /** + * Compares the specified object with this entry. Returns true only if + * the object is a mapping of identical key and value. In other words, + * this must be: + * +

        (o instanceof Map.Entry)
        +&& (getKey() == null ? ((Map.Entry) o).getKey() == null
        +                     : getKey().equals(((Map.Entry) o).getKey()))
        +&& (getValue() == null ? ((Map.Entry) o).getValue() == null
        +                       : getValue().equals(((Map.Entry) o).getValue()))
        + * + * @param o the object to compare + * + * @return true if it is equal + */ + boolean equals(Object o); + } +} diff --git a/libjava/classpath/java/util/MissingFormatArgumentException.java b/libjava/classpath/java/util/MissingFormatArgumentException.java new file mode 100644 index 000000000..e53de0ae3 --- /dev/null +++ b/libjava/classpath/java/util/MissingFormatArgumentException.java @@ -0,0 +1,90 @@ +/* MissingFormatArgumentException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the a format specification for a {@link Formatter} + * refers to an argument that is non-existent, or an argument index + * references a non-existent argument. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MissingFormatArgumentException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19190115L; + + /** + * The format specification which contains the + * unmatched argument. + * + * @serial the format specification. + */ + // Note: name fixed by serialization. + private String s; + + /** + * Constructs a new MissingFormatArgumentException + * for a format specification, s, which refers + * to a non-existent argument. + * + * @param s the format specification. + * @throws NullPointerException if s is null. + */ + public MissingFormatArgumentException(String s) + { + super("The specification, " + s + + ", refers to a non-existent argument."); + if (s == null) + throw new NullPointerException("The specification is null."); + this.s = s; + } + + /** + * Returns the format specification. + * + * @return the format specification. + */ + public String getFormatSpecifier() + { + return s; + } +} diff --git a/libjava/classpath/java/util/MissingFormatWidthException.java b/libjava/classpath/java/util/MissingFormatWidthException.java new file mode 100644 index 000000000..e083172a9 --- /dev/null +++ b/libjava/classpath/java/util/MissingFormatWidthException.java @@ -0,0 +1,88 @@ +/* MissingFormatWidthException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when the a format specification for a {@link Formatter} + * does not include a width for a value where one is required. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class MissingFormatWidthException + extends IllegalFormatException +{ + private static final long serialVersionUID = 15560123L; + + /** + * The format specification which contains the + * unmatched argument. + * + * @serial the format specification. + */ + // Note: name fixed by serialization. + private String s; + + /** + * Constructs a new MissingFormatWidthException + * for a format specification, s, which excludes + * a required width argument. + * + * @param s the format specification. + * @throws NullPointerException if s is null. + */ + public MissingFormatWidthException(String s) + { + super("The specification, " + s + ", misses a required width."); + if (s == null) + throw new NullPointerException("The specification is null."); + this.s = s; + } + + /** + * Returns the format specification. + * + * @return the format specification. + */ + public String getFormatSpecifier() + { + return s; + } +} diff --git a/libjava/classpath/java/util/MissingResourceException.java b/libjava/classpath/java/util/MissingResourceException.java new file mode 100644 index 000000000..26640de90 --- /dev/null +++ b/libjava/classpath/java/util/MissingResourceException.java @@ -0,0 +1,105 @@ +/* MissingResourceException.java -- thrown for a missing resource + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This exception is thrown when a resource is missing. + * + * @author Jochen Hoenicke + * @author Warren Levy (warrenl@cygnus.com) + * @see ResourceBundle + * @since 1.1 + * @status updated to 1.4 + */ +public class MissingResourceException extends RuntimeException +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = -4876345176062000401L; + + /** + * The name of the resource bundle requested by user. + * + * @serial the class name of the resource bundle + */ + private final String className; + + /** + * The key of the resource in the bundle requested by user. + * + * @serial the name of the resouce + */ + private final String key; + + /** + * Creates a new exception, with the specified parameters. + * + * @param s the detail message + * @param className the name of the resource bundle + * @param key the key of the missing resource + */ + public MissingResourceException(String s, String className, String key) + { + super(s); + this.className = className; + this.key = key; + } + + /** + * Gets the name of the resource bundle, for which a resource is missing. + * + * @return the name of the resource bundle + */ + public String getClassName() + { + return className; + } + + /** + * Gets the key of the resource that is missing bundle, this is an empty + * string if the whole resource bundle is missing. + * + * @return the name of the resource bundle + */ + public String getKey() + { + return key; + } +} diff --git a/libjava/classpath/java/util/NoSuchElementException.java b/libjava/classpath/java/util/NoSuchElementException.java new file mode 100644 index 000000000..5e1a2176d --- /dev/null +++ b/libjava/classpath/java/util/NoSuchElementException.java @@ -0,0 +1,88 @@ +/* NoSuchElementException.java -- Attempt to access element that does not exist + Copyright (C) 1998, 1999, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + */ + +/** + * Exception thrown when an attempt is made to access an element that does not + * exist. This exception is thrown by the Enumeration, Iterator and + * ListIterator classes if the nextElement, next or previous method goes + * beyond the end of the list of elements that are being accessed. It is also + * thrown by Vector and Stack when attempting to access the first or last + * element of an empty collection. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Enumeration + * @see Iterator + * @see ListIterator + * @see Enumeration#nextElement() + * @see Iterator#next() + * @see ListIterator#previous() + * @since 1.0 + * @status updated to 1.4 + */ +public class NoSuchElementException extends RuntimeException +{ + /** + * Compatible with JDK 1.0. + */ + private static final long serialVersionUID = 6769829250639411880L; + + /** + * Constructs a NoSuchElementException with no detail message. + */ + public NoSuchElementException() + { + } + + /** + * Constructs a NoSuchElementException with a detail message. + * + * @param detail the detail message for the exception + */ + public NoSuchElementException(String detail) + { + super(detail); + } +} diff --git a/libjava/classpath/java/util/Observable.java b/libjava/classpath/java/util/Observable.java new file mode 100644 index 000000000..916abe4ac --- /dev/null +++ b/libjava/classpath/java/util/Observable.java @@ -0,0 +1,182 @@ +/* Observable.java -- an object to be observed + Copyright (C) 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This class represents an object which is observable. Other objects may + * register their intent to be notified when this object changes; and when + * this object does change, it will trigger the update method + * of each observer. + * + * Note that the notifyObservers() method of this class is + * unrelated to the notify() of Object. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see Observer + * @status updated to 1.4 + */ +public class Observable +{ + /** Tracks whether this object has changed. */ + private boolean changed; + + /* List of the Observers registered as interested in this Observable. */ + private LinkedHashSet observers; + + /** + * Constructs an Observable with zero Observers. + */ + public Observable() + { + observers = new LinkedHashSet(); + } + + /** + * Adds an Observer. If the observer was already added this method does + * nothing. + * + * @param observer Observer to add + * @throws NullPointerException if observer is null + */ + public synchronized void addObserver(Observer observer) + { + if (observer == null) + throw new NullPointerException("can't add null observer"); + observers.add(observer); + } + + /** + * Reset this Observable's state to unchanged. This is called automatically + * by notifyObservers once all observers have been notified. + * + * @see #notifyObservers() + */ + protected synchronized void clearChanged() + { + changed = false; + } + + /** + * Returns the number of observers for this object. + * + * @return number of Observers for this + */ + public synchronized int countObservers() + { + return observers.size(); + } + + /** + * Deletes an Observer of this Observable. + * + * @param victim Observer to delete + */ + public synchronized void deleteObserver(Observer victim) + { + observers.remove(victim); + } + + /** + * Deletes all Observers of this Observable. + */ + public synchronized void deleteObservers() + { + observers.clear(); + } + + /** + * True if setChanged has been called more recently than + * clearChanged. + * + * @return whether or not this Observable has changed + */ + public synchronized boolean hasChanged() + { + return changed; + } + + /** + * If the Observable has actually changed then tell all Observers about it, + * then reset state to unchanged. + * + * @see #notifyObservers(Object) + * @see Observer#update(Observable, Object) + */ + public void notifyObservers() + { + notifyObservers(null); + } + + /** + * If the Observable has actually changed then tell all Observers about it, + * then reset state to unchanged. Note that though the order of + * notification is unspecified in subclasses, in Observable it is in the + * order of registration. + * + * @param obj argument to Observer's update method + * @see Observer#update(Observable, Object) + */ + public void notifyObservers(Object obj) + { + if (! hasChanged()) + return; + // Create clone inside monitor, as that is relatively fast and still + // important to keep threadsafe, but update observers outside of the + // lock since update() can call arbitrary code. + Set s; + synchronized (this) + { + s = (Set) observers.clone(); + } + int i = s.size(); + Iterator iter = s.iterator(); + while (--i >= 0) + ((Observer) iter.next()).update(this, obj); + clearChanged(); + } + + /** + * Marks this Observable as having changed. + */ + protected synchronized void setChanged() + { + changed = true; + } +} diff --git a/libjava/classpath/java/util/Observer.java b/libjava/classpath/java/util/Observer.java new file mode 100644 index 000000000..c59a0ca6c --- /dev/null +++ b/libjava/classpath/java/util/Observer.java @@ -0,0 +1,60 @@ +/* Observer.java -- an object that will be informed of changes in an Observable + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Interface that is implemented when a class wants to be informed of changes + * in Observable objects. + * + * @author Warren Levy (warrenl@cygnus.com) + * @see Observable + * @status updated to 1.4 + */ +public interface Observer +{ + /** + * This method is called whenever the observable object changes, and has + * called notifyObservers. The Observable object can pass + * arbitrary information in the second parameter. + * + * @param observable the Observable object that changed + * @param arg arbitrary information, usually relating to the change + */ + void update(Observable observable, Object arg); +} diff --git a/libjava/classpath/java/util/PriorityQueue.java b/libjava/classpath/java/util/PriorityQueue.java new file mode 100644 index 000000000..5b6a36804 --- /dev/null +++ b/libjava/classpath/java/util/PriorityQueue.java @@ -0,0 +1,336 @@ +/* PriorityQueue.java -- Unbounded priority queue + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class PriorityQueue extends AbstractQueue implements Serializable +{ + private static final int DEFAULT_CAPACITY = 11; + + private static final long serialVersionUID = -7720805057305804111L; + + /** Number of elements actually used in the storage array. */ + int used; + + /** + * This is the storage for the underlying binomial heap. + * The idea is, each node is less than or equal to its children. + * A node at index N (0-based) has two direct children, at + * nodes 2N+1 and 2N+2. + */ + E[] storage; + + /** + * The comparator we're using, or null for natural ordering. + */ + Comparator comparator; + + public PriorityQueue() + { + this(DEFAULT_CAPACITY, null); + } + + public PriorityQueue(Collection c) + { + this(Math.max(1, (int) (1.1 * c.size())), null); + + // Special case where we can find the comparator to use. + if (c instanceof SortedSet) + { + SortedSet ss = (SortedSet) c; + this.comparator = (Comparator) ss.comparator(); + // We can insert the elements directly, since they are sorted. + int i = 0; + for (E val : ss) + { + if (val == null) + throw new NullPointerException(); + storage[i++] = val; + } + } + else if (c instanceof PriorityQueue) + { + PriorityQueue pq = (PriorityQueue) c; + this.comparator = (Comparator)pq.comparator(); + // We can just copy the contents. + System.arraycopy(pq.storage, 0, storage, 0, pq.storage.length); + } + + addAll(c); + } + + public PriorityQueue(int cap) + { + this(cap, null); + } + + public PriorityQueue(int cap, Comparator comp) + { + if (cap < 1) + throw new IllegalArgumentException(); + this.used = 0; + this.storage = (E[]) new Object[cap]; + this.comparator = comp; + } + + public PriorityQueue(PriorityQueue c) + { + this(Math.max(1, (int) (1.1 * c.size())), + (Comparator)c.comparator()); + // We can just copy the contents. + System.arraycopy(c.storage, 0, storage, 0, c.storage.length); + } + + public PriorityQueue(SortedSet c) + { + this(Math.max(1, (int) (1.1 * c.size())), + (Comparator)c.comparator()); + // We can insert the elements directly, since they are sorted. + int i = 0; + for (E val : c) + { + if (val == null) + throw new NullPointerException(); + storage[i++] = val; + } + } + + public void clear() + { + Arrays.fill(storage, null); + used = 0; + } + + public Comparator comparator() + { + return comparator; + } + + public Iterator iterator() + { + return new Iterator() + { + int index = -1; + int count = 0; + + public boolean hasNext() + { + return count < used; + } + + public E next() + { + while (storage[++index] == null) + ; + + ++count; + return storage[index]; + } + + public void remove() + { + PriorityQueue.this.remove(index); + index--; + } + }; + } + + public boolean offer(E o) + { + if (o == null) + throw new NullPointerException(); + + int slot = findSlot(-1); + + storage[slot] = o; + ++used; + bubbleUp(slot); + + return true; + } + + public E peek() + { + return used == 0 ? null : storage[0]; + } + + public E poll() + { + if (used == 0) + return null; + E result = storage[0]; + remove(0); + return result; + } + + public boolean remove(Object o) + { + if (o != null) + { + for (int i = 0; i < storage.length; ++i) + { + if (o.equals(storage[i])) + { + remove(i); + return true; + } + } + } + return false; + } + + public int size() + { + return used; + } + + // It is more efficient to implement this locally -- less searching + // for free slots. + public boolean addAll(Collection c) + { + if (c == this) + throw new IllegalArgumentException(); + + int newSlot = -1; + int save = used; + for (E val : c) + { + if (val == null) + throw new NullPointerException(); + newSlot = findSlot(newSlot); + storage[newSlot] = val; + ++used; + bubbleUp(newSlot); + } + + return save != used; + } + + int findSlot(int start) + { + int slot; + if (used == storage.length) + { + resize(); + slot = used; + } + else + { + for (slot = start + 1; slot < storage.length; ++slot) + { + if (storage[slot] == null) + break; + } + // We'll always find a slot. + } + return slot; + } + + void remove(int index) + { + // Remove the element at INDEX. We do this by finding the least + // child and moving it into place, then iterating until we reach + // the bottom of the tree. + while (storage[index] != null) + { + int child = 2 * index + 1; + + // See if we went off the end. + if (child >= storage.length) + { + storage[index] = null; + break; + } + + // Find which child we want to promote. If one is not null, + // we pick it. If both are null, it doesn't matter, we're + // about to leave. If neither is null, pick the lesser. + if (child + 1 >= storage.length || storage[child + 1] == null) + { + // Nothing. + } + else if (storage[child] == null + || (Collections.compare(storage[child], storage[child + 1], + comparator) > 0)) + ++child; + storage[index] = storage[child]; + index = child; + } + --used; + } + + void bubbleUp(int index) + { + // The element at INDEX was inserted into a blank spot. Now move + // it up the tree to its natural resting place. + while (index > 0) + { + // This works regardless of whether we're at 2N+1 or 2N+2. + int parent = (index - 1) / 2; + if (Collections.compare(storage[parent], storage[index], comparator) + <= 0) + { + // Parent is the same or smaller than this element, so the + // invariant is preserved. Note that if the new element + // is smaller than the parent, then it is necessarily + // smaller than the parent's other child. + break; + } + + E temp = storage[index]; + storage[index] = storage[parent]; + storage[parent] = temp; + + index = parent; + } + } + + void resize() + { + E[] new_data = (E[]) new Object[2 * storage.length]; + System.arraycopy(storage, 0, new_data, 0, storage.length); + storage = new_data; + } +} diff --git a/libjava/classpath/java/util/Properties.java b/libjava/classpath/java/util/Properties.java new file mode 100644 index 000000000..b0f436f42 --- /dev/null +++ b/libjava/classpath/java/util/Properties.java @@ -0,0 +1,822 @@ +/* Properties.java -- a set of persistent properties + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Reader; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Element; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; + +/** + * A set of persistent properties, which can be saved or loaded from a stream. + * A property list may also contain defaults, searched if the main list + * does not contain a property for a given key. + * + * An example of a properties file for the german language is given + * here. This extends the example given in ListResourceBundle. + * Create a file MyResource_de.properties with the following contents + * and put it in the CLASSPATH. (The character + * \u00e4 is the german umlaut) + * + * +
        s1=3
        +s2=MeineDisk
        +s3=3. M\u00e4rz 96
        +s4=Die Diskette ''{1}'' enth\u00e4lt {0} in {2}.
        +s5=0
        +s6=keine Dateien
        +s7=1
        +s8=eine Datei
        +s9=2
        +s10={0,number} Dateien
        +s11=Das Formatieren schlug fehl mit folgender Exception: {0}
        +s12=FEHLER
        +s13=Ergebnis
        +s14=Dialog
        +s15=Auswahlkriterium
        +s16=1,3
        + * + *

        Although this is a sub class of a hash table, you should never + * insert anything other than strings to this property, or several + * methods, that need string keys and values, will fail. To ensure + * this, you should use the get/setProperty method instead + * of get/put. + * + * Properties are saved in ISO 8859-1 encoding, using Unicode escapes with + * a single u for any character which cannot be represented. + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @see PropertyResourceBundle + * @status updated to 1.4 + */ +public class Properties extends Hashtable +{ + // WARNING: Properties is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * The property list that contains default values for any keys not + * in this property list. + * + * @serial the default properties + */ + protected Properties defaults; + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 4112578634029874840L; + + /** + * Creates a new empty property list with no default values. + */ + public Properties() + { + } + + /** + * Create a new empty property list with the specified default values. + * + * @param defaults a Properties object containing the default values + */ + public Properties(Properties defaults) + { + this.defaults = defaults; + } + + /** + * Adds the given key/value pair to this properties. This calls + * the hashtable method put. + * + * @param key the key for this property + * @param value the value for this property + * @return The old value for the given key + * @see #getProperty(String) + * @since 1.2 + */ + public Object setProperty(String key, String value) + { + return put(key, value); + } + + /** + * Reads a property list from a character stream. The stream should + * have the following format:
        + * + * An empty line or a line starting with # or + * ! is ignored. An backslash (\) at the + * end of the line makes the line continueing on the next line + * (but make sure there is no whitespace after the backslash). + * Otherwise, each line describes a key/value pair.
        + * + * The chars up to the first whitespace, = or : are the key. You + * can include this caracters in the key, if you precede them with + * a backslash (\). The key is followed by optional + * whitespaces, optionally one = or :, + * and optionally some more whitespaces. The rest of the line is + * the resource belonging to the key.
        + * + * Escape sequences \t, \n, \r, \\, \", \', \!, \#, \ (a + * space), and unicode characters with the + * \\uxxxx notation are detected, and + * converted to the corresponding single character.
        + * + * +

        # This is a comment
        +key     = value
        +k\:5      \ a string starting with space and ending with newline\n
        +# This is a multiline specification; note that the value contains
        +# no white space.
        +weekdays: Sunday,Monday,Tuesday,Wednesday,\\
        +          Thursday,Friday,Saturday
        +# The safest way to include a space at the end of a value:
        +label   = Name:\\u0020
        + * + * @param inReader the input {@link java.io.Reader}. + * @throws IOException if an error occurred when reading the input + * @throws NullPointerException if in is null + * @since 1.6 + */ + public void load(Reader inReader) throws IOException + { + BufferedReader reader = new BufferedReader(inReader); + String line; + + while ((line = reader.readLine()) != null) + { + char c = 0; + int pos = 0; + // Leading whitespaces must be deleted first. + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + + // If empty line or begins with a comment character, skip this line. + if ((line.length() - pos) == 0 + || line.charAt(pos) == '#' || line.charAt(pos) == '!') + continue; + + // The characters up to the next Whitespace, ':', or '=' + // describe the key. But look for escape sequences. + // Try to short-circuit when there is no escape char. + int start = pos; + boolean needsEscape = line.indexOf('\\', pos) != -1; + CPStringBuilder key = needsEscape ? new CPStringBuilder() : null; + while (pos < line.length() + && ! Character.isWhitespace(c = line.charAt(pos++)) + && c != '=' && c != ':') + { + if (needsEscape && c == '\\') + { + if (pos == line.length()) + { + // The line continues on the next line. If there + // is no next line, just treat it as a key with an + // empty value. + line = reader.readLine(); + if (line == null) + line = ""; + pos = 0; + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + } + else + { + c = line.charAt(pos++); + switch (c) + { + case 'n': + key.append('\n'); + break; + case 't': + key.append('\t'); + break; + case 'r': + key.append('\r'); + break; + case 'u': + if (pos + 4 <= line.length()) + { + char uni = (char) Integer.parseInt + (line.substring(pos, pos + 4), 16); + key.append(uni); + pos += 4; + } // else throw exception? + break; + default: + key.append(c); + break; + } + } + } + else if (needsEscape) + key.append(c); + } + + boolean isDelim = (c == ':' || c == '='); + + String keyString; + if (needsEscape) + keyString = key.toString(); + else if (isDelim || Character.isWhitespace(c)) + keyString = line.substring(start, pos - 1); + else + keyString = line.substring(start, pos); + + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + + if (! isDelim && (c == ':' || c == '=')) + { + pos++; + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + } + + // Short-circuit if no escape chars found. + if (!needsEscape) + { + put(keyString, line.substring(pos)); + continue; + } + + // Escape char found so iterate through the rest of the line. + StringBuilder element = new StringBuilder(line.length() - pos); + while (pos < line.length()) + { + c = line.charAt(pos++); + if (c == '\\') + { + if (pos == line.length()) + { + // The line continues on the next line. + line = reader.readLine(); + + // We might have seen a backslash at the end of + // the file. The JDK ignores the backslash in + // this case, so we follow for compatibility. + if (line == null) + break; + + pos = 0; + while (pos < line.length() + && Character.isWhitespace(c = line.charAt(pos))) + pos++; + element.ensureCapacity(line.length() - pos + + element.length()); + } + else + { + c = line.charAt(pos++); + switch (c) + { + case 'n': + element.append('\n'); + break; + case 't': + element.append('\t'); + break; + case 'r': + element.append('\r'); + break; + case 'u': + if (pos + 4 <= line.length()) + { + char uni = (char) Integer.parseInt + (line.substring(pos, pos + 4), 16); + element.append(uni); + pos += 4; + } // else throw exception? + break; + default: + element.append(c); + break; + } + } + } + else + element.append(c); + } + put(keyString, element.toString()); + } + } + + /** + * Reads a property list from the supplied input stream. + * This method has the same functionality as {@link #load(Reader)} + * but the character encoding is assumed to be ISO-8859-1. + * Unicode characters not within the Latin1 set supplied by + * ISO-8859-1 should be escaped using '\\uXXXX' where XXXX + * is the UTF-16 code unit in hexadecimal. + * + * @param inStream the byte stream to read the property list from. + * @throws IOException if an I/O error occurs. + * @see #load(Reader) + * @since 1.2 + */ + public void load(InputStream inStream) throws IOException + { + load(new InputStreamReader(inStream, "ISO-8859-1")); + } + + /** + * Calls store(OutputStream out, String header) and + * ignores the IOException that may be thrown. + * + * @param out the stream to write to + * @param header a description of the property list + * @throws ClassCastException if this property contains any key or + * value that are not strings + * @deprecated use {@link #store(OutputStream, String)} instead + */ + @Deprecated + public void save(OutputStream out, String header) + { + try + { + store(out, header); + } + catch (IOException ex) + { + } + } + + /** + * Writes the key/value pairs to the given output stream, in a format + * suitable for load.
        + * + * If header is not null, this method writes a comment containing + * the header as first line to the stream. The next line (or first + * line if header is null) contains a comment with the current date. + * Afterwards the key/value pairs are written to the stream in the + * following format.
        + * + * Each line has the form key = value. Newlines, + * Returns and tabs are written as \n,\t,\r resp. + * The characters \, !, #, = and : are + * preceeded by a backslash. Spaces are preceded with a backslash, + * if and only if they are at the beginning of the key. Characters + * that are not in the ascii range 33 to 127 are written in the + * \uxxxx Form.
        + * + * Following the listing, the output stream is flushed but left open. + * + * @param out the output stream + * @param header the header written in the first line, may be null + * @throws ClassCastException if this property contains any key or + * value that isn't a string + * @throws IOException if writing to the stream fails + * @throws NullPointerException if out is null + * @since 1.2 + */ + public void store(OutputStream out, String header) throws IOException + { + // The spec says that the file must be encoded using ISO-8859-1. + PrintWriter writer + = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1")); + if (header != null) + writer.println("#" + header); + writer.println ("#" + Calendar.getInstance ().getTime ()); + + Iterator iter = entrySet ().iterator (); + int i = size (); + CPStringBuilder s = new CPStringBuilder (); // Reuse the same buffer. + while (--i >= 0) + { + Map.Entry entry = (Map.Entry) iter.next (); + formatForOutput ((String) entry.getKey (), s, true); + s.append ('='); + formatForOutput ((String) entry.getValue (), s, false); + writer.println (s); + } + + writer.flush (); + } + + /** + * Gets the property with the specified key in this property list. + * If the key is not found, the default property list is searched. + * If the property is not found in the default, null is returned. + * + * @param key The key for this property + * @return the value for the given key, or null if not found + * @throws ClassCastException if this property contains any key or + * value that isn't a string + * @see #defaults + * @see #setProperty(String, String) + * @see #getProperty(String, String) + */ + public String getProperty(String key) + { + Properties prop = this; + // Eliminate tail recursion. + do + { + String value = (String) prop.get(key); + if (value != null) + return value; + prop = prop.defaults; + } + while (prop != null); + return null; + } + + /** + * Gets the property with the specified key in this property list. If + * the key is not found, the default property list is searched. If the + * property is not found in the default, the specified defaultValue is + * returned. + * + * @param key The key for this property + * @param defaultValue A default value + * @return The value for the given key + * @throws ClassCastException if this property contains any key or + * value that isn't a string + * @see #defaults + * @see #setProperty(String, String) + */ + public String getProperty(String key, String defaultValue) + { + String prop = getProperty(key); + if (prop == null) + prop = defaultValue; + return prop; + } + + /** + * Returns an enumeration of all keys in this property list, including + * the keys in the default property list. + * + * @return an Enumeration of all defined keys + */ + public Enumeration propertyNames() + { + // We make a new Set that holds all the keys, then return an enumeration + // for that. This prevents modifications from ruining the enumeration, + // as well as ignoring duplicates. + Properties prop = this; + Set s = new HashSet(); + // Eliminate tail recursion. + do + { + s.addAll(prop.keySet()); + prop = prop.defaults; + } + while (prop != null); + return Collections.enumeration(s); + } + + /** + * Prints the key/value pairs to the given print stream. This is + * mainly useful for debugging purposes. + * + * @param out the print stream, where the key/value pairs are written to + * @throws ClassCastException if this property contains a key or a + * value that isn't a string + * @see #list(PrintWriter) + */ + public void list(PrintStream out) + { + PrintWriter writer = new PrintWriter (out); + list (writer); + } + + /** + * Prints the key/value pairs to the given print writer. This is + * mainly useful for debugging purposes. + * + * @param out the print writer where the key/value pairs are written to + * @throws ClassCastException if this property contains a key or a + * value that isn't a string + * @see #list(PrintStream) + * @since 1.1 + */ + public void list(PrintWriter out) + { + out.println ("-- listing properties --"); + + Iterator iter = entrySet ().iterator (); + int i = size (); + while (--i >= 0) + { + Map.Entry entry = (Map.Entry) iter.next (); + out.print ((String) entry.getKey () + "="); + + // JDK 1.3/1.4 restrict the printed value, but not the key, + // to 40 characters, including the truncating ellipsis. + String s = (String ) entry.getValue (); + if (s != null && s.length () > 40) + out.println (s.substring (0, 37) + "..."); + else + out.println (s); + } + out.flush (); + } + + /** + * Formats a key or value for output in a properties file. + * See store for a description of the format. + * + * @param str the string to format + * @param buffer the buffer to add it to + * @param key true if all ' ' must be escaped for the key, false if only + * leading spaces must be escaped for the value + * @see #store(OutputStream, String) + */ + private void formatForOutput(String str, CPStringBuilder buffer, boolean key) + { + if (key) + { + buffer.setLength(0); + buffer.ensureCapacity(str.length()); + } + else + buffer.ensureCapacity(buffer.length() + str.length()); + boolean head = true; + int size = str.length(); + for (int i = 0; i < size; i++) + { + char c = str.charAt(i); + switch (c) + { + case '\n': + buffer.append("\\n"); + break; + case '\r': + buffer.append("\\r"); + break; + case '\t': + buffer.append("\\t"); + break; + case ' ': + buffer.append(head ? "\\ " : " "); + break; + case '\\': + case '!': + case '#': + case '=': + case ':': + buffer.append('\\').append(c); + break; + default: + if (c < ' ' || c > '~') + { + String hex = Integer.toHexString(c); + buffer.append("\\u0000".substring(0, 6 - hex.length())); + buffer.append(hex); + } + else + buffer.append(c); + } + if (c != ' ') + head = key; + } + } + + /** + *

        + * Encodes the properties as an XML file using the UTF-8 encoding. + * The format of the XML file matches the DTD + * + * http://java.sun.com/dtd/properties.dtd. + *

        + *

        + * Invoking this method provides the same behaviour as invoking + * storeToXML(os, comment, "UTF-8"). + *

        + * + * @param os the stream to output to. + * @param comment a comment to include at the top of the XML file, or + * null if one is not required. + * @throws IOException if the serialization fails. + * @throws NullPointerException if os is null. + * @since 1.5 + */ + public void storeToXML(OutputStream os, String comment) + throws IOException + { + storeToXML(os, comment, "UTF-8"); + } + + /** + *

        + * Encodes the properties as an XML file using the supplied encoding. + * The format of the XML file matches the DTD + * + * http://java.sun.com/dtd/properties.dtd. + *

        + * + * @param os the stream to output to. + * @param comment a comment to include at the top of the XML file, or + * null if one is not required. + * @param encoding the encoding to use for the XML output. + * @throws IOException if the serialization fails. + * @throws NullPointerException if os or encoding + * is null. + * @since 1.5 + */ + public void storeToXML(OutputStream os, String comment, String encoding) + throws IOException + { + if (os == null) + throw new NullPointerException("Null output stream supplied."); + if (encoding == null) + throw new NullPointerException("Null encoding supplied."); + try + { + DOMImplementationRegistry registry = + DOMImplementationRegistry.newInstance(); + DOMImplementation domImpl = registry.getDOMImplementation("LS 3.0"); + DocumentType doctype = + domImpl.createDocumentType("properties", null, + "http://java.sun.com/dtd/properties.dtd"); + Document doc = domImpl.createDocument(null, "properties", doctype); + Element root = doc.getDocumentElement(); + if (comment != null) + { + Element commentElement = doc.createElement("comment"); + commentElement.appendChild(doc.createTextNode(comment)); + root.appendChild(commentElement); + } + Iterator iterator = entrySet().iterator(); + while (iterator.hasNext()) + { + Map.Entry entry = (Map.Entry) iterator.next(); + Element entryElement = doc.createElement("entry"); + entryElement.setAttribute("key", (String) entry.getKey()); + entryElement.appendChild(doc.createTextNode((String) + entry.getValue())); + root.appendChild(entryElement); + } + DOMImplementationLS loadAndSave = (DOMImplementationLS) domImpl; + LSSerializer serializer = loadAndSave.createLSSerializer(); + LSOutput output = loadAndSave.createLSOutput(); + output.setByteStream(os); + output.setEncoding(encoding); + serializer.write(doc, output); + } + catch (ClassNotFoundException e) + { + throw (IOException) + new IOException("The XML classes could not be found.").initCause(e); + } + catch (InstantiationException e) + { + throw (IOException) + new IOException("The XML classes could not be instantiated.") + .initCause(e); + } + catch (IllegalAccessException e) + { + throw (IOException) + new IOException("The XML classes could not be accessed.") + .initCause(e); + } + } + + /** + *

        + * Decodes the contents of the supplied InputStream as + * an XML file, which represents a set of properties. The format of + * the XML file must match the DTD + * + * http://java.sun.com/dtd/properties.dtd. + *

        + * + * @param in the input stream from which to receive the XML data. + * @throws IOException if an I/O error occurs in reading the input data. + * @throws InvalidPropertiesFormatException if the input data does not + * constitute an XML properties + * file. + * @throws NullPointerException if in is null. + * @since 1.5 + */ + public void loadFromXML(InputStream in) + throws IOException, InvalidPropertiesFormatException + { + if (in == null) + throw new NullPointerException("Null input stream supplied."); + try + { + XMLInputFactory factory = XMLInputFactory.newInstance(); + // Don't resolve external entity references + factory.setProperty("javax.xml.stream.isSupportingExternalEntities", + Boolean.FALSE); + XMLStreamReader reader = factory.createXMLStreamReader(in); + String name, key = null; + CPStringBuilder buf = null; + while (reader.hasNext()) + { + switch (reader.next()) + { + case XMLStreamConstants.START_ELEMENT: + name = reader.getLocalName(); + if (buf == null && "entry".equals(name)) + { + key = reader.getAttributeValue(null, "key"); + if (key == null) + { + String msg = "missing 'key' attribute"; + throw new InvalidPropertiesFormatException(msg); + } + buf = new CPStringBuilder(); + } + else if (!"properties".equals(name) && !"comment".equals(name)) + { + String msg = "unexpected element name '" + name + "'"; + throw new InvalidPropertiesFormatException(msg); + } + break; + case XMLStreamConstants.END_ELEMENT: + name = reader.getLocalName(); + if (buf != null && "entry".equals(name)) + { + put(key, buf.toString()); + buf = null; + } + else if (!"properties".equals(name) && !"comment".equals(name)) + { + String msg = "unexpected element name '" + name + "'"; + throw new InvalidPropertiesFormatException(msg); + } + break; + case XMLStreamConstants.CHARACTERS: + case XMLStreamConstants.SPACE: + case XMLStreamConstants.CDATA: + if (buf != null) + buf.append(reader.getText()); + break; + } + } + reader.close(); + } + catch (XMLStreamException e) + { + throw (InvalidPropertiesFormatException) + new InvalidPropertiesFormatException("Error in parsing XML."). + initCause(e); + } + } + +} // class Properties diff --git a/libjava/classpath/java/util/PropertyPermission.java b/libjava/classpath/java/util/PropertyPermission.java new file mode 100644 index 000000000..6ed4690d8 --- /dev/null +++ b/libjava/classpath/java/util/PropertyPermission.java @@ -0,0 +1,271 @@ +/* PropertyPermission.java -- permission to get and set System properties + Copyright (C) 1999, 2000, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.security.BasicPermission; +import java.security.Permission; +import java.security.PermissionCollection; + +/** + * This class represents the permission to access and modify a property.
        + * + * The name is the name of the property, e.g. xxx. You can also + * use an asterisk "*" as described in BasicPermission.
        + * + * The action string is a comma-separated list of keywords. There are + * two possible actions: + *
        + *
        read
        + *
        Allows to read the property via System.getProperty.
        + *
        write
        + *
        Allows to write the property via System.setProperty.
        + *
        + * + * The action string is case insensitive (it is converted to lower case). + * + * @see Permission + * @see BasicPermission + * @see SecurityManager + * @author Jochen Hoenicke + * @since 1.2 + * @status updated to 1.4 + */ +public final class PropertyPermission extends BasicPermission +{ + /** + * PropertyPermission uses a more efficient representation than the + * serialized form; this documents the difference. + * + * @serialField action String the action string + */ + private static final ObjectStreamField[] serialPersistentFields = + { + new ObjectStreamField("action", String.class) + }; + + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 885438825399942851L; + + /** Permission to read. */ + private static final int READ = 1; + /** Permission to write. */ + private static final int WRITE = 2; + + /** The set of actions permitted. */ + // Package visible for use by PropertyPermissionCollection. + transient int actions; + + /** + * The String forms of the actions permitted. + */ + private static final String actionStrings[] = + { + "", "read", "write", "read,write" + }; + + /** + * Constructs a PropertyPermission with the specified property. Possible + * actions are read and write, comma-separated and case-insensitive. + * + * @param name the name of the property + * @param actions the action string + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if name string contains an + * illegal wildcard or actions string contains an illegal action + * (this includes a null actions string) + */ + public PropertyPermission(String name, String actions) + { + super(name); + if (actions == null) + throw new IllegalArgumentException(); + setActions(actions); + } + + /** + * Parse the action string and convert actions from external to internal + * form. This will set the internal actions field. + * + * @param str the action string + * @throws IllegalArgumentException if actions string contains an + * illegal action + */ + private void setActions(String str) + { + // Initialising the class java.util.Locale ... + // tries to initialise the Locale.defaultLocale static + // which calls System.getProperty, + // which calls SecurityManager.checkPropertiesAccess, + // which creates a PropertyPermission with action "read,write", + // which calls setActions("read,write"). + // If we now were to call toLowerCase on 'str', + // this would call Locale.getDefault() which returns null + // because Locale.defaultLocale hasn't been set yet + // then toLowerCase will fail with a null pointer exception. + // + // The solution is to take a punt on 'str' being lower case, and + // test accordingly. If that fails, we convert 'str' to lower case + // and try the tests again. + if ("read".equals(str)) + actions = READ; + else if ("write".equals(str)) + actions = WRITE; + else if ("read,write".equals(str) || "write,read".equals(str)) + actions = READ | WRITE; + else + { + String lstr = str.toLowerCase(); + if ("read".equals(lstr)) + actions = READ; + else if ("write".equals(lstr)) + actions = WRITE; + else if ("read,write".equals(lstr) || "write,read".equals(lstr)) + actions = READ | WRITE; + else + throw new IllegalArgumentException("illegal action " + str); + } + } + + /** + * Reads an object from the stream. This converts the external to the + * internal representation. + * + * @param s the stream to read from + * @throws IOException if the stream fails + * @throws ClassNotFoundException if reserialization fails + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + ObjectInputStream.GetField fields = s.readFields(); + setActions((String) fields.get("actions", null)); + } + + /** + * Writes an object to the stream. This converts the internal to the + * external representation. + * + * @param s the stram to write to + * @throws IOException if the stream fails + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + ObjectOutputStream.PutField fields = s.putFields(); + fields.put("actions", getActions()); + s.writeFields(); + } + + /** + * Check if this permission implies p. This returns true iff all of + * the following conditions are true: + *
          + *
        • p is a PropertyPermission
        • + *
        • this.getName() implies p.getName(), + * e.g. java.* implies java.home
        • + *
        • this.getActions is a subset of p.getActions
        • + *
        + * + * @param p the permission to check + * @return true if this permission implies p + */ + public boolean implies(Permission p) + { + // BasicPermission checks for name and type. + if (super.implies(p)) + { + // We have to check the actions. + PropertyPermission pp = (PropertyPermission) p; + return (pp.actions & ~actions) == 0; + } + return false; + } + + /** + * Check to see whether this object is the same as another + * PropertyPermission object; this is true if it has the same name and + * actions. + * + * @param obj the other object + * @return true if the two are equivalent + */ + public boolean equals(Object obj) + { + return super.equals(obj) && actions == ((PropertyPermission) obj).actions; + } + + /** + * Returns the hash code for this permission. It is equivalent to + * getName().hashCode(). + * + * @return the hash code + */ + public int hashCode() + { + return super.hashCode(); + } + + /** + * Returns the action string. Note that this may differ from the string + * given at the constructor: The actions are converted to lowercase and + * may be reordered. + * + * @return one of "read", "write", or "read,write" + */ + public String getActions() + { + return actionStrings[actions]; + } + + /** + * Returns a permission collection suitable to take + * PropertyPermission objects. + * + * @return a new empty PermissionCollection + */ + public PermissionCollection newPermissionCollection() + { + return new PropertyPermissionCollection(); + } +} diff --git a/libjava/classpath/java/util/PropertyPermissionCollection.java b/libjava/classpath/java/util/PropertyPermissionCollection.java new file mode 100644 index 000000000..768b11225 --- /dev/null +++ b/libjava/classpath/java/util/PropertyPermissionCollection.java @@ -0,0 +1,166 @@ +/* PropertyPermissionCollection.java -- a collection of PropertyPermissions + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.security.Permission; +import java.security.PermissionCollection; + +/** + * This class provides the implementation for + * PropertyPermission.newPermissionCollection(). It only accepts + * PropertyPermissions, and correctly implements implies. It + * is synchronized, as specified in the superclass. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @status an undocumented class, but this matches Sun's serialization + */ +class PropertyPermissionCollection extends PermissionCollection +{ + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 7015263904581634791L; + + /** + * The permissions. + * + * @serial the table of permissions in the collection + */ + private final Hashtable permissions = new Hashtable(); + + /** + * A flag to detect if "*" is in the collection. + * + * @serial true if "*" is in the collection + */ + private boolean all_allowed; + + /** + * Adds a PropertyPermission to this collection. + * + * @param permission the permission to add + * @throws IllegalArgumentException if permission is not a PropertyPermission + * @throws SecurityException if collection is read-only + */ + public void add(Permission permission) + { + if (isReadOnly()) + throw new SecurityException("readonly"); + if (! (permission instanceof PropertyPermission)) + throw new IllegalArgumentException(); + PropertyPermission pp = (PropertyPermission) permission; + String name = pp.getName(); + if (name.equals("*")) + all_allowed = true; + PropertyPermission old = (PropertyPermission) permissions.get(name); + if (old != null) + { + if ((pp.actions | old.actions) == old.actions) + pp = old; // Old implies pp. + else if ((pp.actions | old.actions) != pp.actions) + // Here pp doesn't imply old; the only case left is both actions. + pp = new PropertyPermission(name, "read,write"); + } + permissions.put(name, pp); + } + + /** + * Returns true if this collection implies the given permission. This even + * returns true for this case: + * + *
        +   * collection.add(new PropertyPermission("a.*", "read"));
        +   * collection.add(new PropertyPermission("a.b.*", "write"));
        +   * collection.implies(new PropertyPermission("a.b.c", "read,write"));
        +   * 
        + * + * @param permission the permission to check + * @return true if it is implied by this + */ + public boolean implies(Permission permission) + { + if (! (permission instanceof PropertyPermission)) + return false; + PropertyPermission toImply = (PropertyPermission) permission; + int actions = toImply.actions; + + if (all_allowed) + { + int all_actions = ((PropertyPermission) permissions.get("*")).actions; + actions &= ~all_actions; + if (actions == 0) + return true; + } + + String name = toImply.getName(); + if (name.equals("*")) + return false; + + int prefixLength = name.length(); + if (name.endsWith("*")) + prefixLength -= 2; + + while (true) + { + PropertyPermission forName = + (PropertyPermission) permissions.get(name); + if (forName != null) + { + actions &= ~forName.actions; + if (actions == 0) + return true; + } + + prefixLength = name.lastIndexOf('.', prefixLength - 1); + if (prefixLength < 0) + return false; + name = name.substring(0, prefixLength + 1) + '*'; + } + } + + /** + * Enumerate over the collection. + * + * @return an enumeration of the collection contents + */ + public Enumeration elements() + { + return permissions.elements(); + } +} diff --git a/libjava/classpath/java/util/PropertyResourceBundle.java b/libjava/classpath/java/util/PropertyResourceBundle.java new file mode 100644 index 000000000..b528f08ca --- /dev/null +++ b/libjava/classpath/java/util/PropertyResourceBundle.java @@ -0,0 +1,171 @@ +/* PropertyResourceBundle -- a resource bundle built from a Property file + Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +/** + * This class is a concrete ResourceBundle that gets it + * resources from a property file. This implies that the resources are + * strings. For more information about resource bundles see the class + * ResourceBundle. + * + * You should not use this class directly, or subclass it, but you get + * an object of this class automatically when you call + * ResourceBundle.getBundle() and there is a properties + * file. + * + * If there is also a class for this resource and the same locale, the + * class will be chosen. The properties file should have the name of the + * resource bundle, appended with the locale (e.g. _de and the + * extension .properties. The file should have the same format + * as for Properties.load() + * + * An example of a properties file for the german language is given + * here. This extends the example given in ListResourceBundle. + * Create a file MyResource_de.properties with the following contents + * and put it in the CLASSPATH. (The char \u00e4 is the + * german umlaut) + * + * +
        +s1=3
        +s2=MeineDisk
        +s3=3. M\u00e4rz 96
        +s4=Die Diskette ''{1}'' enth\u00e4lt {0} in {2}.
        +s5=0
        +s6=keine Dateien
        +s7=1
        +s8=eine Datei
        +s9=2
        +s10={0,number} Dateien
        +s11=Die Formatierung warf eine Exception: {0}
        +s12=FEHLER
        +s13=Ergebnis
        +s14=Dialog
        +s15=Auswahlkriterium
        +s16=1,3
        +
        + * + * @author Jochen Hoenicke + * @see ResourceBundle + * @see ListResourceBundle + * @see Properties#load(InputStream) + * @since 1.1 + * @status updated to 1.4 + */ +public class PropertyResourceBundle extends ResourceBundle +{ + /** The properties file this bundle is based on. */ + private Properties properties; + + /** + * Creates a new property resource bundle. The property file must + * be encoded using ISO-8859-1. + * + * @param stream an input stream, where the resources are read from + * @throws NullPointerException if stream is null + * @throws IOException if reading the stream fails + */ + public PropertyResourceBundle(InputStream stream) throws IOException + { + properties = new Properties(); + properties.load(stream); + } + + /** + * Creates a new property resource bundle. The encoding of the property + * file is determined by the supplied {@link Reader} object. + * + * @param reader an input stream, where the resources are read from + * @throws NullPointerException if stream is null + * @throws IOException if reading the stream fails + * @since 1.6 + */ + public PropertyResourceBundle(Reader reader) throws IOException + { + properties = new Properties(); + properties.load(reader); + } + + /** + * Called by getObject when a resource is needed. This + * returns the resource given by the key. + * + * @param key the key of the resource + * @return the resource for the key, or null if it doesn't exist + */ + public Object handleGetObject(String key) + { + return properties.getProperty(key); + } + + /** + * This method should return all keys for which a resource exists. + * + * @return an enumeration of the keys + */ + public Enumeration getKeys() + { + if (parent == null) + // FIXME: bogus cast. + return (Enumeration) properties.propertyNames(); + // We make a new Set that holds all the keys, then return an enumeration + // for that. This prevents modifications from ruining the enumeration, + // as well as ignoring duplicates. + Set s = new HashSet(); + // FIXME: bogus cast. + Enumeration e = (Enumeration) properties.propertyNames(); + while (e.hasMoreElements()) + s.add(e.nextElement()); + ResourceBundle bundle = parent; + // Eliminate tail recursion. + do + { + e = bundle.getKeys(); + while (e.hasMoreElements()) + s.add(e.nextElement()); + bundle = bundle.parent; + } + while (bundle != null); + return Collections.enumeration(s); + } +} // class PropertyResourceBundle diff --git a/libjava/classpath/java/util/Random.java b/libjava/classpath/java/util/Random.java new file mode 100644 index 000000000..999e89547 --- /dev/null +++ b/libjava/classpath/java/util/Random.java @@ -0,0 +1,429 @@ +/* Random.java -- a pseudo-random number generator + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * This class generates pseudorandom numbers. It uses the same + * algorithm as the original JDK-class, so that your programs behave + * exactly the same way, if started with the same seed. + * + * The algorithm is described in The Art of Computer Programming, + * Volume 2 by Donald Knuth in Section 3.2.1. It is a 48-bit seed, + * linear congruential formula. + * + * If two instances of this class are created with the same seed and + * the same calls to these classes are made, they behave exactly the + * same way. This should be even true for foreign implementations + * (like this), so every port must use the same algorithm as described + * here. + * + * If you want to implement your own pseudorandom algorithm, you + * should extend this class and overload the next() and + * setSeed(long) method. In that case the above + * paragraph doesn't apply to you. + * + * This class shouldn't be used for security sensitive purposes (like + * generating passwords or encryption keys. See SecureRandom + * in package java.security for this purpose. + * + * For simple random doubles between 0.0 and 1.0, you may consider using + * Math.random instead. + * + * @see java.security.SecureRandom + * @see Math#random() + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @status updated to 1.4 + */ +public class Random implements Serializable +{ + /** + * True if the next nextGaussian is available. This is used by + * nextGaussian, which generates two gaussian numbers by one call, + * and returns the second on the second call. + * + * @serial whether nextNextGaussian is available + * @see #nextGaussian() + * @see #nextNextGaussian + */ + private boolean haveNextNextGaussian; + + /** + * The next nextGaussian, when available. This is used by nextGaussian, + * which generates two gaussian numbers by one call, and returns the + * second on the second call. + * + * @serial the second gaussian of a pair + * @see #nextGaussian() + * @see #haveNextNextGaussian + */ + private double nextNextGaussian; + + /** + * The seed. This is the number set by setSeed and which is used + * in next. + * + * @serial the internal state of this generator + * @see #next(int) + */ + private long seed; + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 3905348978240129619L; + + /** + * Creates a new pseudorandom number generator. The seed is initialized + * to the current time, as if by + * setSeed(System.currentTimeMillis());. + * + * @see System#currentTimeMillis() + */ + public Random() + { + this(System.currentTimeMillis()); + } + + /** + * Creates a new pseudorandom number generator, starting with the + * specified seed, using setSeed(seed);. + * + * @param seed the initial seed + */ + public Random(long seed) + { + setSeed(seed); + } + + /** + * Sets the seed for this pseudorandom number generator. As described + * above, two instances of the same random class, starting with the + * same seed, should produce the same results, if the same methods + * are called. The implementation for java.util.Random is: + * +
        public synchronized void setSeed(long seed)
        +{
        +  this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1);
        +  haveNextNextGaussian = false;
        +}
        + * + * @param seed the new seed + */ + public synchronized void setSeed(long seed) + { + this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1); + haveNextNextGaussian = false; + } + + /** + * Generates the next pseudorandom number. This returns + * an int value whose bits low order bits are + * independent chosen random bits (0 and 1 are equally likely). + * The implementation for java.util.Random is: + * +
        protected synchronized int next(int bits)
        +{
        +  seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
        +  return (int) (seed >>> (48 - bits));
        +}
        + * + * @param bits the number of random bits to generate, in the range 1..32 + * @return the next pseudorandom value + * @since 1.1 + */ + protected synchronized int next(int bits) + { + seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1); + return (int) (seed >>> (48 - bits)); + } + + /** + * Fills an array of bytes with random numbers. All possible values + * are (approximately) equally likely. + * The JDK documentation gives no implementation, but it seems to be: + * +
        public void nextBytes(byte[] bytes)
        +{
        +  for (int i = 0; i < bytes.length; i += 4)
        +  {
        +    int random = next(32);
        +    for (int j = 0; i + j < bytes.length && j < 4; j++)
        +    {
        +      bytes[i+j] = (byte) (random & 0xff)
        +      random >>= 8;
        +    }
        +  }
        +}
        + * + * @param bytes the byte array that should be filled + * @throws NullPointerException if bytes is null + * @since 1.1 + */ + public void nextBytes(byte[] bytes) + { + int random; + // Do a little bit unrolling of the above algorithm. + int max = bytes.length & ~0x3; + for (int i = 0; i < max; i += 4) + { + random = next(32); + bytes[i] = (byte) random; + bytes[i + 1] = (byte) (random >> 8); + bytes[i + 2] = (byte) (random >> 16); + bytes[i + 3] = (byte) (random >> 24); + } + if (max < bytes.length) + { + random = next(32); + for (int j = max; j < bytes.length; j++) + { + bytes[j] = (byte) random; + random >>= 8; + } + } + } + + /** + * Generates the next pseudorandom number. This returns + * an int value whose 32 bits are independent chosen random bits + * (0 and 1 are equally likely). The implementation for + * java.util.Random is: + * +
        public int nextInt()
        +{
        +  return next(32);
        +}
        + * + * @return the next pseudorandom value + */ + public int nextInt() + { + return next(32); + } + + /** + * Generates the next pseudorandom number. This returns + * a value between 0(inclusive) and n(exclusive), and + * each value has the same likelihodd (1/n). + * (0 and 1 are equally likely). The implementation for + * java.util.Random is: + * +
        +public int nextInt(int n)
        +{
        +  if (n <= 0)
        +    throw new IllegalArgumentException("n must be positive");
        +
        +  if ((n & -n) == n)  // i.e., n is a power of 2
        +    return (int)((n * (long) next(31)) >> 31);
        +
        +  int bits, val;
        +  do
        +  {
        +    bits = next(31);
        +    val = bits % n;
        +  }
        +  while(bits - val + (n-1) < 0);
        +
        +  return val;
        +}
        + * + *

        This algorithm would return every value with exactly the same + * probability, if the next()-method would be a perfect random number + * generator. + * + * The loop at the bottom only accepts a value, if the random + * number was between 0 and the highest number less then 1<<31, + * which is divisible by n. The probability for this is high for small + * n, and the worst case is 1/2 (for n=(1<<30)+1). + * + * The special treatment for n = power of 2, selects the high bits of + * the random number (the loop at the bottom would select the low order + * bits). This is done, because the low order bits of linear congruential + * number generators (like the one used in this class) are known to be + * ``less random'' than the high order bits. + * + * @param n the upper bound + * @throws IllegalArgumentException if the given upper bound is negative + * @return the next pseudorandom value + * @since 1.2 + */ + public int nextInt(int n) + { + if (n <= 0) + throw new IllegalArgumentException("n must be positive"); + if ((n & -n) == n) // i.e., n is a power of 2 + return (int) ((n * (long) next(31)) >> 31); + int bits, val; + do + { + bits = next(31); + val = bits % n; + } + while (bits - val + (n - 1) < 0); + return val; + } + + /** + * Generates the next pseudorandom long number. All bits of this + * long are independently chosen and 0 and 1 have equal likelihood. + * The implementation for java.util.Random is: + * +

        public long nextLong()
        +{
        +  return ((long) next(32) << 32) + next(32);
        +}
        + * + * @return the next pseudorandom value + */ + public long nextLong() + { + return ((long) next(32) << 32) + next(32); + } + + /** + * Generates the next pseudorandom boolean. True and false have + * the same probability. The implementation is: + * +
        public boolean nextBoolean()
        +{
        +  return next(1) != 0;
        +}
        + * + * @return the next pseudorandom boolean + * @since 1.2 + */ + public boolean nextBoolean() + { + return next(1) != 0; + } + + /** + * Generates the next pseudorandom float uniformly distributed + * between 0.0f (inclusive) and 1.0f (exclusive). The + * implementation is as follows. + * +
        public float nextFloat()
        +{
        +  return next(24) / ((float)(1 << 24));
        +}
        + * + * @return the next pseudorandom float + */ + public float nextFloat() + { + return next(24) / (float) (1 << 24); + } + + /** + * Generates the next pseudorandom double uniformly distributed + * between 0.0 (inclusive) and 1.0 (exclusive). The + * implementation is as follows. + * +
        public double nextDouble()
        +{
        +  return (((long) next(26) << 27) + next(27)) / (double)(1L << 53);
        +}
        + * + * @return the next pseudorandom double + */ + public double nextDouble() + { + return (((long) next(26) << 27) + next(27)) / (double) (1L << 53); + } + + /** + * Generates the next pseudorandom, Gaussian (normally) distributed + * double value, with mean 0.0 and standard deviation 1.0. + * The algorithm is as follows. + * +
        public synchronized double nextGaussian()
        +{
        +  if (haveNextNextGaussian)
        +  {
        +    haveNextNextGaussian = false;
        +    return nextNextGaussian;
        +  }
        +  else
        +  {
        +    double v1, v2, s;
        +    do
        +    {
        +      v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0
        +      v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0
        +      s = v1 * v1 + v2 * v2;
        +    }
        +    while (s >= 1);
        +
        +    double norm = Math.sqrt(-2 * Math.log(s) / s);
        +    nextNextGaussian = v2 * norm;
        +    haveNextNextGaussian = true;
        +    return v1 * norm;
        +  }
        +}
        + * + *

        This is described in section 3.4.1 of The Art of Computer + * Programming, Volume 2 by Donald Knuth. + * + * @return the next pseudorandom Gaussian distributed double + */ + public synchronized double nextGaussian() + { + if (haveNextNextGaussian) + { + haveNextNextGaussian = false; + return nextNextGaussian; + } + double v1, v2, s; + do + { + v1 = 2 * nextDouble() - 1; // Between -1.0 and 1.0. + v2 = 2 * nextDouble() - 1; // Between -1.0 and 1.0. + s = v1 * v1 + v2 * v2; + } + while (s >= 1); + double norm = Math.sqrt(-2 * Math.log(s) / s); + nextNextGaussian = v2 * norm; + haveNextNextGaussian = true; + return v1 * norm; + } +} diff --git a/libjava/classpath/java/util/RandomAccess.java b/libjava/classpath/java/util/RandomAccess.java new file mode 100644 index 000000000..054266a1c --- /dev/null +++ b/libjava/classpath/java/util/RandomAccess.java @@ -0,0 +1,64 @@ +/* RandomAccess.java -- A tagging interface that lists can use to tailor + operations to the correct algorithm + Copyright (C) 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Marker interface used to inform List implementations that + * they support fast (usually constant time) random access. This allows + * generic list algorithms to tailor their behavior based on the list + * type. + *

        + * + * For example, some sorts are n*log(n) on an array, but decay to quadratic + * time on a linked list. As a rule of thumb, this interface should be + * used is this loop:
        + * for (int i = 0, n = list.size(); i < n; i++) list.get(i); + *
        runs faster than this loop:
        + * for (Iterator i = list.iterator(); i.hasNext(); ) i.next(); + * + * @author Eric Blake (ebb9@email.byu.edu) + * @see List + * @since 1.4 + * @status updated to 1.4 + */ +public interface RandomAccess +{ + // Tagging interface only. +} diff --git a/libjava/classpath/java/util/ResourceBundle.java b/libjava/classpath/java/util/ResourceBundle.java new file mode 100644 index 000000000..966eb026c --- /dev/null +++ b/libjava/classpath/java/util/ResourceBundle.java @@ -0,0 +1,625 @@ +/* ResourceBundle -- aids in loading resource bundles + Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.classpath.VMStackWalker; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A resource bundle contains locale-specific data. If you need localized + * data, you can load a resource bundle that matches the locale with + * getBundle. Now you can get your object by calling + * getObject or getString on that bundle. + * + *

        When a bundle is demanded for a specific locale, the ResourceBundle + * is searched in following order (def. language stands for the + * two letter ISO language code of the default locale (see + * Locale.getDefault()). + * +

        baseName_language code_country code_variant
        +baseName_language code_country code
        +baseName_language code
        +baseName_def. language_def. country_def. variant
        +baseName_def. language_def. country
        +baseName_def. language
        +baseName
        + * + *

        A bundle is backed up by less specific bundles (omitting variant, country + * or language). But it is not backed up by the default language locale. + * + *

        If you provide a bundle for a given locale, say + * Bundle_en_UK_POSIX, you must also provide a bundle for + * all sub locales, ie. Bundle_en_UK, Bundle_en, and + * Bundle. + * + *

        When a bundle is searched, we look first for a class with the given + * name, then for a file with .properties extension in the + * classpath. The name must be a fully qualified classname (with dots as + * path separators). + * + *

        (Note: This implementation always backs up the class with a properties + * file if that is existing, but you shouldn't rely on this, if you want to + * be compatible to the standard JDK.) + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @see Locale + * @see ListResourceBundle + * @see PropertyResourceBundle + * @since 1.1 + * @status updated to 1.4 + */ +public abstract class ResourceBundle +{ + /** + * Maximum size of our cache of ResourceBundles 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. + */ + protected ResourceBundle parent; + + /** + * The locale of this resource bundle. You can read this with + * getLocale and it is automatically set in + * getBundle. + */ + private Locale locale; + + /** + * A VM-wide cache of resource bundles already fetched. + *

        + * This {@link Map} is a Least Recently Used (LRU) cache, of the last + * {@link #CACHE_SIZE} accessed ResourceBundles keyed by the + * tuple: default locale, resource-bundle name, resource-bundle locale, and + * classloader. + * + * @see BundleKey + */ + 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. + */ + public ResourceBundle() + { + } + + /** + * Get a String from this resource bundle. Since most localized Objects + * are Strings, this method provides a convenient way to get them without + * casting. + * + * @param key the name of the resource + * @throws MissingResourceException if the resource can't be found + * @throws NullPointerException if key is null + * @throws ClassCastException if resource is not a string + */ + public final String getString(String key) + { + return (String) getObject(key); + } + + /** + * Get an array of Strings from this resource bundle. This method + * provides a convenient way to get it without casting. + * + * @param key the name of the resource + * @throws MissingResourceException if the resource can't be found + * @throws NullPointerException if key is null + * @throws ClassCastException if resource is not a string + */ + public final String[] getStringArray(String key) + { + return (String[]) getObject(key); + } + + /** + * Get an object from this resource bundle. This will call + * handleGetObject for this resource and all of its parents, + * until it finds a non-null resource. + * + * @param key the name of the resource + * @throws MissingResourceException if the resource can't be found + * @throws NullPointerException if key is null + */ + public final Object getObject(String key) + { + for (ResourceBundle bundle = this; bundle != null; bundle = bundle.parent) + { + Object o = bundle.handleGetObject(key); + if (o != null) + return o; + } + + String className = getClass().getName(); + throw new MissingResourceException("Key '" + key + + "'not found in Bundle: " + + className, className, key); + } + + /** + * Return the actual locale of this bundle. You can use it after calling + * getBundle, to know if the bundle for the desired locale was loaded or + * if the fall back was used. + * + * @return the bundle's locale + */ + public Locale getLocale() + { + return locale; + } + + /** + * Set the parent of this bundle. The parent is consulted when you call + * getObject and there is no such resource in the current bundle. + * + * @param parent the parent of this bundle + */ + protected void setParent(ResourceBundle parent) + { + this.parent = parent; + } + + /** + * Get the appropriate ResourceBundle for the default locale. This is like + * calling getBundle(baseName, Locale.getDefault(), + * getClass().getClassLoader(), except that any security check of + * getClassLoader won't fail. + * + * @param baseName the name of the ResourceBundle + * @return the desired resource bundle + * @throws MissingResourceException if the resource bundle can't be found + * @throws NullPointerException if baseName is null + */ + public static ResourceBundle getBundle(String baseName) + { + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl == null) + cl = ClassLoader.getSystemClassLoader(); + return getBundle(baseName, Locale.getDefault(), cl); + } + + /** + * Get the appropriate ResourceBundle for the given locale. This is like + * calling getBundle(baseName, locale, + * getClass().getClassLoader(), except that any security check of + * getClassLoader won't fail. + * + * @param baseName the name of the ResourceBundle + * @param locale A locale + * @return the desired resource bundle + * @throws MissingResourceException if the resource bundle can't be found + * @throws NullPointerException if baseName or locale is null + */ + public static ResourceBundle getBundle(String baseName, Locale locale) + { + ClassLoader cl = VMStackWalker.getCallingClassLoader(); + if (cl == null) + cl = ClassLoader.getSystemClassLoader(); + return getBundle(baseName, locale, cl); + } + + /** Cache key for the ResourceBundle cache. Resource bundles are keyed + by the combination of bundle name, locale, and class loader. */ + private static class BundleKey + { + Locale defaultLocale; + String baseName; + Locale locale; + ClassLoader classLoader; + int hashcode; + + BundleKey() {} + + BundleKey(Locale dl, String s, Locale l, ClassLoader cl) + { + set(dl, s, l, cl); + } + + void set(Locale dl, String s, Locale l, ClassLoader cl) + { + defaultLocale = dl; + baseName = s; + locale = l; + classLoader = cl; + hashcode = defaultLocale.hashCode() ^ baseName.hashCode() + ^ locale.hashCode() ^ classLoader.hashCode(); + } + + public int hashCode() + { + return hashcode; + } + + public boolean equals(Object o) + { + if (! (o instanceof BundleKey)) + return false; + BundleKey key = (BundleKey) o; + return hashcode == key.hashcode + && defaultLocale.equals(key.defaultLocale) + && baseName.equals(key.baseName) + && locale.equals(key.locale) + && classLoader.equals(key.classLoader); + } + + public String toString() + { + CPStringBuilder builder = new CPStringBuilder(getClass().getName()); + builder.append("[defaultLocale="); + builder.append(defaultLocale); + builder.append(",baseName="); + builder.append(baseName); + builder.append(",locale="); + builder.append(locale); + builder.append(",classLoader="); + builder.append(classLoader); + builder.append("]"); + return builder.toString(); + } + } + + /** A cache lookup key. This avoids having to a new one for every + * getBundle() call. */ + private static final BundleKey lookupKey = new BundleKey(); + + /** Singleton cache entry to represent previous failed lookups. */ + private static final Object nullEntry = new Object(); + + /** + * Get the appropriate ResourceBundle for the given locale. The following + * strategy is used: + * + *

        A sequence of candidate bundle names are generated, and tested in + * this order, where the suffix 1 means the string from the specified + * locale, and the suffix 2 means the string from the default locale:

        + * + *
          + *
        • baseName + "_" + language1 + "_" + country1 + "_" + variant1
        • + *
        • baseName + "_" + language1 + "_" + country1
        • + *
        • baseName + "_" + language1
        • + *
        • baseName + "_" + language2 + "_" + country2 + "_" + variant2
        • + *
        • baseName + "_" + language2 + "_" + country2
        • + *
        • baseName + "_" + language2
        • + *
        • baseName
        • + *
        + * + *

        In the sequence, entries with an empty string are ignored. Next, + * getBundle tries to instantiate the resource bundle:

        + * + *
          + *
        • First, an attempt is made to load a class in the specified classloader + * which is a subclass of ResourceBundle, and which has a public constructor + * with no arguments, via reflection.
        • + *
        • Next, a search is made for a property resource file, by replacing + * '.' with '/' and appending ".properties", and using + * ClassLoader.getResource(). If a file is found, then a + * PropertyResourceBundle is created from the file's contents.
        • + *
        + * If no resource bundle was found, a MissingResourceException is thrown. + * + *

        Next, the parent chain is implemented. The remaining candidate names + * in the above sequence are tested in a similar manner, and if any results + * in a resource bundle, it is assigned as the parent of the first bundle + * using the setParent method (unless the first bundle already + * has a parent).

        + * + *

        For example, suppose the following class and property files are + * provided: MyResources.class, MyResources_fr_CH.properties, + * MyResources_fr_CH.class, MyResources_fr.properties, + * MyResources_en.properties, and MyResources_es_ES.class. The contents of + * all files are valid (that is, public non-abstract subclasses of + * ResourceBundle with public nullary constructors for the ".class" files, + * syntactically correct ".properties" files). The default locale is + * Locale("en", "UK").

        + * + *

        Calling getBundle with the shown locale argument values instantiates + * resource bundles from the following sources:

        + * + *
          + *
        • Locale("fr", "CH"): result MyResources_fr_CH.class, parent + * MyResources_fr.properties, parent MyResources.class
        • + *
        • Locale("fr", "FR"): result MyResources_fr.properties, parent + * MyResources.class
        • + *
        • Locale("de", "DE"): result MyResources_en.properties, parent + * MyResources.class
        • + *
        • Locale("en", "US"): result MyResources_en.properties, parent + * MyResources.class
        • + *
        • Locale("es", "ES"): result MyResources_es_ES.class, parent + * MyResources.class
        • + *
        + * + *

        The file MyResources_fr_CH.properties is never used because it is hidden + * by MyResources_fr_CH.class.

        + * + * @param baseName the name of the ResourceBundle + * @param locale A locale + * @param classLoader a ClassLoader + * @return the desired resource bundle + * @throws MissingResourceException if the resource bundle can't be found + * @throws NullPointerException if any argument is null + * @since 1.2 + */ + // This method is synchronized so that the cache is properly + // handled. + public static synchronized ResourceBundle getBundle + (String baseName, Locale locale, ClassLoader classLoader) + { + Locale defaultLocale = Locale.getDefault(); + // This will throw NullPointerException if any arguments are null. + lookupKey.set(defaultLocale, baseName, locale, classLoader); + Object obj = bundleCache.get(lookupKey); + 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) + { + // 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, ""); + } + // Cache the result and return it. + bundleCache.put(key, bundle); + return bundle; + } + + /** + * Override this method to provide the resource for a keys. This gets + * called by getObject. If you don't have a resource + * for the given key, you should return null instead throwing a + * MissingResourceException. You don't have to ask the parent, getObject() + * already does this; nor should you throw a MissingResourceException. + * + * @param key the key of the resource + * @return the resource for the key, or null if not in bundle + * @throws NullPointerException if key is null + */ + protected abstract Object handleGetObject(String key); + + /** + * This method should return all keys for which a resource exists; you + * should include the enumeration of any parent's keys, after filtering out + * duplicates. + * + * @return an enumeration of the keys + */ + public abstract Enumeration getKeys(); + + /** + * Tries to load a class or a property file with the specified name. + * + * @param localizedName the name + * @param classloader the classloader + * @return the resource bundle if it was loaded, otherwise the backup + */ + private static ResourceBundle tryBundle(String localizedName, + ClassLoader classloader) + { + ResourceBundle bundle = null; + try + { + Class rbClass; + if (classloader == null) + rbClass = Class.forName(localizedName); + else + rbClass = classloader.loadClass(localizedName); + // Note that we do the check up front instead of catching + // ClassCastException. The reason for this is that some crazy + // programs (Eclipse) have classes that do not extend + // ResourceBundle but that have the same name as a property + // bundle; in fact Eclipse relies on ResourceBundle not + // instantiating these classes. + if (ResourceBundle.class.isAssignableFrom(rbClass)) + bundle = (ResourceBundle) rbClass.newInstance(); + } + catch (Exception ex) {} + + if (bundle == null) + { + try + { + InputStream is; + String resourceName + = localizedName.replace('.', '/') + ".properties"; + if (classloader == null) + is = ClassLoader.getSystemResourceAsStream(resourceName); + else + is = classloader.getResourceAsStream(resourceName); + if (is != null) + bundle = new PropertyResourceBundle(is); + } + catch (IOException ex) + { + MissingResourceException mre = new MissingResourceException + ("Failed to load bundle: " + localizedName, localizedName, ""); + mre.initCause(ex); + throw mre; + } + } + + return bundle; + } + + /** + * Tries to load the bundle for a given locale, also loads the backup + * locales with the same language. + * + * @param baseName the raw bundle name, without locale qualifiers + * @param locale the locale + * @param classLoader the classloader + * @param wantBase whether a resource bundle made only from the base name + * (with no locale information attached) should be returned. + * @return the resource bundle if it was loaded, otherwise the backup + */ + private static ResourceBundle tryBundle(String baseName, Locale locale, + ClassLoader classLoader, + boolean wantBase) + { + String language = locale.getLanguage(); + String country = locale.getCountry(); + String variant = locale.getVariant(); + + int baseLen = baseName.length(); + + // Build up a CPStringBuilder containing the complete bundle name, fully + // qualified by locale. + CPStringBuilder sb = new CPStringBuilder(baseLen + variant.length() + 7); + + sb.append(baseName); + + if (language.length() > 0) + { + sb.append('_'); + sb.append(language); + + if (country.length() > 0) + { + sb.append('_'); + sb.append(country); + + if (variant.length() > 0) + { + sb.append('_'); + sb.append(variant); + } + } + } + + // Now try to load bundles, starting with the most specialized name. + // Build up the parent chain as we go. + String bundleName = sb.toString(); + ResourceBundle first = null; // The most specialized bundle. + ResourceBundle last = null; // The least specialized bundle. + + while (true) + { + ResourceBundle foundBundle = tryBundle(bundleName, classLoader); + if (foundBundle != null) + { + if (first == null) + first = foundBundle; + if (last != null) + last.parent = foundBundle; + foundBundle.locale = locale; + last = foundBundle; + } + int idx = bundleName.lastIndexOf('_'); + // Try the non-localized base name only if we already have a + // localized child bundle, or wantBase is true. + if (idx > baseLen || (idx == baseLen && (first != null || wantBase))) + bundleName = bundleName.substring(0, idx); + else + break; + } + + return first; + } + + /** + * Remove all resources from the cache that were loaded + * using the class loader of the calling class. + * + * @since 1.6 + */ + public static final void clearCache() + { + clearCache(VMStackWalker.getCallingClassLoader()); + } + + /** + * Remove all resources from the cache that were loaded + * using the specified class loader. + * + * @param loader the loader used for the bundles that will be removed. + * @throws NullPointerException if {@code loader} is {@code null}. + * @since 1.6 + */ + public static final void clearCache(ClassLoader loader) + { + if (loader == null) + throw new NullPointerException("The loader can not be null."); + synchronized (ResourceBundle.class) + { + Iterator iter = bundleCache.keySet().iterator(); + while (iter.hasNext()) + { + BundleKey key = iter.next(); + if (key.classLoader == loader) + iter.remove(); + } + } + } + +} diff --git a/libjava/classpath/java/util/Scanner.java b/libjava/classpath/java/util/Scanner.java new file mode 100644 index 000000000..59c4cc0ff --- /dev/null +++ b/libjava/classpath/java/util/Scanner.java @@ -0,0 +1,2223 @@ +/* java.util.Scanner -- Parses primitive types and strings using regexps + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.ReadableByteChannel; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.text.ParseException; + +import java.util.Iterator; +import java.util.Locale; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author E0327023 Hernadi Laszlo +*/ +public class Scanner + implements Iterator +{ + private static final String NOT_LONG = "\" is not a long"; //$NON-NLS-1$ + + private static final String ERR_PREFIX = "\""; //$NON-NLS-1$ + + private static final String NOT_INT = "\" is not an integer"; //$NON-NLS-1$ + + private static final String NOT_DOUBLE = "\" is not a double"; //$NON-NLS-1$ + + private static final String NOT_BYTE = "\" is not a byte"; //$NON-NLS-1$ + + private static final String NOT_BOOLEAN = "\" is not a boolean"; //$NON-NLS-1$ + + private static final String IS_NOT = "\" is not "; //$NON-NLS-1$ + + private static final String DEFAULT_PATTERN_S = "\\p{javaWhitespace}+"; //$NON-NLS-1$ + + private static final Pattern DEFAULT_PATTERN = + Pattern.compile (DEFAULT_PATTERN_S); + + private static final String BIG_INTEGER = "BigInteger"; //$NON-NLS-1$ + + private final static String NEW_LINE = + System.getProperty ("line.separator"); + + private IOException lastIOException = null; + + /** + * An InputStream source if a Constructor with an InputStream source is called, otherwise it + * stays null . + */ + private InputStream bIS = null; + + /** + * Length of the input Buffer, which is the maximum bytes to be read at once. + */ + private final int MaxBufferLen = 1000000; + + /** + * Minimum buffer length. If there are less chars in the Buffer than this value reading from + * source is tried. + */ + private final int MIN_BUF_LEN = 100; + + /** + * Maximum number of processed chars in the Buffer. If exeeded, all processed chars from the + * beginning of the Buffer will be discarded to save space. The bytes left are copyed into a new + * Buffer. + */ + private final int MAX_PREFIX = 10000; + + /** + * The Buffer which is used by the Matcher to find given patterns. It is filled up when matcher + * hits end or MIN_BUF_LEN is reached. + */ + private String actBuffer = new String (); + + /** + * The current radix to use by the methods getNextXXX and hasNextXXX. + */ + private int currentRadix = 10; + + /** + * The current locale. + * + * @see #useLocale(Locale) + * @see #locale() + */ + private Locale actLocale = Locale.getDefault (); + + /** + * The current pattern for the matcher. + */ + private Pattern p = DEFAULT_PATTERN; + + /** + * The current position in the Buffer, at which the next match should start. + */ + private int actPos = 0; + + /** + * A global buffer to save new allocations by reading from source. + */ + private final byte[] tmpBuffer = new byte[this.MaxBufferLen]; + + /** + * The charsetName to use with the source. + */ + private String charsetName = null; + + /** + * The Matcher which is used. + */ + private Matcher myMatcher = this.p.matcher (this.actBuffer); + + /** + * The MatchResult is generated at each match, even if match() isn't called. + */ + private MatchResult actResult = null; + + /** + * A Readable source if a Constructor with a Readable source is called, otherwise it stays + * null . + */ + private Readable readableSource = null; + + /** + * A ReadableByteChannel source if a Constructor with a ReadableByteChannel source is called, + * otherwise it stays null . + */ + private ReadableByteChannel rbcSource = null; + + /** + * Indicates if the close() method was called. + */ + private boolean isClosed = false; + + /** + * For performance reasons the last Found is saved, if a hasNextXXX method was called. + */ + private String lastFound = null; + + private boolean lastFoundPresent = false; + + private int lastNextPos = 0; + + private int lastPatternHash = 0; + + private int last_RegionStart = 0; + + private int last_RegionEnd = 0; + + private boolean last_anchor = false; + + private boolean last_transparent = false; + + private MatchResult lastResult = null; + + /** + * To keep track of the current position in the stream for the toString method, each time + * processed chars are removed the amount is added to processedChars. + */ + private int procesedChars = 0; + + /** + * needInput is set true before a read method, and if there is no input it blocks + * and stays true. Right after a read it is set to false. + */ + private boolean needInput = false; + + private boolean skipped = false; + + /** + * {@link #doSkipp} indicates that the found pattern belongs to the result. If + * {@link #doSkipp} is false the match result ends at the beginning of the match. + * In both cases the current position is set after the pattern, if the found pattern has to be + * removed, a nextXXX method is called. + */ + private boolean doSkipp = false; + + /** + * Indicates if the last match was valid or not. + */ + private boolean matchValid = false; + + private NumberFormat actFormat = NumberFormat.getInstance (this.actLocale); + + private DecimalFormat df = (DecimalFormat) this.actFormat; + + /** + * Indicates if current Locale should be used at the input. + */ + private boolean useLocale = true; + + private DecimalFormatSymbols dfs = + new DecimalFormatSymbols (this.actLocale); + + /** + * Constructs a new Scanner with the given File as source. + * {@link #Scanner(InputStream, String)} is called with null as charsetName. + * + * @param source + * The File to use as source. + * @throws FileNotFoundException + * If the file is not found an Exception is thrown. + */ + public Scanner (final File source) throws FileNotFoundException // TESTED + { + this (source, null); + } + + /** + * Constructs a new Scanner with the given File as source.
        + * {@link #Scanner(InputStream, String)} is called with the given charsetName. + * + * @param source + * The File to use as source. + * @param charsetName + * Current charset name of the file. If charsetName is null it behaves if it was not + * set. + * @throws FileNotFoundException + * If the file is not found an Exception is thrown. + */ + public Scanner (final File source, + final String charsetName) throws FileNotFoundException + { + this (new FileInputStream (source), charsetName); + } + + /** + * Constructs a new Scanner with the given inputStream.
        + * {@link #Scanner(InputStream, String)} is called with null as charsetName. + * + * @param source + * The InputStream to use as source. + */ + public Scanner (final InputStream source) // TESTED + { + this (source, null); + } + + /** + * Constructs a new Scanner with the InputSream and a charsetName. Afterwards the Buffer is + * filled. + * + * @param source + * The InputStream to use as source. + * @param charsetName + * The charsetName to apply on the source's data. + */ + public Scanner (final InputStream source, final String charsetName) + { + this.bIS = (new BufferedInputStream (source)); + this.charsetName = charsetName; + myFillBuffer (); + } + + /** + * Constructs a new Scanner with a Readable input as source. + * + * @param source + * The Readable to use as source. + */ + public Scanner (final Readable source) + { + this.readableSource = source; + myFillBuffer (); + } + + /** + * Constructs a new Scanner with a ReadableByteChannel as + * source. Therfore the {@link #Scanner(ReadableByteChannel, + * String)} is called with null as charsetName. + * + * @param source + * The ReadableByteChannel to use as source. + */ + public Scanner (final ReadableByteChannel source) + { + this (source, null); + } + + /** + * Constructs a new Scanner with a ReadableByteChannel as source and + * a given charsetName, which is to be applied on it.
        It also + * initiates the main Buffer. + * + * @param source + * The ReadableByteChannel to use as source. + * @param charsetName + * The charsetName to be applied on the source. + */ + public Scanner (final ReadableByteChannel source, final String charsetName) + { + this.charsetName = charsetName; + this.rbcSource = source; + myFillBuffer (); + } + + /** + * Constructs a new Scanner using the given String as input only. + * + * @param source + * The whole String to be used as source. + */ + public Scanner (final String source) // TESTED + { + this.actBuffer = new String (source); + this.myMatcher.reset (this.actBuffer); + } + + /** + * Closes this Scanner. If an {@link IOException} occurs it is + * catched and is available under {@link #ioException()}.
        After + * the Scanner is closed, all searches will lead to a {@link + * IllegalStateException}. + */ + public void close () + { + try + { + if (this.bIS != null) + this.bIS.close (); + if (this.rbcSource != null) + this.rbcSource.close (); + this.isClosed = true; + } + catch (IOException ioe) + { + this.lastIOException = ioe; + } + } + + /** + * Returns the current delimiter. + * + * @return the current delimiter. + */ + public Pattern delimiter () // TESTED + { + return this.p; + } + + /** + * Tries to find the pattern in the current line. + * + * @param pattern The pattern which should be searched in the + * current line of the input. + * @throws NoSuchElementException + * If the pattern was not found. + * @return If the search was successful, the result or otherwise a + * {@link NoSuchElementException} is thrown. + */ + public String findInLine (final Pattern pattern) throws NoSuchElementException // TESTED + { + String tmpStr = myNextLine (false); + return myFindPInStr (pattern, tmpStr, 0); + } + + /** + * Compiles the given pattern into a {@link Pattern} and calls + * {@link #findInLine(Pattern)} with the compiled pattern and + * returns whatever it returns. + * + * @param pattern + * The pattern which should be matched in the input. + * @throws NoSuchElementException + * If the pattern was not found. + * @return The match in the current line. + */ + public String findInLine (final String pattern) // TESTED + { + return findInLine (Pattern.compile (pattern)); + } + + /** + * Trys to match the pattern within the given horizon. + * + * @param pattern + * Pattern to search. + * @param horizon + * @return The result of the match. + * @throws IllegalArgumentException + * if the horizon is negative. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public String findWithinHorizon (final Pattern pattern, final int horizon) + throws IllegalArgumentException, IllegalStateException + { + if (horizon < 0) + { + throw new IllegalArgumentException (horizon + " is negative"); + } + + if (this.isClosed) + { + throw new IllegalStateException ("Scanner is closed"); + } + + // doSkipp is set true to get the matching patern together with the found String + this.doSkipp = true; + String rc = myFindPInStr (pattern, this.actBuffer, horizon); + + if (rc != null) + { + this.actPos += rc.length (); + } + + return rc; + } + + /** + * Compile the pattern and call {@link #findWithinHorizon(Pattern, + * int)}. + * + * @param pattern + * Pattern to search. + * @param horizon + * @return The result of the match. + * @throws IllegalArgumentException + * if the horizon is negative. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public String findWithinHorizon (final String pattern, final int horizon) + throws IllegalArgumentException, IllegalStateException + { + return findWithinHorizon (Pattern.compile (pattern), horizon); + } + + /** + * Checks if there is any next String using the current + * delimiter. Therefore the string must not be null + * and the length must be greater then 0. If a {@link + * NoSuchElementException} is thrown by the search method, it is + * catched and false is returned. + * + * @return true if there is any result using the current delimiter. This wouldn't + * lead to a {@link NoSuchElementException}. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNext () throws IllegalStateException // TESTED + { + String tmpStr = null; + + try + { + tmpStr = myCoreNext (false, this.p); + } + catch (NoSuchElementException nf) + { + } + + if (tmpStr == null || tmpStr.length () <= 0) + { + return false; + } + return true; + } + + /** + * Searches the pattern in the next subString before the next + * current delimiter. + * + * @param pattern + * The pattern to search for. + * @return true if the pattern is found before the current delimiter. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNext (final Pattern pattern) throws IllegalStateException // TESTED + { + String tmpStr; + + tmpStr = myNext (pattern, false); + + if (tmpStr == null || tmpStr.length () <= 0) + { + return false; + } + return true; + } + + /** + * Compiles the pattern to a {@link Pattern} and calls {@link + * #hasNext(Pattern)}. + * + * @see #hasNext(Pattern) + * @param pattern + * The pattern as string to search for. + * @return true if the pattern is found before the current delimiter. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNext (final String pattern) throws IllegalStateException // TESTED + { + return hasNext (Pattern.compile (pattern)); + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a BigDecimal number.
        BigDecimal numbers are always tryed + * with radix 10. + * + * @see #nextBigDecimal() + * @return true if the next string is a BigDecimal number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextBigDecimal () throws IllegalStateException // TESTED + { + try + { + myBigDecimal (false); + return true; + } + catch (InputMismatchException nfe) + { + return false; + } + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a BigInteger number.
        Call {@link #hasNextBigInteger(int)} + * with the current radix. + * + * @see #nextBigInteger() + * @return true if the next string is a BigInteger number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextBigInteger () throws IllegalStateException // TESTED + { + return hasNextBigInteger (this.currentRadix); + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a BigInteger number.
        + * + * @param radix + * The radix to use for this check. The global radix of the Scanner will not be + * changed. + * @return true if the next string is a BigInteger number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextBigInteger (final int radix) throws + IllegalStateException + { + try + { + myNextBigInteger (radix, false, BIG_INTEGER); + return true; + } + catch (InputMismatchException ime) + { + return false; + } + } + + /** + * Checks if the next string could be a boolean. The method handles + * the input not case sensitiv, so "true" and "TRUE" and even "tRuE" + * are true . + * + * @see #nextBoolean() + * @return Return true if the next string is a boolean. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextBoolean () throws IllegalStateException // TESTED + { + try + { + myNextBoolean (false); + return true; + } + catch (InputMismatchException ime) + { + return false; + } + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a byte number.
        Calls {@link #hasNextByte(int)} with the + * current radix. + * + * @see #nextByte() + * @return true if the next string is a byte number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextByte () throws IllegalStateException // TESTED + { + return hasNextByte (this.currentRadix); + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a byte number with the given radix.
        To check, the private + * method {@link #myNextByte(int, boolean)} is called, and if no + * error occurs the next string could be a byte. + * + * @see #nextByte(int) + * @param radix The radix to use for this check. The global radix of + * the Scanner will not be changed. + * @return true if the next string is a byte number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextByte (final int radix) throws IllegalStateException + { + try + { + myNextByte (radix, false); + return true; + } + catch (InputMismatchException ime) + { + return false; + } + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a double number.
        To check, the private method {@link + * #myNextDouble(boolean)} is called, and if no error occurs the + * next string could be a double. + * + * @see #nextDouble() + * @return true if the next string is a double number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextDouble () throws IllegalStateException // TESTED + { + try + { + myNextDouble (false); + return true; + } + catch (InputMismatchException ime) + { + return false; + } + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a double number. Because every float is a double this is + * checked.
        To check, the private method {@link + * #myNextDouble(boolean)} is called, and if no error occurs the + * next string could be a double. + * + * @see #nextFloat() + * @return true if the next string is a double number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextFloat () throws IllegalStateException // TESTED + { + try + { + myNextDouble (false); + // myNextFloat(false); + return true; + } + catch (InputMismatchException ime) + { + return false; + } + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * an int number.
        To check, the private method {@link + * #myNextInt(int, boolean)} is called, and if no error occurs the + * next string could be an int. + * + * @see #nextInt(int) + * @return true if the next string is an int number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextInt () throws IllegalStateException // TESTED + { + return hasNextInt (this.currentRadix); + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * an int number with the given radix.
        To check, the private + * method {@link #myNextInt(int, boolean)} is called, and if no + * error occurs the next string could be an int. + * + * @see #nextInt(int) + * @param radix + * The radix to use for this check. The global radix of the Scanner will not be + * changed. + * @return true if the next string is an int number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextInt (final int radix) throws IllegalStateException + { + try + { + myNextInt (radix, false); + return true; + } + catch (InputMismatchException ime) + { + return false; + } + } + + /** + * Checks if there is a current line, which ends at the next line + * break or the end of the input. + * + * @return true if there is a current line. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextLine () throws IllegalStateException // TESTED + { + return (myNextLine (false) != null); + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a long number.
        To check, the private method {@link + * #myNextLong(int, boolean)} is called, and if no error occurs the + * next string could be a long. + * + * @see #nextLong() + * @return true if the next string is a long number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextLong () throws IllegalStateException // TESTED + { + return hasNextLong (this.currentRadix); + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a long number with the given radix.
        To check, the private + * method {@link #myNextLong(int, boolean)} is called, and if no + * error occurs the next string could be a long. + * + * @see #nextLong(int) + * @param radix + * The radix to use for this check. The global radix of the Scanner will not be + * changed. + * @return true if the next string is a long number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextLong (final int radix) throws IllegalStateException + { + try + { + myNextLong (radix, false); + return true; + } + catch (InputMismatchException ime) + { + return false; + } + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a short number with the given radix.
        To check, the private + * method {@link #myNextShort(int, boolean)} is called, and if no + * error occurs the next string could be a short. + * + * @see #nextShort(int) + * @return true if the next string is a short number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextShort () throws IllegalStateException // TESTED + { + return hasNextShort (this.currentRadix); + } + + /** + * Checks if the string to the next delimiter can be interpreted as + * a short number.
        To check, the private method {@link + * #myNextShort(int, boolean)} is called, and if no error occurs the + * next string could be a short. + * + * @see #nextShort(int) + * @param radix + * The radix to use for this check. The global radix of the Scanner will not be + * changed. + * @return true if the next string is a short number. + * @throws IllegalStateException + * if the Scanner is closed. + */ + public boolean hasNextShort (final int radix) throws IllegalStateException + { + try + { + myNextShort (radix, false); + return true; + } + catch (InputMismatchException ime) + { + return false; + } + } + + /** + * Returns the last {@link IOException} occured. + * + * @return Returns the last {@link IOException}. + */ + public IOException ioException () + { + return this.lastIOException; + } + + /** + * Returns the current value of {@link #useLocale}. This is used to + * tell the Scanner if it should use the Locale format or just + * handle numbers of the default format. + * + * @see #setUseLocale(boolean) + * @return the useLoclae. + */ + public boolean isUseLocale () // TESTED + { + return this.useLocale; + } + + /** + * Returns the current Locale. It is initialized with {@link + * Locale#getDefault()}. + * + * @see #useLocale(Locale) + * @return Returns the current Locale. + */ + public Locale locale () // TESTED + { + return this.actLocale; + } + + /** + * Returns the last MatchResult found. This is updated after every + * successfully search. + * + * @return Returns the last {@link MatchResult} found. + */ + public MatchResult match () // TESTED + { + return this.actResult; + } + + /** + * Uses the current delimiter to find the next string in the + * buffer. If a string is found the current position is set after + * the delimiter, otherwise a {@link NoSuchElementException} is + * thrown. A successful match sets the matchResult. + * + * @see #match() + * @return Returns the next string of the buffer. + * @throws NoSuchElementException + * If no element was found an exception is thrown. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public String next () throws NoSuchElementException, IllegalStateException // TESTED + { + return myCoreNext (true, this.p); + } + + /** + * Tries to match the buffer with the given pattern. The current + * delimiter will not be changed. + * + * @param pattern + * The pattern to match. + * @return Returns the next string matching the pattern. + * @throws NoSuchElementException + * If no element was found an exception is thrown. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public String next (final Pattern pattern) throws NoSuchElementException, IllegalStateException // TESTED + { + return myNext (pattern, true); + } + + /** + * Tries to match the buffer with the given pattern. The current + * delimiter will not be changed. Calls the {@link #next(Pattern)} + * with the compiled pattern. + * + * @see #next(Pattern) + * @param pattern + * The pattern to match. + * @return Returns the next string matching the pattern. + * @throws NoSuchElementException + * If no element was found an exception is thrown. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public String next (final String pattern) throws NoSuchElementException, IllegalStateException // TESTED + { + return next (Pattern.compile (pattern)); + } + + /** + * Tries to interpret the next string as a BigDecimal value. + * + * @return Returns the BigDecimal value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a BigDecimal. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public BigDecimal nextBigDecimal () throws NoSuchElementException, IllegalStateException // TESTED + { + return myBigDecimal (true); + } + + /** + * Tries to interpret the next string as a BigInteger value. Call + * {@link #nextBigInteger(int)} with the current radix as parameter, + * and return the value. + * + * @see #nextBigInteger(int) + * @return Returns the BigInteger value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a BigInteger. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public BigInteger nextBigInteger () throws NoSuchElementException, IllegalStateException // TESTED + { + return nextBigInteger (this.currentRadix); + } + + /** + * Tries to interpret the next string as a BigInteger value with the + * given radix. + * + * @param radix + * The radix to be used for this BigInteger. The current radix of the Scanner is not + * changed. + * @return Returns the BigInteger value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a BigInteger. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public BigInteger nextBigInteger (final int radix) throws + NoSuchElementException, IllegalStateException + { + return myNextBigInteger (radix, true, BIG_INTEGER); + } + + /** + * Tries to interpret the next string to the delimiter as a boolean + * value, ignoring case. + * + * @return Returns the boolean value of the next matching string or throws an exception. + * @throws NoSuchElementException + * If no string is found or the string is not a boolean. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public boolean nextBoolean () throws NoSuchElementException, IllegalStateException // TESTED + { + return myNextBoolean (true); + } + + /** + * Tries to interpret the next string as a byte value. Call {@link + * #nextByte(int)} with the current radix as parameter, and return + * the value. + * + * @see #nextByte(int) + * @return Returns the byte value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a byte + * @throws IllegalStateException + * If the Scanner is closed. + */ + public byte nextByte () throws NoSuchElementException, IllegalStateException // TESTED + { + return nextByte (this.currentRadix); + } + + /** + * Tries to interpret the next string as a byte value with the given + * radix. + * + * @param radix + * The radix to be used for this byte. The current radix of the Scanner is not + * changed. + * @return Returns the byte value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a byte. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public byte nextByte (final int radix) throws NoSuchElementException, + IllegalStateException + { + return myNextByte (radix, true); + } + + /** + * Tries to interpret the next string as a double value. + * + * @return Returns the int value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a double. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public double nextDouble () throws NoSuchElementException, IllegalStateException // TESTED + { + return myNextDouble (true); + } + + /** + * Tries to interpret the next string as a double value, and then + * casts down to float. + * + * @return Returns the int value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a double. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public float nextFloat () throws NoSuchElementException, IllegalStateException // TESTED + { + return (float) myNextDouble (true); + // return myNextFloat(true); + } + + /** + * Tries to interpret the next string as an int value. Calls {@link + * #nextInt(int)} with the current radix as parameter, and return + * the value. + * + * @see #nextInt(int) + * @return Returns the int value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not an int. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public int nextInt () throws NoSuchElementException, IllegalStateException // TESTED + { + return nextInt (this.currentRadix); + } + + /** + * Tries to interpret the next string as an int value with the given + * radix. + * + * @param radix + * The radix to be used for this int. The current radix of the Scanner is not changed + * @return Returns the int value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not an int. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public int nextInt (final int radix) throws NoSuchElementException, + IllegalStateException + { + return myNextInt (radix, true); + } + + /** + * Tries to match the system line seperator, and returns the current + * line. + * + * @return Returns the current line. + * @throws NoSuchElementException + * If the current delimiter is not found. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public String nextLine () throws NoSuchElementException, IllegalStateException // TESTED + { + return myNextLine (true); + } + + /** + * Tries to interpret the next string as a long value. Calls {@link + * #nextLong(int)} with the current radix as parameter, and return + * the value. + * + * @see #nextLong(int) + * @return Returns the long value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a long. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public long nextLong () throws NoSuchElementException, IllegalStateException // TESTED + { + return nextLong (this.currentRadix); + } + + /** + * Tries to interpret the next string as a long value with the given + * radix. + * + * @param radix + * The radix to be used for this long. The current radix of the Scanner is not + * changed + * @return Returns the long value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a long. + * @throws IllegalStateException + * If the Scanner is closed. + */ + public long nextLong (final int radix) throws NoSuchElementException, + IllegalStateException + { + return myNextLong (radix, true); + } + + /** + * Tries to interpret the next string as a short value. Calls {@link + * #nextShort(int)} with the current radix as parameter, and return + * the value. + * + * @see #nextShort(int) + * @return Returns the short value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a short. + */ + public short nextShort () throws NoSuchElementException // TESTED + { + return nextShort (this.currentRadix); + } + + /** + * Tries to interpret the next string as a short value with the + * given radix. + * + * @param radix + * The radix to be used for this short. The current radix of the Scanner is not + * changed. + * @return Returns the short value of the next string. + * @throws NoSuchElementException + * If no string is found or the string is not a short. + */ + public short nextShort (final int radix) throws NoSuchElementException + { + return myNextShort (radix, true); + } + + /** + * @return Returns the current radix. + */ + public int radix () + { + return this.currentRadix; + } + + /** + * The remove operation is not supported by this implementation of + * Iterator. + */ + public void remove () + { + } + + /** + * @param useLocale the useLocale to set. + */ + public void setUseLocale (final boolean useLocale) // TESTED + { + this.useLocale = useLocale; + } + + /** + * Skips the given pattern. Sets skipped true. + * + * @param pattern + * Pattern which should be skipped. + * @return this with the skipped buffer. + * @throws NoSuchElementException + * If the Pattern is not found. + */ + public Scanner skip (final Pattern pattern) throws NoSuchElementException + { + this.doSkipp = true; + int end; + boolean found; + Matcher matcher = pattern.matcher (this.actBuffer); + matcher.region (this.actPos - 1, this.actBuffer.length ()); + + found = matcher.find (); + found = myFillBuffer_loop (matcher, this.actPos - 1, found); + end = matcher.end (); + + this.actPos = end + 1; + + this.doSkipp = false; + this.skipped = true; + + actResult = null; + + if (!found) + { + throw new NoSuchElementException (); + } + return this; + } + + /** + * Skips a given pattern. Calls {@link #skip(Pattern)} with the + * compiled pattern. + * + * @see #skip(Pattern) + * @param pattern + * Pattern which should be skipped. + * @return this with the skipped buffer. + */ + public Scanner skip (final String pattern) + { + return skip (Pattern.compile (pattern)); + } + + /** + * Returns the string representation of this Scanner. + */ + @Override + public String toString () + { + String tmpStr2; + String rc = this.getClass ().getName (); + tmpStr2 = rc; + tmpStr2 = "[delimiters=" + this.p.pattern () + "]"; + rc += tmpStr2; + tmpStr2 = "[position=" + (this.procesedChars + this.actPos) + "]"; + rc += tmpStr2; + tmpStr2 = "[match valid=" + this.matchValid + "]"; + rc += tmpStr2; + tmpStr2 = "[need input=" + this.needInput + "]"; + rc += tmpStr2; + tmpStr2 = "[source closed=" + this.isClosed + "]"; + rc += tmpStr2; + tmpStr2 = "[skipped=" + this.skipped + "]"; + rc += tmpStr2; + tmpStr2 = "[group separator=\\" + this.dfs.getGroupingSeparator () + "]"; + rc += tmpStr2; + tmpStr2 = "[decimal separator=\\" + this.dfs.getDecimalSeparator () + "]"; + rc += tmpStr2; + tmpStr2 = + "[positive prefix=" + myConvert (this.df.getPositivePrefix ()) + "]"; + rc += tmpStr2; + tmpStr2 = + "[negative prefix=" + myConvert (this.df.getNegativePrefix ()) + "]"; + rc += tmpStr2; + tmpStr2 = + "[positive suffix=" + myConvert (this.df.getPositiveSuffix ()) + "]"; + rc += tmpStr2; + tmpStr2 = + "[negative suffix=" + myConvert (this.df.getNegativeSuffix ()) + "]"; + rc += tmpStr2; + tmpStr2 = "[NaN string=" + myConvert (this.dfs.getNaN ()) + "]"; + rc += tmpStr2; + tmpStr2 = "[infinity string=" + myConvert (this.dfs.getInfinity ()) + "]"; + rc += tmpStr2; + return rc; + } + + /** + * Sets the current pattern to the given parameter, and updates the + * {@link Matcher} with the new pattern. + * + * @param pattern + * The new pattern to use. + * @return Returns the Scanner (this) with the new pattern. + */ + public Scanner useDelimiter (final Pattern pattern) // TESTED + { + if (pattern != null) + { + this.p = pattern; + this.myMatcher = this.p.matcher (this.actBuffer); + } + return this; + } + + /** + * Sets the current pattern to the given parameter. Compiles the + * pattern and calls {@link #useDelimiter(Pattern)} + * + * @see #useDelimiter(Pattern) + * @param pattern + * The new pattern to use. + * @return Returns the Scanner (this) with the new pattern. + */ + public Scanner useDelimiter (final String pattern) // TESTED + { + return useDelimiter (Pattern.compile (pattern)); + } + + /** + * Sets the current Locale to the given parameter. Formats and + * Symbols are also set using the new Locale. + * + * @param locale The new Locale to use. If it is null + * nothing happens. + * @return Returns the Scanner (this) with the new Locale. + */ + public Scanner useLocale (final Locale locale) // TESTED + { + if (locale != null) + { + this.actLocale = locale; + this.actFormat = NumberFormat.getInstance (this.actLocale); + this.dfs = new DecimalFormatSymbols (this.actLocale); + this.df = (DecimalFormat) this.actFormat; + } + return this; + } + + /** + * Sets the current radix to the current value if the given radix is + * >= 2 and <= 36 otherwise an {@link IllegalArgumentException} is + * thrown. + * + * @param radix + * the new radix to use as default. + * @return this with the new radix value. + * @throws IllegalArgumentException + * When the given radix is out of bounds. + */ + public Scanner useRadix (final int radix) throws IllegalArgumentException + { + if (radix < 2 || radix > 36) + { + throw new IllegalArgumentException (); + } + this.currentRadix = radix; + return this; + } + + /** + * Checks if it is necessary to apply the current Locale on the + * String. If so the String is converted using the {@link + * NumberFormat#parse(String)} into a Number and then back to a + * default stringrepresentation of that Number. + * + * @see #setUseLocale(boolean) + * @param str + * String to convert into another string. + * @param radix Radix of the Number in the original string. It has + * to be 10 for anything to happen. + * @return Eighter the Stringrepresention of the number without the + * Locale or an unchanged string. + * @throws ParseException + * if {@link NumberFormat#parse(String)} fails to parse. + */ + private String myApplyLocale (final String str, + final int radix) throws ParseException + { + String rc; + + if (this.useLocale && radix == 10) + { + rc = this.actFormat.parse (str).toString (); + return rc; + } + + return str; + } + + /** + * If {@link #useLocale} is set and radix is 10 the string is tryed + * to be converted to string without Locale settings, because the + * "normal" convert from Local has only double precision and it is + * not enough for the about 50 digits of precision of the + * BigDecimal. So in the first step the string is seperated into the + * integer part which is converted to a long, and the fraction part + * is appended afterwards. Between the integer and the fraction part + * comes a ".". Finally the resulting string is returned. + * + * @see #setUseLocale(boolean) + * @param str String representation of a BigDecimal number. + * @return The default String representation (without Locale) of the + * BigInteger. + * @throws ParseException + * If the String has more than one decimal seperators a parse exception is thrown. + */ + private String myApplyLocaleBD (final String str) throws ParseException + { + if (!this.useLocale || this.currentRadix != 10) + { + return str; + } + + String negPrefix = this.df.getNegativePrefix (); + String negSuffix = this.df.getNegativeSuffix (); + String posPrefix = this.df.getPositivePrefix (); + String posSuffix = this.df.getPositiveSuffix (); + + char d = this.dfs.getDecimalSeparator (); + int begin1, begin2; + boolean isNegativ = false; + String parts = null; + + String tmpStr1 = ""; + + begin1 = str.indexOf (d); + begin2 = str.indexOf (d, begin1 + 1); + + if (begin2 > 0) + { + throw new ParseException ("more than one Decimal seperators", begin2); + } + + parts = str.substring (0, begin1); + + if ((negPrefix.length () > 0 + && str.substring (0, negPrefix.length ()).equals (negPrefix)) + || (negSuffix.length () > 0 + && str.substring (str.length () - + negSuffix.length ()).equals (negSuffix))) + { + parts += negSuffix; + isNegativ = true; + } + else + if ((posPrefix.length () > 0 + && str.substring (0, posPrefix.length ()).equals (posPrefix)) + || (posSuffix.length () > 0 + && str.substring (str.length () - + posSuffix.length ()).equals (posSuffix))) + { + parts += posSuffix; + } + + tmpStr1 = this.actFormat.parse (parts).toString (); + + if (isNegativ) + { + tmpStr1 += + "." + str.substring (str.indexOf (d) + 1, + str.length () - negSuffix.length ()); + } + else + { + tmpStr1 += + "." + str.substring (str.indexOf (d) + 1, + str.length () - posSuffix.length ()); + } + + return tmpStr1; + } + + /** + * Tries to interpret the next String as a BigDecimal. Therfore the + * next String is get with {@link #myCoreNext(boolean, Pattern)} and + * then {@link #myApplyLocaleBD(String)} is called to convert the + * String into a BigDecimal. + * + * @param delete + * Should the found string be deleted or not. + * @return Returns the BigDecimal value of the next string. + * @throws InputMismatchException + * If the string is not a BigDecimal + */ + private BigDecimal myBigDecimal (final boolean delete) throws + InputMismatchException + { + BigDecimal rc; + String tmp = myCoreNext (delete, this.p); + try + { + tmp = myApplyLocaleBD (tmp); + } + catch (ParseException e) + { + throw new InputMismatchException (ERR_PREFIX + tmp + IS_NOT + + "BigDecimal!!"); + } + rc = new BigDecimal (tmp); + + return rc; + } + + /** + * Applies suffix ("\E") and prefix ("\Q") if str.length != 0 Used + * by the toString method. + * + * @param str + * the string on which the suffix and prefix should be applied. + * @return The new new string with the suffix and prefix. + */ + private String myConvert (final String str) + { + if (str != null && str.length () > 0) + { + return "\\Q" + str + "\\E"; + } + return str; + } + + /** + * Searches the current Matcher for the current Pattern. If the end + * is reached during the search it tried to read again from the + * source. The search results are always saved in {@link #actResult} + * which is returned when match() is called. If doSkip is true the + * pattern is also taken. + * + * @param delete + * if true the aktPos is set. + * @param pattern + * pattern to search for. + * @return Returns the String which matches the pattern. + * @throws NoSuchElementException + * If the search has no result. + */ + private String myCoreNext (final boolean delete, final Pattern pattern) + throws NoSuchElementException + { + if (this.isClosed) + { + throw new IllegalStateException ("Scanner closed"); + } + if (shallUseLastFound (pattern != null ? pattern : this.p)) + { + if (this.last_RegionEnd != this.myMatcher.regionEnd ()) + { + System.out.println (this.last_RegionEnd + " != " + + this.myMatcher.regionEnd () + " (" + + (this.last_RegionEnd - + this.myMatcher.regionEnd ()) + ")"); + } + if (delete) + { + this.actPos = this.lastNextPos; + this.lastFoundPresent = false; + this.actResult = this.lastResult; + } + return this.lastFound; + } + + boolean found = false; + int left; + int endIndex; + + String tmp2 = null; + + if (this.actPos > this.MAX_PREFIX) + { + // skipp the processed chars so that the size of the buffer don't grow to much even with + // huge files + this.procesedChars += this.actPos; + this.actBuffer = this.actBuffer.substring (this.actPos); + this.actPos = 0; + this.myMatcher = pattern.matcher (this.actBuffer); + } + + left = this.actBuffer.length () - this.actPos; + if (left < this.MIN_BUF_LEN) + { + myFillBuffer (); + } + found = this.myMatcher.find (this.actPos); + + found = myFillBuffer_loop (this.myMatcher, this.actPos, found); + + this.needInput = false; + + if (found) + { + if (this.doSkipp) + { + endIndex = this.myMatcher.end (); + } + else + { + endIndex = this.myMatcher.start (); + } + tmp2 = this.actBuffer.substring (this.actPos, endIndex); + this.lastNextPos = this.myMatcher.end (); + /* + * if the delete flag is set, just set the current position after the end of the matched + * pattern. + */ + if (delete) + { + this.actPos = this.lastNextPos; + } + else + { + this.lastFound = tmp2; + this.lastFoundPresent = true; + this.lastPatternHash = pattern.hashCode (); + } + this.last_RegionStart = this.myMatcher.regionStart (); + this.last_RegionEnd = this.myMatcher.regionEnd (); + this.last_anchor = this.myMatcher.hasAnchoringBounds (); + this.last_transparent = this.myMatcher.hasTransparentBounds (); + } + else if (this.myMatcher.hitEnd ()) + // the end of input is matched + { + tmp2 = this.actBuffer.substring (this.actPos); + if (tmp2.length() == 0) + tmp2 = null; + this.lastNextPos = this.actBuffer.length (); + if (delete) + { + this.actPos = this.lastNextPos; + } + else + { + this.lastFound = tmp2; + this.lastFoundPresent = true; + this.lastPatternHash = pattern.hashCode (); + } + this.last_RegionStart = this.myMatcher.regionStart (); + this.last_RegionEnd = this.myMatcher.regionEnd (); + this.last_anchor = this.myMatcher.hasAnchoringBounds (); + this.last_transparent = this.myMatcher.hasTransparentBounds (); + } + else + { + /* + * if no match found an Exception is throwed + */ + throw new NoSuchElementException (); + } + /* + * change the Result only when a nextXXX() method was called, not if a hasNextXXX() method + * is called + */ + if (delete) + { + this.actResult = this.myMatcher.toMatchResult (); + + this.matchValid = this.actResult != null; + } + else + { + this.lastResult = this.myMatcher.toMatchResult (); + } + + this.skipped = this.doSkipp; + this.doSkipp = false; + + return tmp2; + } + + /** + * Used to fill the String buffer from a source. Therfore the 3 + * possible sources are checked if they are not null + * and this not used, otherwise the read method is called on the + * source. If a charsetName is set and not null it is + * applied to convert to String. + */ + private void myFillBuffer () + { + int len; + String tmpStr; + CharBuffer cb = null; + ByteBuffer bb = null; + + if (this.bIS != null) + { + try + { + len = this.bIS.read (this.tmpBuffer); + if (len < 0) + { + return; + } + if (this.charsetName != null) + { + tmpStr = new String (this.tmpBuffer, 0, len, this.charsetName); + } + else + { + tmpStr = new String (this.tmpBuffer, 0, len); + } + this.actBuffer += tmpStr; + } + catch (IOException e) + { + this.lastIOException = e; + } + } + else if (this.readableSource != null) + { + try + { + cb = CharBuffer.allocate (1000); + this.needInput = true; + len = this.readableSource.read (cb); + if (len < 0) + { + return; + } + this.needInput = false; + tmpStr = new String (cb.array ()); + this.actBuffer += tmpStr; + } + catch (IOException e) + { + this.lastIOException = e; + } + } + else if (this.rbcSource != null) + { + try + { + bb = ByteBuffer.allocate (1000); + this.needInput = true; + len = this.rbcSource.read (bb); + this.needInput = false; + if (len < 0) + { + return; + } + if (this.charsetName != null) + { + tmpStr = new String (bb.array (), 0, len, this.charsetName); + } + else + { + tmpStr = new String (bb.array (), 0, len); + } + this.actBuffer += tmpStr; + } + catch (IOException e) + { + this.lastIOException = e; + } + } + + this.myMatcher.reset (this.actBuffer); + } + + /** + * A loop in which the {@link #myFillBuffer()} is called and checked + * if the pattern is found in the matcher and if the buffersize + * changes after the read. + * + * @param aktM + * The current Matcher. + * @param pos + * Position from which the matcher should start matching. + * @param found + * if already found. + * @return true if the matcher has found a match. + */ + private boolean myFillBuffer_loop (final Matcher aktM, final int pos, + boolean found) + { + int tmp; + + tmp = this.actBuffer.length (); + while (aktM.hitEnd () + && ((this.bIS != null) || (this.readableSource != null) + || (this.rbcSource != null))) + { + myFillBuffer (); + if (tmp == this.actBuffer.length ()) + { + break; + } + found = aktM.find (pos); + this.needInput = true; + } + return found; + } + + /** + * Used to find the given pattern in the given string before the + * given horizon. Therfore the current matcher is copied, and + * overwritten using the given pattern and the given Sting.
        + * After the search the original values are restored, and skipped is + * set true . + * + * @param pattern + * Pattern which should be matched. + * @param str + * The String in which the pattern should be matched. + * @param horizon + * the horizon whithin the match should be, if 0 then it is ignored. + * @return Returns the String in the given String that matches the pattern. + */ + private String myFindPInStr (final Pattern pattern, final String str, + final int horizon) + { + String rc = null; + int curPos = this.actPos; + Matcher aktMatcher = this.myMatcher; + + this.myMatcher = pattern.matcher (str); + if (horizon > 0) + { + this.myMatcher.useAnchoringBounds (true); + this.myMatcher.useTransparentBounds (true); + this.myMatcher.region (this.actPos, this.actPos + horizon); + } + rc = myCoreNext (true, pattern); + this.myMatcher = aktMatcher; + + this.actPos = curPos; + this.skipped = true; + + return rc; + } + + /** + * Used by the {@link #hasNext(Pattern)} and {@link #next(Pattern)} + * methods. Therfore a substring is taken first to the current + * delimiter, afterwards the given pattern is searched in this + * subsring.
        Finally the current Buffer and matcher (which have + * been temporarily changed) are set back.

        The {@link + * #skipped} is set true . + * + * @param pattern + * Pattern to find until the current delimiter. + * @param delete + * Is true if a next method is called.
        + * Is false if a hasNext method is called. + * @return Returns the String which is returned by the public methods. + */ + private String myNext (final Pattern pattern, final boolean delete) + { + String tmpStr; + Matcher aktMatcher = this.myMatcher; + String result; + String currBuffer = this.actBuffer; + int currAktPos; + + tmpStr = myCoreNext (delete, this.p); + this.myMatcher = pattern.matcher (tmpStr); + this.actBuffer = tmpStr; + currAktPos = this.actPos; + this.actPos = 0; + result = myCoreNext (delete, pattern); + this.actPos = currAktPos; + + this.actBuffer = currBuffer; + this.myMatcher = aktMatcher; + this.skipped = true; + + return result; + } + + /** + * Calls the next() method internally to get the next String, and + * trys to apply a locale which is only applied if the radix is 10 + * and useLocale is true . Afterwards it is tried to + * call the Constructor of a {@link BigInteger} with the given + * radix. + * + * @param radix The radix to use. + * @param delete If the found String should be removed from input or + * not. + * @param name name of "BigInteger" in case of an Error. + * @return Returns the new BigInteger created if there is no Error. + * @throws InputMismatchException + * If there is a {@link ParseException} or a {@link NumberFormatException}. + */ + private BigInteger myNextBigInteger (final int radix, final boolean delete, + final String name) + { + BigInteger rc; + String tmp = myPrepareForNext (this.p, delete); + + try + { + tmp = myApplyLocale (tmp, radix); + rc = new BigInteger (tmp, radix); + return rc; + } + catch (NumberFormatException nfe) + { + } + catch (ParseException e) + { + } + throw new InputMismatchException (ERR_PREFIX + tmp + IS_NOT + name); + } + + /** + * Checks if the next String is either "true" or "false", otherwise + * an {@link InputMismatchException} is thrown. It ignores the case + * of the string so that "true" and "TRUE" and even "TrUe" are + * accepted. + * + * @param delete Should the found value be removed from the input or + * not. + * @return Returns the boolean value (if it is a boolean). + * @throws InputMismatchException + * If the next String is not a boolean. + */ + private boolean myNextBoolean (final boolean delete) throws + InputMismatchException + { + String tmp = myPrepareForNext (this.p, delete); + if (tmp.equalsIgnoreCase ("true")) + { + return true; + } + else if (tmp.equalsIgnoreCase ("false")) + { + return false; + } + else + { + throw new InputMismatchException (ERR_PREFIX + tmp + NOT_BOOLEAN); + } + } + + /** + * Calls the {@link #myPrepareForNext(Pattern, boolean)} which calls + * the {@link #myCoreNext(boolean, Pattern)} to return the next + * String matching the current delimier. Afterwards it is tryed to + * convert the String into a byte. Any Error will lead into a {@link + * InputMismatchException}. + * + * @param radix The radix to use. + * @param delete Should the found String be removed from the input. + * @return Returns the byte value of the String. + * @throws InputMismatchException if the next String is not a byte. + */ + private byte myNextByte (final int radix, + final boolean delete) throws InputMismatchException + { + byte rc; + String tmp = myPrepareForNext (this.p, delete); + + try + { + tmp = myApplyLocale (tmp, radix); + rc = Byte.parseByte (tmp, radix); + return rc; + } + catch (NumberFormatException nfe) + { + } + catch (ParseException e) + { + } + throw new InputMismatchException (ERR_PREFIX + tmp + NOT_BYTE); + } + + /** + * Tries to interpret the next String as a double value. To verify + * if the double value is correct, it is converted back to a String + * using the default Locale and this String is compared with the + * String from which the double was converted. If the two Strings + * don't match, an {@link InputMismatchException} is thrown.
        + *
        The radix used is always 10 even if the global radix is + * changed. + * + * @param delete Should the String be removed, if true it will be + * also removed if the String is not a double value. + * @return Returns the double value of the next String. + * @throws InputMismatchException if the next String is not a + * double. + */ + private double myNextDouble (final boolean delete) throws + InputMismatchException + { + double rc; + String tmp = myPrepareForNext (this.p, delete); + + try + { + tmp = myApplyLocale (tmp, 10); + rc = Double.parseDouble (tmp); + if (("" + rc).equals (tmp)) + { + return rc; + } + } + catch (ParseException e) + { + } + throw new InputMismatchException (ERR_PREFIX + tmp + NOT_DOUBLE); + } + + /** + * Tries to interpret the next String as an int value. Therfore + * {@link #myApplyLocale(String, int)} decides if the current Locale + * should be applied or not and then the result is parsed using + * {@link Integer#parseInt(String, int)}. Any Error will lead to an + * {@link InputMismatchException}. + * + * @param radix The radix to use. + * @param delete true if the String should be deleted + * from the input. + * @return Returns the int value of the String. + * @throws InputMismatchException if the next String is not an int. + */ + private int myNextInt (final int radix, + final boolean delete) throws InputMismatchException + { + int rc; + String tmp = myPrepareForNext (this.p, delete); + try + { + tmp = myApplyLocale (tmp, radix); + rc = Integer.parseInt (tmp, radix); + return rc; + } + catch (NumberFormatException nfe) + { + } + catch (ParseException e) + { + } + throw new InputMismatchException (ERR_PREFIX + tmp + NOT_INT); + } + + /** + * Finds the next line using the {@link #NEW_LINE} constant which is + * set to the system specific line seperator. + * + * @param delete should the found line be deleted from the input. + * @return the current line. + */ + private String myNextLine (final boolean delete) + { + return myPrepareForNext (Pattern.compile (NEW_LINE), delete); + } + + /** + * Tries to interpret the next String as a long value with the given + * radix. Therfore the {@link Long#parseLong(String, int)} is called + * and every Error will lead into a {@link InputMismatchException}. + * + * @param radix The radix to be used. + * @param delete Should the found String be deleted from the input. + * @return the long value of the next String. + * @throws InputMismatchException if the next String is not a long. + */ + private long myNextLong (final int radix, + final boolean delete) throws InputMismatchException + { + long rc; + String tmp = myPrepareForNext (this.p, delete); + + try + { + tmp = myApplyLocale (tmp, radix); + rc = Long.parseLong (tmp, radix); + return rc; + } + catch (NumberFormatException nfe) + { + } + catch (ParseException e) + { + } + throw new InputMismatchException (ERR_PREFIX + tmp + NOT_LONG); + } + + /** + * Tries to interpret the next String as a short value with the + * given radix. Therfore the {@link Short#parseShort(String, int)} + * is called and every Error will lead into a {@link + * InputMismatchException} . + * + * @param radix + * The radix to be used. + * @param delete + * Should the found String be deleted from the input. + * @return the long value of the next String. + * @throws InputMismatchException + * if the next String is not a short. + */ + private short myNextShort (final int radix, + final boolean delete) throws + InputMismatchException + { + short rc; + String tmp = myPrepareForNext (this.p, delete); + + try + { + tmp = myApplyLocale (tmp, radix); + rc = Short.parseShort (tmp, radix); + return rc; + } + catch (NumberFormatException nfe) + { + } + catch (ParseException e) + { + } + throw new InputMismatchException (ERR_PREFIX + tmp + + "\" is not a short"); + } + + /** + * Sets the current pattern to the given pattern and calls the + * {@link #myCoreNext(boolean, Pattern)}. Finally sets the pattern + * back to its old value. + * + * @param aktPattern Pattern to be used for the next match. + * @param delete Should the found String be deleted or not. + * @return Return the String returned from {@link + * #myCoreNext(boolean, Pattern)}. + */ + private String myPrepareForNext (final Pattern aktPattern, + final boolean delete) + { + + String rc; + Pattern oldPattern = this.p; + useDelimiter (aktPattern); + + rc = myCoreNext (delete, aktPattern); + + useDelimiter (oldPattern); + + return rc; + } + + /** + * Determinates if the last found can be used, so that after a + * hasNextXXX the nextXXX has not to search if nothing has + * changed.
        Used in {@link #myCoreNext(boolean, Pattern)}. + * + * @param aktP The pattern which should be checked. + * @return true if the searchresult is already ready. + */ + private boolean shallUseLastFound (final Pattern aktP) + { + if (this.lastFoundPresent && + this.lastPatternHash == aktP.hashCode () && + this.last_RegionStart == this.myMatcher.regionStart () && + this.last_anchor == this.myMatcher.hasAnchoringBounds () && + this.last_transparent == this.myMatcher.hasTransparentBounds ()) + { + if (this.last_RegionEnd != this.myMatcher.regionEnd ()) + { + int tmpVal = + this.myMatcher.regionEnd () - + this.last_RegionEnd - this.MAX_PREFIX; + if (tmpVal > 0 && tmpVal < 20) + { + this.last_RegionEnd = + this.myMatcher.regionEnd (); + return true; + } + } + else + return true; + } + return false; + } + +} diff --git a/libjava/classpath/java/util/ServiceConfigurationError.java b/libjava/classpath/java/util/ServiceConfigurationError.java new file mode 100644 index 000000000..40d744c23 --- /dev/null +++ b/libjava/classpath/java/util/ServiceConfigurationError.java @@ -0,0 +1,94 @@ +/* ServiceConfigurationError.java -- An error on service loading. + Copyright (C) 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +/** + *

        + * An error thrown when a problem occurs during the loading + * of a service provider by a {@link ServiceLoader}. Such + * an error can occur for a number of reasons: + *

        + *
          + *
        • An I/O error occurs
        • + *
        • The configuration file doesn't meet the specifications
        • + *
        • A listed class can not be found
        • + *
        • A listed class does not implement the service
        • + *
        • A listed class can not be instantiated
        • + *
        + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public class ServiceConfigurationError + extends Error +{ + + /** + * Compatible with JDK 1.6 + */ + private static final long serialVersionUID = 74132770414881L; + + /** + * Constructs a new {@link ServiceConfigurationError} + * with the specified message. + * + * @param message a message describing the error, or + * null if none is required. + */ + public ServiceConfigurationError(String message) + { + super(message); + } + + /** + * Constructs a new {@link ServiceConfigurationError} + * with the specified message and cause. + * + * @param message a message describing the error, or + * null if none is required. + * @param cause the cause of the error, or + * null if this is unknown + * or inappropriate. + */ + public ServiceConfigurationError(String message, + Throwable cause) + { + super(message,cause); + } + +} diff --git a/libjava/classpath/java/util/ServiceLoader.java b/libjava/classpath/java/util/ServiceLoader.java new file mode 100644 index 000000000..bcac0704a --- /dev/null +++ b/libjava/classpath/java/util/ServiceLoader.java @@ -0,0 +1,274 @@ +/* ServiceLoader.java -- Allows loading of plug-in services. + Copyright (C) 2006, 2007 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +import gnu.classpath.ServiceFactory; + +/** + *

        + * Facilities for loading service providers. A service is + * defined by a set of interfaces or abstract classes, and + * a service provider gives a concrete implementation of this. + * Service providers may be installed as part of the runtime + * environment using JAR files in the extension directories, + * or may be simply supplied on the classpath. + *

        + *

        + * In terms of loading a service, the service is defined by + * a single interface or abstract class which the provider + * implements. This may not constitute the entire service, + * but is simply a mechanism by which a provider of the + * service can be loaded and its capabilities determined. + * The variety of possible services means that no more + * requirements are made of the service provider other than + * that it must have an accessible zero argument constructor + * in order to allow an instance to be created. + *

        + *

        + * Service providers are listed in a file named after the + * service type in the directory META-INF/services. + * The file contains a list of classes, and must be encoded + * using UTF-8. Whitespace is ignored. Comments can be + * included by using a '#' prefix; anything occurring + * on the same line after this symbol is ignored. Duplicate classes + * are ignored. + *

        + *

        + * The classes are loaded using the same classloader that was + * queried in order to locate the configuration file. As a result, + * the providers do not need to reside in the same JAR file as the + * resource; they merely have to be accessible to this classloader, + * which may differ from the one that loaded the file itself. + *

        + *

        + * Providers are located and instantiated lazily, as calls to the + * {@link #iterator()} are made. Providers are cached, and those in + * the cache are returned first. The cache may be cleared by calling + * {@link #reload()}. Service loaders always execute in the security + * context of the caller, so ideally calls should be made from a trusted + * source. + *

        + *

        + * Note that this class is not thread-safe, and that strange errors may + * occur as the result of the use of remote URLs occurring on the classpath, + * which lead to erroneous web pages. + *

        + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public final class ServiceLoader + implements Iterable +{ + + /** + * The class of the service provider. + */ + private Class spi; + + /** + * The class loader for the service provider. + */ + private ClassLoader loader; + + /** + * The cache of service providers. + */ + private List cache; + + /** + * The {@link gnu.classpath.ServiceFactory} iterator + * from which providers are obtained. + */ + private Iterator serviceIt; + + /** + * Constructs a new {@link ServiceLoader} with + * the specified provider and class loader. + * + * @param spi the service to load. + * @param loader the class loader to use. + */ + private ServiceLoader(Class spi, ClassLoader loader) + { + this.spi = spi; + this.loader = loader; + cache = new ArrayList(); + } + + /** + * Lazily loads the available providers. The iterator first returns + * providers from the cache, in instantiation order, followed by any + * remaining providers, which are added to the cache after loading. + * The actual loading and parsing of the configuration file takes + * place in the {@link Iterator#hasNext()} and {@link Iterator#next()} + * methods, which means that they may result in a + * {@link ServiceConfigurationError} being thrown. If such an error + * does occur, subsequent invocations will attempt to recover. + * The {@link remove()} method is not supported and instead throws + * an {@link UnsupportedOperationException}. + * + * @return an iterator that lazily loads service providers. + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * The cache iterator. + */ + private Iterator cacheIt = cache.iterator(); + + public boolean hasNext() + { + if (cacheIt.hasNext()) + return true; + if (serviceIt == null) + serviceIt = + ServiceFactory.lookupProviders(spi, loader, true); + return serviceIt.hasNext(); + } + + public S next() + { + if (cacheIt.hasNext()) + return cacheIt.next(); + if (serviceIt == null) + serviceIt = + ServiceFactory.lookupProviders(spi, loader, true); + S nextService = serviceIt.next(); + cache.add(nextService); + return nextService; + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Creates a new service loader for the given service, + * using the context class loader of the current thread. + * This is equivalent to calling ServiceLoader.load(service, + * Thread.currentThread().getContextClassLoader()). + * + * @param service the interface or abstract class that represents + * the service. + * @return a new {@link ServiceLoader} instance. + */ + public static ServiceLoader load(Class service) + { + return load(service, + Thread.currentThread().getContextClassLoader()); + } + + /** + * Creates a new service loader for the given service, + * using the specified class loader. The class loader is + * used to access the configuration file and the service + * provider instances themselves. If the loader is + * null, the system class loader (or, if + * this is also null, the bootstrap class + * loader). + * + * @param service the interface or abstract class that represents + * the service. + * @param loader the class loader used to load the configuration + * file and service providers. + * @return a new {@link ServiceLoader} instance. + */ + public static ServiceLoader load(Class service, + ClassLoader loader) + { + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + return new ServiceLoader(service, loader); + } + + /** + * Creates a new service loader for the given service, + * using the extension class loader. If the extension + * class loader can not be found, the system class loader + * is used (or, if this is null, the + * bootstrap class loader). The primary use of this method + * is to only obtain installed services, ignoring any which + * may appear on the classpath. This is equivalent to calling + * load(service, extClassLoader) where + * extClassLoader is the extension class loader + * (or null if this is unavailable). + * + * @param service the interface or abstract class that represents + * the service. + * @return a new {@link ServiceLoader} instance. + */ + public static ServiceLoader loadInstalled(Class service) + { + /* We expect the extension class loader to be the parent + * of the system class loader, as in + * ClassLoader.getDefaultSystemClassLoader() */ + return load(service, + ClassLoader.getSystemClassLoader().getParent()); + } + + /** + * Clears the cache of the provider, so that all providers + * are again read from the configuration file and instantiated. + */ + public void reload() + { + cache.clear(); + } + + /** + * Returns a textual representation of this + * {@link ServiceLoader}. + * + * @return a textual representation of the + * service loader. + */ + public String toString() + { + return getClass().getName() + + "[spi=" + spi + + ",loader=" + loader + + "]"; + } + +} diff --git a/libjava/classpath/java/util/Set.java b/libjava/classpath/java/util/Set.java new file mode 100644 index 000000000..35f75b5e7 --- /dev/null +++ b/libjava/classpath/java/util/Set.java @@ -0,0 +1,265 @@ +/* Set.java -- A collection that prohibits duplicates + Copyright (C) 1998, 2001, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A collection that contains no duplicates. In other words, for two set + * elements e1 and e2, e1.equals(e2) returns false. There + * are additional stipulations on add, equals + * and hashCode, as well as the requirements that constructors + * do not permit duplicate elements. The Set interface is incompatible with + * List; you cannot implement both simultaneously. + *

        + * + * Note: Be careful about using mutable objects in sets. In particular, + * if a mutable object changes to become equal to another set element, you + * have violated the contract. As a special case of this, a Set is not + * allowed to be an element of itself, without risking undefined behavior. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see SortedSet + * @see HashSet + * @see TreeSet + * @see LinkedHashSet + * @see AbstractSet + * @see Collections#singleton(Object) + * @see Collections#EMPTY_SET + * @since 1.2 + * @status updated to 1.4 + */ +public interface Set extends Collection +{ + /** + * Adds the specified element to the set if it is not already present + * (optional operation). In particular, the comparison algorithm is + * o == null ? e == null : o.equals(e). Sets need not permit + * all values, and may document what exceptions will be thrown if + * a value is not permitted. + * + * @param o the object to add + * @return true if the object was not previously in the set + * @throws UnsupportedOperationException if this operation is not allowed + * @throws ClassCastException if the class of o prevents it from being added + * @throws IllegalArgumentException if some aspect of o prevents it from + * being added + * @throws NullPointerException if null is not permitted in this set + */ + boolean add(E o); + + /** + * Adds all of the elements of the given collection to this set (optional + * operation). If the argument is also a Set, this returns the mathematical + * union of the two. The behavior is unspecified if the set is + * modified while this is taking place. + * + * @param c the collection to add + * @return true if the set changed as a result + * @throws UnsupportedOperationException if this operation is not allowed + * @throws ClassCastException if the class of an element prevents it from + * being added + * @throws IllegalArgumentException if something about an element prevents + * it from being added + * @throws NullPointerException if null is not permitted in this set, or + * if the argument c is null + * @see #add(Object) + */ + boolean addAll(Collection c); + + /** + * Removes all elements from this set (optional operation). This set will + * be empty afterwords, unless an exception occurs. + * + * @throws UnsupportedOperationException if this operation is not allowed + */ + void clear(); + + /** + * Returns true if the set contains the specified element. In other words, + * this looks for o == null ? e == null : o.equals(e). + * + * @param o the object to look for + * @return true if it is found in the set + * @throws ClassCastException if the type of o is not a valid type + * for this set. + * @throws NullPointerException if o is null and this set doesn't + * support null values. + */ + boolean contains(Object o); + + /** + * Returns true if this set contains all elements in the specified + * collection. If the argument is also a set, this is the subset + * relationship. + * + * @param c the collection to check membership in + * @return true if all elements in this set are in c + * @throws NullPointerException if c is null + * @throws ClassCastException if the type of any element in c is not + * a valid type for this set. + * @throws NullPointerException if some element of c is null and this + * set doesn't support null values. + * @see #contains(Object) + */ + boolean containsAll(Collection c); + + /** + * Compares the specified object to this for equality. For sets, the object + * must be a set, the two must have the same size, and every element in + * one must be in the other. + * + * @param o the object to compare to + * @return true if it is an equal set + */ + boolean equals(Object o); + + /** + * Returns the hash code for this set. In order to satisfy the contract of + * equals, this is the sum of the hashcode of all elements in the set. + * + * @return the sum of the hashcodes of all set elements + * @see #equals(Object) + */ + int hashCode(); + + /** + * Returns true if the set contains no elements. + * + * @return true if the set is empty + */ + boolean isEmpty(); + + /** + * Returns an iterator over the set. The iterator has no specific order, + * unless further specified. + * + * @return a set iterator + */ + Iterator iterator(); + + /** + * Removes the specified element from this set (optional operation). If + * an element e exists, o == null ? e == null : o.equals(e), + * it is removed from the set. + * + * @param o the object to remove + * @return true if the set changed (an object was removed) + * @throws UnsupportedOperationException if this operation is not allowed + * @throws ClassCastException if the type of o is not a valid type + * for this set. + * @throws NullPointerException if o is null and this set doesn't allow + * the removal of a null value. + */ + boolean remove(Object o); + + /** + * Removes from this set all elements contained in the specified collection + * (optional operation). If the argument is a set, this returns the + * asymmetric set difference of the two sets. + * + * @param c the collection to remove from this set + * @return true if this set changed as a result + * @throws UnsupportedOperationException if this operation is not allowed + * @throws NullPointerException if c is null + * @throws ClassCastException if the type of any element in c is not + * a valid type for this set. + * @throws NullPointerException if some element of c is null and this + * set doesn't support removing null values. + * @see #remove(Object) + */ + boolean removeAll(Collection c); + + /** + * Retains only the elements in this set that are also in the specified + * collection (optional operation). If the argument is also a set, this + * performs the intersection of the two sets. + * + * @param c the collection to keep + * @return true if this set was modified + * @throws UnsupportedOperationException if this operation is not allowed + * @throws NullPointerException if c is null + * @throws ClassCastException if the type of any element in c is not + * a valid type for this set. + * @throws NullPointerException if some element of c is null and this + * set doesn't support retaining null values. + * @see #remove(Object) + */ + boolean retainAll(Collection c); + + /** + * Returns the number of elements in the set. If there are more + * than Integer.MAX_VALUE mappings, return Integer.MAX_VALUE. This is + * the cardinality of the set. + * + * @return the number of elements + */ + int size(); + + /** + * Returns an array containing the elements of this set. If the set + * makes a guarantee about iteration order, the array has the same + * order. The array is distinct from the set; modifying one does not + * affect the other. + * + * @return an array of this set's elements + * @see #toArray(Object[]) + */ + Object[] toArray(); + + /** + * Returns an array containing the elements of this set, of the same runtime + * type of the argument. If the given set is large enough, it is reused, + * and null is inserted in the first unused slot. Otherwise, reflection + * is used to build a new array. If the set makes a guarantee about iteration + * order, the array has the same order. The array is distinct from the set; + * modifying one does not affect the other. + * + * @param a the array to determine the return type; if it is big enough + * it is used and returned + * @return an array holding the elements of the set + * @throws ArrayStoreException if the runtime type of a is not a supertype + * of all elements in the set + * @throws NullPointerException if a is null + * @see #toArray() + */ + T[] toArray(T[] a); +} diff --git a/libjava/classpath/java/util/SimpleTimeZone.java b/libjava/classpath/java/util/SimpleTimeZone.java new file mode 100644 index 000000000..6b3b55f17 --- /dev/null +++ b/libjava/classpath/java/util/SimpleTimeZone.java @@ -0,0 +1,1052 @@ +/* java.util.SimpleTimeZone + Copyright (C) 1998, 1999, 2000, 2003, 2004, 2005, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + + +/** + * This class represents a simple time zone offset and handles + * daylight savings. It can only handle one daylight savings rule, so + * it can't represent historical changes. + * + * This object is tightly bound to the Gregorian calendar. It assumes + * a regular seven days week, and the month lengths are that of the + * Gregorian Calendar. It can only handle daylight savings for years + * lying in the AD era. + * + * @see Calendar + * @see GregorianCalendar + * @author Jochen Hoenicke + */ +public class SimpleTimeZone extends TimeZone +{ + /** + * The raw time zone offset in milliseconds to GMT, ignoring + * daylight savings. + * @serial + */ + private int rawOffset; + + /** + * True, if this timezone uses daylight savings, false otherwise. + * @serial + */ + private boolean useDaylight; + + /** + * The daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an hour. + * @serial + * @since JDK1.1.4 + */ + private int dstSavings = 60 * 60 * 1000; + + /** + * The first year, in which daylight savings rules applies. + * @serial + */ + private int startYear; + private static final int DOM_MODE = 1; + private static final int DOW_IN_MONTH_MODE = 2; + private static final int DOW_GE_DOM_MODE = 3; + private static final int DOW_LE_DOM_MODE = 4; + + /** + * The mode of the start rule. This takes one of the following values: + *

        + *
        DOM_MODE (1)
        + *
        startDay contains the day in month of the start date, + * startDayOfWeek is unused.
        + *
        DOW_IN_MONTH_MODE (2)
        + *
        The startDay gives the day of week in month, and + * startDayOfWeek the day of week. For example startDay=2 and + * startDayOfWeek=Calender.SUNDAY specifies that the change is on + * the second sunday in that month. You must make sure, that this + * day always exists (ie. don't specify the 5th sunday). + *
        + *
        DOW_GE_DOM_MODE (3)
        + *
        The start is on the first startDayOfWeek on or after + * startDay. For example startDay=13 and + * startDayOfWeek=Calendar.FRIDAY specifies that the daylight + * savings start on the first FRIDAY on or after the 13th of that + * Month. Make sure that the change is always in the given month, or + * the result is undefined. + *
        + *
        DOW_LE_DOM_MONTH (4)
        + *
        The start is on the first startDayOfWeek on or before the + * startDay. Make sure that the change is always in the given + * month, or the result is undefined. +
        + *
        + * @serial */ + private int startMode; + + /** + * The month in which daylight savings start. This is one of the + * constants Calendar.JANUARY, ..., Calendar.DECEMBER. + * @serial + */ + private int startMonth; + + /** + * This variable can have different meanings. See startMode for details + * @see #startMode + * @serial + */ + private int startDay; + + /** + * This variable specifies the day of week the change takes place. If + * startMode == DOM_MODE, this is undefined. + * @serial + * @see #startMode + */ + private int startDayOfWeek; + + /** + * This variable specifies the time of change to daylight savings. + * This time is given in milliseconds after midnight in startTimeMode + * chosen time mode. + * @serial + */ + private int startTime; + + /** + * This variable specifies the mode that startTime is specified in. By + * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. For + * startTime, STANDARD_TIME and WALL_TIME are equivalent. + * @serial + */ + private int startTimeMode = WALL_TIME; + + /** + * The month in which daylight savings ends. This is one of the + * constants Calendar.JANUARY, ..., Calendar.DECEMBER. + * @serial + */ + private int endMonth; + + /** + * This variable gives the mode for the end of daylight savings rule. + * It can take the same values as startMode. + * @serial + * @see #startMode + */ + private int endMode; + + /** + * This variable can have different meanings. See startMode for details + * @serial + * @see #startMode + */ + private int endDay; + + /** + * This variable specifies the day of week the change takes place. If + * endMode == DOM_MODE, this is undefined. + * @serial + * @see #startMode + */ + private int endDayOfWeek; + + /** + * This variable specifies the time of change back to standard time. + * This time is given in milliseconds after midnight in endTimeMode + * chosen time mode. + * @serial + */ + private int endTime; + + /** + * This variable specifies the mode that endTime is specified in. By + * default it is WALL_TIME, but can also be STANDARD_TIME or UTC_TIME. + * @serial + */ + private int endTimeMode = WALL_TIME; + + /** + * This variable points to a deprecated array from JDK 1.1. It is + * ignored in JDK 1.2 but streamed out for compatibility with JDK 1.1. + * The array contains the lengths of the months in the year and is + * assigned from a private static final field to avoid allocating + * the array for every instance of the object. + * Note that static final fields are not serialized. + * @serial + */ + private byte[] monthLength = monthArr; + private static final byte[] monthArr = + { + 31, 28, 31, 30, 31, 30, 31, 31, 30, + 31, 30, 31 + }; + + /** + * The version of the serialized data on the stream. + *
        + *
        0 or not present on stream
        + *
        JDK 1.1.3 or earlier, only provides this fields: + * rawOffset, startDay, startDayOfWeek, startMonth, startTime, + * startYear, endDay, endDayOfWeek, endMonth, endTime + *
        + *
        JDK 1.1.4 or later. This includes three new fields, namely + * startMode, endMode and dstSavings. And there is a optional section + * as described in writeObject. + *
        + *
        + * + * XXX - JDK 1.2 Beta 4 docu states 1.1.4, but my 1.1.5 has the old + * version. + * + * When streaming out this class it is always written in the latest + * version. + * @serial + * @since JDK1.1.4 + */ + private int serialVersionOnStream = 2; + private static final long serialVersionUID = -403250971215465050L; + + /** + * Constant to indicate that start and end times are specified in standard + * time, without adjusting for daylight savings. + */ + public static final int STANDARD_TIME = 1; + + /** + * Constant to indicate that start and end times are specified in wall + * time, adjusting for daylight savings. This is the default. + */ + public static final int WALL_TIME = 0; + + /** + * Constant to indicate that start and end times are specified in UTC. + */ + public static final int UTC_TIME = 2; + + /** + * Create a SimpleTimeZone with the given time offset + * from GMT and without daylight savings. + * @param rawOffset the time offset from GMT in milliseconds. + * @param id The identifier of this time zone. + */ + public SimpleTimeZone(int rawOffset, String id) + { + this.rawOffset = rawOffset; + setID(id); + useDaylight = false; + startYear = 0; + } + + /** + * Create a SimpleTimeZone with the given time offset + * from GMT and with daylight savings. The start/end parameters + * can have different meaning (replace WEEKDAY with a real day of + * week). Only the first two meanings were supported by earlier + * versions of jdk. + * + *
        + *
        day > 0, dayOfWeek = Calendar.WEEKDAY
        + *
        The start/end of daylight savings is on the day-th + * WEEKDAY in the given month.
        + *
        day < 0, dayOfWeek = Calendar.WEEKDAY
        + *
        The start/end of daylight savings is on the -day-th + * WEEKDAY counted from the end of the month.
        + *
        day > 0, dayOfWeek = 0
        + *
        The start/end of daylight is on the day-th day of + * the month.
        + *
        day > 0, dayOfWeek = -Calendar.WEEKDAY
        + *
        The start/end of daylight is on the first WEEKDAY on or after + * the day-th day of the month. You must make sure that + * this day lies in the same month.
        + *
        day < 0, dayOfWeek = -Calendar.WEEKDAY
        + *
        The start/end of daylight is on the first WEEKDAY on or + * before the -day-th day of the month. You + * must make sure that this day lies in the same month.
        + *
        + * + * If you give a non existing month, a day that is zero, or too big, + * or a dayOfWeek that is too big, the result is undefined. + * + * The start rule must have a different month than the end rule. + * This restriction shouldn't hurt for all possible time zones. + * + * @param rawOffset The time offset from GMT in milliseconds. + * @param id The identifier of this time zone. + * @param startMonth The start month of daylight savings; use the + * constants in Calendar. + * @param startDayOfWeekInMonth A day in month or a day of week number, as + * described above. + * @param startDayOfWeek The start rule day of week; see above. + * @param startTime A time in millis in standard time. + * @param endMonth The end month of daylight savings; use the + * constants in Calendar. + * @param endDayOfWeekInMonth A day in month or a day of week number, as + * described above. + * @param endDayOfWeek The end rule day of week; see above. + * @param endTime A time in millis in standard time. + * @throws IllegalArgumentException if parameters are invalid or out of + * range. + */ + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int endMonth, int endDayOfWeekInMonth, + int endDayOfWeek, int endTime) + { + this.rawOffset = rawOffset; + setID(id); + useDaylight = true; + + setStartRule(startMonth, startDayOfWeekInMonth, startDayOfWeek, startTime); + setEndRule(endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); + if (startMonth == endMonth) + throw new IllegalArgumentException("startMonth and endMonth must be different"); + this.startYear = 0; + } + + /** + * This constructs a new SimpleTimeZone that supports a daylight savings + * rule. The parameter are the same as for the constructor above, except + * there is the additional dstSavaings parameter. + * + * @param dstSavings the amount of savings for daylight savings + * time in milliseconds. This must be positive. + * @since 1.2 + */ + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int endMonth, int endDayOfWeekInMonth, + int endDayOfWeek, int endTime, int dstSavings) + { + this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek, + startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); + + this.dstSavings = dstSavings; + } + + /** + * This constructs a new SimpleTimeZone that supports a daylight savings + * rule. The parameter are the same as for the constructor above, except + * there are the additional startTimeMode, endTimeMode, and dstSavings + * parameters. + * + * @param startTimeMode the mode that start times are specified in. One of + * WALL_TIME, STANDARD_TIME, or UTC_TIME. + * @param endTimeMode the mode that end times are specified in. One of + * WALL_TIME, STANDARD_TIME, or UTC_TIME. + * @param dstSavings the amount of savings for daylight savings + * time in milliseconds. This must be positive. + * @throws IllegalArgumentException if parameters are invalid or out of + * range. + * @since 1.4 + */ + public SimpleTimeZone(int rawOffset, String id, int startMonth, + int startDayOfWeekInMonth, int startDayOfWeek, + int startTime, int startTimeMode, int endMonth, + int endDayOfWeekInMonth, int endDayOfWeek, + int endTime, int endTimeMode, int dstSavings) + { + this(rawOffset, id, startMonth, startDayOfWeekInMonth, startDayOfWeek, + startTime, endMonth, endDayOfWeekInMonth, endDayOfWeek, endTime); + + if (startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) + throw new IllegalArgumentException("startTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); + if (endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) + throw new IllegalArgumentException("endTimeMode must be one of WALL_TIME, STANDARD_TIME, or UTC_TIME"); + + this.dstSavings = dstSavings; + this.startTimeMode = startTimeMode; + this.endTimeMode = endTimeMode; + } + + /** + * Sets the first year, where daylight savings applies. The daylight + * savings rule never apply for years in the BC era. Note that this + * is gregorian calendar specific. + * @param year the start year. + */ + public void setStartYear(int year) + { + startYear = year; + useDaylight = true; + } + + /** + * Checks if the month, day, dayOfWeek arguments are in range and + * returns the mode of the rule. + * @param month the month parameter as in the constructor + * @param day the day parameter as in the constructor + * @param dayOfWeek the day of week parameter as in the constructor + * @return the mode of this rule see startMode. + * @exception IllegalArgumentException if parameters are out of range. + * @see #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int) + * @see #startMode + */ + private int checkRule(int month, int day, int dayOfWeek) + { + if (month < 0 || month > 11) + throw new IllegalArgumentException("month out of range"); + + int daysInMonth = getDaysInMonth(month, 1); + if (dayOfWeek == 0) + { + if (day <= 0 || day > daysInMonth) + throw new IllegalArgumentException("day out of range"); + return DOM_MODE; + } + else if (dayOfWeek > 0) + { + if (Math.abs(day) > (daysInMonth + 6) / 7) + throw new IllegalArgumentException("dayOfWeekInMonth out of range"); + if (dayOfWeek > Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + return DOW_IN_MONTH_MODE; + } + else + { + if (day == 0 || Math.abs(day) > daysInMonth) + throw new IllegalArgumentException("day out of range"); + if (dayOfWeek < -Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + if (day < 0) + return DOW_LE_DOM_MODE; + else + return DOW_GE_DOM_MODE; + } + } + + /** + * Sets the daylight savings start rule. You must also set the + * end rule with setEndRule or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The month where daylight savings start, zero + * based. You should use the constants in Calendar. + * @param day A day of month or day of week in month. + * @param dayOfWeek The day of week where daylight savings start. + * @param time The time in milliseconds standard time where daylight + * savings start. + * @exception IllegalArgumentException if parameters are out of range. + * @see SimpleTimeZone + */ + public void setStartRule(int month, int day, int dayOfWeek, int time) + { + this.startMode = checkRule(month, day, dayOfWeek); + this.startMonth = month; + this.startDay = day; + this.startDayOfWeek = Math.abs(dayOfWeek); + this.startTime = time; + this.startTimeMode = WALL_TIME; + } + + /** + * Sets the daylight savings start rule. You must also set the + * end rule with setEndRule or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * Note that this API isn't incredibly well specified. It appears that the + * after flag must override the parameters, since normally, the day and + * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or + * before mode is chosen. But if after == true, this implementation + * overrides the signs of the other arguments. And if dayOfWeek == 0, it + * falls back to the behavior in the other APIs. I guess this should be + * checked against Sun's implementation. + * + * @param month The month where daylight savings start, zero + * based. You should use the constants in Calendar. + * @param day A day of month or day of week in month. + * @param dayOfWeek The day of week where daylight savings start. + * @param time The time in milliseconds standard time where daylight + * savings start. + * @param after If true, day and dayOfWeek specify first day of week on or + * after day, else first day of week on or before. + * @since 1.2 + * @see SimpleTimeZone + */ + public void setStartRule(int month, int day, int dayOfWeek, int time, + boolean after) + { + if (after) + setStartRule(month, day, -dayOfWeek, time); + else + setStartRule(month, -day, -dayOfWeek, time); + } + + /** + * Sets the daylight savings start rule. You must also set the + * end rule with setEndRule or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The month where daylight savings start, zero + * based. You should use the constants in Calendar. + * @param day A day of month or day of week in month. + * @param time The time in milliseconds standard time where daylight + * savings start. + * @see SimpleTimeZone + * @since 1.2 + */ + public void setStartRule(int month, int day, int time) + { + setStartRule(month, day, 0, time); + } + + /** + * Sets the daylight savings end rule. You must also set the + * start rule with setStartRule or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The end month of daylight savings. + * @param day A day in month, or a day of week in month. + * @param dayOfWeek A day of week, when daylight savings ends. + * @param time A time in millis in standard time. + * @see #setStartRule(int, int, int, int) + */ + public void setEndRule(int month, int day, int dayOfWeek, int time) + { + this.endMode = checkRule(month, day, dayOfWeek); + this.endMonth = month; + this.endDay = day; + this.endDayOfWeek = Math.abs(dayOfWeek); + this.endTime = time; + this.endTimeMode = WALL_TIME; + useDaylight = true; + } + + /** + * Sets the daylight savings end rule. You must also set the + * start rule with setStartRule or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * Note that this API isn't incredibly well specified. It appears that the + * after flag must override the parameters, since normally, the day and + * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or + * before mode is chosen. But if after == true, this implementation + * overrides the signs of the other arguments. And if dayOfWeek == 0, it + * falls back to the behavior in the other APIs. I guess this should be + * checked against Sun's implementation. + * + * @param month The end month of daylight savings. + * @param day A day in month, or a day of week in month. + * @param dayOfWeek A day of week, when daylight savings ends. + * @param time A time in millis in standard time. + * @param after If true, day and dayOfWeek specify first day of week on or + * after day, else first day of week on or before. + * @since 1.2 + * @see #setStartRule(int, int, int, int, boolean) + */ + public void setEndRule(int month, int day, int dayOfWeek, int time, + boolean after) + { + if (after) + setEndRule(month, day, -dayOfWeek, time); + else + setEndRule(month, -day, -dayOfWeek, time); + } + + /** + * Sets the daylight savings end rule. You must also set the + * start rule with setStartRule or the result of + * getOffset is undefined. For the parameters see the ten-argument + * constructor above. + * + * @param month The end month of daylight savings. + * @param day A day in month, or a day of week in month. + * @param time A time in millis in standard time. + * @see #setStartRule(int, int, int) + */ + public void setEndRule(int month, int day, int time) + { + setEndRule(month, day, 0, time); + } + + /** + * Gets the time zone offset, for current date, modified in case of + * daylight savings. This is the offset to add to UTC to get the local + * time. + * + * In the standard JDK the results given by this method may result in + * inaccurate results at the end of February or the beginning of March. + * To avoid this, you should use Calendar instead: + * offset = cal.get(Calendar.ZONE_OFFSET) + * + cal.get(Calendar.DST_OFFSET); + * + * This version doesn't suffer this inaccuracy. + * + * The arguments don't follow the approach for setting start and end rules. + * The day must be a positive number and dayOfWeek must be a positive value + * from Calendar. dayOfWeek is redundant, but must match the other values + * or an inaccurate result may be returned. + * + * @param era the era of the given date + * @param year the year of the given date + * @param month the month of the given date, 0 for January. + * @param day the day of month + * @param dayOfWeek the day of week; this must match the other fields. + * @param millis the millis in the day (in local standard time) + * @return the time zone offset in milliseconds. + * @throws IllegalArgumentException if arguments are incorrect. + */ + public int getOffset(int era, int year, int month, int day, int dayOfWeek, + int millis) + { + int daysInMonth = getDaysInMonth(month, year); + if (day < 1 || day > daysInMonth) + throw new IllegalArgumentException("day out of range"); + if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) + throw new IllegalArgumentException("dayOfWeek out of range"); + if (month < Calendar.JANUARY || month > Calendar.DECEMBER) + throw new IllegalArgumentException("month out of range:" + month); + + // This method is called by Calendar, so we mustn't use that class. + int daylightSavings = 0; + if (useDaylight && era == GregorianCalendar.AD && year >= startYear) + { + int orig_year = year; + int time = startTime + (startTimeMode == UTC_TIME ? rawOffset : 0); + // This does only work for Gregorian calendars :-( + // This is mainly because setStartYear doesn't take an era. + boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis, + startMode, startMonth, startDay, + startDayOfWeek, time); + millis += dstSavings; + if (millis >= 24 * 60 * 60 * 1000) + { + millis -= 24 * 60 * 60 * 1000; + dayOfWeek = (dayOfWeek % 7) + 1; + if (++day > daysInMonth) + { + day = 1; + if (month++ == Calendar.DECEMBER) + { + month = Calendar.JANUARY; + year++; + } + } + } + time = endTime + (endTimeMode == UTC_TIME ? rawOffset : 0); + if (endTimeMode != WALL_TIME) + time += dstSavings; + boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis, + endMode, endMonth, endDay, endDayOfWeek, + time); + + if (year != orig_year) + afterStart = false; + if (startMonth < endMonth) + // use daylight savings, if the date is after the start of + // savings, and before the end of savings. + daylightSavings = afterStart && beforeEnd ? dstSavings : 0; + else + // use daylight savings, if the date is before the end of + // savings, or after the start of savings. + daylightSavings = beforeEnd || afterStart ? dstSavings : 0; + } + return rawOffset + daylightSavings; + } + + /** + * Returns the time zone offset to GMT in milliseconds, ignoring + * day light savings. + * @return the time zone offset. + */ + public int getRawOffset() + { + return rawOffset; + } + + /** + * Sets the standard time zone offset to GMT. + * @param rawOffset The time offset from GMT in milliseconds. + */ + public void setRawOffset(int rawOffset) + { + this.rawOffset = rawOffset; + } + + /** + * Gets the daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an our. + * @return the daylight savings offset in milliseconds. + * + * @since 1.2 + */ + public int getDSTSavings() + { + return dstSavings; + } + + /** + * Sets the daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. + * + * @param dstSavings the daylight savings offset in milliseconds. + * + * @since 1.2 + */ + public void setDSTSavings(int dstSavings) + { + if (dstSavings <= 0) + throw new IllegalArgumentException("illegal value for dstSavings"); + + this.dstSavings = dstSavings; + } + + /** + * Returns if this time zone uses daylight savings time. + * @return true, if we use daylight savings time, false otherwise. + */ + public boolean useDaylightTime() + { + return useDaylight; + } + + /** + * Returns the number of days in the given month. + * Uses gregorian rules prior to 1582 (The default and earliest cutover) + * @param month The month, zero based; use one of the Calendar constants. + * @param year The year. + */ + private int getDaysInMonth(int month, int year) + { + if (month == Calendar.FEBRUARY) + { + if ((year & 3) != 0) + return 28; + + // Assume default Gregorian cutover, + // all years prior to this must be Julian + if (year < 1582) + return 29; + + // Gregorian rules + return ((year % 100) != 0 || (year % 400) == 0) ? 29 : 28; + } + else + return monthArr[month]; + } + + /** + * Checks if the date given in calXXXX, is before the change between + * dst and standard time. + * @param calYear the year of the date to check (for leap day checking). + * @param calMonth the month of the date to check. + * @param calDayOfMonth the day of month of the date to check. + * @param calDayOfWeek the day of week of the date to check. + * @param calMillis the millis of day of the date to check (standard time). + * @param mode the change mode; same semantic as startMode. + * @param month the change month; same semantic as startMonth. + * @param day the change day; same semantic as startDay. + * @param dayOfWeek the change day of week; + * @param millis the change time in millis since midnight standard time. + * same semantic as startDayOfWeek. + * @return true, if cal is before the change, false if cal is on + * or after the change. + */ + private boolean isBefore(int calYear, int calMonth, int calDayOfMonth, + int calDayOfWeek, int calMillis, int mode, + int month, int day, int dayOfWeek, int millis) + { + // This method is called by Calendar, so we mustn't use that class. + // We have to do all calculations by hand. + // check the months: + // XXX - this is not correct: + // for the DOW_GE_DOM and DOW_LE_DOM modes the change date may + // be in a different month. + if (calMonth != month) + return calMonth < month; + + // check the day: + switch (mode) + { + case DOM_MODE: + if (calDayOfMonth != day) + return calDayOfMonth < day; + break; + case DOW_IN_MONTH_MODE: + { + // This computes the day of month of the day of type + // "dayOfWeek" that lies in the same (sunday based) week as cal. + calDayOfMonth += (dayOfWeek - calDayOfWeek); + + // Now we convert it to 7 based number (to get a one based offset + // after dividing by 7). If we count from the end of the + // month, we get want a -7 based number counting the days from + // the end: + if (day < 0) + calDayOfMonth -= getDaysInMonth(calMonth, calYear) + 7; + else + calDayOfMonth += 6; + + // day > 0 day < 0 + // S M T W T F S S M T W T F S + // 7 8 9 10 11 12 -36-35-34-33-32-31 + // 13 14 15 16 17 18 19 -30-29-28-27-26-25-24 + // 20 21 22 23 24 25 26 -23-22-21-20-19-18-17 + // 27 28 29 30 31 32 33 -16-15-14-13-12-11-10 + // 34 35 36 -9 -8 -7 + // Now we calculate the day of week in month: + int week = calDayOfMonth / 7; + + // day > 0 day < 0 + // S M T W T F S S M T W T F S + // 1 1 1 1 1 1 -5 -5 -4 -4 -4 -4 + // 1 2 2 2 2 2 2 -4 -4 -4 -3 -3 -3 -3 + // 2 3 3 3 3 3 3 -3 -3 -3 -2 -2 -2 -2 + // 3 4 4 4 4 4 4 -2 -2 -2 -1 -1 -1 -1 + // 4 5 5 -1 -1 -1 + if (week != day) + return week < day; + + if (calDayOfWeek != dayOfWeek) + return calDayOfWeek < dayOfWeek; + + // daylight savings starts/ends on the given day. + break; + } + case DOW_LE_DOM_MODE: + // The greatest sunday before or equal December, 12 + // is the same as smallest sunday after or equal December, 6. + day = Math.abs(day) - 6; + case DOW_GE_DOM_MODE: + // Calculate the day of month of the day of type + // "dayOfWeek" that lies before (or on) the given date. + calDayOfMonth -= (calDayOfWeek < dayOfWeek ? 7 : 0) + calDayOfWeek + - dayOfWeek; + if (calDayOfMonth < day) + return true; + if (calDayOfWeek != dayOfWeek || calDayOfMonth >= day + 7) + return false; + + // now we have the same day + break; + } + + // the millis decides: + return (calMillis < millis); + } + + /** + * Determines if the given date is in daylight savings time. + * @return true, if it is in daylight savings time, false otherwise. + */ + public boolean inDaylightTime(Date date) + { + Calendar cal = Calendar.getInstance(this); + cal.setTime(date); + return (cal.get(Calendar.DST_OFFSET) != 0); + } + + /** + * Generates the hashCode for the SimpleDateFormat object. It is + * the rawOffset, possibly, if useDaylightSavings is true, xored + * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime. + */ + public synchronized int hashCode() + { + return rawOffset + ^ (useDaylight + ? startMonth ^ startDay ^ startDayOfWeek ^ startTime ^ endMonth + ^ endDay ^ endDayOfWeek ^ endTime : 0); + } + + public synchronized boolean equals(Object o) + { + if (this == o) + return true; + if (! (o instanceof SimpleTimeZone)) + return false; + SimpleTimeZone zone = (SimpleTimeZone) o; + if (zone.hashCode() != hashCode() || ! getID().equals(zone.getID()) + || rawOffset != zone.rawOffset || useDaylight != zone.useDaylight) + return false; + if (! useDaylight) + return true; + return (startYear == zone.startYear && startMonth == zone.startMonth + && startDay == zone.startDay + && startDayOfWeek == zone.startDayOfWeek + && startTime == zone.startTime + && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth + && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek + && endTime == zone.endTime && endTimeMode == zone.endTimeMode); + } + + /** + * Test if the other time zone uses the same rule and only + * possibly differs in ID. This implementation for this particular + * class will return true if the other object is a SimpleTimeZone, + * the raw offsets and useDaylight are identical and if useDaylight + * is true, also the start and end datas are identical. + * @return true if this zone uses the same rule. + */ + public boolean hasSameRules(TimeZone other) + { + if (this == other) + return true; + if (! (other instanceof SimpleTimeZone)) + return false; + SimpleTimeZone zone = (SimpleTimeZone) other; + if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset + || useDaylight != zone.useDaylight) + return false; + if (! useDaylight) + return true; + return (startYear == zone.startYear && startMonth == zone.startMonth + && startDay == zone.startDay + && startDayOfWeek == zone.startDayOfWeek + && startTime == zone.startTime + && startTimeMode == zone.startTimeMode && endMonth == zone.endMonth + && endDay == zone.endDay && endDayOfWeek == zone.endDayOfWeek + && endTime == zone.endTime && endTimeMode == zone.endTimeMode); + } + + /** + * Returns a string representation of this SimpleTimeZone object. + * @return a string representation of this SimpleTimeZone object. + */ + public String toString() + { + // the test for useDaylight is an incompatibility to jdk1.2, but + // I think this shouldn't hurt. + return getClass().getName() + "[" + "id=" + getID() + ",offset=" + + rawOffset + ",dstSavings=" + dstSavings + ",useDaylight=" + + useDaylight + + (useDaylight + ? ",startYear=" + startYear + ",startMode=" + startMode + + ",startMonth=" + startMonth + ",startDay=" + startDay + + ",startDayOfWeek=" + startDayOfWeek + ",startTime=" + + startTime + ",startTimeMode=" + startTimeMode + ",endMode=" + + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay + + ",endDayOfWeek=" + endDayOfWeek + ",endTime=" + endTime + + ",endTimeMode=" + endTimeMode : "") + "]"; + } + + /** + * Reads a serialized simple time zone from stream. + * @see #writeObject + */ + private void readObject(java.io.ObjectInputStream input) + throws java.io.IOException, ClassNotFoundException + { + input.defaultReadObject(); + if (serialVersionOnStream == 0) + { + // initialize the new fields to default values. + dstSavings = 60 * 60 * 1000; + endMode = DOW_IN_MONTH_MODE; + startMode = DOW_IN_MONTH_MODE; + startTimeMode = WALL_TIME; + endTimeMode = WALL_TIME; + serialVersionOnStream = 2; + } + else + { + int length = input.readInt(); + byte[] byteArray = new byte[length]; + input.read(byteArray, 0, length); + if (length >= 4) + { + // Lets hope that Sun does extensions to the serialized + // form in a sane manner. + startDay = byteArray[0]; + startDayOfWeek = byteArray[1]; + endDay = byteArray[2]; + endDayOfWeek = byteArray[3]; + } + } + } + + /** + * Serializes this object to a stream. @serialdata The object is + * first written in the old JDK 1.1 format, so that it can be read + * by the old classes. This means, that the + * start/endDay(OfWeek)-Fields are written in the + * DOW_IN_MONTH_MODE rule, since this was the only supported rule + * in 1.1. + * + * In the optional section, we write first the length of an byte + * array as int and afterwards the byte array itself. The byte + * array contains in this release four elements, namely the real + * startDay, startDayOfWeek endDay, endDayOfWeek in that Order. + * These fields are needed, because for compatibility reasons only + * approximative values are written to the required section, as + * described above. + */ + private void writeObject(java.io.ObjectOutputStream output) + throws java.io.IOException + { + byte[] byteArray = new byte[] + { + (byte) startDay, (byte) startDayOfWeek, (byte) endDay, + (byte) endDayOfWeek + }; + + /* calculate the approximation for JDK 1.1 */ + switch (startMode) + { + case DOM_MODE: + startDayOfWeek = Calendar.SUNDAY; // random day of week + + // fall through + case DOW_GE_DOM_MODE: + case DOW_LE_DOM_MODE: + startDay = (startDay + 6) / 7; + } + switch (endMode) + { + case DOM_MODE: + endDayOfWeek = Calendar.SUNDAY; + + // fall through + case DOW_GE_DOM_MODE: + case DOW_LE_DOM_MODE: + endDay = (endDay + 6) / 7; + } + + // the required part: + output.defaultWriteObject(); + // the optional part: + output.writeInt(byteArray.length); + output.write(byteArray, 0, byteArray.length); + } +} diff --git a/libjava/classpath/java/util/SortedMap.java b/libjava/classpath/java/util/SortedMap.java new file mode 100644 index 000000000..2b98848f4 --- /dev/null +++ b/libjava/classpath/java/util/SortedMap.java @@ -0,0 +1,173 @@ +/* SortedMap.java -- A map that makes guarantees about the order of its keys + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A map which guarantees its key's iteration order. The entries in the + * map are related by the natural ordering of the keys if they + * are Comparable, or by the provided Comparator. Additional operations + * take advantage of the sorted nature of the map. + *

        + * + * All keys entered in the map must be mutually comparable; in other words, + * k1.compareTo(k2) or comparator.compare(k1, k2) + * must not throw a ClassCastException. The ordering must be consistent + * with equals (see {@link Comparator} for this definition), if the + * map is to obey the general contract of the Map interface. If not, + * the results are well-defined, but probably not what you wanted. + *

        + * + * It is recommended that all implementing classes provide four constructors: + * 1) one that takes no arguments and builds an empty map sorted by natural + * order of the keys; 2) one that takes a Comparator for the sorting order; + * 3) one that takes a Map and sorts according to the natural order of its + * keys; and 4) one that takes a SortedMap and sorts by the same comparator. + * Unfortunately, the Java language does not provide a way to enforce this. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Map + * @see TreeMap + * @see SortedSet + * @see Comparable + * @see Comparator + * @see Collection + * @see ClassCastException + * @since 1.2 + * @status updated to 1.4 + */ +public interface SortedMap extends Map +{ + /** + * Returns the comparator used in sorting this map, or null if it is + * the keys' natural ordering. + * + * @return the sorting comparator + */ + Comparator comparator(); + + /** + * Returns the first (lowest sorted) key in the map. + * + * @return the first key + * @throws NoSuchElementException if this map is empty. + */ + K firstKey(); + + /** + * Returns a view of the portion of the map strictly less than toKey. The + * view is backed by this map, so changes in one show up in the other. + * The submap supports all optional operations of the original. + *

        + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of toKey. Note that the endpoint, toKey, + * is not included; if you want this value to be included, pass its successor + * object in to toKey. For example, for Integers, you could request + * headMap(new Integer(limit.intValue() + 1)). + * + * @param toKey the exclusive upper range of the submap + * @return the submap + * @throws ClassCastException if toKey is not comparable to the map contents + * @throws IllegalArgumentException if this is a subMap, and toKey is out + * of range + * @throws NullPointerException if toKey is null but the map does not allow + * null keys + */ + SortedMap headMap(K toKey); + + /** + * Returns the last (highest sorted) key in the map. + * + * @return the last key + * @throws NoSuchElementException if this map is empty. + */ + K lastKey(); + + /** + * Returns a view of the portion of the map greater than or equal to + * fromKey, and strictly less than toKey. The view is backed by this map, + * so changes in one show up in the other. The submap supports all + * optional operations of the original. + *

        + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey and toKey. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you could request + * subMap(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1)) to reverse + * the inclusiveness of both endpoints. + * + * @param fromKey the inclusive lower range of the submap + * @param toKey the exclusive upper range of the submap + * @return the submap + * @throws ClassCastException if fromKey or toKey is not comparable to + * the map contents + * @throws IllegalArgumentException if this is a subMap, and fromKey or + * toKey is out of range + * @throws NullPointerException if fromKey or toKey is null but the map + * does not allow null keys + */ + SortedMap subMap(K fromKey, K toKey); + + /** + * Returns a view of the portion of the map greater than or equal to + * fromKey. The view is backed by this map, so changes in one show up + * in the other. The submap supports all optional operations of the original. + *

        + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey. Note that the endpoint, fromKey, is + * included; if you do not want this value to be included, pass its successor object in + * to fromKey. For example, for Integers, you could request + * tailMap(new Integer(limit.intValue() + 1)). + * + * @param fromKey the inclusive lower range of the submap + * @return the submap + * @throws ClassCastException if fromKey is not comparable to the map + * contents + * @throws IllegalArgumentException if this is a subMap, and fromKey is out + * of range + * @throws NullPointerException if fromKey is null but the map does not allow + * null keys + */ + SortedMap tailMap(K fromKey); +} diff --git a/libjava/classpath/java/util/SortedSet.java b/libjava/classpath/java/util/SortedSet.java new file mode 100644 index 000000000..89f155a0d --- /dev/null +++ b/libjava/classpath/java/util/SortedSet.java @@ -0,0 +1,176 @@ +/* SortedSet.java -- A set that makes guarantees about the order of its + elements + Copyright (C) 1998, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * A set which guarantees its iteration order. The elements in the set + * are related by the natural ordering if they are Comparable, or + * by the provided Comparator. Additional operations take advantage of + * the sorted nature of the set. + *

        + * + * All elements entered in the set must be mutually comparable; in other words, + * k1.compareTo(k2) or comparator.compare(k1, k2) + * must not throw a ClassCastException. The ordering must be consistent + * with equals (see {@link Comparator} for this definition), if the + * set is to obey the general contract of the Set interface. If not, + * the results are well-defined, but probably not what you wanted. + *

        + * + * It is recommended that all implementing classes provide four constructors: + * 1) one that takes no arguments and builds an empty set sorted by natural + * order of the elements; 2) one that takes a Comparator for the sorting order; + * 3) one that takes a Set and sorts according to the natural order of its + * elements; and 4) one that takes a SortedSet and sorts by the same + * comparator. Unfortunately, the Java language does not provide a way to + * enforce this. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Set + * @see TreeSet + * @see SortedMap + * @see Collection + * @see Comparable + * @see Comparator + * @see ClassCastException + * @since 1.2 + * @status updated to 1.4 + */ +public interface SortedSet extends Set +{ + /** + * Returns the comparator used in sorting this set, or null if it is + * the elements' natural ordering. + * + * @return the sorting comparator + */ + Comparator comparator(); + + /** + * Returns the first (lowest sorted) element in the set. + * + * @return the first element + * @throws NoSuchElementException if the set is empty. + */ + E first(); + + /** + * Returns a view of the portion of the set strictly less than toElement. The + * view is backed by this set, so changes in one show up in the other. + * The subset supports all optional operations of the original. + *

        + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of toElement. Note that the endpoint, toElement, + * is not included; if you want this value included, pass its successor object in to + * toElement. For example, for Integers, you could request + * headSet(new Integer(limit.intValue() + 1)). + * + * @param toElement the exclusive upper range of the subset + * @return the subset + * @throws ClassCastException if toElement is not comparable to the set + * contents + * @throws IllegalArgumentException if this is a subSet, and toElement is out + * of range + * @throws NullPointerException if toElement is null but the set does not + * allow null elements + */ + SortedSet headSet(E toElement); + + /** + * Returns the last (highest sorted) element in the set. + * + * @return the last element + * @throws NoSuchElementException if the set is empty. + */ + E last(); + + /** + * Returns a view of the portion of the set greater than or equal to + * fromElement, and strictly less than toElement. The view is backed by + * this set, so changes in one show up in the other. The subset supports all + * optional operations of the original. + *

        + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement and toElement. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you can request + * subSet(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1)) to reverse + * the inclusiveness of both endpoints. + * + * @param fromElement the inclusive lower range of the subset + * @param toElement the exclusive upper range of the subset + * @return the subset + * @throws ClassCastException if fromElement or toElement is not comparable + * to the set contents + * @throws IllegalArgumentException if this is a subSet, and fromElement or + * toElement is out of range + * @throws NullPointerException if fromElement or toElement is null but the + * set does not allow null elements + */ + SortedSet subSet(E fromElement, E toElement); + + /** + * Returns a view of the portion of the set greater than or equal to + * fromElement. The view is backed by this set, so changes in one show up + * in the other. The subset supports all optional operations of the original. + *

        + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement. Note that the endpoint, + * fromElement, is included; if you do not want this value to be included, pass its + * successor object in to fromElement. For example, for Integers, you could request + * tailSet(new Integer(limit.intValue() + 1)). + * + * @param fromElement the inclusive lower range of the subset + * @return the subset + * @throws ClassCastException if fromElement is not comparable to the set + * contents + * @throws IllegalArgumentException if this is a subSet, and fromElement is + * out of range + * @throws NullPointerException if fromElement is null but the set does not + * allow null elements + */ + SortedSet tailSet(E fromElement); +} diff --git a/libjava/classpath/java/util/Stack.java b/libjava/classpath/java/util/Stack.java new file mode 100644 index 000000000..1d8792882 --- /dev/null +++ b/libjava/classpath/java/util/Stack.java @@ -0,0 +1,161 @@ +/* Stack.java - Class that provides a Last In First Out (LIFO) + datatype, known more commonly as a Stack + Copyright (C) 1998, 1999, 2001, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 + * "The Java Language Specification", ISBN 0-201-63451-1 + * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. + * Status: Believed complete and correct + +/** + * Stack provides a Last In First Out (LIFO) data type, commonly known + * as a Stack. Stack itself extends Vector and provides the additional + * methods for stack manipulation (push, pop, peek). You can also seek for + * the 1-based position of an element on the stack. + * + * @author Warren Levy (warrenl@cygnus.com) + * @author Eric Blake (ebb9@email.byu.edu) + * @see List + * @see AbstractList + * @see LinkedList + * @since 1.0 + * @status updated to 1.4 + */ +public class Stack extends Vector +{ + // We could use Vector methods internally for the following methods, + // but have used Vector fields directly for efficiency (i.e. this + // often reduces out duplicate bounds checking). + + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 1224463164541339165L; + + /** + * This constructor creates a new Stack, initially empty + */ + public Stack() + { + } + + /** + * Pushes an Object onto the top of the stack. This method is effectively + * the same as addElement(item). + * + * @param item the Object to push onto the stack + * @return the Object pushed onto the stack + * @see Vector#addElement(Object) + */ + public T push(T item) + { + // When growing the Stack, use the Vector routines in case more + // memory is needed. + // Note: spec indicates that this method *always* returns obj passed in! + + addElement(item); + return item; + } + + /** + * Pops an item from the stack and returns it. The item popped is + * removed from the Stack. + * + * @return the Object popped from the stack + * @throws EmptyStackException if the stack is empty + */ + @SuppressWarnings("unchecked") + public synchronized T pop() + { + if (elementCount == 0) + throw new EmptyStackException(); + + modCount++; + T obj = (T) elementData[--elementCount]; + + // Set topmost element to null to assist the gc in cleanup. + elementData[elementCount] = null; + return obj; + } + + /** + * Returns the top Object on the stack without removing it. + * + * @return the top Object on the stack + * @throws EmptyStackException if the stack is empty + */ + @SuppressWarnings("unchecked") + public synchronized T peek() + { + if (elementCount == 0) + throw new EmptyStackException(); + + return (T) elementData[elementCount - 1]; + } + + /** + * Tests if the stack is empty. + * + * @return true if the stack contains no items, false otherwise + */ + public synchronized boolean empty() + { + return elementCount == 0; + } + + /** + * Returns the position of an Object on the stack, with the top + * most Object being at position 1, and each Object deeper in the + * stack at depth + 1. + * + * @param o The object to search for + * @return The 1 based depth of the Object, or -1 if the Object + * is not on the stack + */ + public synchronized int search(Object o) + { + int i = elementCount; + while (--i >= 0) + if (equals(o, elementData[i])) + return elementCount - i; + return -1; + } +} diff --git a/libjava/classpath/java/util/StringTokenizer.java b/libjava/classpath/java/util/StringTokenizer.java new file mode 100644 index 000000000..d16ec9bc4 --- /dev/null +++ b/libjava/classpath/java/util/StringTokenizer.java @@ -0,0 +1,269 @@ +/* StringTokenizer -- breaks a String into tokens + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This class splits a string into tokens. The caller can set on which + * delimiters the string should be split and if the delimiters should be + * returned. This is much simpler than {@link java.io.StreamTokenizer}. + * + *

        You may change the delimiter set on the fly by calling + * nextToken(String). But the semantic is quite difficult; it even + * depends on calling hasMoreTokens(). You should call + * hasMoreTokens() before, otherwise the old delimiters + * after the last token are candidates for being returned. + * + *

        If you want to get the delimiters, you have to use the three argument + * constructor. The delimiters are returned as token consisting of a + * single character. + * + * @author Jochen Hoenicke + * @author Warren Levy (warrenl@cygnus.com) + * @see java.io.StreamTokenizer + * @status updated to 1.4 + */ +public class StringTokenizer implements Enumeration +{ + // WARNING: StringTokenizer is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * The position in the str, where we currently are. + */ + private int pos; + + /** + * The string that should be split into tokens. + */ + private final String str; + + /** + * The length of the string. + */ + private final int len; + + /** + * The string containing the delimiter characters. + */ + private String delim; + + /** + * Tells, if we should return the delimiters. + */ + private final boolean retDelims; + + /** + * Creates a new StringTokenizer for the string str, + * that should split on the default delimiter set (space, tab, + * newline, return and formfeed), and which doesn't return the + * delimiters. + * + * @param str The string to split + * @throws NullPointerException if str is null + */ + public StringTokenizer(String str) + { + this(str, " \t\n\r\f", false); + } + + /** + * Create a new StringTokenizer, that splits the given string on + * the given delimiter characters. It doesn't return the delimiter + * characters. + * + * @param str the string to split + * @param delim a string containing all delimiter characters + * @throws NullPointerException if either argument is null + */ + public StringTokenizer(String str, String delim) + { + this(str, delim, false); + } + + /** + * Create a new StringTokenizer, that splits the given string on + * the given delimiter characters. If you set + * returnDelims to true, the delimiter + * characters are returned as tokens of their own. The delimiter + * tokens always consist of a single character. + * + * @param str the string to split + * @param delim a string containing all delimiter characters + * @param returnDelims tells, if you want to get the delimiters + * @throws NullPointerException if str or delim is null + */ + public StringTokenizer(String str, String delim, boolean returnDelims) + { + len = str.length(); + this.str = str; + this.delim = delim; + this.retDelims = returnDelims; + this.pos = 0; + } + + /** + * Tells if there are more tokens. + * + * @return true if the next call of nextToken() will succeed + */ + public boolean hasMoreTokens() + { + if (! retDelims) + { + while (pos < len && delim.indexOf(str.charAt(pos)) >= 0) + pos++; + } + return pos < len; + } + + /** + * Returns the nextToken, changing the delimiter set to the given + * delim. The change of the delimiter set is + * permanent, ie. the next call of nextToken(), uses the same + * delimiter set. + * + * @param delim a string containing the new delimiter characters + * @return the next token with respect to the new delimiter characters + * @throws NoSuchElementException if there are no more tokens + * @throws NullPointerException if delim is null + */ + public String nextToken(String delim) throws NoSuchElementException + { + this.delim = delim; + return nextToken(); + } + + /** + * Returns the nextToken of the string. + * + * @return the next token with respect to the current delimiter characters + * @throws NoSuchElementException if there are no more tokens + */ + public String nextToken() throws NoSuchElementException + { + if (pos < len && delim.indexOf(str.charAt(pos)) >= 0) + { + if (retDelims) + return str.substring(pos, ++pos); + while (++pos < len && delim.indexOf(str.charAt(pos)) >= 0) + ; + } + if (pos < len) + { + int start = pos; + while (++pos < len && delim.indexOf(str.charAt(pos)) < 0) + ; + + return str.substring(start, pos); + } + throw new NoSuchElementException(); + } + + /** + * This does the same as hasMoreTokens. This is the + * Enumeration interface method. + * + * @return true, if the next call of nextElement() will succeed + * @see #hasMoreTokens() + */ + public boolean hasMoreElements() + { + return hasMoreTokens(); + } + + /** + * This does the same as nextTokens. This is the + * Enumeration interface method. + * + * @return the next token with respect to the current delimiter characters + * @throws NoSuchElementException if there are no more tokens + * @see #nextToken() + */ + public Object nextElement() throws NoSuchElementException + { + return nextToken(); + } + + /** + * This counts the number of remaining tokens in the string, with + * respect to the current delimiter set. + * + * @return the number of times nextTokens() will succeed + * @see #nextToken() + */ + public int countTokens() + { + int count = 0; + int delimiterCount = 0; + boolean tokenFound = false; // Set when a non-delimiter is found + int tmpPos = pos; + + // Note for efficiency, we count up the delimiters rather than check + // retDelims every time we encounter one. That way, we can + // just do the conditional once at the end of the method + while (tmpPos < len) + { + if (delim.indexOf(str.charAt(tmpPos++)) >= 0) + { + if (tokenFound) + { + // Got to the end of a token + count++; + tokenFound = false; + } + delimiterCount++; // Increment for this delimiter + } + else + { + tokenFound = true; + // Get to the end of the token + while (tmpPos < len + && delim.indexOf(str.charAt(tmpPos)) < 0) + ++tmpPos; + } + } + + // Make sure to count the last token + if (tokenFound) + count++; + + // if counting delmiters add them into the token count + return retDelims ? count + delimiterCount : count; + } +} // class StringTokenizer diff --git a/libjava/classpath/java/util/TimeZone.java b/libjava/classpath/java/util/TimeZone.java new file mode 100644 index 000000000..86a62918e --- /dev/null +++ b/libjava/classpath/java/util/TimeZone.java @@ -0,0 +1,1781 @@ +/* java.util.TimeZone + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.classpath.SystemProperties; +import gnu.java.lang.CPStringBuilder; +import gnu.java.util.ZoneInfo; + +import java.io.File; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.DateFormatSymbols; + +/** + * This class represents a time zone offset and handles daylight savings. + * + * You can get the default time zone with getDefault. + * This represents the time zone where program is running. + * + * Another way to create a time zone is getTimeZone, where + * you can give an identifier as parameter. For instance, the identifier + * of the Central European Time zone is "CET". + * + * With the getAvailableIDs method, you can get all the + * supported time zone identifiers. + * + * @see Calendar + * @see SimpleTimeZone + * @author Jochen Hoenicke + */ +public abstract class TimeZone implements java.io.Serializable, Cloneable +{ + + /** + * Constant used to indicate that a short timezone abbreviation should + * be returned, such as "EST" + */ + public static final int SHORT = 0; + + /** + * Constant used to indicate that a long timezone name should be + * returned, such as "Eastern Standard Time". + */ + public static final int LONG = 1; + + /** + * The time zone identifier, e.g. PST. + */ + private String ID; + + /** + * The default time zone, as returned by getDefault. + */ + private static TimeZone defaultZone0; + + /** + * Tries to get the default TimeZone for this system if not already + * set. It will call getDefaultTimeZone(String) with + * the result of System.getProperty("user.timezone"). + * If that fails it calls VMTimeZone.getDefaultTimeZoneId(). + * If that also fails GMT is returned. + */ + private static synchronized TimeZone defaultZone() + { + /* Look up default timezone */ + if (defaultZone0 == null) + { + defaultZone0 = (TimeZone) AccessController.doPrivileged + (new PrivilegedAction() + { + public Object run() + { + TimeZone zone = null; + + // Prefer System property user.timezone. + String tzid = System.getProperty("user.timezone"); + if (tzid != null && !tzid.equals("")) + zone = getDefaultTimeZone(tzid); + + // Try platfom specific way. + if (zone == null) + zone = VMTimeZone.getDefaultTimeZoneId(); + + // Fall back on GMT. + if (zone == null) + zone = getTimeZone ("GMT"); + + return zone; + } + }); + } + + return defaultZone0; + } + + private static final long serialVersionUID = 3581463369166924961L; + + /** + * Flag whether zoneinfo data should be used, + * otherwise builtin timezone data will be provided. + */ + private static String zoneinfo_dir; + + /** + * Cached copy of getAvailableIDs(). + */ + private static String[] availableIDs = null; + + /** + * JDK 1.1.x compatibility aliases. + */ + private static HashMap aliases0; + + /** + * HashMap for timezones by ID. + */ + private static HashMap timezones0; + /* initialize this static field lazily to overhead if + * it is not needed: + */ + // Package-private to avoid a trampoline. + static HashMap timezones() + { + if (timezones0 == null) + { + HashMap timezones = new HashMap(); + timezones0 = timezones; + + zoneinfo_dir = SystemProperties.getProperty("gnu.java.util.zoneinfo.dir"); + if (zoneinfo_dir != null && !new File(zoneinfo_dir).isDirectory()) + zoneinfo_dir = null; + + if (zoneinfo_dir != null) + { + aliases0 = new HashMap(); + + // These deprecated aliases for JDK 1.1.x compatibility + // should take precedence over data files read from + // /usr/share/zoneinfo. + aliases0.put("ACT", "Australia/Darwin"); + aliases0.put("AET", "Australia/Sydney"); + aliases0.put("AGT", "America/Argentina/Buenos_Aires"); + aliases0.put("ART", "Africa/Cairo"); + aliases0.put("AST", "America/Juneau"); + aliases0.put("BST", "Asia/Colombo"); + aliases0.put("CAT", "Africa/Gaborone"); + aliases0.put("CNT", "America/St_Johns"); + aliases0.put("CST", "CST6CDT"); + aliases0.put("CTT", "Asia/Brunei"); + aliases0.put("EAT", "Indian/Comoro"); + aliases0.put("ECT", "CET"); + aliases0.put("EST", "EST5EDT"); + aliases0.put("EST5", "EST5EDT"); + aliases0.put("IET", "EST5EDT"); + aliases0.put("IST", "Asia/Calcutta"); + aliases0.put("JST", "Asia/Seoul"); + aliases0.put("MIT", "Pacific/Niue"); + aliases0.put("MST", "MST7MDT"); + aliases0.put("MST7", "MST7MDT"); + aliases0.put("NET", "Indian/Mauritius"); + aliases0.put("NST", "Pacific/Auckland"); + aliases0.put("PLT", "Indian/Kerguelen"); + aliases0.put("PNT", "MST7MDT"); + aliases0.put("PRT", "America/Anguilla"); + aliases0.put("PST", "PST8PDT"); + aliases0.put("SST", "Pacific/Ponape"); + aliases0.put("VST", "Asia/Bangkok"); + return timezones; + } + + TimeZone tz; + // Automatically generated by scripts/timezones.pl + // XXX - Should we read this data from a file? + tz = new SimpleTimeZone(-11000 * 3600, "MIT"); + timezones0.put("MIT", tz); + timezones0.put("Pacific/Apia", tz); + timezones0.put("Pacific/Midway", tz); + timezones0.put("Pacific/Niue", tz); + timezones0.put("Pacific/Pago_Pago", tz); + tz = new SimpleTimeZone + (-10000 * 3600, "America/Adak", + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Adak", tz); + tz = new SimpleTimeZone(-10000 * 3600, "HST"); + timezones0.put("HST", tz); + timezones0.put("Pacific/Fakaofo", tz); + timezones0.put("Pacific/Honolulu", tz); + timezones0.put("Pacific/Johnston", tz); + timezones0.put("Pacific/Rarotonga", tz); + timezones0.put("Pacific/Tahiti", tz); + tz = new SimpleTimeZone(-9500 * 3600, "Pacific/Marquesas"); + timezones0.put("Pacific/Marquesas", tz); + tz = new SimpleTimeZone + (-9000 * 3600, "AST", + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("AST", tz); + timezones0.put("America/Anchorage", tz); + timezones0.put("America/Juneau", tz); + timezones0.put("America/Nome", tz); + timezones0.put("America/Yakutat", tz); + tz = new SimpleTimeZone(-9000 * 3600, "Pacific/Gambier"); + timezones0.put("Pacific/Gambier", tz); + tz = new SimpleTimeZone + (-8000 * 3600, "America/Tijuana", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Tijuana", tz); + tz = new SimpleTimeZone + (-8000 * 3600, "PST", + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("PST", tz); + timezones0.put("PST8PDT", tz); + timezones0.put("America/Dawson", tz); + timezones0.put("America/Los_Angeles", tz); + timezones0.put("America/Vancouver", tz); + timezones0.put("America/Whitehorse", tz); + timezones0.put("US/Pacific-New", tz); + tz = new SimpleTimeZone(-8000 * 3600, "Pacific/Pitcairn"); + timezones0.put("Pacific/Pitcairn", tz); + tz = new SimpleTimeZone + (-7000 * 3600, "America/Chihuahua", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Chihuahua", tz); + timezones0.put("America/Mazatlan", tz); + tz = new SimpleTimeZone(-7000 * 3600, "MST7"); + timezones0.put("MST7", tz); + timezones0.put("PNT", tz); + timezones0.put("America/Dawson_Creek", tz); + timezones0.put("America/Hermosillo", tz); + timezones0.put("America/Phoenix", tz); + tz = new SimpleTimeZone + (-7000 * 3600, "MST", + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("MST", tz); + timezones0.put("MST7MDT", tz); + timezones0.put("America/Boise", tz); + timezones0.put("America/Cambridge_Bay", tz); + timezones0.put("America/Denver", tz); + timezones0.put("America/Edmonton", tz); + timezones0.put("America/Inuvik", tz); + timezones0.put("America/Shiprock", tz); + timezones0.put("America/Yellowknife", tz); + tz = new SimpleTimeZone + (-6000 * 3600, "America/Cancun", + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Cancun", tz); + timezones0.put("America/Merida", tz); + timezones0.put("America/Mexico_City", tz); + timezones0.put("America/Monterrey", tz); + tz = new SimpleTimeZone(-6000 * 3600, "America/Belize"); + timezones0.put("America/Belize", tz); + timezones0.put("America/Costa_Rica", tz); + timezones0.put("America/El_Salvador", tz); + timezones0.put("America/Guatemala", tz); + timezones0.put("America/Managua", tz); + timezones0.put("America/Regina", tz); + timezones0.put("America/Swift_Current", tz); + timezones0.put("America/Tegucigalpa", tz); + timezones0.put("Pacific/Galapagos", tz); + tz = new SimpleTimeZone + (-6000 * 3600, "CST", + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("CST", tz); + timezones0.put("CST6CDT", tz); + timezones0.put("America/Chicago", tz); + timezones0.put("America/Indiana/Knox", tz); + timezones0.put("America/Indiana/Petersburg", tz); + timezones0.put("America/Indiana/Vincennes", tz); + timezones0.put("America/Menominee", tz); + timezones0.put("America/North_Dakota/Center", tz); + timezones0.put("America/North_Dakota/New_Salem", tz); + timezones0.put("America/Rainy_River", tz); + timezones0.put("America/Rankin_Inlet", tz); + timezones0.put("America/Winnipeg", tz); + tz = new SimpleTimeZone + (-6000 * 3600, "Pacific/Easter", + Calendar.OCTOBER, 2, Calendar.SATURDAY, 22000 * 3600, + Calendar.MARCH, 2, Calendar.SATURDAY, 22000 * 3600); + timezones0.put("Pacific/Easter", tz); + tz = new SimpleTimeZone(-5000 * 3600, "EST5"); + timezones0.put("EST5", tz); + timezones0.put("IET", tz); + timezones0.put("America/Atikokan", tz); + timezones0.put("America/Bogota", tz); + timezones0.put("America/Cayman", tz); + timezones0.put("America/Eirunepe", tz); + timezones0.put("America/Guayaquil", tz); + timezones0.put("America/Jamaica", tz); + timezones0.put("America/Lima", tz); + timezones0.put("America/Panama", tz); + timezones0.put("America/Rio_Branco", tz); + tz = new SimpleTimeZone + (-5000 * 3600, "America/Havana", + Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600); + timezones0.put("America/Havana", tz); + tz = new SimpleTimeZone + (-5000 * 3600, "America/Grand_Turk", + Calendar.APRIL, 1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Grand_Turk", tz); + timezones0.put("America/Port-au-Prince", tz); + tz = new SimpleTimeZone + (-5000 * 3600, "EST", + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("EST", tz); + timezones0.put("EST5EDT", tz); + timezones0.put("America/Detroit", tz); + timezones0.put("America/Indiana/Indianapolis", tz); + timezones0.put("America/Indiana/Marengo", tz); + timezones0.put("America/Indiana/Vevay", tz); + timezones0.put("America/Iqaluit", tz); + timezones0.put("America/Kentucky/Louisville", tz); + timezones0.put("America/Kentucky/Monticello", tz); + timezones0.put("America/Montreal", tz); + timezones0.put("America/Nassau", tz); + timezones0.put("America/New_York", tz); + timezones0.put("America/Nipigon", tz); + timezones0.put("America/Pangnirtung", tz); + timezones0.put("America/Thunder_Bay", tz); + timezones0.put("America/Toronto", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Asuncion", + Calendar.OCTOBER, 3, Calendar.SUNDAY, 0 * 3600, + Calendar.MARCH, 2, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Asuncion", tz); + tz = new SimpleTimeZone(-4000 * 3600, "PRT"); + timezones0.put("PRT", tz); + timezones0.put("America/Anguilla", tz); + timezones0.put("America/Antigua", tz); + timezones0.put("America/Aruba", tz); + timezones0.put("America/Barbados", tz); + timezones0.put("America/Blanc-Sablon", tz); + timezones0.put("America/Boa_Vista", tz); + timezones0.put("America/Caracas", tz); + timezones0.put("America/Curacao", tz); + timezones0.put("America/Dominica", tz); + timezones0.put("America/Grenada", tz); + timezones0.put("America/Guadeloupe", tz); + timezones0.put("America/Guyana", tz); + timezones0.put("America/La_Paz", tz); + timezones0.put("America/Manaus", tz); + timezones0.put("America/Martinique", tz); + timezones0.put("America/Montserrat", tz); + timezones0.put("America/Port_of_Spain", tz); + timezones0.put("America/Porto_Velho", tz); + timezones0.put("America/Puerto_Rico", tz); + timezones0.put("America/Santo_Domingo", tz); + timezones0.put("America/St_Kitts", tz); + timezones0.put("America/St_Lucia", tz); + timezones0.put("America/St_Thomas", tz); + timezones0.put("America/St_Vincent", tz); + timezones0.put("America/Tortola", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Campo_Grande", + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 0 * 3600, + Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Campo_Grande", tz); + timezones0.put("America/Cuiaba", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Goose_Bay", + Calendar.MARCH, 2, Calendar.SUNDAY, 60000, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 60000); + timezones0.put("America/Goose_Bay", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Glace_Bay", + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Glace_Bay", tz); + timezones0.put("America/Halifax", tz); + timezones0.put("America/Moncton", tz); + timezones0.put("America/Thule", tz); + timezones0.put("Atlantic/Bermuda", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "America/Santiago", + Calendar.OCTOBER, 9, -Calendar.SUNDAY, 0 * 3600, + Calendar.MARCH, 9, -Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Santiago", tz); + timezones0.put("Antarctica/Palmer", tz); + tz = new SimpleTimeZone + (-4000 * 3600, "Atlantic/Stanley", + Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.APRIL, 3, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Atlantic/Stanley", tz); + tz = new SimpleTimeZone + (-3500 * 3600, "CNT", + Calendar.MARCH, 2, Calendar.SUNDAY, 60000, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 60000); + timezones0.put("CNT", tz); + timezones0.put("America/St_Johns", tz); + tz = new SimpleTimeZone + (-3000 * 3600, "America/Godthab", + Calendar.MARCH, 30, -Calendar.SATURDAY, 22000 * 3600, + Calendar.OCTOBER, 30, -Calendar.SATURDAY, 23000 * 3600); + timezones0.put("America/Godthab", tz); + tz = new SimpleTimeZone + (-3000 * 3600, "America/Miquelon", + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600, + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Miquelon", tz); + tz = new SimpleTimeZone + (-3000 * 3600, "America/Montevideo", + Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, 2, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("America/Montevideo", tz); + tz = new SimpleTimeZone + (-3000 * 3600, "America/Sao_Paulo", + Calendar.NOVEMBER, 1, Calendar.SUNDAY, 0 * 3600, + Calendar.FEBRUARY, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("America/Sao_Paulo", tz); + tz = new SimpleTimeZone(-3000 * 3600, "AGT"); + timezones0.put("AGT", tz); + timezones0.put("America/Araguaina", tz); + timezones0.put("America/Argentina/Buenos_Aires", tz); + timezones0.put("America/Argentina/Catamarca", tz); + timezones0.put("America/Argentina/Cordoba", tz); + timezones0.put("America/Argentina/Jujuy", tz); + timezones0.put("America/Argentina/La_Rioja", tz); + timezones0.put("America/Argentina/Mendoza", tz); + timezones0.put("America/Argentina/Rio_Gallegos", tz); + timezones0.put("America/Argentina/San_Juan", tz); + timezones0.put("America/Argentina/Tucuman", tz); + timezones0.put("America/Argentina/Ushuaia", tz); + timezones0.put("America/Bahia", tz); + timezones0.put("America/Belem", tz); + timezones0.put("America/Cayenne", tz); + timezones0.put("America/Fortaleza", tz); + timezones0.put("America/Maceio", tz); + timezones0.put("America/Paramaribo", tz); + timezones0.put("America/Recife", tz); + timezones0.put("Antarctica/Rothera", tz); + tz = new SimpleTimeZone(-2000 * 3600, "America/Noronha"); + timezones0.put("America/Noronha", tz); + timezones0.put("Atlantic/South_Georgia", tz); + tz = new SimpleTimeZone + (-1000 * 3600, "America/Scoresbysund", + Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 1000 * 3600); + timezones0.put("America/Scoresbysund", tz); + timezones0.put("Atlantic/Azores", tz); + tz = new SimpleTimeZone(-1000 * 3600, "Atlantic/Cape_Verde"); + timezones0.put("Atlantic/Cape_Verde", tz); + tz = new SimpleTimeZone(0 * 3600, "GMT"); + timezones0.put("GMT", tz); + timezones0.put("UTC", tz); + timezones0.put("Africa/Abidjan", tz); + timezones0.put("Africa/Accra", tz); + timezones0.put("Africa/Bamako", tz); + timezones0.put("Africa/Banjul", tz); + timezones0.put("Africa/Bissau", tz); + timezones0.put("Africa/Casablanca", tz); + timezones0.put("Africa/Conakry", tz); + timezones0.put("Africa/Dakar", tz); + timezones0.put("Africa/El_Aaiun", tz); + timezones0.put("Africa/Freetown", tz); + timezones0.put("Africa/Lome", tz); + timezones0.put("Africa/Monrovia", tz); + timezones0.put("Africa/Nouakchott", tz); + timezones0.put("Africa/Ouagadougou", tz); + timezones0.put("Africa/Sao_Tome", tz); + timezones0.put("America/Danmarkshavn", tz); + timezones0.put("Atlantic/Reykjavik", tz); + timezones0.put("Atlantic/St_Helena", tz); + tz = new SimpleTimeZone + (0 * 3600, "WET", + Calendar.MARCH, -1, Calendar.SUNDAY, 1000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("WET", tz); + timezones0.put("Atlantic/Canary", tz); + timezones0.put("Atlantic/Faroe", tz); + timezones0.put("Atlantic/Madeira", tz); + timezones0.put("Europe/Dublin", tz); + timezones0.put("Europe/Guernsey", tz); + timezones0.put("Europe/Isle_of_Man", tz); + timezones0.put("Europe/Jersey", tz); + timezones0.put("Europe/Lisbon", tz); + timezones0.put("Europe/London", tz); + tz = new SimpleTimeZone(1000 * 3600, "Africa/Algiers"); + timezones0.put("Africa/Algiers", tz); + timezones0.put("Africa/Bangui", tz); + timezones0.put("Africa/Brazzaville", tz); + timezones0.put("Africa/Douala", tz); + timezones0.put("Africa/Kinshasa", tz); + timezones0.put("Africa/Lagos", tz); + timezones0.put("Africa/Libreville", tz); + timezones0.put("Africa/Luanda", tz); + timezones0.put("Africa/Malabo", tz); + timezones0.put("Africa/Ndjamena", tz); + timezones0.put("Africa/Niamey", tz); + timezones0.put("Africa/Porto-Novo", tz); + tz = new SimpleTimeZone + (1000 * 3600, "Africa/Windhoek", + Calendar.SEPTEMBER, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.APRIL, 1, Calendar.SUNDAY, 2000 * 3600); + timezones0.put("Africa/Windhoek", tz); + tz = new SimpleTimeZone + (1000 * 3600, "CET", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("CET", tz); + timezones0.put("ECT", tz); + timezones0.put("MET", tz); + timezones0.put("Africa/Ceuta", tz); + timezones0.put("Africa/Tunis", tz); + timezones0.put("Arctic/Longyearbyen", tz); + timezones0.put("Atlantic/Jan_Mayen", tz); + timezones0.put("Europe/Amsterdam", tz); + timezones0.put("Europe/Andorra", tz); + timezones0.put("Europe/Belgrade", tz); + timezones0.put("Europe/Berlin", tz); + timezones0.put("Europe/Bratislava", tz); + timezones0.put("Europe/Brussels", tz); + timezones0.put("Europe/Budapest", tz); + timezones0.put("Europe/Copenhagen", tz); + timezones0.put("Europe/Gibraltar", tz); + timezones0.put("Europe/Ljubljana", tz); + timezones0.put("Europe/Luxembourg", tz); + timezones0.put("Europe/Madrid", tz); + timezones0.put("Europe/Malta", tz); + timezones0.put("Europe/Monaco", tz); + timezones0.put("Europe/Oslo", tz); + timezones0.put("Europe/Paris", tz); + timezones0.put("Europe/Podgorica", tz); + timezones0.put("Europe/Prague", tz); + timezones0.put("Europe/Rome", tz); + timezones0.put("Europe/San_Marino", tz); + timezones0.put("Europe/Sarajevo", tz); + timezones0.put("Europe/Skopje", tz); + timezones0.put("Europe/Stockholm", tz); + timezones0.put("Europe/Tirane", tz); + timezones0.put("Europe/Vaduz", tz); + timezones0.put("Europe/Vatican", tz); + timezones0.put("Europe/Vienna", tz); + timezones0.put("Europe/Warsaw", tz); + timezones0.put("Europe/Zagreb", tz); + timezones0.put("Europe/Zurich", tz); + tz = new SimpleTimeZone + (2000 * 3600, "ART", + Calendar.APRIL, -1, Calendar.FRIDAY, 0 * 3600, + Calendar.SEPTEMBER, -1, Calendar.THURSDAY, 24000 * 3600); + timezones0.put("ART", tz); + timezones0.put("Africa/Cairo", tz); + tz = new SimpleTimeZone(2000 * 3600, "CAT"); + timezones0.put("CAT", tz); + timezones0.put("Africa/Blantyre", tz); + timezones0.put("Africa/Bujumbura", tz); + timezones0.put("Africa/Gaborone", tz); + timezones0.put("Africa/Harare", tz); + timezones0.put("Africa/Johannesburg", tz); + timezones0.put("Africa/Kigali", tz); + timezones0.put("Africa/Lubumbashi", tz); + timezones0.put("Africa/Lusaka", tz); + timezones0.put("Africa/Maputo", tz); + timezones0.put("Africa/Maseru", tz); + timezones0.put("Africa/Mbabane", tz); + timezones0.put("Africa/Tripoli", tz); + timezones0.put("Asia/Jerusalem", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Amman", + Calendar.MARCH, -1, Calendar.THURSDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.FRIDAY, 1000 * 3600); + timezones0.put("Asia/Amman", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Beirut", + Calendar.MARCH, -1, Calendar.SUNDAY, 0 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 0 * 3600); + timezones0.put("Asia/Beirut", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Damascus", + Calendar.APRIL, 1, 0, 0 * 3600, + Calendar.OCTOBER, 1, 0, 0 * 3600); + timezones0.put("Asia/Damascus", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Asia/Gaza", + Calendar.APRIL, 1, 0, 0 * 3600, + Calendar.OCTOBER, 3, Calendar.FRIDAY, 0 * 3600); + timezones0.put("Asia/Gaza", tz); + tz = new SimpleTimeZone + (2000 * 3600, "EET", + Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 4000 * 3600); + timezones0.put("EET", tz); + timezones0.put("Asia/Istanbul", tz); + timezones0.put("Asia/Nicosia", tz); + timezones0.put("Europe/Athens", tz); + timezones0.put("Europe/Bucharest", tz); + timezones0.put("Europe/Chisinau", tz); + timezones0.put("Europe/Helsinki", tz); + timezones0.put("Europe/Istanbul", tz); + timezones0.put("Europe/Kiev", tz); + timezones0.put("Europe/Mariehamn", tz); + timezones0.put("Europe/Nicosia", tz); + timezones0.put("Europe/Riga", tz); + timezones0.put("Europe/Simferopol", tz); + timezones0.put("Europe/Sofia", tz); + timezones0.put("Europe/Tallinn", tz); + timezones0.put("Europe/Uzhgorod", tz); + timezones0.put("Europe/Vilnius", tz); + timezones0.put("Europe/Zaporozhye", tz); + tz = new SimpleTimeZone + (2000 * 3600, "Europe/Kaliningrad", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Europe/Kaliningrad", tz); + timezones0.put("Europe/Minsk", tz); + tz = new SimpleTimeZone + (3000 * 3600, "Asia/Baghdad", + Calendar.APRIL, 1, 0, 3000 * 3600, + Calendar.OCTOBER, 1, 0, 4000 * 3600); + timezones0.put("Asia/Baghdad", tz); + tz = new SimpleTimeZone + (3000 * 3600, "Europe/Moscow", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Europe/Moscow", tz); + timezones0.put("Europe/Volgograd", tz); + tz = new SimpleTimeZone(3000 * 3600, "EAT"); + timezones0.put("EAT", tz); + timezones0.put("Africa/Addis_Ababa", tz); + timezones0.put("Africa/Asmara", tz); + timezones0.put("Africa/Dar_es_Salaam", tz); + timezones0.put("Africa/Djibouti", tz); + timezones0.put("Africa/Kampala", tz); + timezones0.put("Africa/Khartoum", tz); + timezones0.put("Africa/Mogadishu", tz); + timezones0.put("Africa/Nairobi", tz); + timezones0.put("Antarctica/Syowa", tz); + timezones0.put("Asia/Aden", tz); + timezones0.put("Asia/Bahrain", tz); + timezones0.put("Asia/Kuwait", tz); + timezones0.put("Asia/Qatar", tz); + timezones0.put("Asia/Riyadh", tz); + timezones0.put("Indian/Antananarivo", tz); + timezones0.put("Indian/Comoro", tz); + timezones0.put("Indian/Mayotte", tz); + tz = new SimpleTimeZone(3500 * 3600, "Asia/Tehran"); + timezones0.put("Asia/Tehran", tz); + tz = new SimpleTimeZone + (4000 * 3600, "Asia/Baku", + Calendar.MARCH, -1, Calendar.SUNDAY, 4000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 5000 * 3600); + timezones0.put("Asia/Baku", tz); + tz = new SimpleTimeZone + (4000 * 3600, "Asia/Yerevan", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Yerevan", tz); + timezones0.put("Europe/Samara", tz); + tz = new SimpleTimeZone(4000 * 3600, "NET"); + timezones0.put("NET", tz); + timezones0.put("Asia/Dubai", tz); + timezones0.put("Asia/Muscat", tz); + timezones0.put("Asia/Tbilisi", tz); + timezones0.put("Indian/Mahe", tz); + timezones0.put("Indian/Mauritius", tz); + timezones0.put("Indian/Reunion", tz); + tz = new SimpleTimeZone(4500 * 3600, "Asia/Kabul"); + timezones0.put("Asia/Kabul", tz); + tz = new SimpleTimeZone + (5000 * 3600, "Asia/Yekaterinburg", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Yekaterinburg", tz); + tz = new SimpleTimeZone(5000 * 3600, "PLT"); + timezones0.put("PLT", tz); + timezones0.put("Asia/Aqtau", tz); + timezones0.put("Asia/Aqtobe", tz); + timezones0.put("Asia/Ashgabat", tz); + timezones0.put("Asia/Dushanbe", tz); + timezones0.put("Asia/Karachi", tz); + timezones0.put("Asia/Oral", tz); + timezones0.put("Asia/Samarkand", tz); + timezones0.put("Asia/Tashkent", tz); + timezones0.put("Indian/Kerguelen", tz); + timezones0.put("Indian/Maldives", tz); + tz = new SimpleTimeZone(5500 * 3600, "BST"); + timezones0.put("BST", tz); + timezones0.put("IST", tz); + timezones0.put("Asia/Calcutta", tz); + timezones0.put("Asia/Colombo", tz); + tz = new SimpleTimeZone(5750 * 3600, "Asia/Katmandu"); + timezones0.put("Asia/Katmandu", tz); + tz = new SimpleTimeZone(6000 * 3600, "Antarctica/Mawson"); + timezones0.put("Antarctica/Mawson", tz); + timezones0.put("Antarctica/Vostok", tz); + timezones0.put("Asia/Almaty", tz); + timezones0.put("Asia/Bishkek", tz); + timezones0.put("Asia/Dhaka", tz); + timezones0.put("Asia/Qyzylorda", tz); + timezones0.put("Asia/Thimphu", tz); + timezones0.put("Indian/Chagos", tz); + tz = new SimpleTimeZone + (6000 * 3600, "Asia/Novosibirsk", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Novosibirsk", tz); + timezones0.put("Asia/Omsk", tz); + tz = new SimpleTimeZone(6500 * 3600, "Asia/Rangoon"); + timezones0.put("Asia/Rangoon", tz); + timezones0.put("Indian/Cocos", tz); + tz = new SimpleTimeZone(7000 * 3600, "VST"); + timezones0.put("VST", tz); + timezones0.put("Antarctica/Davis", tz); + timezones0.put("Asia/Bangkok", tz); + timezones0.put("Asia/Jakarta", tz); + timezones0.put("Asia/Phnom_Penh", tz); + timezones0.put("Asia/Pontianak", tz); + timezones0.put("Asia/Saigon", tz); + timezones0.put("Asia/Vientiane", tz); + timezones0.put("Indian/Christmas", tz); + tz = new SimpleTimeZone + (7000 * 3600, "Asia/Hovd", + Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600, + Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600); + timezones0.put("Asia/Hovd", tz); + tz = new SimpleTimeZone + (7000 * 3600, "Asia/Krasnoyarsk", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Krasnoyarsk", tz); + tz = new SimpleTimeZone(8000 * 3600, "CTT"); + timezones0.put("CTT", tz); + timezones0.put("Antarctica/Casey", tz); + timezones0.put("Asia/Brunei", tz); + timezones0.put("Asia/Chongqing", tz); + timezones0.put("Asia/Harbin", tz); + timezones0.put("Asia/Hong_Kong", tz); + timezones0.put("Asia/Kashgar", tz); + timezones0.put("Asia/Kuala_Lumpur", tz); + timezones0.put("Asia/Kuching", tz); + timezones0.put("Asia/Macau", tz); + timezones0.put("Asia/Makassar", tz); + timezones0.put("Asia/Manila", tz); + timezones0.put("Asia/Shanghai", tz); + timezones0.put("Asia/Singapore", tz); + timezones0.put("Asia/Taipei", tz); + timezones0.put("Asia/Urumqi", tz); + timezones0.put("Australia/Perth", tz); + tz = new SimpleTimeZone + (8000 * 3600, "Asia/Irkutsk", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Irkutsk", tz); + tz = new SimpleTimeZone + (8000 * 3600, "Asia/Ulaanbaatar", + Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600, + Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600); + timezones0.put("Asia/Ulaanbaatar", tz); + tz = new SimpleTimeZone(8750 * 3600, "Australia/Eucla"); + timezones0.put("Australia/Eucla", tz); + tz = new SimpleTimeZone + (9000 * 3600, "Asia/Choibalsan", + Calendar.MARCH, -1, Calendar.SATURDAY, 2000 * 3600, + Calendar.SEPTEMBER, -1, Calendar.SATURDAY, 2000 * 3600); + timezones0.put("Asia/Choibalsan", tz); + tz = new SimpleTimeZone(9000 * 3600, "JST"); + timezones0.put("JST", tz); + timezones0.put("Asia/Dili", tz); + timezones0.put("Asia/Jayapura", tz); + timezones0.put("Asia/Pyongyang", tz); + timezones0.put("Asia/Seoul", tz); + timezones0.put("Asia/Tokyo", tz); + timezones0.put("Pacific/Palau", tz); + tz = new SimpleTimeZone + (9000 * 3600, "Asia/Yakutsk", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Yakutsk", tz); + tz = new SimpleTimeZone + (9500 * 3600, "Australia/Adelaide", + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Australia/Adelaide", tz); + timezones0.put("Australia/Broken_Hill", tz); + tz = new SimpleTimeZone(9500 * 3600, "ACT"); + timezones0.put("ACT", tz); + timezones0.put("Australia/Darwin", tz); + tz = new SimpleTimeZone(10000 * 3600, "Antarctica/DumontDUrville"); + timezones0.put("Antarctica/DumontDUrville", tz); + timezones0.put("Australia/Brisbane", tz); + timezones0.put("Australia/Lindeman", tz); + timezones0.put("Pacific/Guam", tz); + timezones0.put("Pacific/Port_Moresby", tz); + timezones0.put("Pacific/Saipan", tz); + timezones0.put("Pacific/Truk", tz); + tz = new SimpleTimeZone + (10000 * 3600, "Asia/Sakhalin", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Sakhalin", tz); + timezones0.put("Asia/Vladivostok", tz); + tz = new SimpleTimeZone + (10000 * 3600, "Australia/Currie", + Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Australia/Currie", tz); + timezones0.put("Australia/Hobart", tz); + tz = new SimpleTimeZone + (10000 * 3600, "AET", + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("AET", tz); + timezones0.put("Australia/Melbourne", tz); + timezones0.put("Australia/Sydney", tz); + tz = new SimpleTimeZone + (10500 * 3600, "Australia/Lord_Howe", + Calendar.OCTOBER, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, 500 * 3600); + timezones0.put("Australia/Lord_Howe", tz); + tz = new SimpleTimeZone + (11000 * 3600, "Asia/Magadan", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Magadan", tz); + tz = new SimpleTimeZone(11000 * 3600, "SST"); + timezones0.put("SST", tz); + timezones0.put("Pacific/Efate", tz); + timezones0.put("Pacific/Guadalcanal", tz); + timezones0.put("Pacific/Kosrae", tz); + timezones0.put("Pacific/Noumea", tz); + timezones0.put("Pacific/Ponape", tz); + tz = new SimpleTimeZone(11500 * 3600, "Pacific/Norfolk"); + timezones0.put("Pacific/Norfolk", tz); + tz = new SimpleTimeZone + (12000 * 3600, "NST", + Calendar.OCTOBER, 1, Calendar.SUNDAY, 2000 * 3600, + Calendar.MARCH, 3, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("NST", tz); + timezones0.put("Antarctica/McMurdo", tz); + timezones0.put("Antarctica/South_Pole", tz); + timezones0.put("Pacific/Auckland", tz); + tz = new SimpleTimeZone + (12000 * 3600, "Asia/Anadyr", + Calendar.MARCH, -1, Calendar.SUNDAY, 2000 * 3600, + Calendar.OCTOBER, -1, Calendar.SUNDAY, 3000 * 3600); + timezones0.put("Asia/Anadyr", tz); + timezones0.put("Asia/Kamchatka", tz); + tz = new SimpleTimeZone(12000 * 3600, "Pacific/Fiji"); + timezones0.put("Pacific/Fiji", tz); + timezones0.put("Pacific/Funafuti", tz); + timezones0.put("Pacific/Kwajalein", tz); + timezones0.put("Pacific/Majuro", tz); + timezones0.put("Pacific/Nauru", tz); + timezones0.put("Pacific/Tarawa", tz); + timezones0.put("Pacific/Wake", tz); + timezones0.put("Pacific/Wallis", tz); + tz = new SimpleTimeZone + (12750 * 3600, "Pacific/Chatham", + Calendar.OCTOBER, 1, Calendar.SUNDAY, 2750 * 3600, + Calendar.MARCH, 3, Calendar.SUNDAY, 3750 * 3600); + timezones0.put("Pacific/Chatham", tz); + tz = new SimpleTimeZone(13000 * 3600, "Pacific/Enderbury"); + timezones0.put("Pacific/Enderbury", tz); + timezones0.put("Pacific/Tongatapu", tz); + tz = new SimpleTimeZone(14000 * 3600, "Pacific/Kiritimati"); + timezones0.put("Pacific/Kiritimati", tz); + } + return timezones0; + } + + /** + * Maps a time zone name (with optional GMT offset and daylight time + * zone name) to one of the known time zones. This method called + * with the result of System.getProperty("user.timezone") + * or getDefaultTimeZoneId(). Note that giving one of + * the standard tz data names from ftp://elsie.nci.nih.gov/pub/ is + * preferred. + * The time zone name can be given as follows: + * (standard zone name)[(GMT offset)[(DST zone name)[DST offset]]] + * + *

        + * If only a (standard zone name) is given (no numbers in the + * String) then it gets mapped directly to the TimeZone with that + * name, if that fails null is returned. + *

        + * Alternately, a POSIX-style TZ string can be given, defining the time zone: + * std offset dst offset,date/time,date/time + * See the glibc manual, or the man page for tzset for details + * of this format. + *

        + * A GMT offset is the offset to add to the local time to get GMT. + * If a (GMT offset) is included (either in seconds or hours) then + * an attempt is made to find a TimeZone name matching both the name + * and the offset (that doesn't observe daylight time, if the + * timezone observes daylight time then you must include a daylight + * time zone name after the offset), if that fails then a TimeZone + * with the given GMT offset is returned (whether or not the + * TimeZone observes daylight time is ignored), if that also fails + * the GMT TimeZone is returned. + *

        + * If the String ends with (GMT offset)(daylight time zone name) + * then an attempt is made to find a TimeZone with the given name and + * GMT offset that also observes (the daylight time zone name is not + * currently used in any other way), if that fails a TimeZone with + * the given GMT offset that observes daylight time is returned, if + * that also fails the GMT TimeZone is returned. + *

        + * Examples: In Chicago, the time zone id could be "CST6CDT", but + * the preferred name would be "America/Chicago". In Indianapolis + * (which does not have Daylight Savings Time) the string could be + * "EST5", but the preferred name would be "America/Indianapolis". + * The standard time zone name for The Netherlands is "Europe/Amsterdam", + * but can also be given as "CET-1CEST". + */ + static TimeZone getDefaultTimeZone(String sysTimeZoneId) + { + String stdName = null; + int stdOffs; + int dstOffs; + try + { + int idLength = sysTimeZoneId.length(); + + int index = 0; + int prevIndex; + char c; + + // get std + do + c = sysTimeZoneId.charAt(index); + while (c != '+' && c != '-' && c != ',' && c != ':' + && ! Character.isDigit(c) && c != '\0' && ++index < idLength); + + if (index >= idLength) + return getTimeZoneInternal(sysTimeZoneId); + + stdName = sysTimeZoneId.substring(0, index); + prevIndex = index; + + // get the std offset + do + c = sysTimeZoneId.charAt(index++); + while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c)) + && index < idLength); + if (index < idLength) + index--; + + { // convert the dst string to a millis number + String offset = sysTimeZoneId.substring(prevIndex, index); + prevIndex = index; + + if (offset.charAt(0) == '+' || offset.charAt(0) == '-') + stdOffs = parseTime(offset.substring(1)); + else + stdOffs = parseTime(offset); + + if (offset.charAt(0) == '-') + stdOffs = -stdOffs; + + // TZ timezone offsets are positive when WEST of the meridian. + stdOffs = -stdOffs; + } + + // Done yet? (Format: std offset) + if (index >= idLength) + { + // Do we have an existing timezone with that name and offset? + TimeZone tz = getTimeZoneInternal(stdName); + if (tz != null) + if (tz.getRawOffset() == stdOffs) + return tz; + + // Custom then. + return new SimpleTimeZone(stdOffs, stdName); + } + + // get dst + do + c = sysTimeZoneId.charAt(index); + while (c != '+' && c != '-' && c != ',' && c != ':' + && ! Character.isDigit(c) && c != '\0' && ++index < idLength); + + // Done yet? (Format: std offset dst) + if (index >= idLength) + { + // Do we have an existing timezone with that name and offset + // which has DST? + TimeZone tz = getTimeZoneInternal(stdName); + if (tz != null) + if (tz.getRawOffset() == stdOffs && tz.useDaylightTime()) + return tz; + + // Custom then. + return new SimpleTimeZone(stdOffs, stdName); + } + + // get the dst offset + prevIndex = index; + do + c = sysTimeZoneId.charAt(index++); + while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c)) + && index < idLength); + if (index < idLength) + index--; + + if (index == prevIndex && (c == ',' || c == ';')) + { + // Missing dst offset defaults to one hour ahead of standard + // time. + dstOffs = stdOffs + 60 * 60 * 1000; + } + else + { // convert the dst string to a millis number + String offset = sysTimeZoneId.substring(prevIndex, index); + prevIndex = index; + + if (offset.charAt(0) == '+' || offset.charAt(0) == '-') + dstOffs = parseTime(offset.substring(1)); + else + dstOffs = parseTime(offset); + + if (offset.charAt(0) == '-') + dstOffs = -dstOffs; + + // TZ timezone offsets are positive when WEST of the meridian. + dstOffs = -dstOffs; + } + + // Done yet? (Format: std offset dst offset) + // FIXME: We don't support DST without a rule given. Should we? + if (index >= idLength) + { + // Time Zone existing with same name, dst and offsets? + TimeZone tz = getTimeZoneInternal(stdName); + if (tz != null) + if (tz.getRawOffset() == stdOffs && tz.useDaylightTime() + && tz.getDSTSavings() == (dstOffs - stdOffs)) + return tz; + + return new SimpleTimeZone(stdOffs, stdName); + } + + // get the DST rule + if (sysTimeZoneId.charAt(index) == ',' + || sysTimeZoneId.charAt(index) == ';') + { + index++; + int offs = index; + while (sysTimeZoneId.charAt(index) != ',' + && sysTimeZoneId.charAt(index) != ';') + index++; + String startTime = sysTimeZoneId.substring(offs, index); + index++; + String endTime = sysTimeZoneId.substring(index); + + index = startTime.indexOf('/'); + int startMillis; + int endMillis; + String startDate; + String endDate; + if (index != -1) + { + startDate = startTime.substring(0, index); + startMillis = parseTime(startTime.substring(index + 1)); + } + else + { + startDate = startTime; + // if time isn't given, default to 2:00:00 AM. + startMillis = 2 * 60 * 60 * 1000; + } + index = endTime.indexOf('/'); + if (index != -1) + { + endDate = endTime.substring(0, index); + endMillis = parseTime(endTime.substring(index + 1)); + } + else + { + endDate = endTime; + // if time isn't given, default to 2:00:00 AM. + endMillis = 2 * 60 * 60 * 1000; + } + + int[] start = getDateParams(startDate); + int[] end = getDateParams(endDate); + return new SimpleTimeZone(stdOffs, stdName, start[0], start[1], + start[2], startMillis, end[0], end[1], + end[2], endMillis, (dstOffs - stdOffs)); + } + } + + // FIXME: Produce a warning here? + catch (IndexOutOfBoundsException _) + { + } + catch (NumberFormatException _) + { + } + + return null; + } + + /** + * Parses and returns the params for a POSIX TZ date field, + * in the format int[]{ month, day, dayOfWeek }, following the + * SimpleTimeZone constructor rules. + */ + private static int[] getDateParams(String date) + { + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int month; + + if (date.charAt(0) == 'M' || date.charAt(0) == 'm') + { + int day; + + // Month, week of month, day of week + + // "Mm.w.d". d is between 0 (Sunday) and 6. Week w is + // between 1 and 5; Week 1 is the first week in which day d + // occurs and Week 5 specifies the last d day in the month. + // Month m is between 1 and 12. + + month = Integer.parseInt(date.substring(1, date.indexOf('.'))); + int week = Integer.parseInt(date.substring(date.indexOf('.') + 1, + date.lastIndexOf('.'))); + int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.') + + 1)); + dayOfWeek++; // Java day of week is one-based, Sunday is first day. + + if (week == 5) + day = -1; // last day of month is -1 in java, 5 in TZ + else + { + // First day of week starting on or after. For example, + // to specify the second Sunday of April, set month to + // APRIL, day-of-month to 8, and day-of-week to -SUNDAY. + day = (week - 1) * 7 + 1; + dayOfWeek = -dayOfWeek; + } + + month--; // Java month is zero-based. + return new int[] { month, day, dayOfWeek }; + } + + // julian day, either zero-based 0<=n<=365 (incl feb 29) + // or one-based 1<=n<=365 (no feb 29) + int julianDay; // Julian day, + + if (date.charAt(0) != 'J' || date.charAt(0) != 'j') + { + julianDay = Integer.parseInt(date.substring(1)); + julianDay++; // make 1-based + // Adjust day count to include feb 29. + dayCount = new int[] + { + 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 + }; + } + else + // 1-based julian day + julianDay = Integer.parseInt(date); + + int i = 11; + while (i > 0) + if (dayCount[i] < julianDay) + break; + else + i--; + julianDay -= dayCount[i]; + month = i; + return new int[] { month, julianDay, 0 }; + } + + /** + * Parses a time field hh[:mm[:ss]], returning the result + * in milliseconds. No leading sign. + */ + private static int parseTime(String time) + { + int millis = 0; + int i = 0; + + while (i < time.length()) + if (time.charAt(i) == ':') + break; + else + i++; + millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i)); + if (i >= time.length()) + return millis; + + int iprev = ++i; + while (i < time.length()) + if (time.charAt(i) == ':') + break; + else + i++; + millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i)); + if (i >= time.length()) + return millis; + + millis += 1000 * Integer.parseInt(time.substring(++i)); + return millis; + } + + /** + * Gets the time zone offset, for current date, modified in case of + * daylight savings. This is the offset to add to UTC to get the local + * time. + * @param era the era of the given date + * @param year the year of the given date + * @param month the month of the given date, 0 for January. + * @param day the day of month + * @param dayOfWeek the day of week + * @param milliseconds the millis in the day (in local standard time) + * @return the time zone offset in milliseconds. + */ + public abstract int getOffset(int era, int year, int month, + int day, int dayOfWeek, int milliseconds); + + /** + * Get the time zone offset for the specified date, modified in case of + * daylight savings. This is the offset to add to UTC to get the local + * time. + * @param date the date represented in millisecends + * since January 1, 1970 00:00:00 GMT. + * @since 1.4 + */ + public int getOffset(long date) + { + return (inDaylightTime(new Date(date)) + ? getRawOffset() + getDSTSavings() + : getRawOffset()); + } + + /** + * Gets the time zone offset, ignoring daylight savings. This is + * the offset to add to UTC to get the local time. + * @return the time zone offset in milliseconds. + */ + public abstract int getRawOffset(); + + /** + * Sets the time zone offset, ignoring daylight savings. This is + * the offset to add to UTC to get the local time. + * @param offsetMillis the time zone offset to GMT. + */ + public abstract void setRawOffset(int offsetMillis); + + /** + * Gets the identifier of this time zone. For instance, PST for + * Pacific Standard Time. + * @returns the ID of this time zone. + */ + public String getID() + { + return ID; + } + + /** + * Sets the identifier of this time zone. For instance, PST for + * Pacific Standard Time. + * @param id the new time zone ID. + * @throws NullPointerException if id is null + */ + public void setID(String id) + { + if (id == null) + throw new NullPointerException(); + + this.ID = id; + } + + /** + * This method returns a string name of the time zone suitable + * for displaying to the user. The string returned will be the long + * description of the timezone in the current locale. The name + * displayed will assume daylight savings time is not in effect. + * + * @return The name of the time zone. + */ + public final String getDisplayName() + { + return (getDisplayName(false, LONG, Locale.getDefault())); + } + + /** + * This method returns a string name of the time zone suitable + * for displaying to the user. The string returned will be the long + * description of the timezone in the specified locale. The name + * displayed will assume daylight savings time is not in effect. + * + * @param locale The locale for this timezone name. + * + * @return The name of the time zone. + */ + public final String getDisplayName(Locale locale) + { + return (getDisplayName(false, LONG, locale)); + } + + /** + * This method returns a string name of the time zone suitable + * for displaying to the user. The string returned will be of the + * specified type in the current locale. + * + * @param dst Whether or not daylight savings time is in effect. + * @param style LONG for a long name, SHORT for + * a short abbreviation. + * + * @return The name of the time zone. + */ + public final String getDisplayName(boolean dst, int style) + { + return (getDisplayName(dst, style, Locale.getDefault())); + } + + + /** + * This method returns a string name of the time zone suitable + * for displaying to the user. The string returned will be of the + * specified type in the specified locale. + * + * @param dst Whether or not daylight savings time is in effect. + * @param style LONG for a long name, SHORT for + * a short abbreviation. + * @param locale The locale for this timezone name. + * + * @return The name of the time zone. + */ + public String getDisplayName(boolean dst, int style, Locale locale) + { + DateFormatSymbols dfs; + try + { + dfs = new DateFormatSymbols(locale); + + // The format of the value returned is defined by us. + String[][]zoneinfo = dfs.getZoneStrings(); + for (int i = 0; i < zoneinfo.length; i++) + { + if (zoneinfo[i][0].equals(getID())) + { + if (!dst) + { + if (style == SHORT) + return (zoneinfo[i][2]); + else + return (zoneinfo[i][1]); + } + else + { + if (style == SHORT) + return (zoneinfo[i][4]); + else + return (zoneinfo[i][3]); + } + } + } + } + catch (MissingResourceException e) + { + } + + return getDefaultDisplayName(dst); + } + + private String getDefaultDisplayName(boolean dst) + { + int offset = getRawOffset() + (dst ? getDSTSavings() : 0); + + CPStringBuilder sb = new CPStringBuilder(9); + sb.append("GMT"); + + offset = offset / (1000 * 60); + int hours = Math.abs(offset) / 60; + int minutes = Math.abs(offset) % 60; + + if (minutes != 0 || hours != 0) + { + sb.append(offset >= 0 ? '+' : '-'); + sb.append((char) ('0' + hours / 10)); + sb.append((char) ('0' + hours % 10)); + sb.append(':'); + sb.append((char) ('0' + minutes / 10)); + sb.append((char) ('0' + minutes % 10)); + } + + return sb.toString(); + } + + /** + * Returns true, if this time zone uses Daylight Savings Time. + */ + public abstract boolean useDaylightTime(); + + /** + * Returns true, if the given date is in Daylight Savings Time in this + * time zone. + * @param date the given Date. + */ + public abstract boolean inDaylightTime(Date date); + + /** + * Gets the daylight savings offset. This is a positive offset in + * milliseconds with respect to standard time. Typically this + * is one hour, but for some time zones this may be half an our. + *

        The default implementation returns 3600000 milliseconds + * (one hour) if the time zone uses daylight savings time + * (as specified by {@link #useDaylightTime()}), otherwise + * it returns 0. + * @return the daylight savings offset in milliseconds. + * @since 1.4 + */ + public int getDSTSavings () + { + return useDaylightTime () ? 3600000 : 0; + } + + /** + * Gets the TimeZone for the given ID. + * @param ID the time zone identifier. + * @return The time zone for the identifier or GMT, if no such time + * zone exists. + */ + private static TimeZone getTimeZoneInternal(String ID) + { + // First check timezones hash + TimeZone tz = null; + TimeZone tznew = null; + for (int pass = 0; pass < 2; pass++) + { + synchronized (TimeZone.class) + { + tz = (TimeZone) timezones().get(ID); + if (tz != null) + { + if (!tz.getID().equals(ID)) + { + // We always return a timezone with the requested ID. + // This is the same behaviour as with JDK1.2. + tz = (TimeZone) tz.clone(); + tz.setID(ID); + // We also save the alias, so that we return the same + // object again if getTimeZone is called with the same + // alias. + timezones().put(ID, tz); + } + return tz; + } + else if (tznew != null) + { + timezones().put(ID, tznew); + return tznew; + } + } + + if (pass == 1 || zoneinfo_dir == null) + return null; + + // aliases0 is never changing after first timezones(), so should + // be safe without synchronization. + String zonename = (String) aliases0.get(ID); + if (zonename == null) + zonename = ID; + + // Read the file outside of the critical section, it is expensive. + tznew = ZoneInfo.readTZFile (ID, zoneinfo_dir + + File.separatorChar + zonename); + if (tznew == null) + return null; + } + + return null; + } + + /** + * Gets the TimeZone for the given ID. + * @param ID the time zone identifier. + * @return The time zone for the identifier or GMT, if no such time + * zone exists. + */ + public static TimeZone getTimeZone(String ID) + { + // Check for custom IDs first + if (ID.startsWith("GMT") && ID.length() > 3) + { + int pos = 3; + int offset_direction = 1; + + if (ID.charAt(pos) == '-') + { + offset_direction = -1; + pos++; + } + else if (ID.charAt(pos) == '+') + { + pos++; + } + + try + { + int hour, minute; + + String offset_str = ID.substring(pos); + int idx = offset_str.indexOf(":"); + if (idx != -1) + { + hour = Integer.parseInt(offset_str.substring(0, idx)); + minute = Integer.parseInt(offset_str.substring(idx + 1)); + } + else + { + int offset_length = offset_str.length(); + if (offset_length <= 2) + { + // Only hour + hour = Integer.parseInt(offset_str); + minute = 0; + } + else + { + // hour and minute, not separated by colon + hour = Integer.parseInt + (offset_str.substring(0, offset_length - 2)); + minute = Integer.parseInt + (offset_str.substring(offset_length - 2)); + } + } + + // Custom IDs have to be normalized + CPStringBuilder sb = new CPStringBuilder(9); + sb.append("GMT"); + + sb.append(offset_direction >= 0 ? '+' : '-'); + sb.append((char) ('0' + hour / 10)); + sb.append((char) ('0' + hour % 10)); + sb.append(':'); + sb.append((char) ('0' + minute / 10)); + sb.append((char) ('0' + minute % 10)); + ID = sb.toString(); + + return new SimpleTimeZone((hour * (60 * 60 * 1000) + + minute * (60 * 1000)) + * offset_direction, ID); + } + catch (NumberFormatException e) + { + } + } + + TimeZone tz = getTimeZoneInternal(ID); + if (tz != null) + return tz; + + return new SimpleTimeZone(0, "GMT"); + } + + /** + * Gets the available IDs according to the given time zone + * offset. + * @param rawOffset the given time zone GMT offset. + * @return An array of IDs, where the time zone has the specified GMT + * offset. For example {"Phoenix", "Denver"}, since both have + * GMT-07:00, but differ in daylight savings behaviour. + */ + public static String[] getAvailableIDs(int rawOffset) + { + synchronized (TimeZone.class) + { + HashMap h = timezones(); + int count = 0; + if (zoneinfo_dir == null) + { + Iterator iter = h.entrySet().iterator(); + while (iter.hasNext()) + { + // Don't iterate the values, since we want to count + // doubled values (aliases) + Map.Entry entry = (Map.Entry) iter.next(); + if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset) + count++; + } + + String[] ids = new String[count]; + count = 0; + iter = h.entrySet().iterator(); + while (iter.hasNext()) + { + Map.Entry entry = (Map.Entry) iter.next(); + if (((TimeZone) entry.getValue()).getRawOffset() == rawOffset) + ids[count++] = (String) entry.getKey(); + } + return ids; + } + } + + String[] s = getAvailableIDs(); + int count = 0; + for (int i = 0; i < s.length; i++) + { + TimeZone t = getTimeZoneInternal(s[i]); + if (t == null || t.getRawOffset() != rawOffset) + s[i] = null; + else + count++; + } + String[] ids = new String[count]; + count = 0; + for (int i = 0; i < s.length; i++) + if (s[i] != null) + ids[count++] = s[i]; + + return ids; + } + + private static int getAvailableIDs(File d, String prefix, ArrayList list) + { + String[] files = d.list(); + int count = files.length; + boolean top = prefix.length() == 0; + list.add (files); + for (int i = 0; i < files.length; i++) + { + if (top + && (files[i].equals("posix") + || files[i].equals("right") + || files[i].endsWith(".tab") + || aliases0.get(files[i]) != null)) + { + files[i] = null; + count--; + continue; + } + + File f = new File(d, files[i]); + if (f.isDirectory()) + { + count += getAvailableIDs(f, prefix + files[i] + + File.separatorChar, list) - 1; + files[i] = null; + } + else + files[i] = prefix + files[i]; + } + return count; + } + + /** + * Gets all available IDs. + * @return An array of all supported IDs. + */ + public static String[] getAvailableIDs() + { + synchronized (TimeZone.class) + { + HashMap h = timezones(); + if (zoneinfo_dir == null) + return (String[]) h.keySet().toArray(new String[h.size()]); + + if (availableIDs != null) + { + String[] ids = new String[availableIDs.length]; + for (int i = 0; i < availableIDs.length; i++) + ids[i] = availableIDs[i]; + return ids; + } + + File d = new File(zoneinfo_dir); + ArrayList list = new ArrayList(30); + int count = getAvailableIDs(d, "", list) + aliases0.size(); + availableIDs = new String[count]; + String[] ids = new String[count]; + + count = 0; + for (int i = 0; i < list.size(); i++) + { + String[] s = (String[]) list.get(i); + for (int j = 0; j < s.length; j++) + if (s[j] != null) + { + availableIDs[count] = s[j]; + ids[count++] = s[j]; + } + } + + Iterator iter = aliases0.entrySet().iterator(); + while (iter.hasNext()) + { + Map.Entry entry = (Map.Entry) iter.next(); + availableIDs[count] = (String) entry.getKey(); + ids[count++] = (String) entry.getKey(); + } + + return ids; + } + } + + /** + * Returns the time zone under which the host is running. This + * can be changed with setDefault. + * + * @return A clone of the current default time zone for this host. + * @see #setDefault + */ + public static TimeZone getDefault() + { + return (TimeZone) defaultZone().clone(); + } + + public static void setDefault(TimeZone zone) + { + // Hmmmm. No Security checks? + defaultZone0 = zone; + } + + /** + * Test if the other time zone uses the same rule and only + * possibly differs in ID. This implementation for this particular + * class will return true if the raw offsets are identical. Subclasses + * should override this method if they use daylight savings. + * @return true if this zone has the same raw offset + */ + public boolean hasSameRules(TimeZone other) + { + return other.getRawOffset() == getRawOffset(); + } + + /** + * Returns a clone of this object. I can't imagine, why this is + * useful for a time zone. + */ + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException ex) + { + return null; + } + } +} diff --git a/libjava/classpath/java/util/Timer.java b/libjava/classpath/java/util/Timer.java new file mode 100644 index 000000000..99027554c --- /dev/null +++ b/libjava/classpath/java/util/Timer.java @@ -0,0 +1,704 @@ +/* Timer.java -- Timer that runs TimerTasks at a later time. + Copyright (C) 2000, 2001, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +/** + * Timer that can run TimerTasks at a later time. + * TimerTasks can be scheduled for one time execution at some time in the + * future. They can be scheduled to be rescheduled at a time period after the + * task was last executed. Or they can be scheduled to be executed repeatedly + * at a fixed rate. + *

        + * The normal scheduling will result in a more or less even delay in time + * between successive executions, but the executions could drift in time if + * the task (or other tasks) takes a long time to execute. Fixed delay + * scheduling guarantees more or less that the task will be executed at a + * specific time, but if there is ever a delay in execution then the period + * between successive executions will be shorter. The first method of + * repeated scheduling is preferred for repeated tasks in response to user + * interaction, the second method of repeated scheduling is preferred for tasks + * that act like alarms. + *

        + * The Timer keeps a binary heap as a task priority queue which means that + * scheduling and serving of a task in a queue of n tasks costs O(log n). + * + * @see TimerTask + * @since 1.3 + * @author Mark Wielaard (mark@klomp.org) + */ +public class Timer +{ + /** + * Priority Task Queue. + * TimerTasks are kept in a binary heap. + * The scheduler calls sleep() on the queue when it has nothing to do or + * has to wait. A sleeping scheduler can be notified by calling interrupt() + * which is automatically called by the enqueue(), cancel() and + * timerFinalized() methods. + */ + private static final class TaskQueue + { + /** Default size of this queue */ + private static final int DEFAULT_SIZE = 32; + + /** Whether to return null when there is nothing in the queue */ + private boolean nullOnEmpty; + + /** + * The heap containing all the scheduled TimerTasks + * sorted by the TimerTask.scheduled field. + * Null when the stop() method has been called. + */ + private TimerTask heap[]; + + /** + * The actual number of elements in the heap + * Can be less then heap.length. + * Note that heap[0] is used as a sentinel. + */ + private int elements; + + /** + * Creates a TaskQueue of default size without any elements in it. + */ + public TaskQueue() + { + heap = new TimerTask[DEFAULT_SIZE]; + elements = 0; + nullOnEmpty = false; + } + + /** + * Adds a TimerTask at the end of the heap. + * Grows the heap if necessary by doubling the heap in size. + */ + private void add(TimerTask task) + { + elements++; + if (elements == heap.length) + { + TimerTask new_heap[] = new TimerTask[heap.length * 2]; + System.arraycopy(heap, 0, new_heap, 0, heap.length); + heap = new_heap; + } + heap[elements] = task; + } + + /** + * Removes the last element from the heap. + * Shrinks the heap in half if + * elements+DEFAULT_SIZE/2 <= heap.length/4. + */ + private void remove() + { + // clear the entry first + heap[elements] = null; + elements--; + if (elements + DEFAULT_SIZE / 2 <= (heap.length / 4)) + { + TimerTask new_heap[] = new TimerTask[heap.length / 2]; + System.arraycopy(heap, 0, new_heap, 0, elements + 1); + heap = new_heap; + } + } + + /** + * Adds a task to the queue and puts it at the correct place + * in the heap. + */ + public synchronized void enqueue(TimerTask task) + { + // Check if it is legal to add another element + if (heap == null) + { + throw new IllegalStateException + ("cannot enqueue when stop() has been called on queue"); + } + + heap[0] = task; // sentinel + add(task); // put the new task at the end + // Now push the task up in the heap until it has reached its place + int child = elements; + int parent = child / 2; + while (heap[parent].scheduled > task.scheduled) + { + heap[child] = heap[parent]; + child = parent; + parent = child / 2; + } + // This is the correct place for the new task + heap[child] = task; + heap[0] = null; // clear sentinel + // Maybe sched() is waiting for a new element + this.notify(); + } + + /** + * Returns the top element of the queue. + * Can return null when no task is in the queue. + */ + private TimerTask top() + { + if (elements == 0) + { + return null; + } + else + { + return heap[1]; + } + } + + /** + * Returns the top task in the Queue. + * Removes the element from the heap and reorders the heap first. + * Can return null when there is nothing in the queue. + */ + public synchronized TimerTask serve() + { + // The task to return + TimerTask task = null; + + while (task == null) + { + // Get the next task + task = top(); + + // return null when asked to stop + // or if asked to return null when the queue is empty + if ((heap == null) || (task == null && nullOnEmpty)) + { + return null; + } + + // Do we have a task? + if (task != null) + { + // The time to wait until the task should be served + long time = task.scheduled - System.currentTimeMillis(); + if (time > 0) + { + // This task should not yet be served + // So wait until this task is ready + // or something else happens to the queue + task = null; // set to null to make sure we call top() + try + { + this.wait(time); + } + catch (InterruptedException _) + { + } + } + } + else + { + // wait until a task is added + // or something else happens to the queue + try + { + this.wait(); + } + catch (InterruptedException _) + { + } + } + } + + // reconstruct the heap + TimerTask lastTask = heap[elements]; + remove(); + + // drop lastTask at the beginning and move it down the heap + int parent = 1; + int child = 2; + heap[1] = lastTask; + while (child <= elements) + { + if (child < elements) + { + if (heap[child].scheduled > heap[child + 1].scheduled) + { + child++; + } + } + + if (lastTask.scheduled <= heap[child].scheduled) + break; // found the correct place (the parent) - done + + heap[parent] = heap[child]; + parent = child; + child = parent * 2; + } + + // this is the correct new place for the lastTask + heap[parent] = lastTask; + + // return the task + return task; + } + + /** + * When nullOnEmpty is true the serve() method will return null when + * there are no tasks in the queue, otherwise it will wait until + * a new element is added to the queue. It is used to indicate to + * the scheduler that no new tasks will ever be added to the queue. + */ + public synchronized void setNullOnEmpty(boolean nullOnEmpty) + { + this.nullOnEmpty = nullOnEmpty; + this.notify(); + } + + /** + * When this method is called the current and all future calls to + * serve() will return null. It is used to indicate to the Scheduler + * that it should stop executing since no more tasks will come. + */ + public synchronized void stop() + { + this.heap = null; + this.elements = 0; + this.notify(); + } + + /** + * Remove all canceled tasks from the queue. + */ + public synchronized int purge() + { + int removed = 0; + // Null out any elements that are canceled. Skip element 0 as + // it is the sentinel. + for (int i = elements; i > 0; --i) + { + if (heap[i].scheduled < 0) + { + ++removed; + + // Remove an element by pushing the appropriate child + // into place, and then iterating to the bottom of the + // tree. + int index = i; + while (heap[index] != null) + { + int child = 2 * index; + if (child >= heap.length) + { + // Off end; we're done. + heap[index] = null; + break; + } + + if (child + 1 >= heap.length || heap[child + 1] == null) + { + // Nothing -- we're done. + } + else if (heap[child] == null + || (heap[child].scheduled + > heap[child + 1].scheduled)) + ++child; + heap[index] = heap[child]; + index = child; + } + } + } + + // Make a new heap if we shrank enough. + int newLen = heap.length; + while (elements - removed + DEFAULT_SIZE / 2 <= newLen / 4) + newLen /= 2; + if (newLen != heap.length) + { + TimerTask[] newHeap = new TimerTask[newLen]; + System.arraycopy(heap, 0, newHeap, 0, elements + 1); + heap = newHeap; + } + + return removed; + } + } // TaskQueue + + /** + * The scheduler that executes all the tasks on a particular TaskQueue, + * reschedules any repeating tasks and that waits when no task has to be + * executed immediately. Stops running when canceled or when the parent + * Timer has been finalized and no more tasks have to be executed. + */ + private static final class Scheduler implements Runnable + { + // The priority queue containing all the TimerTasks. + private TaskQueue queue; + + /** + * Creates a new Scheduler that will schedule the tasks on the + * given TaskQueue. + */ + public Scheduler(TaskQueue queue) + { + this.queue = queue; + } + + public void run() + { + TimerTask task; + while ((task = queue.serve()) != null) + { + // If this task has not been canceled + if (task.scheduled >= 0) + { + + // Mark execution time + task.lastExecutionTime = task.scheduled; + + // Repeatable task? + if (task.period < 0) + { + // Last time this task is executed + task.scheduled = -1; + } + + // Run the task + try + { + task.run(); + } + catch (ThreadDeath death) + { + // If an exception escapes, the Timer becomes invalid. + queue.stop(); + throw death; + } + catch (Throwable t) + { + // If an exception escapes, the Timer becomes invalid. + queue.stop(); + } + } + + // Calculate next time and possibly re-enqueue. + if (task.scheduled >= 0) + { + if (task.fixed) + { + task.scheduled += task.period; + } + else + { + task.scheduled = task.period + System.currentTimeMillis(); + } + + try + { + queue.enqueue(task); + } + catch (IllegalStateException ise) + { + // Ignore. Apparently the Timer queue has been stopped. + } + } + } + } + } // Scheduler + + // Number of Timers created. + // Used for creating nice Thread names. + private static int nr; + + // The queue that all the tasks are put in. + // Given to the scheduler + private TaskQueue queue; + + // The Scheduler that does all the real work + private Scheduler scheduler; + + // Used to run the scheduler. + // Also used to checked if the Thread is still running by calling + // thread.isAlive(). Sometimes a Thread is suddenly killed by the system + // (if it belonged to an Applet). + private Thread thread; + + // When cancelled we don't accept any more TimerTasks. + private boolean canceled; + + /** + * Creates a new Timer with a non daemon Thread as Scheduler, with normal + * priority and a default name. + */ + public Timer() + { + this(false); + } + + /** + * Creates a new Timer with a daemon Thread as scheduler if daemon is true, + * with normal priority and a default name. + */ + public Timer(boolean daemon) + { + this(daemon, Thread.NORM_PRIORITY); + } + + /** + * Create a new Timer whose Thread has the indicated name. It will have + * normal priority and will not be a daemon thread. + * @param name the name of the Thread + * @since 1.5 + */ + public Timer(String name) + { + this(false, Thread.NORM_PRIORITY, name); + } + + /** + * Create a new Timer whose Thread has the indicated name. It will have + * normal priority. The boolean argument controls whether or not it + * will be a daemon thread. + * @param name the name of the Thread + * @param daemon true if the Thread should be a daemon thread + * @since 1.5 + */ + public Timer(String name, boolean daemon) + { + this(daemon, Thread.NORM_PRIORITY, name); + } + + /** + * Creates a new Timer with a daemon Thread as scheduler if daemon is true, + * with the priority given and a default name. + */ + private Timer(boolean daemon, int priority) + { + this(daemon, priority, "Timer-" + (++nr)); + } + + /** + * Creates a new Timer with a daemon Thread as scheduler if daemon is true, + * with the priority and name given.E + */ + private Timer(boolean daemon, int priority, String name) + { + canceled = false; + queue = new TaskQueue(); + scheduler = new Scheduler(queue); + thread = new Thread(scheduler, name); + thread.setDaemon(daemon); + thread.setPriority(priority); + thread.start(); + } + + /** + * Cancels the execution of the scheduler. If a task is executing it will + * normally finish execution, but no other tasks will be executed and no + * more tasks can be scheduled. + */ + public void cancel() + { + canceled = true; + queue.stop(); + } + + /** + * Schedules the task at Time time, repeating every period + * milliseconds if period is positive and at a fixed rate if fixed is true. + * + * @exception IllegalArgumentException if time is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + private void schedule(TimerTask task, long time, long period, boolean fixed) + { + if (time < 0) + throw new IllegalArgumentException("negative time"); + + if (task.scheduled == 0 && task.lastExecutionTime == -1) + { + task.scheduled = time; + task.period = period; + task.fixed = fixed; + } + else + { + throw new IllegalStateException + ("task was already scheduled or canceled"); + } + + if (!this.canceled && this.thread != null) + { + queue.enqueue(task); + } + else + { + throw new IllegalStateException + ("timer was canceled or scheduler thread has died"); + } + } + + private static void positiveDelay(long delay) + { + if (delay < 0) + { + throw new IllegalArgumentException("delay is negative"); + } + } + + private static void positivePeriod(long period) + { + if (period < 0) + { + throw new IllegalArgumentException("period is negative"); + } + } + + /** + * Schedules the task at the specified data for one time execution. + * + * @exception IllegalArgumentException if date.getTime() is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, Date date) + { + long time = date.getTime(); + schedule(task, time, -1, false); + } + + /** + * Schedules the task at the specified date and reschedules the task every + * period milliseconds after the last execution of the task finishes until + * this timer or the task is canceled. + * + * @exception IllegalArgumentException if period or date.getTime() is + * negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, Date date, long period) + { + positivePeriod(period); + long time = date.getTime(); + schedule(task, time, period, false); + } + + /** + * Schedules the task after the specified delay milliseconds for one time + * execution. + * + * @exception IllegalArgumentException if delay or + * System.currentTimeMillis + delay is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, long delay) + { + positiveDelay(delay); + long time = System.currentTimeMillis() + delay; + schedule(task, time, -1, false); + } + + /** + * Schedules the task after the delay milliseconds and reschedules the + * task every period milliseconds after the last execution of the task + * finishes until this timer or the task is canceled. + * + * @exception IllegalArgumentException if delay or period is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void schedule(TimerTask task, long delay, long period) + { + positiveDelay(delay); + positivePeriod(period); + long time = System.currentTimeMillis() + delay; + schedule(task, time, period, false); + } + + /** + * Schedules the task at the specified date and reschedules the task at a + * fixed rate every period milliseconds until this timer or the task is + * canceled. + * + * @exception IllegalArgumentException if period or date.getTime() is + * negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void scheduleAtFixedRate(TimerTask task, Date date, long period) + { + positivePeriod(period); + long time = date.getTime(); + schedule(task, time, period, true); + } + + /** + * Schedules the task after the delay milliseconds and reschedules the task + * at a fixed rate every period milliseconds until this timer or the task + * is canceled. + * + * @exception IllegalArgumentException if delay or + * System.currentTimeMillis + delay is negative + * @exception IllegalStateException if the task was already scheduled or + * canceled or this Timer is canceled or the scheduler thread has died + */ + public void scheduleAtFixedRate(TimerTask task, long delay, long period) + { + positiveDelay(delay); + positivePeriod(period); + long time = System.currentTimeMillis() + delay; + schedule(task, time, period, true); + } + + /** + * Tells the scheduler that the Timer task died + * so there will be no more new tasks scheduled. + */ + protected void finalize() throws Throwable + { + queue.setNullOnEmpty(true); + } + + /** + * Removes all cancelled tasks from the queue. + * @return the number of tasks removed + * @since 1.5 + */ + public int purge() + { + return queue.purge(); + } +} diff --git a/libjava/classpath/java/util/TimerTask.java b/libjava/classpath/java/util/TimerTask.java new file mode 100644 index 000000000..b03118ad0 --- /dev/null +++ b/libjava/classpath/java/util/TimerTask.java @@ -0,0 +1,145 @@ +/* TimerTask.java -- Task that can be run at a later time if given to a Timer. + Copyright (C) 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util; + +/** + * Task that can be run at a later time if given to a Timer. + * The TimerTask must implement a run method that will be called by the + * Timer when the task is scheduled for execution. The task can check when + * it should have been scheduled and cancel itself when no longer needed. + *

        + * Example: + *

        + *  Timer timer = new Timer();
        + *  TimerTask task = new TimerTask() {
        + *      public void run() {
        + *      if (this.scheduledExecutionTime() < System.currentTimeMillis() + 500)
        + *          // Do something
        + *      else
        + *          // Complain: We are more then half a second late!
        + *      if (someStopCondition)
        + *          this.cancel(); // This was our last execution
        + *  };
        + *  timer.scheduleAtFixedRate(task, 1000, 1000); // schedule every second
        + * 
        + *

        + * Note that a TimerTask object is a one shot object and can only given once + * to a Timer. (The Timer will use the TimerTask object for bookkeeping, + * in this implementation). + *

        + * This class also implements Runnable to make it possible to + * give a TimerTask directly as a target to a Thread. + * + * @see Timer + * @since 1.3 + * @author Mark Wielaard (mark@klomp.org) + */ +public abstract class TimerTask implements Runnable +{ + /** + * If positive the next time this task should be run. + * If negative this TimerTask is canceled or executed for the last time. + */ + long scheduled; + + /** + * If positive the last time this task was run. + * If negative this TimerTask has not yet been scheduled. + */ + long lastExecutionTime; + + /** + * If positive the number of milliseconds between runs of this task. + * If -1 this task doesn't have to be run more then once. + */ + long period; + + /** + * If true the next time this task should be run is relative to + * the last scheduled time, otherwise it can drift in time. + */ + boolean fixed; + + /** + * Creates a TimerTask and marks it as not yet scheduled. + */ + protected TimerTask() + { + this.scheduled = 0; + this.lastExecutionTime = -1; + } + + /** + * Marks the task as canceled and prevents any further execution. + * Returns true if the task was scheduled for any execution in the future + * and this cancel operation prevents that execution from happening. + *

        + * A task that has been canceled can never be scheduled again. + *

        + * In this implementation the TimerTask it is possible that the Timer does + * keep a reference to the TimerTask until the first time the TimerTask + * is actually scheduled. But the reference will disappear immediatly when + * cancel is called from within the TimerTask run method. + */ + public boolean cancel() + { + boolean prevented_execution = (this.scheduled >= 0); + this.scheduled = -1; + return prevented_execution; + } + + /** + * Method that is called when this task is scheduled for execution. + */ + public abstract void run(); + + /** + * Returns the last time this task was scheduled or (when called by the + * task from the run method) the time the current execution of the task + * was scheduled. When the task has not yet run the return value is + * undefined. + *

        + * Can be used (when the task is scheduled at fixed rate) to see the + * difference between the requested schedule time and the actual time + * that can be found with System.currentTimeMillis(). + */ + public long scheduledExecutionTime() + { + return lastExecutionTime; + } +} diff --git a/libjava/classpath/java/util/TooManyListenersException.java b/libjava/classpath/java/util/TooManyListenersException.java new file mode 100644 index 000000000..92ad772f2 --- /dev/null +++ b/libjava/classpath/java/util/TooManyListenersException.java @@ -0,0 +1,78 @@ +/* TooManyListenersException.java -- thrown when a unicast event can't accept + another Listener + Copyright (C) 1998, 1999, 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * This exception is part of the java event model. It is thrown if an + * event listener is added via the addXyzEventListener method, but the + * object doesn't support any more listeners, e.g. it only supports a + * single event listener. + * + * @author Jochen Hoenicke + * @author Warren Levy (warrenl@cygnus.com) + * @see EventListener + * @see EventObject + * @since 1.1 + * @status updated to 1.4 + */ +public class TooManyListenersException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 5074640544770687831L; + + /** + * Constructs a TooManyListenersException with no detail message. + */ + public TooManyListenersException() + { + } + + /** + * Constructs a TooManyListenersException with a detail message. + * + * @param detail the detail message + */ + public TooManyListenersException(String detail) + { + super(detail); + } +} diff --git a/libjava/classpath/java/util/TreeMap.java b/libjava/classpath/java/util/TreeMap.java new file mode 100644 index 000000000..87c532fc1 --- /dev/null +++ b/libjava/classpath/java/util/TreeMap.java @@ -0,0 +1,3322 @@ +/* TreeMap.java -- a class providing a basic Red-Black Tree data structure, + mapping Object --> Object + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import gnu.java.lang.CPStringBuilder; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class provides a red-black tree implementation of the SortedMap + * interface. Elements in the Map will be sorted by either a user-provided + * Comparator object, or by the natural ordering of the keys. + * + * The algorithms are adopted from Corman, Leiserson, and Rivest's + * Introduction to Algorithms. TreeMap guarantees O(log n) + * insertion and deletion of elements. That being said, there is a large + * enough constant coefficient in front of that "log n" (overhead involved + * in keeping the tree balanced), that TreeMap may not be the best choice + * for small collections. If something is already sorted, you may want to + * just use a LinkedHashMap to maintain the order while providing O(1) access. + * + * TreeMap is a part of the JDK1.2 Collections API. Null keys are allowed + * only if a Comparator is used which can deal with them; natural ordering + * cannot cope with null. Null values are always allowed. Note that the + * ordering must be consistent with equals to correctly implement + * the Map interface. If this condition is violated, the map is still + * well-behaved, but you may have suprising results when comparing it to + * other maps.

        + * + * This implementation is not synchronized. If you need to share this between + * multiple threads, do something like:
        + * SortedMap m + * = Collections.synchronizedSortedMap(new TreeMap(...));

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * ConcurrentModificationException rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Map + * @see HashMap + * @see Hashtable + * @see LinkedHashMap + * @see Comparable + * @see Comparator + * @see Collection + * @see Collections#synchronizedSortedMap(SortedMap) + * @since 1.2 + * @status updated to 1.6 + */ +public class TreeMap extends AbstractMap + implements NavigableMap, Cloneable, Serializable +{ + // Implementation note: + // A red-black tree is a binary search tree with the additional properties + // that all paths to a leaf node visit the same number of black nodes, + // and no red node has red children. To avoid some null-pointer checks, + // we use the special node nil which is always black, has no relatives, + // and has key and value of null (but is not equal to a mapping of null). + + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = 919286545866124006L; + + /** + * Color status of a node. Package visible for use by nested classes. + */ + static final int RED = -1, + BLACK = 1; + + /** + * Sentinal node, used to avoid null checks for corner cases and make the + * delete rebalance code simpler. The rebalance code must never assign + * the parent, left, or right of nil, but may safely reassign the color + * to be black. This object must never be used as a key in a TreeMap, or + * it will break bounds checking of a SubMap. + */ + static final Node nil = new Node(null, null, BLACK); + static + { + // Nil is self-referential, so we must initialize it after creation. + nil.parent = nil; + nil.left = nil; + nil.right = nil; + } + + /** + * The root node of this TreeMap. + */ + private transient Node root; + + /** + * The size of this TreeMap. Package visible for use by nested classes. + */ + transient int size; + + /** + * The cache for {@link #entrySet()}. + */ + private transient Set> entries; + + /** + * The cache for {@link #descendingMap()}. + */ + private transient NavigableMap descendingMap; + + /** + * The cache for {@link #navigableKeySet()}. + */ + private transient NavigableSet nKeys; + + /** + * Counts the number of modifications this TreeMap has undergone, used + * by Iterators to know when to throw ConcurrentModificationExceptions. + * Package visible for use by nested classes. + */ + transient int modCount; + + /** + * This TreeMap's comparator, or null for natural ordering. + * Package visible for use by nested classes. + * @serial the comparator ordering this tree, or null + */ + final Comparator comparator; + + /** + * Class to represent an entry in the tree. Holds a single key-value pair, + * plus pointers to parent and child nodes. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class Node extends AbstractMap.SimpleEntry + { + // All fields package visible for use by nested classes. + /** The color of this node. */ + int color; + + /** The left child node. */ + Node left = nil; + /** The right child node. */ + Node right = nil; + /** The parent node. */ + Node parent = nil; + + /** + * Simple constructor. + * @param key the key + * @param value the value + */ + Node(K key, V value, int color) + { + super(key, value); + this.color = color; + } + } + + /** + * Instantiate a new TreeMap with no elements, using the keys' natural + * ordering to sort. All entries in the map must have a key which implements + * Comparable, and which are mutually comparable, otherwise map + * operations may throw a {@link ClassCastException}. Attempts to use + * a null key will throw a {@link NullPointerException}. + * + * @see Comparable + */ + public TreeMap() + { + this((Comparator) null); + } + + /** + * Instantiate a new TreeMap with no elements, using the provided comparator + * to sort. All entries in the map must have keys which are mutually + * comparable by the Comparator, otherwise map operations may throw a + * {@link ClassCastException}. + * + * @param c the sort order for the keys of this map, or null + * for the natural order + */ + public TreeMap(Comparator c) + { + comparator = c; + fabricateTree(0); + } + + /** + * Instantiate a new TreeMap, initializing it with all of the elements in + * the provided Map. The elements will be sorted using the natural + * ordering of the keys. This algorithm runs in n*log(n) time. All entries + * in the map must have keys which implement Comparable and are mutually + * comparable, otherwise map operations may throw a + * {@link ClassCastException}. + * + * @param map a Map, whose entries will be put into this TreeMap + * @throws ClassCastException if the keys in the provided Map are not + * comparable + * @throws NullPointerException if map is null + * @see Comparable + */ + public TreeMap(Map map) + { + this((Comparator) null); + putAll(map); + } + + /** + * Instantiate a new TreeMap, initializing it with all of the elements in + * the provided SortedMap. The elements will be sorted using the same + * comparator as in the provided SortedMap. This runs in linear time. + * + * @param sm a SortedMap, whose entries will be put into this TreeMap + * @throws NullPointerException if sm is null + */ + public TreeMap(SortedMap sm) + { + this(sm.comparator()); + int pos = sm.size(); + Iterator itr = sm.entrySet().iterator(); + + fabricateTree(pos); + Node node = firstNode(); + + while (--pos >= 0) + { + Map.Entry me = (Map.Entry) itr.next(); + node.key = me.getKey(); + node.value = me.getValue(); + node = successor(node); + } + } + + /** + * Clears the Map so it has no keys. This is O(1). + */ + public void clear() + { + if (size > 0) + { + modCount++; + root = nil; + size = 0; + } + } + + /** + * Returns a shallow clone of this TreeMap. The Map itself is cloned, + * but its contents are not. + * + * @return the clone + */ + public Object clone() + { + TreeMap copy = null; + try + { + copy = (TreeMap) super.clone(); + } + catch (CloneNotSupportedException x) + { + } + copy.entries = null; + copy.fabricateTree(size); + + Node node = firstNode(); + Node cnode = copy.firstNode(); + + while (node != nil) + { + cnode.key = node.key; + cnode.value = node.value; + node = successor(node); + cnode = copy.successor(cnode); + } + return copy; + } + + /** + * Return the comparator used to sort this map, or null if it is by + * natural order. + * + * @return the map's comparator + */ + public Comparator comparator() + { + return comparator; + } + + /** + * Returns true if the map contains a mapping for the given key. + * + * @param key the key to look for + * @return true if the key has a mapping + * @throws ClassCastException if key is not comparable to map elements + * @throws NullPointerException if key is null and the comparator is not + * tolerant of nulls + */ + public boolean containsKey(Object key) + { + return getNode((K) key) != nil; + } + + /** + * Returns true if the map contains at least one mapping to the given value. + * This requires linear time. + * + * @param value the value to look for + * @return true if the value appears in a mapping + */ + public boolean containsValue(Object value) + { + Node node = firstNode(); + while (node != nil) + { + if (equals(value, node.value)) + return true; + node = successor(node); + } + return false; + } + + /** + * Returns a "set view" of this TreeMap's entries. The set is backed by + * the TreeMap, so changes in one show up in the other. The set supports + * element removal, but not element addition.

        + * + * Note that the iterators for all three views, from keySet(), entrySet(), + * and values(), traverse the TreeMap in sorted sequence. + * + * @return a set view of the entries + * @see #keySet() + * @see #values() + * @see Map.Entry + */ + public Set> entrySet() + { + if (entries == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + entries = new NavigableEntrySet(); + return entries; + } + + /** + * Returns the first (lowest) key in the map. + * + * @return the first key + * @throws NoSuchElementException if the map is empty + */ + public K firstKey() + { + if (root == nil) + throw new NoSuchElementException(); + return firstNode().key; + } + + /** + * Return the value in this TreeMap associated with the supplied key, + * or null if the key maps to nothing. NOTE: Since the value + * could also be null, you must use containsKey to see if this key + * actually maps to something. + * + * @param key the key for which to fetch an associated value + * @return what the key maps to, if present + * @throws ClassCastException if key is not comparable to elements in the map + * @throws NullPointerException if key is null but the comparator does not + * tolerate nulls + * @see #put(Object, Object) + * @see #containsKey(Object) + */ + public V get(Object key) + { + // Exploit fact that nil.value == null. + return getNode((K) key).value; + } + + /** + * Returns a view of this Map including all entries with keys less than + * toKey. The returned map is backed by the original, so changes + * in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned map does not include + * the endpoint; if you want inclusion, pass the successor element + * or call headMap(toKey, true). This is equivalent to + * calling headMap(toKey, false). + * + * @param toKey the (exclusive) cutoff point + * @return a view of the map less than the cutoff + * @throws ClassCastException if toKey is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if toKey is null, but the comparator does not + * tolerate null elements + */ + public SortedMap headMap(K toKey) + { + return headMap(toKey, false); + } + + /** + * Returns a view of this Map including all entries with keys less than + * (or equal to, if inclusive is true) toKey. + * The returned map is backed by the original, so changes in one appear + * in the other. The submap will throw an {@link IllegalArgumentException} + * for any attempt to access or add an element beyond the specified cutoff. + * + * @param toKey the cutoff point + * @param inclusive true if the cutoff point should be included. + * @return a view of the map less than (or equal to, if inclusive + * is true) the cutoff. + * @throws ClassCastException if toKey is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if toKey is null, but the comparator does not + * tolerate null elements + */ + public NavigableMap headMap(K toKey, boolean inclusive) + { + return new SubMap((K)(Object)nil, inclusive + ? successor(getNode(toKey)).key : toKey); + } + + /** + * Returns a "set view" of this TreeMap's keys. The set is backed by the + * TreeMap, so changes in one show up in the other. The set supports + * element removal, but not element addition. + * + * @return a set view of the keys + * @see #values() + * @see #entrySet() + */ + public Set keySet() + { + if (keys == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + keys = new KeySet(); + return keys; + } + + /** + * Returns the last (highest) key in the map. + * + * @return the last key + * @throws NoSuchElementException if the map is empty + */ + public K lastKey() + { + if (root == nil) + throw new NoSuchElementException("empty"); + return lastNode().key; + } + + /** + * Puts the supplied value into the Map, mapped by the supplied key. + * The value may be retrieved by any object which equals() + * this key. NOTE: Since the prior value could also be null, you must + * first use containsKey if you want to see if you are replacing the + * key's mapping. + * + * @param key the key used to locate the value + * @param value the value to be stored in the Map + * @return the prior mapping of the key, or null if there was none + * @throws ClassCastException if key is not comparable to current map keys + * @throws NullPointerException if key is null, but the comparator does + * not tolerate nulls + * @see #get(Object) + * @see Object#equals(Object) + */ + public V put(K key, V value) + { + Node current = root; + Node parent = nil; + int comparison = 0; + + // Find new node's parent. + while (current != nil) + { + parent = current; + comparison = compare(key, current.key); + if (comparison > 0) + current = current.right; + else if (comparison < 0) + current = current.left; + else // Key already in tree. + return current.setValue(value); + } + + // Set up new node. + Node n = new Node(key, value, RED); + n.parent = parent; + + // Insert node in tree. + modCount++; + size++; + if (parent == nil) + { + // Special case inserting into an empty tree. + root = n; + return null; + } + if (comparison > 0) + parent.right = n; + else + parent.left = n; + + // Rebalance after insert. + insertFixup(n); + return null; + } + + /** + * Copies all elements of the given map into this TreeMap. If this map + * already has a mapping for a key, the new mapping replaces the current + * one. + * + * @param m the map to be added + * @throws ClassCastException if a key in m is not comparable with keys + * in the map + * @throws NullPointerException if a key in m is null, and the comparator + * does not tolerate nulls + */ + public void putAll(Map m) + { + Iterator itr = m.entrySet().iterator(); + int pos = m.size(); + while (--pos >= 0) + { + Map.Entry e = (Map.Entry) itr.next(); + put(e.getKey(), e.getValue()); + } + } + + /** + * Removes from the TreeMap and returns the value which is mapped by the + * supplied key. If the key maps to nothing, then the TreeMap remains + * unchanged, and null is returned. NOTE: Since the value + * could also be null, you must use containsKey to see if you are + * actually removing a mapping. + * + * @param key the key used to locate the value to remove + * @return whatever the key mapped to, if present + * @throws ClassCastException if key is not comparable to current map keys + * @throws NullPointerException if key is null, but the comparator does + * not tolerate nulls + */ + public V remove(Object key) + { + Node n = getNode((K)key); + if (n == nil) + return null; + // Note: removeNode can alter the contents of n, so save value now. + V result = n.value; + removeNode(n); + return result; + } + + /** + * Returns the number of key-value mappings currently in this Map. + * + * @return the size + */ + public int size() + { + return size; + } + + /** + * Returns a view of this Map including all entries with keys greater or + * equal to fromKey and less than toKey (a + * half-open interval). The returned map is backed by the original, so + * changes in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoffs. The returned map includes the low + * endpoint but not the high; if you want to reverse this behavior on + * either end, pass in the successor element or call + * {@link #subMap(K,boolean,K,boolean)}. This call is equivalent to + * subMap(fromKey, true, toKey, false). + * + * @param fromKey the (inclusive) low cutoff point + * @param toKey the (exclusive) high cutoff point + * @return a view of the map between the cutoffs + * @throws ClassCastException if either cutoff is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if fromKey or toKey is null, but the + * comparator does not tolerate null elements + * @throws IllegalArgumentException if fromKey is greater than toKey + */ + public SortedMap subMap(K fromKey, K toKey) + { + return subMap(fromKey, true, toKey, false); + } + + /** + * Returns a view of this Map including all entries with keys greater (or + * equal to, if fromInclusive is true) fromKey and + * less than (or equal to, if toInclusive is true) + * toKey. The returned map is backed by the original, so + * changes in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoffs. + * + * @param fromKey the low cutoff point + * @param fromInclusive true if the low cutoff point should be included. + * @param toKey the high cutoff point + * @param toInclusive true if the high cutoff point should be included. + * @return a view of the map for the specified range. + * @throws ClassCastException if either cutoff is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if fromKey or toKey is null, but the + * comparator does not tolerate null elements + * @throws IllegalArgumentException if fromKey is greater than toKey + */ + public NavigableMap subMap(K fromKey, boolean fromInclusive, + K toKey, boolean toInclusive) + { + return new SubMap(fromInclusive ? fromKey : successor(getNode(fromKey)).key, + toInclusive ? successor(getNode(toKey)).key : toKey); + } + + /** + * Returns a view of this Map including all entries with keys greater or + * equal to fromKey. The returned map is backed by the + * original, so changes in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned map includes the + * endpoint; if you want to exclude it, pass in the successor element. + * This is equivalent to calling tailMap(fromKey, true). + * + * @param fromKey the (inclusive) low cutoff point + * @return a view of the map above the cutoff + * @throws ClassCastException if fromKey is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if fromKey is null, but the comparator + * does not tolerate null elements + */ + public SortedMap tailMap(K fromKey) + { + return tailMap(fromKey, true); + } + + /** + * Returns a view of this Map including all entries with keys greater or + * equal to fromKey. The returned map is backed by the + * original, so changes in one appear in the other. The submap will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned map includes the + * endpoint; if you want to exclude it, pass in the successor element. + * + * @param fromKey the low cutoff point + * @param inclusive true if the cutoff point should be included. + * @return a view of the map above the cutoff + * @throws ClassCastException if fromKey is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if fromKey is null, but the comparator + * does not tolerate null elements + */ + public NavigableMap tailMap(K fromKey, boolean inclusive) + { + return new SubMap(inclusive ? fromKey : successor(getNode(fromKey)).key, + (K)(Object)nil); + } + + /** + * Returns a "collection view" (or "bag view") of this TreeMap's values. + * The collection is backed by the TreeMap, so changes in one show up + * in the other. The collection supports element removal, but not element + * addition. + * + * @return a bag view of the values + * @see #keySet() + * @see #entrySet() + */ + public Collection values() + { + if (values == null) + // We don't bother overriding many of the optional methods, as doing so + // wouldn't provide any significant performance advantage. + values = new AbstractCollection() + { + public int size() + { + return size; + } + + public Iterator iterator() + { + return new TreeIterator(VALUES); + } + + public void clear() + { + TreeMap.this.clear(); + } + }; + return values; + } + + /** + * Compares two elements by the set comparator, or by natural ordering. + * Package visible for use by nested classes. + * + * @param o1 the first object + * @param o2 the second object + * @throws ClassCastException if o1 and o2 are not mutually comparable, + * or are not Comparable with natural ordering + * @throws NullPointerException if o1 or o2 is null with natural ordering + */ + final int compare(K o1, K o2) + { + return (comparator == null + ? ((Comparable) o1).compareTo(o2) + : comparator.compare(o1, o2)); + } + + /** + * Maintain red-black balance after deleting a node. + * + * @param node the child of the node just deleted, possibly nil + * @param parent the parent of the node just deleted, never nil + */ + private void deleteFixup(Node node, Node parent) + { + // if (parent == nil) + // throw new InternalError(); + // If a black node has been removed, we need to rebalance to avoid + // violating the "same number of black nodes on any path" rule. If + // node is red, we can simply recolor it black and all is well. + while (node != root && node.color == BLACK) + { + if (node == parent.left) + { + // Rebalance left side. + Node sibling = parent.right; + // if (sibling == nil) + // throw new InternalError(); + if (sibling.color == RED) + { + // Case 1: Sibling is red. + // Recolor sibling and parent, and rotate parent left. + sibling.color = BLACK; + parent.color = RED; + rotateLeft(parent); + sibling = parent.right; + } + + if (sibling.left.color == BLACK && sibling.right.color == BLACK) + { + // Case 2: Sibling has no red children. + // Recolor sibling, and move to parent. + sibling.color = RED; + node = parent; + parent = parent.parent; + } + else + { + if (sibling.right.color == BLACK) + { + // Case 3: Sibling has red left child. + // Recolor sibling and left child, rotate sibling right. + sibling.left.color = BLACK; + sibling.color = RED; + rotateRight(sibling); + sibling = parent.right; + } + // Case 4: Sibling has red right child. Recolor sibling, + // right child, and parent, and rotate parent left. + sibling.color = parent.color; + parent.color = BLACK; + sibling.right.color = BLACK; + rotateLeft(parent); + node = root; // Finished. + } + } + else + { + // Symmetric "mirror" of left-side case. + Node sibling = parent.left; + // if (sibling == nil) + // throw new InternalError(); + if (sibling.color == RED) + { + // Case 1: Sibling is red. + // Recolor sibling and parent, and rotate parent right. + sibling.color = BLACK; + parent.color = RED; + rotateRight(parent); + sibling = parent.left; + } + + if (sibling.right.color == BLACK && sibling.left.color == BLACK) + { + // Case 2: Sibling has no red children. + // Recolor sibling, and move to parent. + sibling.color = RED; + node = parent; + parent = parent.parent; + } + else + { + if (sibling.left.color == BLACK) + { + // Case 3: Sibling has red right child. + // Recolor sibling and right child, rotate sibling left. + sibling.right.color = BLACK; + sibling.color = RED; + rotateLeft(sibling); + sibling = parent.left; + } + // Case 4: Sibling has red left child. Recolor sibling, + // left child, and parent, and rotate parent right. + sibling.color = parent.color; + parent.color = BLACK; + sibling.left.color = BLACK; + rotateRight(parent); + node = root; // Finished. + } + } + } + node.color = BLACK; + } + + /** + * Construct a perfectly balanced tree consisting of n "blank" nodes. This + * permits a tree to be generated from pre-sorted input in linear time. + * + * @param count the number of blank nodes, non-negative + */ + private void fabricateTree(final int count) + { + if (count == 0) + { + root = nil; + size = 0; + return; + } + + // We color every row of nodes black, except for the overflow nodes. + // I believe that this is the optimal arrangement. We construct the tree + // in place by temporarily linking each node to the next node in the row, + // then updating those links to the children when working on the next row. + + // Make the root node. + root = new Node(null, null, BLACK); + size = count; + Node row = root; + int rowsize; + + // Fill each row that is completely full of nodes. + for (rowsize = 2; rowsize + rowsize <= count; rowsize <<= 1) + { + Node parent = row; + Node last = null; + for (int i = 0; i < rowsize; i += 2) + { + Node left = new Node(null, null, BLACK); + Node right = new Node(null, null, BLACK); + left.parent = parent; + left.right = right; + right.parent = parent; + parent.left = left; + Node next = parent.right; + parent.right = right; + parent = next; + if (last != null) + last.right = left; + last = right; + } + row = row.left; + } + + // Now do the partial final row in red. + int overflow = count - rowsize; + Node parent = row; + int i; + for (i = 0; i < overflow; i += 2) + { + Node left = new Node(null, null, RED); + Node right = new Node(null, null, RED); + left.parent = parent; + right.parent = parent; + parent.left = left; + Node next = parent.right; + parent.right = right; + parent = next; + } + // Add a lone left node if necessary. + if (i - overflow == 0) + { + Node left = new Node(null, null, RED); + left.parent = parent; + parent.left = left; + parent = parent.right; + left.parent.right = nil; + } + // Unlink the remaining nodes of the previous row. + while (parent != nil) + { + Node next = parent.right; + parent.right = nil; + parent = next; + } + } + + /** + * Returns the first sorted node in the map, or nil if empty. Package + * visible for use by nested classes. + * + * @return the first node + */ + final Node firstNode() + { + // Exploit fact that nil.left == nil. + Node node = root; + while (node.left != nil) + node = node.left; + return node; + } + + /** + * Return the TreeMap.Node associated with key, or the nil node if no such + * node exists in the tree. Package visible for use by nested classes. + * + * @param key the key to search for + * @return the node where the key is found, or nil + */ + final Node getNode(K key) + { + Node current = root; + while (current != nil) + { + int comparison = compare(key, current.key); + if (comparison > 0) + current = current.right; + else if (comparison < 0) + current = current.left; + else + return current; + } + return current; + } + + /** + * Find the "highest" node which is < key. If key is nil, return last + * node. Package visible for use by nested classes. + * + * @param key the upper bound, exclusive + * @return the previous node + */ + final Node highestLessThan(K key) + { + return highestLessThan(key, false); + } + + /** + * Find the "highest" node which is < (or equal to, + * if equal is true) key. If key is nil, + * return last node. Package visible for use by nested + * classes. + * + * @param key the upper bound, exclusive + * @param equal true if the key should be returned if found. + * @return the previous node + */ + final Node highestLessThan(K key, boolean equal) + { + if (key == nil) + return lastNode(); + + Node last = nil; + Node current = root; + int comparison = 0; + + while (current != nil) + { + last = current; + comparison = compare(key, current.key); + if (comparison > 0) + current = current.right; + else if (comparison < 0) + current = current.left; + else // Exact match. + return (equal ? last : predecessor(last)); + } + return comparison < 0 ? predecessor(last) : last; + } + + /** + * Maintain red-black balance after inserting a new node. + * + * @param n the newly inserted node + */ + private void insertFixup(Node n) + { + // Only need to rebalance when parent is a RED node, and while at least + // 2 levels deep into the tree (ie: node has a grandparent). Remember + // that nil.color == BLACK. + while (n.parent.color == RED && n.parent.parent != nil) + { + if (n.parent == n.parent.parent.left) + { + Node uncle = n.parent.parent.right; + // Uncle may be nil, in which case it is BLACK. + if (uncle.color == RED) + { + // Case 1. Uncle is RED: Change colors of parent, uncle, + // and grandparent, and move n to grandparent. + n.parent.color = BLACK; + uncle.color = BLACK; + uncle.parent.color = RED; + n = uncle.parent; + } + else + { + if (n == n.parent.right) + { + // Case 2. Uncle is BLACK and x is right child. + // Move n to parent, and rotate n left. + n = n.parent; + rotateLeft(n); + } + // Case 3. Uncle is BLACK and x is left child. + // Recolor parent, grandparent, and rotate grandparent right. + n.parent.color = BLACK; + n.parent.parent.color = RED; + rotateRight(n.parent.parent); + } + } + else + { + // Mirror image of above code. + Node uncle = n.parent.parent.left; + // Uncle may be nil, in which case it is BLACK. + if (uncle.color == RED) + { + // Case 1. Uncle is RED: Change colors of parent, uncle, + // and grandparent, and move n to grandparent. + n.parent.color = BLACK; + uncle.color = BLACK; + uncle.parent.color = RED; + n = uncle.parent; + } + else + { + if (n == n.parent.left) + { + // Case 2. Uncle is BLACK and x is left child. + // Move n to parent, and rotate n right. + n = n.parent; + rotateRight(n); + } + // Case 3. Uncle is BLACK and x is right child. + // Recolor parent, grandparent, and rotate grandparent left. + n.parent.color = BLACK; + n.parent.parent.color = RED; + rotateLeft(n.parent.parent); + } + } + } + root.color = BLACK; + } + + /** + * Returns the last sorted node in the map, or nil if empty. + * + * @return the last node + */ + private Node lastNode() + { + // Exploit fact that nil.right == nil. + Node node = root; + while (node.right != nil) + node = node.right; + return node; + } + + /** + * Find the "lowest" node which is >= key. If key is nil, return either + * nil or the first node, depending on the parameter first. Package visible + * for use by nested classes. + * + * @param key the lower bound, inclusive + * @param first true to return the first element instead of nil for nil key + * @return the next node + */ + final Node lowestGreaterThan(K key, boolean first) + { + return lowestGreaterThan(key, first, true); + } + + /** + * Find the "lowest" node which is > (or equal to, if equal + * is true) key. If key is nil, return either nil or the first node, depending + * on the parameter first. Package visible for use by nested classes. + * + * @param key the lower bound, inclusive + * @param first true to return the first element instead of nil for nil key + * @param equal true if the key should be returned if found. + * @return the next node + */ + final Node lowestGreaterThan(K key, boolean first, boolean equal) + { + if (key == nil) + return first ? firstNode() : nil; + + Node last = nil; + Node current = root; + int comparison = 0; + + while (current != nil) + { + last = current; + comparison = compare(key, current.key); + if (comparison > 0) + current = current.right; + else if (comparison < 0) + current = current.left; + else + return (equal ? current : successor(current)); + } + return comparison > 0 ? successor(last) : last; + } + + /** + * Return the node preceding the given one, or nil if there isn't one. + * + * @param node the current node, not nil + * @return the prior node in sorted order + */ + private Node predecessor(Node node) + { + if (node.left != nil) + { + node = node.left; + while (node.right != nil) + node = node.right; + return node; + } + + Node parent = node.parent; + // Exploit fact that nil.left == nil and node is non-nil. + while (node == parent.left) + { + node = parent; + parent = node.parent; + } + return parent; + } + + /** + * Construct a tree from sorted keys in linear time. Package visible for + * use by TreeSet. + * + * @param s the stream to read from + * @param count the number of keys to read + * @param readValues true to read values, false to insert "" as the value + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @see #readObject(ObjectInputStream) + * @see TreeSet#readObject(ObjectInputStream) + */ + final void putFromObjStream(ObjectInputStream s, int count, + boolean readValues) + throws IOException, ClassNotFoundException + { + fabricateTree(count); + Node node = firstNode(); + + while (--count >= 0) + { + node.key = s.readObject(); + node.value = readValues ? s.readObject() : ""; + node = successor(node); + } + } + + /** + * Construct a tree from sorted keys in linear time, with values of "". + * Package visible for use by TreeSet, which uses a value type of String. + * + * @param keys the iterator over the sorted keys + * @param count the number of nodes to insert + * @see TreeSet#TreeSet(SortedSet) + */ + final void putKeysLinear(Iterator keys, int count) + { + fabricateTree(count); + Node node = firstNode(); + + while (--count >= 0) + { + node.key = keys.next(); + node.value = (V) ""; + node = successor(node); + } + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the size (int), followed by key (Object) and value + * (Object) pairs in sorted order + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + int size = s.readInt(); + putFromObjStream(s, size, true); + } + + /** + * Remove node from tree. This will increment modCount and decrement size. + * Node must exist in the tree. Package visible for use by nested classes. + * + * @param node the node to remove + */ + final void removeNode(Node node) + { + Node splice; + Node child; + + modCount++; + size--; + + // Find splice, the node at the position to actually remove from the tree. + if (node.left == nil) + { + // Node to be deleted has 0 or 1 children. + splice = node; + child = node.right; + } + else if (node.right == nil) + { + // Node to be deleted has 1 child. + splice = node; + child = node.left; + } + else + { + // Node has 2 children. Splice is node's predecessor, and we swap + // its contents into node. + splice = node.left; + while (splice.right != nil) + splice = splice.right; + child = splice.left; + node.key = splice.key; + node.value = splice.value; + } + + // Unlink splice from the tree. + Node parent = splice.parent; + if (child != nil) + child.parent = parent; + if (parent == nil) + { + // Special case for 0 or 1 node remaining. + root = child; + return; + } + if (splice == parent.left) + parent.left = child; + else + parent.right = child; + + if (splice.color == BLACK) + deleteFixup(child, parent); + } + + /** + * Rotate node n to the left. + * + * @param node the node to rotate + */ + private void rotateLeft(Node node) + { + Node child = node.right; + // if (node == nil || child == nil) + // throw new InternalError(); + + // Establish node.right link. + node.right = child.left; + if (child.left != nil) + child.left.parent = node; + + // Establish child->parent link. + child.parent = node.parent; + if (node.parent != nil) + { + if (node == node.parent.left) + node.parent.left = child; + else + node.parent.right = child; + } + else + root = child; + + // Link n and child. + child.left = node; + node.parent = child; + } + + /** + * Rotate node n to the right. + * + * @param node the node to rotate + */ + private void rotateRight(Node node) + { + Node child = node.left; + // if (node == nil || child == nil) + // throw new InternalError(); + + // Establish node.left link. + node.left = child.right; + if (child.right != nil) + child.right.parent = node; + + // Establish child->parent link. + child.parent = node.parent; + if (node.parent != nil) + { + if (node == node.parent.right) + node.parent.right = child; + else + node.parent.left = child; + } + else + root = child; + + // Link n and child. + child.right = node; + node.parent = child; + } + + /** + * Return the node following the given one, or nil if there isn't one. + * Package visible for use by nested classes. + * + * @param node the current node, not nil + * @return the next node in sorted order + */ + final Node successor(Node node) + { + if (node.right != nil) + { + node = node.right; + while (node.left != nil) + node = node.left; + return node; + } + + Node parent = node.parent; + // Exploit fact that nil.right == nil and node is non-nil. + while (node == parent.right) + { + node = parent; + parent = parent.parent; + } + return parent; + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the size (int), followed by key (Object) and value + * (Object) pairs in sorted order + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + + Node node = firstNode(); + s.writeInt(size); + while (node != nil) + { + s.writeObject(node.key); + s.writeObject(node.value); + node = successor(node); + } + } + + /** + * Iterate over TreeMap's entries. This implementation is parameterized + * to give a sequential view of keys, values, or entries. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private final class TreeIterator implements Iterator + { + /** + * The type of this Iterator: {@link #KEYS}, {@link #VALUES}, + * or {@link #ENTRIES}. + */ + private final int type; + /** The number of modifications to the backing Map that we know about. */ + private int knownMod = modCount; + /** The last Entry returned by a next() call. */ + private Node last; + /** The next entry that should be returned by next(). */ + private Node next; + /** + * The last node visible to this iterator. This is used when iterating + * on a SubMap. + */ + private final Node max; + + /** + * Construct a new TreeIterator with the supplied type. + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + */ + TreeIterator(int type) + { + this(type, firstNode(), nil); + } + + /** + * Construct a new TreeIterator with the supplied type. Iteration will + * be from "first" (inclusive) to "max" (exclusive). + * + * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} + * @param first where to start iteration, nil for empty iterator + * @param max the cutoff for iteration, nil for all remaining nodes + */ + TreeIterator(int type, Node first, Node max) + { + this.type = type; + this.next = first; + this.max = max; + } + + /** + * Returns true if the Iterator has more elements. + * @return true if there are more elements + */ + public boolean hasNext() + { + return next != max; + } + + /** + * Returns the next element in the Iterator's sequential view. + * @return the next element + * @throws ConcurrentModificationException if the TreeMap was modified + * @throws NoSuchElementException if there is none + */ + public Object next() + { + if (knownMod != modCount) + throw new ConcurrentModificationException(); + if (next == max) + throw new NoSuchElementException(); + last = next; + next = successor(last); + + if (type == VALUES) + return last.value; + else if (type == KEYS) + return last.key; + return last; + } + + /** + * Removes from the backing TreeMap the last element which was fetched + * with the next() method. + * @throws ConcurrentModificationException if the TreeMap was modified + * @throws IllegalStateException if called when there is no last element + */ + public void remove() + { + if (last == null) + throw new IllegalStateException(); + if (knownMod != modCount) + throw new ConcurrentModificationException(); + + removeNode(last); + last = null; + knownMod++; + } + } // class TreeIterator + + /** + * Implementation of {@link #subMap(Object, Object)} and other map + * ranges. This class provides a view of a portion of the original backing + * map, and throws {@link IllegalArgumentException} for attempts to + * access beyond that range. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private final class SubMap + extends AbstractMap + implements NavigableMap + { + /** + * The lower range of this view, inclusive, or nil for unbounded. + * Package visible for use by nested classes. + */ + final K minKey; + + /** + * The upper range of this view, exclusive, or nil for unbounded. + * Package visible for use by nested classes. + */ + final K maxKey; + + /** + * The cache for {@link #entrySet()}. + */ + private Set> entries; + + /** + * The cache for {@link #descendingMap()}. + */ + private NavigableMap descendingMap; + + /** + * The cache for {@link #navigableKeySet()}. + */ + private NavigableSet nKeys; + + /** + * Create a SubMap representing the elements between minKey (inclusive) + * and maxKey (exclusive). If minKey is nil, SubMap has no lower bound + * (headMap). If maxKey is nil, the SubMap has no upper bound (tailMap). + * + * @param minKey the lower bound + * @param maxKey the upper bound + * @throws IllegalArgumentException if minKey > maxKey + */ + SubMap(K minKey, K maxKey) + { + if (minKey != nil && maxKey != nil && compare(minKey, maxKey) > 0) + throw new IllegalArgumentException("fromKey > toKey"); + this.minKey = minKey; + this.maxKey = maxKey; + } + + /** + * Check if "key" is in within the range bounds for this SubMap. The + * lower ("from") SubMap range is inclusive, and the upper ("to") bound + * is exclusive. Package visible for use by nested classes. + * + * @param key the key to check + * @return true if the key is in range + */ + boolean keyInRange(K key) + { + return ((minKey == nil || compare(key, minKey) >= 0) + && (maxKey == nil || compare(key, maxKey) < 0)); + } + + public Entry ceilingEntry(K key) + { + Entry n = TreeMap.this.ceilingEntry(key); + if (n != null && keyInRange(n.getKey())) + return n; + return null; + } + + public K ceilingKey(K key) + { + K found = TreeMap.this.ceilingKey(key); + if (keyInRange(found)) + return found; + else + return null; + } + + public NavigableSet descendingKeySet() + { + return descendingMap().navigableKeySet(); + } + + public NavigableMap descendingMap() + { + if (descendingMap == null) + descendingMap = new DescendingMap(this); + return descendingMap; + } + + public void clear() + { + Node next = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + while (next != max) + { + Node current = next; + next = successor(current); + removeNode(current); + } + } + + public Comparator comparator() + { + return comparator; + } + + public boolean containsKey(Object key) + { + return keyInRange((K) key) && TreeMap.this.containsKey(key); + } + + public boolean containsValue(Object value) + { + Node node = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + while (node != max) + { + if (equals(value, node.getValue())) + return true; + node = successor(node); + } + return false; + } + + public Set> entrySet() + { + if (entries == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + entries = new SubMap.NavigableEntrySet(); + return entries; + } + + public Entry firstEntry() + { + Node node = lowestGreaterThan(minKey, true); + if (node == nil || ! keyInRange(node.key)) + return null; + return node; + } + + public K firstKey() + { + Entry e = firstEntry(); + if (e == null) + throw new NoSuchElementException(); + return e.getKey(); + } + + public Entry floorEntry(K key) + { + Entry n = TreeMap.this.floorEntry(key); + if (n != null && keyInRange(n.getKey())) + return n; + return null; + } + + public K floorKey(K key) + { + K found = TreeMap.this.floorKey(key); + if (keyInRange(found)) + return found; + else + return null; + } + + public V get(Object key) + { + if (keyInRange((K) key)) + return TreeMap.this.get(key); + return null; + } + + public SortedMap headMap(K toKey) + { + return headMap(toKey, false); + } + + public NavigableMap headMap(K toKey, boolean inclusive) + { + if (!keyInRange(toKey)) + throw new IllegalArgumentException("Key outside submap range"); + return new SubMap(minKey, (inclusive ? + successor(getNode(toKey)).key : toKey)); + } + + public Set keySet() + { + if (this.keys == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + this.keys = new SubMap.KeySet(); + return this.keys; + } + + public Entry higherEntry(K key) + { + Entry n = TreeMap.this.higherEntry(key); + if (n != null && keyInRange(n.getKey())) + return n; + return null; + } + + public K higherKey(K key) + { + K found = TreeMap.this.higherKey(key); + if (keyInRange(found)) + return found; + else + return null; + } + + public Entry lastEntry() + { + return lowerEntry(maxKey); + } + + public K lastKey() + { + Entry e = lastEntry(); + if (e == null) + throw new NoSuchElementException(); + return e.getKey(); + } + + public Entry lowerEntry(K key) + { + Entry n = TreeMap.this.lowerEntry(key); + if (n != null && keyInRange(n.getKey())) + return n; + return null; + } + + public K lowerKey(K key) + { + K found = TreeMap.this.lowerKey(key); + if (keyInRange(found)) + return found; + else + return null; + } + + public NavigableSet navigableKeySet() + { + if (this.nKeys == null) + // Create an AbstractSet with custom implementations of those methods + // that can be overriden easily and efficiently. + this.nKeys = new SubMap.NavigableKeySet(); + return this.nKeys; + } + + public Entry pollFirstEntry() + { + Entry e = firstEntry(); + if (e != null) + removeNode((Node) e); + return e; + } + + public Entry pollLastEntry() + { + Entry e = lastEntry(); + if (e != null) + removeNode((Node) e); + return e; + } + + public V put(K key, V value) + { + if (! keyInRange(key)) + throw new IllegalArgumentException("Key outside range"); + return TreeMap.this.put(key, value); + } + + public V remove(Object key) + { + if (keyInRange((K)key)) + return TreeMap.this.remove(key); + return null; + } + + public int size() + { + Node node = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + int count = 0; + while (node != max) + { + count++; + node = successor(node); + } + return count; + } + + public SortedMap subMap(K fromKey, K toKey) + { + return subMap(fromKey, true, toKey, false); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, + K toKey, boolean toInclusive) + { + if (! keyInRange(fromKey) || ! keyInRange(toKey)) + throw new IllegalArgumentException("key outside range"); + return new SubMap(fromInclusive ? fromKey : successor(getNode(fromKey)).key, + toInclusive ? successor(getNode(toKey)).key : toKey); + } + + public SortedMap tailMap(K fromKey) + { + return tailMap(fromKey, true); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) + { + if (! keyInRange(fromKey)) + throw new IllegalArgumentException("key outside range"); + return new SubMap(inclusive ? fromKey : successor(getNode(fromKey)).key, + maxKey); + } + + public Collection values() + { + if (this.values == null) + // Create an AbstractCollection with custom implementations of those + // methods that can be overriden easily and efficiently. + this.values = new AbstractCollection() + { + public int size() + { + return SubMap.this.size(); + } + + public Iterator iterator() + { + Node first = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + return new TreeIterator(VALUES, first, max); + } + + public void clear() + { + SubMap.this.clear(); + } + }; + return this.values; + } + + private class KeySet + extends AbstractSet + { + public int size() + { + return SubMap.this.size(); + } + + public Iterator iterator() + { + Node first = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + return new TreeIterator(KEYS, first, max); + } + + public void clear() + { + SubMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! keyInRange((K) o)) + return false; + return getNode((K) o) != nil; + } + + public boolean remove(Object o) + { + if (! keyInRange((K) o)) + return false; + Node n = getNode((K) o); + if (n != nil) + { + removeNode(n); + return true; + } + return false; + } + + } // class SubMap.KeySet + + private final class NavigableKeySet + extends KeySet + implements NavigableSet + { + + public K ceiling(K k) + { + return SubMap.this.ceilingKey(k); + } + + public Comparator comparator() + { + return comparator; + } + + public Iterator descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet descendingSet() + { + return new DescendingSet(this); + } + + public K first() + { + return SubMap.this.firstKey(); + } + + public K floor(K k) + { + return SubMap.this.floorKey(k); + } + + public SortedSet headSet(K to) + { + return headSet(to, false); + } + + public NavigableSet headSet(K to, boolean inclusive) + { + return SubMap.this.headMap(to, inclusive).navigableKeySet(); + } + + public K higher(K k) + { + return SubMap.this.higherKey(k); + } + + public K last() + { + return SubMap.this.lastKey(); + } + + public K lower(K k) + { + return SubMap.this.lowerKey(k); + } + + public K pollFirst() + { + return SubMap.this.pollFirstEntry().getKey(); + } + + public K pollLast() + { + return SubMap.this.pollLastEntry().getKey(); + } + + public SortedSet subSet(K from, K to) + { + return subSet(from, true, to, false); + } + + public NavigableSet subSet(K from, boolean fromInclusive, + K to, boolean toInclusive) + { + return SubMap.this.subMap(from, fromInclusive, + to, toInclusive).navigableKeySet(); + } + + public SortedSet tailSet(K from) + { + return tailSet(from, true); + } + + public NavigableSet tailSet(K from, boolean inclusive) + { + return SubMap.this.tailMap(from, inclusive).navigableKeySet(); + } + + } // class SubMap.NavigableKeySet + + /** + * Implementation of {@link #entrySet()}. + */ + private class EntrySet + extends AbstractSet> + { + + public int size() + { + return SubMap.this.size(); + } + + public Iterator> iterator() + { + Node first = lowestGreaterThan(minKey, true); + Node max = lowestGreaterThan(maxKey, false); + return new TreeIterator(ENTRIES, first, max); + } + + public void clear() + { + SubMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry me = (Map.Entry) o; + K key = me.getKey(); + if (! keyInRange(key)) + return false; + Node n = getNode(key); + return n != nil && AbstractSet.equals(me.getValue(), n.value); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry me = (Map.Entry) o; + K key = me.getKey(); + if (! keyInRange(key)) + return false; + Node n = getNode(key); + if (n != nil && AbstractSet.equals(me.getValue(), n.value)) + { + removeNode(n); + return true; + } + return false; + } + } // class SubMap.EntrySet + + private final class NavigableEntrySet + extends EntrySet + implements NavigableSet> + { + + public Entry ceiling(Entry e) + { + return SubMap.this.ceilingEntry(e.getKey()); + } + + public Comparator> comparator() + { + return new Comparator>() + { + public int compare(Entry t1, Entry t2) + { + return comparator.compare(t1.getKey(), t2.getKey()); + } + }; + } + + public Iterator> descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet> descendingSet() + { + return new DescendingSet(this); + } + + public Entry first() + { + return SubMap.this.firstEntry(); + } + + public Entry floor(Entry e) + { + return SubMap.this.floorEntry(e.getKey()); + } + + public SortedSet> headSet(Entry to) + { + return headSet(to, false); + } + + public NavigableSet> headSet(Entry to, boolean inclusive) + { + return (NavigableSet>) + SubMap.this.headMap(to.getKey(), inclusive).entrySet(); + } + + public Entry higher(Entry e) + { + return SubMap.this.higherEntry(e.getKey()); + } + + public Entry last() + { + return SubMap.this.lastEntry(); + } + + public Entry lower(Entry e) + { + return SubMap.this.lowerEntry(e.getKey()); + } + + public Entry pollFirst() + { + return SubMap.this.pollFirstEntry(); + } + + public Entry pollLast() + { + return SubMap.this.pollLastEntry(); + } + + public SortedSet> subSet(Entry from, Entry to) + { + return subSet(from, true, to, false); + } + + public NavigableSet> subSet(Entry from, boolean fromInclusive, + Entry to, boolean toInclusive) + { + return (NavigableSet>) + SubMap.this.subMap(from.getKey(), fromInclusive, + to.getKey(), toInclusive).entrySet(); + } + + public SortedSet> tailSet(Entry from) + { + return tailSet(from, true); + } + + public NavigableSet> tailSet(Entry from, boolean inclusive) + { + return (NavigableSet>) + SubMap.this.tailMap(from.getKey(), inclusive).navigableKeySet(); + } + + } // class SubMap.NavigableEntrySet + +} // class SubMap + + /** + * Returns the entry associated with the least or lowest key + * that is greater than or equal to the specified key, or + * null if there is no such key. + * + * @param key the key relative to the returned entry. + * @return the entry with the least key greater than or equal + * to the given key, or null if there is + * no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is null + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public Entry ceilingEntry(K key) + { + Node n = lowestGreaterThan(key, false); + return (n == nil) ? null : n; + } + + /** + * Returns the the least or lowest key that is greater than + * or equal to the specified key, or null if + * there is no such key. + * + * @param key the key relative to the returned entry. + * @return the least key greater than or equal to the given key, + * or null if there is no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is null + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public K ceilingKey(K key) + { + Entry e = ceilingEntry(key); + return (e == null) ? null : e.getKey(); + } + + /** + * Returns a reverse ordered {@link NavigableSet} view of this + * map's keys. The set is backed by the {@link TreeMap}, so changes + * in one show up in the other. The set supports element removal, + * but not element addition. + * + * @return a reverse ordered set view of the keys. + * @since 1.6 + * @see #descendingMap() + */ + public NavigableSet descendingKeySet() + { + return descendingMap().navigableKeySet(); + } + + /** + * Returns a view of the map in reverse order. The descending map + * is backed by the original map, so that changes affect both maps. + * Any changes occurring to either map while an iteration is taking + * place (with the exception of a {@link Iterator#remove()} operation) + * result in undefined behaviour from the iteration. The ordering + * of the descending map is the same as for a map with a + * {@link Comparator} given by {@link Collections#reverseOrder()}, + * and calling {@link #descendingMap()} on the descending map itself + * results in a view equivalent to the original map. + * + * @return a reverse order view of the map. + * @since 1.6 + */ + public NavigableMap descendingMap() + { + if (descendingMap == null) + descendingMap = new DescendingMap(this); + return descendingMap; + } + + /** + * Returns the entry associated with the least or lowest key + * in the map, or null if the map is empty. + * + * @return the lowest entry, or null if the map + * is empty. + * @since 1.6 + */ + public Entry firstEntry() + { + Node n = firstNode(); + return (n == nil) ? null : n; + } + + /** + * Returns the entry associated with the greatest or highest key + * that is less than or equal to the specified key, or + * null if there is no such key. + * + * @param key the key relative to the returned entry. + * @return the entry with the greatest key less than or equal + * to the given key, or null if there is + * no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is null + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public Entry floorEntry(K key) + { + Node n = highestLessThan(key, true); + return (n == nil) ? null : n; + } + + /** + * Returns the the greatest or highest key that is less than + * or equal to the specified key, or null if + * there is no such key. + * + * @param key the key relative to the returned entry. + * @return the greatest key less than or equal to the given key, + * or null if there is no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is null + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public K floorKey(K key) + { + Entry e = floorEntry(key); + return (e == null) ? null : e.getKey(); + } + + /** + * Returns the entry associated with the least or lowest key + * that is strictly greater than the specified key, or + * null if there is no such key. + * + * @param key the key relative to the returned entry. + * @return the entry with the least key greater than + * the given key, or null if there is + * no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is null + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public Entry higherEntry(K key) + { + Node n = lowestGreaterThan(key, false, false); + return (n == nil) ? null : n; + } + + /** + * Returns the the least or lowest key that is strictly + * greater than the specified key, or null if + * there is no such key. + * + * @param key the key relative to the returned entry. + * @return the least key greater than the given key, + * or null if there is no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is null + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public K higherKey(K key) + { + Entry e = higherEntry(key); + return (e == null) ? null : e.getKey(); + } + + /** + * Returns the entry associated with the greatest or highest key + * in the map, or null if the map is empty. + * + * @return the highest entry, or null if the map + * is empty. + * @since 1.6 + */ + public Entry lastEntry() + { + Node n = lastNode(); + return (n == nil) ? null : n; + } + + /** + * Returns the entry associated with the greatest or highest key + * that is strictly less than the specified key, or + * null if there is no such key. + * + * @param key the key relative to the returned entry. + * @return the entry with the greatest key less than + * the given key, or null if there is + * no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is null + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public Entry lowerEntry(K key) + { + Node n = highestLessThan(key); + return (n == nil) ? null : n; + } + + /** + * Returns the the greatest or highest key that is strictly + * less than the specified key, or null if + * there is no such key. + * + * @param key the key relative to the returned entry. + * @return the greatest key less than the given key, + * or null if there is no such key. + * @throws ClassCastException if the specified key can not + * be compared with those in the map. + * @throws NullPointerException if the key is null + * and this map either uses natural + * ordering or a comparator that does + * not permit null keys. + * @since 1.6 + */ + public K lowerKey(K key) + { + Entry e = lowerEntry(key); + return (e == null) ? null : e.getKey(); + } + + /** + * Returns a {@link NavigableSet} view of this map's keys. The set is + * backed by the {@link TreeMap}, so changes in one show up in the other. + * Any changes occurring to either while an iteration is taking + * place (with the exception of a {@link Iterator#remove()} operation) + * result in undefined behaviour from the iteration. The ordering + * The set supports element removal, but not element addition. + * + * @return a {@link NavigableSet} view of the keys. + * @since 1.6 + */ + public NavigableSet navigableKeySet() + { + if (nKeys == null) + nKeys = new NavigableKeySet(); + return nKeys; + } + + /** + * Removes and returns the entry associated with the least + * or lowest key in the map, or null if the map + * is empty. + * + * @return the removed first entry, or null if the + * map is empty. + * @since 1.6 + */ + public Entry pollFirstEntry() + { + Entry e = firstEntry(); + if (e != null) + removeNode((Node)e); + return e; + } + + /** + * Removes and returns the entry associated with the greatest + * or highest key in the map, or null if the map + * is empty. + * + * @return the removed last entry, or null if the + * map is empty. + * @since 1.6 + */ + public Entry pollLastEntry() + { + Entry e = lastEntry(); + if (e != null) + removeNode((Node)e); + return e; + } + + /** + * Implementation of {@link #descendingMap()} and associated + * derivatives. This class provides a view of the + * original backing map in reverse order, and throws + * {@link IllegalArgumentException} for attempts to + * access beyond that range. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static final class DescendingMap + implements NavigableMap + { + + /** + * The cache for {@link #entrySet()}. + */ + private Set> entries; + + /** + * The cache for {@link #keySet()}. + */ + private Set keys; + + /** + * The cache for {@link #navigableKeySet()}. + */ + private NavigableSet nKeys; + + /** + * The cache for {@link #values()}. + */ + private Collection values; + + /** + * The backing {@link NavigableMap}. + */ + private NavigableMap map; + + /** + * Create a {@link DescendingMap} around the specified + * map. + * + * @param map the map to wrap. + */ + public DescendingMap(NavigableMap map) + { + this.map = map; + } + + public Map.Entry ceilingEntry(DK key) + { + return map.floorEntry(key); + } + + public DK ceilingKey(DK key) + { + return map.floorKey(key); + } + + public void clear() + { + map.clear(); + } + + public Comparator comparator() + { + return Collections.reverseOrder(map.comparator()); + } + + public boolean containsKey(Object o) + { + return map.containsKey(o); + } + + public boolean containsValue(Object o) + { + return map.containsValue(o); + } + + public NavigableSet descendingKeySet() + { + return descendingMap().navigableKeySet(); + } + + public NavigableMap descendingMap() + { + return map; + } + + public Set> entrySet() + { + if (entries == null) + entries = + new DescendingSet>((NavigableSet>) + map.entrySet()); + return entries; + } + + public boolean equals(Object o) + { + return map.equals(o); + } + + public Entry firstEntry() + { + return map.lastEntry(); + } + + public DK firstKey() + { + return map.lastKey(); + } + + public Entry floorEntry(DK key) + { + return map.ceilingEntry(key); + } + + public DK floorKey(DK key) + { + return map.ceilingKey(key); + } + + public DV get(Object key) + { + return map.get(key); + } + + public int hashCode() + { + return map.hashCode(); + } + + public SortedMap headMap(DK toKey) + { + return headMap(toKey, false); + } + + public NavigableMap headMap(DK toKey, boolean inclusive) + { + return new DescendingMap(map.tailMap(toKey, inclusive)); + } + + public Entry higherEntry(DK key) + { + return map.lowerEntry(key); + } + + public DK higherKey(DK key) + { + return map.lowerKey(key); + } + + public Set keySet() + { + if (keys == null) + keys = new DescendingSet(map.navigableKeySet()); + return keys; + } + + public boolean isEmpty() + { + return map.isEmpty(); + } + + public Entry lastEntry() + { + return map.firstEntry(); + } + + public DK lastKey() + { + return map.firstKey(); + } + + public Entry lowerEntry(DK key) + { + return map.higherEntry(key); + } + + public DK lowerKey(DK key) + { + return map.higherKey(key); + } + + public NavigableSet navigableKeySet() + { + if (nKeys == null) + nKeys = new DescendingSet(map.navigableKeySet()); + return nKeys; + } + + public Entry pollFirstEntry() + { + return pollLastEntry(); + } + + public Entry pollLastEntry() + { + return pollFirstEntry(); + } + + public DV put(DK key, DV value) + { + return map.put(key, value); + } + + public void putAll(Map m) + { + map.putAll(m); + } + + public DV remove(Object key) + { + return map.remove(key); + } + + public int size() + { + return map.size(); + } + + public SortedMap subMap(DK fromKey, DK toKey) + { + return subMap(fromKey, true, toKey, false); + } + + public NavigableMap subMap(DK fromKey, boolean fromInclusive, + DK toKey, boolean toInclusive) + { + return new DescendingMap(map.subMap(fromKey, fromInclusive, + toKey, toInclusive)); + } + + public SortedMap tailMap(DK fromKey) + { + return tailMap(fromKey, true); + } + + public NavigableMap tailMap(DK fromKey, boolean inclusive) + { + return new DescendingMap(map.headMap(fromKey, inclusive)); + } + + public String toString() + { + CPStringBuilder r = new CPStringBuilder("{"); + final Iterator> it = entrySet().iterator(); + while (it.hasNext()) + { + final Entry e = it.next(); + r.append(e.getKey()); + r.append('='); + r.append(e.getValue()); + r.append(", "); + } + r.replace(r.length() - 2, r.length(), "}"); + return r.toString(); + } + + public Collection values() + { + if (values == null) + // Create an AbstractCollection with custom implementations of those + // methods that can be overriden easily and efficiently. + values = new AbstractCollection() + { + public int size() + { + return DescendingMap.this.size(); + } + + public Iterator iterator() + { + return new Iterator() + { + /** The last Entry returned by a next() call. */ + private Entry last; + + /** The next entry that should be returned by next(). */ + private Entry next = firstEntry(); + + public boolean hasNext() + { + return next != null; + } + + public DV next() + { + if (next == null) + throw new NoSuchElementException(); + last = next; + next = higherEntry(last.getKey()); + + return last.getValue(); + } + + public void remove() + { + if (last == null) + throw new IllegalStateException(); + + DescendingMap.this.remove(last.getKey()); + last = null; + } + }; + } + + public void clear() + { + DescendingMap.this.clear(); + } + }; + return values; + } + + } // class DescendingMap + + /** + * Implementation of {@link #keySet()}. + */ + private class KeySet + extends AbstractSet + { + + public int size() + { + return size; + } + + public Iterator iterator() + { + return new TreeIterator(KEYS); + } + + public void clear() + { + TreeMap.this.clear(); + } + + public boolean contains(Object o) + { + return containsKey(o); + } + + public boolean remove(Object key) + { + Node n = getNode((K) key); + if (n == nil) + return false; + removeNode(n); + return true; + } + } // class KeySet + + /** + * Implementation of {@link #navigableKeySet()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private final class NavigableKeySet + extends KeySet + implements NavigableSet + { + + public K ceiling(K k) + { + return ceilingKey(k); + } + + public Comparator comparator() + { + return comparator; + } + + public Iterator descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet descendingSet() + { + return new DescendingSet(this); + } + + public K first() + { + return firstKey(); + } + + public K floor(K k) + { + return floorKey(k); + } + + public SortedSet headSet(K to) + { + return headSet(to, false); + } + + public NavigableSet headSet(K to, boolean inclusive) + { + return headMap(to, inclusive).navigableKeySet(); + } + + public K higher(K k) + { + return higherKey(k); + } + + public K last() + { + return lastKey(); + } + + public K lower(K k) + { + return lowerKey(k); + } + + public K pollFirst() + { + return pollFirstEntry().getKey(); + } + + public K pollLast() + { + return pollLastEntry().getKey(); + } + + public SortedSet subSet(K from, K to) + { + return subSet(from, true, to, false); + } + + public NavigableSet subSet(K from, boolean fromInclusive, + K to, boolean toInclusive) + { + return subMap(from, fromInclusive, + to, toInclusive).navigableKeySet(); + } + + public SortedSet tailSet(K from) + { + return tailSet(from, true); + } + + public NavigableSet tailSet(K from, boolean inclusive) + { + return tailMap(from, inclusive).navigableKeySet(); + } + + + } // class NavigableKeySet + + /** + * Implementation of {@link #descendingSet()} and associated + * derivatives. This class provides a view of the + * original backing set in reverse order, and throws + * {@link IllegalArgumentException} for attempts to + * access beyond that range. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ + private static final class DescendingSet + implements NavigableSet + { + + /** + * The backing {@link NavigableSet}. + */ + private NavigableSet set; + + /** + * Create a {@link DescendingSet} around the specified + * set. + * + * @param map the set to wrap. + */ + public DescendingSet(NavigableSet set) + { + this.set = set; + } + + public boolean add(D e) + { + return set.add(e); + } + + public boolean addAll(Collection c) + { + return set.addAll(c); + } + + public D ceiling(D e) + { + return set.floor(e); + } + + public void clear() + { + set.clear(); + } + + public Comparator comparator() + { + return Collections.reverseOrder(set.comparator()); + } + + public boolean contains(Object o) + { + return set.contains(o); + } + + public boolean containsAll(Collection c) + { + return set.containsAll(c); + } + + public Iterator descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet descendingSet() + { + return set; + } + + public boolean equals(Object o) + { + return set.equals(o); + } + + public D first() + { + return set.last(); + } + + public D floor(D e) + { + return set.ceiling(e); + } + + public int hashCode() + { + return set.hashCode(); + } + + public SortedSet headSet(D to) + { + return headSet(to, false); + } + + public NavigableSet headSet(D to, boolean inclusive) + { + return new DescendingSet(set.tailSet(to, inclusive)); + } + + public D higher(D e) + { + return set.lower(e); + } + + public boolean isEmpty() + { + return set.isEmpty(); + } + + public Iterator iterator() + { + return new Iterator() + { + + /** The last element returned by a next() call. */ + private D last; + + /** The next element that should be returned by next(). */ + private D next = first(); + + public boolean hasNext() + { + return next != null; + } + + public D next() + { + if (next == null) + throw new NoSuchElementException(); + last = next; + next = higher(last); + + return last; + } + + public void remove() + { + if (last == null) + throw new IllegalStateException(); + + DescendingSet.this.remove(last); + last = null; + } + }; + } + + public D last() + { + return set.first(); + } + + public D lower(D e) + { + return set.higher(e); + } + + public D pollFirst() + { + return set.pollLast(); + } + + public D pollLast() + { + return set.pollFirst(); + } + + public boolean remove(Object o) + { + return set.remove(o); + } + + public boolean removeAll(Collection c) + { + return set.removeAll(c); + } + + public boolean retainAll(Collection c) + { + return set.retainAll(c); + } + + public int size() + { + return set.size(); + } + + public SortedSet subSet(D from, D to) + { + return subSet(from, true, to, false); + } + + public NavigableSet subSet(D from, boolean fromInclusive, + D to, boolean toInclusive) + { + return new DescendingSet(set.subSet(from, fromInclusive, + to, toInclusive)); + } + + public SortedSet tailSet(D from) + { + return tailSet(from, true); + } + + public NavigableSet tailSet(D from, boolean inclusive) + { + return new DescendingSet(set.headSet(from, inclusive)); + } + + public Object[] toArray() + { + D[] array = (D[]) set.toArray(); + Arrays.sort(array, comparator()); + return array; + } + + public T[] toArray(T[] a) + { + T[] array = set.toArray(a); + Arrays.sort(array, (Comparator) comparator()); + return array; + } + + public String toString() + { + CPStringBuilder r = new CPStringBuilder("["); + final Iterator it = iterator(); + while (it.hasNext()) + { + final D o = it.next(); + if (o == this) + r.append(""); + else + r.append(o); + r.append(", "); + } + r.replace(r.length() - 2, r.length(), "]"); + return r.toString(); + } + + } // class DescendingSet + + private class EntrySet + extends AbstractSet> + { + public int size() + { + return size; + } + + public Iterator> iterator() + { + return new TreeIterator(ENTRIES); + } + + public void clear() + { + TreeMap.this.clear(); + } + + public boolean contains(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry me = (Map.Entry) o; + Node n = getNode(me.getKey()); + return n != nil && AbstractSet.equals(me.getValue(), n.value); + } + + public boolean remove(Object o) + { + if (! (o instanceof Map.Entry)) + return false; + Map.Entry me = (Map.Entry) o; + Node n = getNode(me.getKey()); + if (n != nil && AbstractSet.equals(me.getValue(), n.value)) + { + removeNode(n); + return true; + } + return false; + } + } + + private final class NavigableEntrySet + extends EntrySet + implements NavigableSet> + { + + public Entry ceiling(Entry e) + { + return ceilingEntry(e.getKey()); + } + + public Comparator> comparator() + { + return new Comparator>() + { + public int compare(Entry t1, Entry t2) + { + return comparator.compare(t1.getKey(), t2.getKey()); + } + }; + } + + public Iterator> descendingIterator() + { + return descendingSet().iterator(); + } + + public NavigableSet> descendingSet() + { + return new DescendingSet(this); + } + + public Entry first() + { + return firstEntry(); + } + + public Entry floor(Entry e) + { + return floorEntry(e.getKey()); + } + + public SortedSet> headSet(Entry to) + { + return headSet(to, false); + } + + public NavigableSet> headSet(Entry to, boolean inclusive) + { + return (NavigableSet>) headMap(to.getKey(), inclusive).entrySet(); + } + + public Entry higher(Entry e) + { + return higherEntry(e.getKey()); + } + + public Entry last() + { + return lastEntry(); + } + + public Entry lower(Entry e) + { + return lowerEntry(e.getKey()); + } + + public Entry pollFirst() + { + return pollFirstEntry(); + } + + public Entry pollLast() + { + return pollLastEntry(); + } + + public SortedSet> subSet(Entry from, Entry to) + { + return subSet(from, true, to, false); + } + + public NavigableSet> subSet(Entry from, boolean fromInclusive, + Entry to, boolean toInclusive) + { + return (NavigableSet>) subMap(from.getKey(), fromInclusive, + to.getKey(), toInclusive).entrySet(); + } + + public SortedSet> tailSet(Entry from) + { + return tailSet(from, true); + } + + public NavigableSet> tailSet(Entry from, boolean inclusive) + { + return (NavigableSet>) tailMap(from.getKey(), inclusive).navigableKeySet(); + } + + } // class NavigableEntrySet + +} // class TreeMap diff --git a/libjava/classpath/java/util/TreeSet.java b/libjava/classpath/java/util/TreeSet.java new file mode 100644 index 000000000..06f4fa5b3 --- /dev/null +++ b/libjava/classpath/java/util/TreeSet.java @@ -0,0 +1,641 @@ +/* TreeSet.java -- a class providing a TreeMap-backed SortedSet + Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * This class provides a TreeMap-backed implementation of the SortedSet + * interface. The elements will be sorted according to their natural + * order, or according to the provided Comparator.

        + * + * Most operations are O(log n), but there is so much overhead that this + * makes small sets expensive. Note that the ordering must be consistent + * with equals to correctly implement the Set interface. If this + * condition is violated, the set is still well-behaved, but you may have + * suprising results when comparing it to other sets.

        + * + * This implementation is not synchronized. If you need to share this between + * multiple threads, do something like:
        + * SortedSet s + * = Collections.synchronizedSortedSet(new TreeSet(...));

        + * + * The iterators are fail-fast, meaning that any structural + * modification, except for remove() called on the iterator + * itself, cause the iterator to throw a + * ConcurrentModificationException rather than exhibit + * non-deterministic behavior. + * + * @author Jon Zeppieri + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @see Collection + * @see Set + * @see HashSet + * @see LinkedHashSet + * @see Comparable + * @see Comparator + * @see Collections#synchronizedSortedSet(SortedSet) + * @see TreeMap + * @since 1.2 + * @status updated to 1.6 + */ +public class TreeSet extends AbstractSet + implements NavigableSet, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.2. + */ + private static final long serialVersionUID = -2479143000061671589L; + + /** + * The NavigableMap which backs this Set. + */ + // Not final because of readObject. This will always be one of TreeMap or + // TreeMap.SubMap, which both extend AbstractMap. + private transient NavigableMap map; + + /** + * Construct a new TreeSet whose backing TreeMap using the "natural" + * ordering of keys. Elements that are not mutually comparable will cause + * ClassCastExceptions down the road. + * + * @see Comparable + */ + public TreeSet() + { + map = new TreeMap(); + } + + /** + * Construct a new TreeSet whose backing TreeMap uses the supplied + * Comparator. Elements that are not mutually comparable will cause + * ClassCastExceptions down the road. + * + * @param comparator the Comparator this Set will use + */ + public TreeSet(Comparator comparator) + { + map = new TreeMap(comparator); + } + + /** + * Construct a new TreeSet whose backing TreeMap uses the "natural" + * orering of the keys and which contains all of the elements in the + * supplied Collection. This runs in n*log(n) time. + * + * @param collection the new Set will be initialized with all + * of the elements in this Collection + * @throws ClassCastException if the elements of the collection are not + * comparable + * @throws NullPointerException if the collection is null + * @see Comparable + */ + public TreeSet(Collection collection) + { + map = new TreeMap(); + addAll(collection); + } + + /** + * Construct a new TreeSet, using the same key ordering as the supplied + * SortedSet and containing all of the elements in the supplied SortedSet. + * This constructor runs in linear time. + * + * @param sortedSet the new TreeSet will use this SortedSet's comparator + * and will initialize itself with all its elements + * @throws NullPointerException if sortedSet is null + */ + public TreeSet(SortedSet sortedSet) + { + Iterator itr; + + map = new TreeMap + ((Comparator)sortedSet.comparator()); + itr = ((SortedSet) sortedSet).iterator(); + ((TreeMap) map).putKeysLinear(itr, sortedSet.size()); + } + + /** + * This private constructor is used to implement the subSet() calls around + * a backing TreeMap.SubMap. + * + * @param backingMap the submap + */ + private TreeSet(NavigableMap backingMap) + { + map = backingMap; + } + + /** + * Adds the spplied Object to the Set if it is not already in the Set; + * returns true if the element is added, false otherwise. + * + * @param obj the Object to be added to this Set + * @throws ClassCastException if the element cannot be compared with objects + * already in the set + */ + public boolean add(T obj) + { + return map.put(obj, "") == null; + } + + /** + * Adds all of the elements in the supplied Collection to this TreeSet. + * + * @param c The collection to add + * @return true if the Set is altered, false otherwise + * @throws NullPointerException if c is null + * @throws ClassCastException if an element in c cannot be compared with + * objects already in the set + */ + public boolean addAll(Collection c) + { + boolean result = false; + int pos = c.size(); + Iterator itr = c.iterator(); + while (--pos >= 0) + result |= (map.put(itr.next(), "") == null); + return result; + } + + /** + * Removes all elements in this Set. + */ + public void clear() + { + map.clear(); + } + + /** + * Returns a shallow copy of this Set. The elements are not cloned. + * + * @return the cloned set + */ + public Object clone() + { + TreeSet copy = null; + try + { + copy = (TreeSet) super.clone(); + // Map may be either TreeMap or TreeMap.SubMap, hence the ugly casts. + copy.map = (NavigableMap) ((AbstractMap) map).clone(); + } + catch (CloneNotSupportedException x) + { + // Impossible result. + } + return copy; + } + + /** + * Returns this Set's comparator. + * + * @return the comparator, or null if the set uses natural ordering + */ + public Comparator comparator() + { + return map.comparator(); + } + + /** + * Returns true if this Set contains the supplied Object, false otherwise. + * + * @param obj the Object to check for + * @return true if it is in the set + * @throws ClassCastException if obj cannot be compared with objects + * already in the set + */ + public boolean contains(Object obj) + { + return map.containsKey(obj); + } + + /** + * Returns the first (by order) element in this Set. + * + * @return the first element + * @throws NoSuchElementException if the set is empty + */ + public T first() + { + return map.firstKey(); + } + + /** + * Returns a view of this Set including all elements less than + * to. The returned set is backed by the original, so changes + * in one appear in the other. The subset will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned set does not include + * the endpoint; if you want inclusion, pass the successor element or + * call {@link #headSet(T,boolean)}. This call is equivalent to + * headSet(to, false). + * + * @param to the (exclusive) cutoff point + * @return a view of the set less than the cutoff + * @throws ClassCastException if to is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if to is null, but the comparator does not + * tolerate null elements + */ + public SortedSet headSet(T to) + { + return headSet(to, false); + } + + /** + * Returns a view of this Set including all elements less than + * (or equal to, if inclusive is true) to. + * The returned set is backed by the original, so changes + * in one appear in the other. The subset will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. + * + * @param to the cutoff point + * @param inclusive true if to should be included. + * @return a view of the set for the specified range. + * @throws ClassCastException if to is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if to is null, but the comparator does not + * tolerate null elements + */ + public NavigableSet headSet(T to, boolean inclusive) + { + return new TreeSet(map.headMap(to, inclusive)); + } + + /** + * Returns true if this Set has size 0, false otherwise. + * + * @return true if the set is empty + */ + public boolean isEmpty() + { + return map.isEmpty(); + } + + /** + * Returns in Iterator over the elements in this TreeSet, which traverses + * in ascending order. + * + * @return an iterator + */ + public Iterator iterator() + { + return map.keySet().iterator(); + } + + /** + * Returns the last (by order) element in this Set. + * + * @return the last element + * @throws NoSuchElementException if the set is empty + */ + public T last() + { + return map.lastKey(); + } + + /** + * If the supplied Object is in this Set, it is removed, and true is + * returned; otherwise, false is returned. + * + * @param obj the Object to remove from this Set + * @return true if the set was modified + * @throws ClassCastException if obj cannot be compared to set elements + */ + public boolean remove(Object obj) + { + return map.remove(obj) != null; + } + + /** + * Returns the number of elements in this Set + * + * @return the set size + */ + public int size() + { + return map.size(); + } + + /** + * Returns a view of this Set including all elements greater or equal to + * from and less than to (a half-open interval). + * The returned set is backed by the original, so changes in one appear in + * the other. The subset will throw an {@link IllegalArgumentException} + * for any attempt to access or add an element beyond the specified cutoffs. + * The returned set includes the low endpoint but not the high; if you want + * to reverse this behavior on either end, pass in the successor element + * or call {@link #subSet(T,boolean,T,boolean)}. This is equivalent to + * calling subSet(from,true,to,false). + * + * @param from the (inclusive) low cutoff point + * @param to the (exclusive) high cutoff point + * @return a view of the set between the cutoffs + * @throws ClassCastException if either cutoff is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if from or to is null, but the comparator + * does not tolerate null elements + * @throws IllegalArgumentException if from is greater than to + */ + public SortedSet subSet(T from, T to) + { + return subSet(from, true, to, false); + } + + /** + * Returns a view of this Set including all elements greater than (or equal to, + * if fromInclusive is true from and less than + * (or equal to, if toInclusive is true) to. + * The returned set is backed by the original, so changes in one appear in + * the other. The subset will throw an {@link IllegalArgumentException} + * for any attempt to access or add an element beyond the specified cutoffs. + * + * @param from the low cutoff point + * @param fromInclusive true if from should be included. + * @param to the high cutoff point + * @param toInclusive true if to should be included. + * @return a view of the set for the specified range. + * @throws ClassCastException if either cutoff is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if from or to is null, but the comparator + * does not tolerate null elements + * @throws IllegalArgumentException if from is greater than to + */ + public NavigableSet subSet(T from, boolean fromInclusive, + T to, boolean toInclusive) + { + return new TreeSet(map.subMap(from, fromInclusive, + to, toInclusive)); + } + + /** + * Returns a view of this Set including all elements greater or equal to + * from. The returned set is backed by the original, so + * changes in one appear in the other. The subset will throw an + * {@link IllegalArgumentException} for any attempt to access or add an + * element beyond the specified cutoff. The returned set includes the + * endpoint; if you want to exclude it, pass in the successor element + * or call {@link #tailSet(T,boolean)}. This is equivalent to calling + * tailSet(from, true). + * + * @param from the (inclusive) low cutoff point + * @return a view of the set above the cutoff + * @throws ClassCastException if from is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if from is null, but the comparator + * does not tolerate null elements + */ + public SortedSet tailSet(T from) + { + return tailSet(from, true); + } + + /** + * Returns a view of this Set including all elements greater (or equal to, + * if inclusive is true) from. The returned set + * is backed by the original, so changes in one appear in the other. The + * subset will throw an {@link IllegalArgumentException} for any attempt + * to access or add an element beyond the specified cutoff. + * + * @param from the low cutoff point. + * @param inclusive true if from should be included. + * @return a view of the set for the specified range. + * @throws ClassCastException if from is not compatible with + * the comparator (or is not Comparable, for natural ordering) + * @throws NullPointerException if from is null, but the comparator + * does not tolerate null elements + */ + public NavigableSet tailSet(T from, boolean inclusive) + { + return new TreeSet(map.tailMap(from, inclusive)); + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData the comparator (Object), followed by the set size + * (int), the the elements in sorted order (Object) + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + s.defaultWriteObject(); + Iterator itr = map.keySet().iterator(); + int pos = map.size(); + s.writeObject(map.comparator()); + s.writeInt(pos); + while (--pos >= 0) + s.writeObject(itr.next()); + } + + /** + * Deserializes this object from the given stream. + * + * @param s the stream to read from + * @throws ClassNotFoundException if the underlying stream fails + * @throws IOException if the underlying stream fails + * @serialData the comparator (Object), followed by the set size + * (int), the the elements in sorted order (Object) + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + Comparator comparator = (Comparator) s.readObject(); + int size = s.readInt(); + map = new TreeMap(comparator); + ((TreeMap) map).putFromObjStream(s, size, false); + } + + /** + * Returns the least or lowest element in the set greater than or + * equal to the given element, or null if there is + * no such element. + * + * @param e the element relative to the returned element. + * @return the least element greater than or equal + * to the given element, or null if there is + * no such element. + * @throws ClassCastException if the specified element can not + * be compared with those in the map. + * @throws NullPointerException if the element is null + * and this set either uses natural + * ordering or a comparator that does + * not permit null elements. + * @since 1.6 + */ + public T ceiling(T e) + { + return map.ceilingKey(e); + } + + /** + * Returns an iterator over the elements of this set in descending + * order. This is equivalent to calling + * descendingSet().iterator(). + * + * @return an iterator over the elements in descending order. + * @since 1.6 + */ + public Iterator descendingIterator() + { + return descendingSet().iterator(); + } + + /** + * Returns a view of the set in reverse order. The descending set + * is backed by the original set, so that changes affect both sets. + * Any changes occurring to either set while an iteration is taking + * place (with the exception of a {@link Iterator#remove()} operation) + * result in undefined behaviour from the iteration. The ordering + * of the descending set is the same as for a set with a + * {@link Comparator} given by {@link Collections#reverseOrder()}, + * and calling {@link #descendingSet()} on the descending set itself + * results in a view equivalent to the original set. + * + * @return a reverse order view of the set. + * @since 1.6 + */ + public NavigableSet descendingSet() + { + return map.descendingKeySet(); + } + + /** + * Returns the greatest or highest element in the set less than or + * equal to the given element, or null if there is + * no such element. + * + * @param e the element relative to the returned element. + * @return the greatest element less than or equal + * to the given element, or null if there is + * no such element. + * @throws ClassCastException if the specified element can not + * be compared with those in the map. + * @throws NullPointerException if the element is null + * and this set either uses natural + * ordering or a comparator that does + * not permit null elements. + * @since 1.6 + */ + public T floor(T e) + { + return map.floorKey(e); + } + + /** + * Returns the least or lowest element in the set strictly greater + * than the given element, or null if there is + * no such element. + * + * @param e the element relative to the returned element. + * @return the least element greater than + * the given element, or null if there is + * no such element. + * @throws ClassCastException if the specified element can not + * be compared with those in the map. + * @throws NullPointerException if the element is null + * and this set either uses natural + * ordering or a comparator that does + * not permit null elements. + * @since 1.6 + */ + public T higher(T e) + { + return map.higherKey(e); + } + + /** + * Returns the greatest or highest element in the set strictly less + * than the given element, or null if there is + * no such element. + * + * @param e the element relative to the returned element. + * @return the greatest element less than + * the given element, or null if there is + * no such element. + * @throws ClassCastException if the specified element can not + * be compared with those in the map. + * @throws NullPointerException if the element is null + * and this set either uses natural + * ordering or a comparator that does + * not permit null elements. + * @since 1.6 + */ + public T lower(T e) + { + return map.lowerKey(e); + } + + /** + * Removes and returns the least or lowest element in the set, + * or null if the map is empty. + * + * @return the removed first element, or null if the + * map is empty. + * @since 1.6 + */ + public T pollFirst() + { + return map.pollFirstEntry().getKey(); + } + + /** + * Removes and returns the greatest or highest element in the set, + * or null if the map is empty. + * + * @return the removed last element, or null if the + * map is empty. + * @since 1.6 + */ + public T pollLast() + { + return map.pollLastEntry().getKey(); + } + +} diff --git a/libjava/classpath/java/util/UUID.java b/libjava/classpath/java/util/UUID.java new file mode 100644 index 000000000..bb25e071c --- /dev/null +++ b/libjava/classpath/java/util/UUID.java @@ -0,0 +1,372 @@ +/* UUID.java -- Class that represents a UUID object. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * This class represents a 128-bit UUID value. + * + * There are several types of UUID, and while this class can be used to store + * them, only the Leach-Salz (variant 2) UUID specified in RFC-4122 will + * give meaningful results from the method calls. + * See: http://tools.ietf.org/html/4122 for the details + * + * The format of a Leach-Salz (variant 2) time-based (version 1) UUID + * is as follows: + * time_low - upper 32 bits of the most significant 64 bits, + * this is the least-significant part of the timestamp. + * + * time_mid - bits 16-31 of the most significant 64 bits, + * this is the middle portion of the timestamp. + * + * version - bits 8-15 of the most significant 64 bits. + * + * time_hi - bits 0-7 of the most significant 64 bits, + * the most significant portion of the timestamp. + * + * clock_and_reserved - bits 48-63 of the least significant 64 bits. + * a variable number of bits hold the variant + * (see the spec) + * + * node identifier - bits 0-47 of the least signficant 64 bits. + * + * These fields are valid only for version 1, in the remaining versions, + * only the version and variant fields are set, all others are used for data. + * + * @since 1.5 + * @author Sven de Marothy + */ +public final class UUID + extends Object + implements Serializable, Comparable +{ + private static final long serialVersionUID = -4856846361193249489L; + + /** + * Serialized field - most significant 64 bits. + */ + private long mostSigBits; + + /** + * Serialized field - least significant 64 bits. + */ + private long leastSigBits; + + /** + * Random-number generator. + */ + private static transient Random r = new Random(); + + /** + * Constructs a new UUID. + * + * @since 1.5 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * Returns the clock-sequence value of this UUID. + * This field only exists in a time-based (version 1) UUID. + * + * @throws UnsupportedOperationException if the UUID type is not 1. + * @returns an int containing the clock-sequence value. + */ + public int clockSequence() + { + if( version() != 1 ) + throw new UnsupportedOperationException("Not a type 1 UUID"); + return (int)((leastSigBits & 0x3FFF000000000000L) >> 48); + } + + /** + * Compare this UUID to another. + * The comparison is performed as between two 128-bit integers. + * + * @return -1 if this < val, 0 if they are equal, 1 if this > val. + */ + public int compareTo(UUID o) + { + if( mostSigBits < o.mostSigBits ) + return -1; + if( mostSigBits > o.mostSigBits ) + return 1; + if( leastSigBits < o.leastSigBits ) + return -1; + if( leastSigBits > o.mostSigBits ) + return 1; + return 0; + } + + /** + * Compare a (UUID) object to this one + */ + public boolean equals(Object obj) + { + if( !(obj instanceof UUID ) ) + return false; + return ( ((UUID)obj).mostSigBits == mostSigBits && + ((UUID)obj).leastSigBits == leastSigBits ); + } + + /** + * Creates a UUID object from a Sting representation. + * + * For the format of the string, + * @see #toString() + * + * @return a new UUID object. + */ + public static UUID fromString(String name) + { + StringTokenizer st = new StringTokenizer( name.trim(), "-" ); + if( st.countTokens() < 5 ) + throw new IllegalArgumentException( "Incorrect UUID string"+ + " representation:"+name ); + + long msb = (Long.parseLong(st.nextToken(), 16) << 32); // time low + msb |= (Long.parseLong(st.nextToken(), 16) << 16); // time mid + msb |= Long.parseLong(st.nextToken(), 16); // time high + + long lsb = (Long.parseLong(st.nextToken(), 16) << 48); // clock + lsb |= Long.parseLong(st.nextToken(), 16); // node + + return new UUID(msb, lsb); + } + + /** + * Returns a String representation of the UUID. + * + * The format of the standard string representation (given in RFC4122) is: + * + * time-low "-" time-mid "-" + * time-high-and-version "-" + * clock-seq-and-reserved + * clock-seq-low "-" node + * + * Where each field is represented as a hex string. + * + * @return the String representation. + */ + public String toString() + { + return // time-low first + padHex( (( mostSigBits & 0xFFFFFFFF00000000L) >> 32) & 0xFFFFFFFFL, 8) + + "-" + // then time-mid + padHex( (( mostSigBits & 0xFFFF0000L ) >> 16), 4 ) + + "-" + // time-high + padHex( ( mostSigBits & 0x0000000000000000FFFFL ), 4 ) + + "-" + // clock (note - no reason to separate high and low here) + padHex( (((leastSigBits & 0xFFFF000000000000L) >> 48) & 0xFFFF), 4 ) + + "-" + // finally the node value. + padHex(leastSigBits & 0xFFFFFFFFFFFFL, 12); + } + + /** + * Returns the least significant 64 bits of the UUID as a long. + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * Returns the most significant 64 bits of the UUID as a long. + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * Returns a hash of this UUID. + */ + public int hashCode() + { + int l1 = (int)(leastSigBits & 0xFFFFFFFFL); + int l2 = (int)((leastSigBits & 0xFFFFFFFF00000000L) >> 32); + int m1 = (int)(mostSigBits & 0xFFFFFFFFL); + int m2 = (int)((mostSigBits & 0xFFFFFFFF00000000L) >> 32); + + return (l1 ^ l2) ^ (m1 ^ m2); + } + + /** + * Creates a UUID version 3 object (name based with MD5 hashing) + * from a series of bytes representing a name. + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + long msb, lsb; + byte[] hash; + + try + { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + hash = md5.digest( name ); + } + catch (NoSuchAlgorithmException e) + { + throw new UnsupportedOperationException("No MD5 algorithm available."); + } + + msb = ((hash[0] & 0xFFL) << 56) | ((hash[1] & 0xFFL) << 48) | + ((hash[2] & 0xFFL) << 40) | ((hash[3] & 0xFFL) << 32) | + ((hash[4] & 0xFFL) << 24) | ((hash[5] & 0xFFL) << 16) | + ((hash[6] & 0xFFL) << 8) | (hash[7] & 0xFFL); + + lsb = ((hash[8] & 0xFFL) << 56) | ((hash[9] & 0xFFL) << 48) | + ((hash[10] & 0xFFL) << 40) | ((hash[11] & 0xFFL) << 32) | + ((hash[12] & 0xFFL) << 24) | ((hash[13] & 0xFFL) << 16) | + ((hash[14] & 0xFFL) << 8) | (hash[15] & 0xFFL); + + lsb &= 0x3FFFFFFFFFFFFFFFL; + lsb |= 0x8000000000000000L; // set top two bits to variant 2 + + msb &= 0xFFFFFFFFFFFF0FFFL; + msb |= 0x3000; // Version 3; + + return new UUID(msb, lsb); + } + + /** + * Returns the 48-bit node value in a long. + * This field only exists in a time-based (version 1) UUID. + * + * @throws UnsupportedOperationException if the UUID type is not 1. + * @returns a long with the node value in the lower 48 bits. + */ + public long node() + { + if( version() != 1 ) + throw new UnsupportedOperationException("Not a type 1 UUID"); + return (leastSigBits & 0xFFFFFFFFFFFFL); + } + + /** + * Returns the 60-bit timestamp value of the UUID in a long. + * This field only exists in a time-based (version 1) UUID. + * + * @throws UnsupportedOperationException if the UUID type is not 1. + * @returns a long with the timestamp value. + */ + public long timestamp() + { + if( version() != 1 ) + throw new UnsupportedOperationException("Not a type 1 UUID"); + long time = (( mostSigBits & 0xFFFFFFFF00000000L) >> 32); + time |= (( mostSigBits & 0xFFFF0000L ) << 16); + long time_hi = ( mostSigBits & 0xFFFL ); + time |= (time_hi << 48); + return time; + } + + /** + * Generate a Leach-Salz (Variant 2) randomly generated (version 4) + * UUID. + * + */ + public static UUID randomUUID() + { + long lsb = r.nextLong(); + long msb = r.nextLong(); + + lsb &= 0x3FFFFFFFFFFFFFFFL; + lsb |= 0x8000000000000000L; // set top two bits to variant 2 + + msb &= 0xFFFFFFFFFFFF0FFFL; + msb |= 0x4000; // Version 4; + + return new UUID( msb, lsb ); + } + + /** + * Returns a hex String from l, padded to n spaces. + */ + private String padHex( long l, int n ) + { + String s = Long.toHexString( l ); + while( s.length() < n ) + s = "0" + s; + return s; + } + + /** + * Returns the variant of the UUID + * + * This may be: + * 0 = Reserved for NCS backwards-compatibility + * 2 = Leach-Salz (supports the other methods in this class) + * 6 = Reserved for Microsoft backwards-compatibility + * 7 = (reserved for future use) + */ + public int variant() + { + // Get the top 3 bits (not all may be part of the variant) + int v = (int)((leastSigBits & 0xE000000000000000L) >> 61); + if( (v & 0x04) == 0 ) // msb of the variant is 0 + return 0; + if( (v & 0x02) == 0 ) // variant is 0 1 (Leach-Salz) + return 2; + return v; // 6 or 7 + } + + /** + * Returns the version # of the UUID. + * + * Valid version numbers for a variant 2 UUID are: + * 1 = Time based UUID + * 2 = DCE security UUID + * 3 = Name-based UUID using MD5 hashing + * 4 = Randomly generated UUID + * 5 = Name-based UUID using SHA-1 hashing + * + * @return the version number + */ + public int version() + { + return (int)((mostSigBits & 0xF000L) >> 12); + } +} diff --git a/libjava/classpath/java/util/UnknownFormatConversionException.java b/libjava/classpath/java/util/UnknownFormatConversionException.java new file mode 100644 index 000000000..7326a8250 --- /dev/null +++ b/libjava/classpath/java/util/UnknownFormatConversionException.java @@ -0,0 +1,86 @@ +/* UnknownFormatConversionException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a {@link Formatter} is supplied with an + * unknown conversion. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class UnknownFormatConversionException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19060418L; + + /** + * The unknown conversion. + * + * @serial the unknown conversion. + */ + // Note: name fixed by serialization. + private String s; + + /** + * Constructs a new UnknownFormatConversionException + * for the specified conversion string. + * + * @param s the conversion string. + * @throws NullPointerException if the conversion string is null. + */ + public UnknownFormatConversionException(String s) + { + super("Unknown format conversion: " + s); + if (s == null) + throw new NullPointerException("The conversion string is null."); + this.s = s; + } + + /** + * Returns the conversion string. + * + * @return the conversion string. + */ + public String getConversion() + { + return s; + } +} diff --git a/libjava/classpath/java/util/UnknownFormatFlagsException.java b/libjava/classpath/java/util/UnknownFormatFlagsException.java new file mode 100644 index 000000000..a7f4fc6ab --- /dev/null +++ b/libjava/classpath/java/util/UnknownFormatFlagsException.java @@ -0,0 +1,88 @@ +/* UnknownFormatFlagsException.java + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +/** + * Thrown when a {@link Formatter} is supplied with an + * unknown flag. + * + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.5 + */ +public class UnknownFormatFlagsException + extends IllegalFormatException +{ + private static final long serialVersionUID = 19370506L; + + /** + * The set of flags containing the unknown flag. + * + * @serial the unknown conversion. + */ + // Note: name fixed by serialization. + private String flags; + + /** + * Constructs a new UnknownFormatFlagsException + * which specifies that the supplied set of flags contains a + * unknown. + * + * @param flags the flags containing a unknown. + * @throws NullPointerException if flags is null. + */ + public UnknownFormatFlagsException(String s) + { + super("Unknown flag passed in " + s); + if (s == null) + throw new + NullPointerException("Null flags value passed to constructor."); + this.flags = s; + } + + /** + * Returns the flags which contain a unknown. + * + * @return the flags. + */ + public String getFlags() + { + return flags; + } +} diff --git a/libjava/classpath/java/util/Vector.java b/libjava/classpath/java/util/Vector.java new file mode 100644 index 000000000..44370a1e4 --- /dev/null +++ b/libjava/classpath/java/util/Vector.java @@ -0,0 +1,958 @@ +/* Vector.java -- Class that provides growable arrays. + Copyright (C) 1998, 1999, 2000, 2001, 2004, 2005, 2006, + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * The Vector classes implements growable arrays of Objects. + * You can access elements in a Vector with an index, just as you + * can in a built in array, but Vectors can grow and shrink to accommodate + * more or fewer objects.

        + * + * Vectors try to mantain efficiency in growing by having a + * capacityIncrement that can be specified at instantiation. + * When a Vector can no longer hold a new Object, it grows by the amount + * in capacityIncrement. If this value is 0, the vector doubles in + * size.

        + * + * Vector implements the JDK 1.2 List interface, and is therefore a fully + * compliant Collection object. The iterators are fail-fast - if external + * code structurally modifies the vector, any operation on the iterator will + * then throw a {@link ConcurrentModificationException}. The Vector class is + * fully synchronized, but the iterators are not. So, when iterating over a + * vector, be sure to synchronize on the vector itself. If you don't want the + * expense of synchronization, use ArrayList instead. On the other hand, the + * Enumeration of elements() is not thread-safe, nor is it fail-fast; so it + * can lead to undefined behavior even in a single thread if you modify the + * vector during iteration.

        + * + * Note: Some methods, especially those specified by List, specify throwing + * {@link IndexOutOfBoundsException}, but it is easier to implement by + * throwing the subclass {@link ArrayIndexOutOfBoundsException}. Others + * directly specify this subclass. + * + * @author Scott G. Miller + * @author Bryce McKinlay + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see List + * @see ArrayList + * @see LinkedList + * @since 1.0 + * @status updated to 1.4 + */ +public class Vector extends AbstractList + implements List, RandomAccess, Cloneable, Serializable +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = -2767605614048989439L; + + /** + * The internal array used to hold members of a Vector. The elements are + * in positions 0 through elementCount - 1, and all remaining slots are null. + * @serial the elements + */ + protected Object[] elementData; + + /** + * The number of elements currently in the vector, also returned by + * {@link #size}. + * @serial the size + */ + protected int elementCount; + + /** + * The amount the Vector's internal array should be increased in size when + * a new element is added that exceeds the current size of the array, + * or when {@link #ensureCapacity} is called. If <= 0, the vector just + * doubles in size. + * @serial the amount to grow the vector by + */ + protected int capacityIncrement; + + /** + * Constructs an empty vector with an initial size of 10, and + * a capacity increment of 0 + */ + public Vector() + { + this(10, 0); + } + + /** + * Constructs a vector containing the contents of Collection, in the + * order given by the collection. + * + * @param c collection of elements to add to the new vector + * @throws NullPointerException if c is null + * @since 1.2 + */ + public Vector(Collection c) + { + elementCount = c.size(); + elementData = c.toArray(new Object[elementCount]); + } + + /** + * Constructs a Vector with the initial capacity and capacity + * increment specified. + * + * @param initialCapacity the initial size of the Vector's internal array + * @param capacityIncrement the amount the internal array should be + * increased by when necessary, 0 to double the size + * @throws IllegalArgumentException if initialCapacity < 0 + */ + public Vector(int initialCapacity, int capacityIncrement) + { + if (initialCapacity < 0) + throw new IllegalArgumentException(); + elementData = new Object[initialCapacity]; + this.capacityIncrement = capacityIncrement; + } + + /** + * Constructs a Vector with the initial capacity specified, and a capacity + * increment of 0 (double in size). + * + * @param initialCapacity the initial size of the Vector's internal array + * @throws IllegalArgumentException if initialCapacity < 0 + */ + public Vector(int initialCapacity) + { + this(initialCapacity, 0); + } + + /** + * Copies the contents of the Vector into the provided array. If the + * array is too small to fit all the elements in the Vector, an + * {@link IndexOutOfBoundsException} is thrown without modifying the array. + * Old elements in the array are overwritten by the new elements. + * + * @param a target array for the copy + * @throws IndexOutOfBoundsException the array is not large enough + * @throws NullPointerException the array is null + * @see #toArray(Object[]) + */ + public synchronized void copyInto(Object[] a) + { + System.arraycopy(elementData, 0, a, 0, elementCount); + } + + /** + * Trims the Vector down to size. If the internal data array is larger + * than the number of Objects its holding, a new array is constructed + * that precisely holds the elements. Otherwise this does nothing. + */ + public synchronized void trimToSize() + { + // Don't bother checking for the case where size() == the capacity of the + // vector since that is a much less likely case; it's more efficient to + // not do the check and lose a bit of performance in that infrequent case + + T[] newArray = (T[]) new Object[elementCount]; + System.arraycopy(elementData, 0, newArray, 0, elementCount); + elementData = newArray; + } + + /** + * Ensures that minCapacity elements can fit within this Vector. + * If elementData is too small, it is expanded as follows: + * If the elementCount + capacityIncrement is adequate, that + * is the new size. If capacityIncrement is non-zero, the + * candidate size is double the current. If that is not enough, the new + * size is minCapacity. + * + * @param minCapacity the desired minimum capacity, negative values ignored + */ + public synchronized void ensureCapacity(int minCapacity) + { + if (elementData.length >= minCapacity) + return; + + int newCapacity; + if (capacityIncrement <= 0) + newCapacity = elementData.length * 2; + else + newCapacity = elementData.length + capacityIncrement; + + T[] newArray = (T[]) new Object[Math.max(newCapacity, minCapacity)]; + + System.arraycopy(elementData, 0, newArray, 0, elementCount); + elementData = newArray; + } + + /** + * Explicitly sets the size of the vector (but not necessarily the size of + * the internal data array). If the new size is smaller than the old one, + * old values that don't fit are lost. If the new size is larger than the + * old one, the vector is padded with null entries. + * + * @param newSize The new size of the internal array + * @throws ArrayIndexOutOfBoundsException if the new size is negative + */ + public synchronized void setSize(int newSize) + { + // Don't bother checking for the case where size() == the capacity of the + // vector since that is a much less likely case; it's more efficient to + // not do the check and lose a bit of performance in that infrequent case + modCount++; + ensureCapacity(newSize); + if (newSize < elementCount) + Arrays.fill(elementData, newSize, elementCount, null); + elementCount = newSize; + } + + /** + * Returns the size of the internal data array (not the amount of elements + * contained in the Vector). + * + * @return capacity of the internal data array + */ + public synchronized int capacity() + { + return elementData.length; + } + + /** + * Returns the number of elements stored in this Vector. + * + * @return the number of elements in this Vector + */ + public synchronized int size() + { + return elementCount; + } + + /** + * Returns true if this Vector is empty, false otherwise + * + * @return true if the Vector is empty, false otherwise + */ + public synchronized boolean isEmpty() + { + return elementCount == 0; + } + + /** + * Returns an Enumeration of the elements of this Vector. The enumeration + * visits the elements in increasing index order, but is NOT thread-safe. + * + * @return an Enumeration + * @see #iterator() + */ + // No need to synchronize as the Enumeration is not thread-safe! + public Enumeration elements() + { + return new Enumeration() + { + private int i = 0; + + public boolean hasMoreElements() + { + return i < elementCount; + } + + @SuppressWarnings("unchecked") + public T nextElement() + { + if (i >= elementCount) + throw new NoSuchElementException(); + return (T) elementData[i++]; + } + }; + } + + /** + * Returns true when elem is contained in this Vector. + * + * @param elem the element to check + * @return true if the object is contained in this Vector, false otherwise + */ + public boolean contains(Object elem) + { + return indexOf(elem, 0) >= 0; + } + + /** + * Returns the first occurrence of elem in the Vector, or -1 if + * elem is not found. + * + * @param elem the object to search for + * @return the index of the first occurrence, or -1 if not found + */ + public int indexOf(Object elem) + { + return indexOf(elem, 0); + } + + /** + * Searches the vector starting at index for object + * elem and returns the index of the first occurrence of this + * Object. If the object is not found, or index is larger than the size + * of the vector, -1 is returned. + * + * @param e the Object to search for + * @param index start searching at this index + * @return the index of the next occurrence, or -1 if it is not found + * @throws IndexOutOfBoundsException if index < 0 + */ + public synchronized int indexOf(Object e, int index) + { + for (int i = index; i < elementCount; i++) + if (equals(e, elementData[i])) + return i; + return -1; + } + + /** + * Returns the last index of elem within this Vector, or -1 + * if the object is not within the Vector. + * + * @param elem the object to search for + * @return the last index of the object, or -1 if not found + */ + public int lastIndexOf(Object elem) + { + return lastIndexOf(elem, elementCount - 1); + } + + /** + * Returns the index of the first occurrence of elem, when + * searching backwards from index. If the object does not + * occur in this Vector, or index is less than 0, -1 is returned. + * + * @param e the object to search for + * @param index the index to start searching in reverse from + * @return the index of the Object if found, -1 otherwise + * @throws IndexOutOfBoundsException if index >= size() + */ + public synchronized int lastIndexOf(Object e, int index) + { + checkBoundExclusive(index); + for (int i = index; i >= 0; i--) + if (equals(e, elementData[i])) + return i; + return -1; + } + + /** + * Returns the Object stored at index. + * + * @param index the index of the Object to retrieve + * @return the object at index + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size() + * @see #get(int) + */ + @SuppressWarnings("unchecked") + public synchronized T elementAt(int index) + { + checkBoundExclusive(index); + return (T) elementData[index]; + } + + /** + * Returns the first element (index 0) in the Vector. + * + * @return the first Object in the Vector + * @throws NoSuchElementException the Vector is empty + */ + @SuppressWarnings("unchecked") + public synchronized T firstElement() + { + if (elementCount == 0) + throw new NoSuchElementException(); + + return (T) elementData[0]; + } + + /** + * Returns the last element in the Vector. + * + * @return the last Object in the Vector + * @throws NoSuchElementException the Vector is empty + */ + @SuppressWarnings("unchecked") + public synchronized T lastElement() + { + if (elementCount == 0) + throw new NoSuchElementException(); + + return (T) elementData[elementCount - 1]; + } + + /** + * Changes the element at index to be obj + * + * @param obj the object to store + * @param index the position in the Vector to store the object + * @throws ArrayIndexOutOfBoundsException the index is out of range + * @see #set(int, Object) + */ + public void setElementAt(T obj, int index) + { + set(index, obj); + } + + /** + * Removes the element at index, and shifts all elements at + * positions greater than index to their index - 1. + * + * @param index the index of the element to remove + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size(); + * @see #remove(int) + */ + public void removeElementAt(int index) + { + remove(index); + } + + /** + * Inserts a new element into the Vector at index. Any elements + * at or greater than index are shifted up one position. + * + * @param obj the object to insert + * @param index the index at which the object is inserted + * @throws ArrayIndexOutOfBoundsException index < 0 || index > size() + * @see #add(int, Object) + */ + public synchronized void insertElementAt(T obj, int index) + { + checkBoundInclusive(index); + if (elementCount == elementData.length) + ensureCapacity(elementCount + 1); + modCount++; + System.arraycopy(elementData, index, elementData, index + 1, + elementCount - index); + elementCount++; + elementData[index] = obj; + } + + /** + * Adds an element to the Vector at the end of the Vector. The vector + * is increased by ensureCapacity(size() + 1) if needed. + * + * @param obj the object to add to the Vector + */ + public synchronized void addElement(T obj) + { + if (elementCount == elementData.length) + ensureCapacity(elementCount + 1); + modCount++; + elementData[elementCount++] = obj; + } + + /** + * Removes the first (the lowest index) occurrence of the given object from + * the Vector. If such a remove was performed (the object was found), true + * is returned. If there was no such object, false is returned. + * + * @param obj the object to remove from the Vector + * @return true if the Object was in the Vector, false otherwise + * @see #remove(Object) + */ + public synchronized boolean removeElement(Object obj) + { + int idx = indexOf(obj, 0); + if (idx >= 0) + { + remove(idx); + return true; + } + return false; + } + + /** + * Removes all elements from the Vector. Note that this does not + * resize the internal data array. + * + * @see #clear() + */ + public synchronized void removeAllElements() + { + if (elementCount == 0) + return; + + modCount++; + Arrays.fill(elementData, 0, elementCount, null); + elementCount = 0; + } + + /** + * Creates a new Vector with the same contents as this one. The clone is + * shallow; elements are not cloned. + * + * @return the clone of this vector + */ + public synchronized Object clone() + { + try + { + Vector clone = (Vector) super.clone(); + clone.elementData = (Object[]) elementData.clone(); + return clone; + } + catch (CloneNotSupportedException ex) + { + // Impossible to get here. + throw new InternalError(ex.toString()); + } + } + + /** + * Returns an Object array with the contents of this Vector, in the order + * they are stored within this Vector. Note that the Object array returned + * is not the internal data array, and that it holds only the elements + * within the Vector. This is similar to creating a new Object[] with the + * size of this Vector, then calling Vector.copyInto(yourArray). + * + * @return an Object[] containing the contents of this Vector in order + * @since 1.2 + */ + public synchronized Object[] toArray() + { + Object[] newArray = new Object[elementCount]; + copyInto(newArray); + return newArray; + } + + /** + * Returns an array containing the contents of this Vector. + * If the provided array is large enough, the contents are copied + * into that array, and a null is placed in the position size(). + * In this manner, you can obtain the size of a Vector by the position + * of the null element, if you know the vector does not itself contain + * null entries. If the array is not large enough, reflection is used + * to create a bigger one of the same runtime type. + * + * @param a an array to copy the Vector into if large enough + * @return an array with the contents of this Vector in order + * @throws ArrayStoreException the runtime type of the provided array + * cannot hold the elements of the Vector + * @throws NullPointerException if a is null + * @since 1.2 + */ + public synchronized S[] toArray(S[] a) + { + if (a.length < elementCount) + a = (S[]) Array.newInstance(a.getClass().getComponentType(), + elementCount); + else if (a.length > elementCount) + a[elementCount] = null; + System.arraycopy(elementData, 0, a, 0, elementCount); + return a; + } + + /** + * Returns the element at position index. + * + * @param index the position from which an element will be retrieved + * @return the element at that position + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size() + * @since 1.2 + */ + public T get(int index) + { + return elementAt(index); + } + + /** + * Puts element into the Vector at position index + * and returns the Object that previously occupied that position. + * + * @param index the index within the Vector to place the Object + * @param element the Object to store in the Vector + * @return the previous object at the specified index + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size() + * @since 1.2 + */ + @SuppressWarnings("unchecked") + public synchronized T set(int index, T element) + { + checkBoundExclusive(index); + T temp = (T) elementData[index]; + elementData[index] = element; + return temp; + } + + /** + * Adds an object to the Vector. + * + * @param o the element to add to the Vector + * @return true, as specified by List + * @since 1.2 + */ + public boolean add(T o) + { + addElement(o); + return true; + } + + /** + * Removes the given Object from the Vector. If it exists, true + * is returned, if not, false is returned. + * + * @param o the object to remove from the Vector + * @return true if the Object existed in the Vector, false otherwise + * @since 1.2 + */ + public boolean remove(Object o) + { + return removeElement(o); + } + + /** + * Adds an object at the specified index. Elements at or above + * index are shifted up one position. + * + * @param index the index at which to add the element + * @param element the element to add to the Vector + * @throws ArrayIndexOutOfBoundsException index < 0 || index > size() + * @since 1.2 + */ + public void add(int index, T element) + { + insertElementAt(element, index); + } + + /** + * Removes the element at the specified index, and returns it. + * + * @param index the position from which to remove the element + * @return the object removed + * @throws ArrayIndexOutOfBoundsException index < 0 || index >= size() + * @since 1.2 + */ + @SuppressWarnings("unchecked") + public synchronized T remove(int index) + { + checkBoundExclusive(index); + T temp = (T) elementData[index]; + modCount++; + elementCount--; + if (index < elementCount) + System.arraycopy(elementData, index + 1, elementData, index, + elementCount - index); + elementData[elementCount] = null; + return temp; + } + + /** + * Clears all elements in the Vector and sets its size to 0. + */ + public void clear() + { + removeAllElements(); + } + + /** + * Returns true if this Vector contains all the elements in c. + * + * @param c the collection to compare to + * @return true if this vector contains all elements of c + * @throws NullPointerException if c is null + * @since 1.2 + */ + public synchronized boolean containsAll(Collection c) + { + // Here just for the sychronization. + return super.containsAll(c); + } + + /** + * Appends all elements of the given collection to the end of this Vector. + * Behavior is undefined if the collection is modified during this operation + * (for example, if this == c). + * + * @param c the collection to append + * @return true if this vector changed, in other words c was not empty + * @throws NullPointerException if c is null + * @since 1.2 + */ + public synchronized boolean addAll(Collection c) + { + return addAll(elementCount, c); + } + + /** + * Remove from this vector all elements contained in the given collection. + * + * @param c the collection to filter out + * @return true if this vector changed + * @throws NullPointerException if c is null + * @since 1.2 + */ + public synchronized boolean removeAll(Collection c) + { + // 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; + for (i = 0; i < elementCount; i++) + if (c.contains(elementData[i])) + break; + if (i == elementCount) + return false; + + modCount++; + for (j = i++; i < elementCount; i++) + if (! c.contains(elementData[i])) + elementData[j++] = elementData[i]; + elementCount -= i - j; + return true; + } + + /** + * Retain in this vector only the elements contained in the given collection. + * + * @param c the collection to filter by + * @return true if this vector changed + * @throws NullPointerException if c is null + * @since 1.2 + */ + public synchronized boolean retainAll(Collection c) + { + // 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; + for (i = 0; i < elementCount; i++) + if (! c.contains(elementData[i])) + break; + if (i == elementCount) + return false; + + modCount++; + for (j = i++; i < elementCount; i++) + if (c.contains(elementData[i])) + elementData[j++] = elementData[i]; + elementCount -= i - j; + return true; + } + + /** + * Inserts all elements of the given collection at the given index of + * this Vector. Behavior is undefined if the collection is modified during + * this operation (for example, if this == c). + * + * @param c the collection to append + * @return true if this vector changed, in other words c was not empty + * @throws NullPointerException if c is null + * @throws ArrayIndexOutOfBoundsException index < 0 || index > size() + * @since 1.2 + */ + public synchronized boolean addAll(int index, Collection c) + { + checkBoundInclusive(index); + Iterator itr = c.iterator(); + int csize = c.size(); + + modCount++; + ensureCapacity(elementCount + csize); + int end = index + csize; + if (elementCount > 0 && index != elementCount) + System.arraycopy(elementData, index, + elementData, end, elementCount - index); + elementCount += csize; + for ( ; index < end; index++) + elementData[index] = itr.next(); + return (csize > 0); + } + + /** + * Compares this to the given object. + * + * @param o the object to compare to + * @return true if the two are equal + * @since 1.2 + */ + public synchronized boolean equals(Object o) + { + // Here just for the sychronization. + return super.equals(o); + } + + /** + * Computes the hashcode of this object. + * + * @return the hashcode + * @since 1.2 + */ + public synchronized int hashCode() + { + // Here just for the sychronization. + return super.hashCode(); + } + + /** + * Returns a string representation of this Vector in the form + * "[element0, element1, ... elementN]". + * + * @return the String representation of this Vector + */ + public synchronized String toString() + { + // Here just for the sychronization. + return super.toString(); + } + + /** + * Obtain a List view of a subsection of this list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list is modifiable, and changes in one + * reflect in the other. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. + *

        + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this vector + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() + * @throws IllegalArgumentException if fromIndex > toIndex + * @see ConcurrentModificationException + * @since 1.2 + */ + public synchronized List subList(int fromIndex, int toIndex) + { + List sub = super.subList(fromIndex, toIndex); + // We must specify the correct object to synchronize upon, hence the + // use of a non-public API + return new Collections.SynchronizedList(this, sub); + } + + /** + * Removes a range of elements from this list. + * Does nothing when toIndex is equal to fromIndex. + * + * @param fromIndex the index to start deleting from (inclusive) + * @param toIndex the index to delete up to (exclusive) + * @throws IndexOutOfBoundsException if fromIndex > toIndex + */ + // This does not need to be synchronized, because it is only called through + // clear() of a sublist, and clear() had already synchronized. + protected void removeRange(int fromIndex, int toIndex) + { + int change = toIndex - fromIndex; + if (change > 0) + { + modCount++; + System.arraycopy(elementData, toIndex, elementData, fromIndex, + elementCount - toIndex); + int save = elementCount; + elementCount -= change; + Arrays.fill(elementData, elementCount, save, null); + } + else if (change < 0) + throw new IndexOutOfBoundsException(); + } + + /** + * Checks that the index is in the range of possible elements (inclusive). + * + * @param index the index to check + * @throws ArrayIndexOutOfBoundsException if index > size + */ + private void checkBoundInclusive(int index) + { + // Implementation note: we do not check for negative ranges here, since + // use of a negative index will cause an ArrayIndexOutOfBoundsException + // with no effort on our part. + if (index > elementCount) + raiseBoundsError(index, " > "); + } + + /** + * Checks that the index is in the range of existing elements (exclusive). + * + * @param index the index to check + * @throws ArrayIndexOutOfBoundsException if index >= size + */ + private void checkBoundExclusive(int index) + { + // Implementation note: we do not check for negative ranges here, since + // use of a negative index will cause an ArrayIndexOutOfBoundsException + // with no effort on our part. + if (index >= elementCount) + raiseBoundsError(index, " >= "); + } + + /** + * Raise the ArrayIndexOfOutBoundsException. + * + * @param index the index of the access + * @param operator the operator to include in the error message + * @throws IndexOutOfBoundsException unconditionally + */ + private void raiseBoundsError(int index, String operator) + { + // Implementaion note: put in a separate method to make the JITs job easier + // (separate common from uncommon code at method boundaries when trivial to + // do so). + throw new ArrayIndexOutOfBoundsException(index + operator + elementCount); + } + + /** + * Serializes this object to the given stream. + * + * @param s the stream to write to + * @throws IOException if the underlying stream fails + * @serialData just calls default write function + */ + private synchronized void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + } + +} diff --git a/libjava/classpath/java/util/WeakHashMap.java b/libjava/classpath/java/util/WeakHashMap.java new file mode 100644 index 000000000..3e4d34796 --- /dev/null +++ b/libjava/classpath/java/util/WeakHashMap.java @@ -0,0 +1,880 @@ +/* WeakHashMap -- a hashtable that keeps only weak references + to its keys, allowing the virtual machine to reclaim them + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +/** + * A weak hash map has only weak references to the key. This means that it + * allows the key to be garbage collected if it is not used otherwise. If + * this happens, the entry will eventually disappear from the map, + * asynchronously. + * + *

        A weak hash map makes most sense when the keys doesn't override the + * equals method: If there is no other reference to the + * key nobody can ever look up the key in this table and so the entry + * can be removed. This table also works when the equals + * method is overloaded, such as String keys, but you should be prepared + * to deal with some entries disappearing spontaneously. + * + *

        Other strange behaviors to be aware of: The size of this map may + * spontaneously shrink (even if you use a synchronized map and synchronize + * it); it behaves as if another thread removes entries from this table + * without synchronization. The entry set returned by entrySet + * has similar phenomenons: The size may spontaneously shrink, or an + * entry, that was in the set before, suddenly disappears. + * + *

        A weak hash map is not meant for caches; use a normal map, with + * soft references as values instead, or try {@link LinkedHashMap}. + * + *

        The weak hash map supports null values and null keys. The null key + * is never deleted from the map (except explictly of course). The + * performance of the methods are similar to that of a hash map. + * + *

        The value objects are strongly referenced by this table. So if a + * value object maintains a strong reference to the key (either direct + * or indirect) the key will never be removed from this map. According + * to Sun, this problem may be fixed in a future release. It is not + * possible to do it with the jdk 1.2 reference model, though. + * + * @author Jochen Hoenicke + * @author Eric Blake (ebb9@email.byu.edu) + * @author Tom Tromey (tromey@redhat.com) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * + * @see HashMap + * @see WeakReference + * @see LinkedHashMap + * @since 1.2 + * @status updated to 1.4 (partial 1.5) + */ +public class WeakHashMap extends AbstractMap +{ + // WARNING: WeakHashMap is a CORE class in the bootstrap cycle. See the + // comments in vm/reference/java/lang/Runtime for implications of this fact. + + /** + * The default capacity for an instance of HashMap. + * Sun's documentation mildly suggests that this (11) is the correct + * value. + */ + private static final int DEFAULT_CAPACITY = 11; + + /** + * The default load factor of a HashMap. + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75F; + + /** + * This is used instead of the key value null. It is needed + * to distinguish between an null key and a removed key. + */ + // Package visible for use by nested classes. + static final Object NULL_KEY = new Object() + { + /** + * Sets the hashCode to 0, since that's what null would map to. + * @return the hash code 0 + */ + public int hashCode() + { + return 0; + } + + /** + * Compares this key to the given object. Normally, an object should + * NEVER compare equal to null, but since we don't publicize NULL_VALUE, + * it saves bytecode to do so here. + * @return true iff o is this or null + */ + public boolean equals(Object o) + { + return null == o || this == o; + } + }; + + /** + * The reference queue where our buckets (which are WeakReferences) are + * registered to. + */ + private final ReferenceQueue queue; + + /** + * The number of entries in this hash map. + */ + // Package visible for use by nested classes. + int size; + + /** + * The load factor of this WeakHashMap. This is the maximum ratio of + * size versus number of buckets. If size grows the number of buckets + * must grow, too. + */ + private float loadFactor; + + /** + * The rounded product of the capacity (i.e. number of buckets) and + * the load factor. When the number of elements exceeds the + * threshold, the HashMap calls rehash(). + */ + private int threshold; + + /** + * The number of structural modifications. This is used by + * iterators, to see if they should fail. This doesn't count + * the silent key removals, when a weak reference is cleared + * by the garbage collection. Instead the iterators must make + * sure to have strong references to the entries they rely on. + */ + // Package visible for use by nested classes. + int modCount; + + /** + * The entry set. There is only one instance per hashmap, namely + * theEntrySet. Note that the entry set may silently shrink, just + * like the WeakHashMap. + */ + private final class WeakEntrySet extends AbstractSet + { + /** + * Non-private constructor to reduce bytecode emitted. + */ + WeakEntrySet() + { + } + + /** + * Returns the size of this set. + * + * @return the set size + */ + public int size() + { + return size; + } + + /** + * Returns an iterator for all entries. + * + * @return an Entry iterator + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * The entry that was returned by the last + * next() call. This is also the entry whose + * bucket should be removed by the remove call.
        + * + * It is null, if the next method wasn't + * called yet, or if the entry was already removed.
        + * + * Remembering this entry here will also prevent it from + * being removed under us, since the entry strongly refers + * to the key. + */ + WeakBucket.WeakEntry lastEntry; + + /** + * The entry that will be returned by the next + * next() call. It is null if there + * is no further entry.
        + * + * Remembering this entry here will also prevent it from + * being removed under us, since the entry strongly refers + * to the key. + */ + WeakBucket.WeakEntry nextEntry = findNext(null); + + /** + * The known number of modification to the list, if it differs + * from the real number, we throw an exception. + */ + int knownMod = modCount; + + /** + * Check the known number of modification to the number of + * modifications of the table. If it differs from the real + * number, we throw an exception. + * @throws ConcurrentModificationException if the number + * of modifications doesn't match. + */ + private void checkMod() + { + // This method will get inlined. + cleanQueue(); + if (knownMod != modCount) + throw new ConcurrentModificationException(knownMod + " != " + + modCount); + } + + /** + * Get a strong reference to the next entry after + * lastBucket. + * @param lastEntry the previous bucket, or null if we should + * get the first entry. + * @return the next entry. + */ + private WeakBucket.WeakEntry findNext(WeakBucket.WeakEntry lastEntry) + { + int slot; + WeakBucket nextBucket; + if (lastEntry != null) + { + nextBucket = lastEntry.getBucket().next; + slot = lastEntry.getBucket().slot; + } + else + { + nextBucket = buckets[0]; + slot = 0; + } + + while (true) + { + while (nextBucket != null) + { + WeakBucket.WeakEntry entry = nextBucket.getEntry(); + if (entry != null) + // This is the next entry. + return entry; + + // Entry was cleared, try next. + nextBucket = nextBucket.next; + } + + slot++; + if (slot == buckets.length) + // No more buckets, we are through. + return null; + + nextBucket = buckets[slot]; + } + } + + /** + * Checks if there are more entries. + * @return true, iff there are more elements. + */ + public boolean hasNext() + { + return nextEntry != null; + } + + /** + * Returns the next entry. + * @return the next entry. + * @throws ConcurrentModificationException if the hash map was + * modified. + * @throws NoSuchElementException if there is no entry. + */ + public Object next() + { + checkMod(); + if (nextEntry == null) + throw new NoSuchElementException(); + lastEntry = nextEntry; + nextEntry = findNext(lastEntry); + return lastEntry; + } + + /** + * Removes the last returned entry from this set. This will + * also remove the bucket of the underlying weak hash map. + * @throws ConcurrentModificationException if the hash map was + * modified. + * @throws IllegalStateException if next() was + * never called or the element was already removed. + */ + public void remove() + { + checkMod(); + if (lastEntry == null) + throw new IllegalStateException(); + modCount++; + internalRemove(lastEntry.getBucket()); + lastEntry = null; + knownMod++; + } + }; + } + } + + /** + * A bucket is a weak reference to the key, that contains a strong + * reference to the value, a pointer to the next bucket and its slot + * number.
        + * + * It would be cleaner to have a WeakReference as field, instead of + * extending it, but if a weak reference gets cleared, we only get + * the weak reference (by queue.poll) and wouldn't know where to + * look for this reference in the hashtable, to remove that entry. + * + * @author Jochen Hoenicke + */ + private static class WeakBucket extends WeakReference + { + /** + * The value of this entry. The key is stored in the weak + * reference that we extend. + */ + V value; + + /** + * The next bucket describing another entry that uses the same + * slot. + */ + WeakBucket next; + + /** + * The slot of this entry. This should be + * Math.abs(key.hashCode() % buckets.length). + * + * But since the key may be silently removed we have to remember + * the slot number. + * + * If this bucket was removed the slot is -1. This marker will + * prevent the bucket from being removed twice. + */ + int slot; + + /** + * Creates a new bucket for the given key/value pair and the specified + * slot. + * @param key the key + * @param queue the queue the weak reference belongs to + * @param value the value + * @param slot the slot. This must match the slot where this bucket + * will be enqueued. + */ + public WeakBucket(K key, ReferenceQueue queue, V value, + int slot) + { + super(key, queue); + this.value = value; + this.slot = slot; + } + + /** + * This class gives the Entry representation of the + * current bucket. It also keeps a strong reference to the + * key; bad things may happen otherwise. + */ + class WeakEntry implements Map.Entry + { + /** + * The strong ref to the key. + */ + K key; + + /** + * Creates a new entry for the key. + * @param key the key + */ + public WeakEntry(K key) + { + this.key = key; + } + + /** + * Returns the underlying bucket. + * @return the owning bucket + */ + public WeakBucket getBucket() + { + return WeakBucket.this; + } + + /** + * Returns the key. + * @return the key + */ + public K getKey() + { + return key == NULL_KEY ? null : key; + } + + /** + * Returns the value. + * @return the value + */ + public V getValue() + { + return value; + } + + /** + * This changes the value. This change takes place in + * the underlying hash map. + * @param newVal the new value + * @return the old value + */ + public V setValue(V newVal) + { + V oldVal = value; + value = newVal; + return oldVal; + } + + /** + * The hashCode as specified in the Entry interface. + * @return the hash code + */ + public int hashCode() + { + return key.hashCode() ^ WeakHashMap.hashCode(value); + } + + /** + * The equals method as specified in the Entry interface. + * @param o the object to compare to + * @return true iff o represents the same key/value pair + */ + public boolean equals(Object o) + { + if (o instanceof Map.Entry) + { + Map.Entry e = (Map.Entry) o; + return WeakHashMap.equals(getKey(), e.getKey()) + && WeakHashMap.equals(value, e.getValue()); + } + return false; + } + + public String toString() + { + return getKey() + "=" + value; + } + } + + /** + * This returns the entry stored in this bucket, or null, if the + * bucket got cleared in the mean time. + * @return the Entry for this bucket, if it exists + */ + WeakEntry getEntry() + { + final K key = this.get(); + if (key == null) + return null; + return new WeakEntry(key); + } + } + + /** + * The entry set returned by entrySet(). + */ + private final WeakEntrySet theEntrySet; + + /** + * The hash buckets. These are linked lists. Package visible for use in + * nested classes. + */ + WeakBucket[] buckets; + + /** + * Creates a new weak hash map with default load factor and default + * capacity. + */ + public WeakHashMap() + { + this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); + } + + /** + * Creates a new weak hash map with default load factor and the given + * capacity. + * @param initialCapacity the initial capacity + * @throws IllegalArgumentException if initialCapacity is negative + */ + public WeakHashMap(int initialCapacity) + { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Creates a new weak hash map with the given initial capacity and + * load factor. + * @param initialCapacity the initial capacity. + * @param loadFactor the load factor (see class description of HashMap). + * @throws IllegalArgumentException if initialCapacity is negative, or + * loadFactor is non-positive + */ + public WeakHashMap(int initialCapacity, float loadFactor) + { + // Check loadFactor for NaN as well. + if (initialCapacity < 0 || ! (loadFactor > 0)) + throw new IllegalArgumentException(); + if (initialCapacity == 0) + initialCapacity = 1; + this.loadFactor = loadFactor; + threshold = (int) (initialCapacity * loadFactor); + theEntrySet = new WeakEntrySet(); + queue = new ReferenceQueue(); + buckets = new WeakBucket[initialCapacity]; + } + + /** + * Construct a new WeakHashMap with the same mappings as the given map. + * The WeakHashMap has a default load factor of 0.75. + * + * @param m the map to copy + * @throws NullPointerException if m is null + * @since 1.3 + */ + public WeakHashMap(Map m) + { + this(m.size(), DEFAULT_LOAD_FACTOR); + putAll(m); + } + + /** + * Simply hashes a non-null Object to its array index. + * @param key the key to hash + * @return its slot number + */ + private int hash(Object key) + { + return Math.abs(key.hashCode() % buckets.length); + } + + /** + * Cleans the reference queue. This will poll all references (which + * are WeakBuckets) from the queue and remove them from this map. + * This will not change modCount, even if it modifies the map. The + * iterators have to make sure that nothing bad happens.
        + * + * Currently the iterator maintains a strong reference to the key, so + * that is no problem. + */ + // Package visible for use by nested classes. + void cleanQueue() + { + Object bucket = queue.poll(); + while (bucket != null) + { + internalRemove((WeakBucket) bucket); + bucket = queue.poll(); + } + } + + /** + * Rehashes this hashtable. This will be called by the + * add() method if the size grows beyond the threshold. + * It will grow the bucket size at least by factor two and allocates + * new buckets. + */ + private void rehash() + { + WeakBucket[] oldBuckets = buckets; + int newsize = buckets.length * 2 + 1; // XXX should be prime. + threshold = (int) (newsize * loadFactor); + buckets = new WeakBucket[newsize]; + + // Now we have to insert the buckets again. + for (int i = 0; i < oldBuckets.length; i++) + { + WeakBucket bucket = oldBuckets[i]; + WeakBucket nextBucket; + while (bucket != null) + { + nextBucket = bucket.next; + + Object key = bucket.get(); + if (key == null) + { + // This bucket should be removed; it is probably + // already on the reference queue. We don't insert it + // at all, and mark it as cleared. + bucket.slot = -1; + size--; + } + else + { + // Add this bucket to its new slot. + int slot = hash(key); + bucket.slot = slot; + bucket.next = buckets[slot]; + buckets[slot] = bucket; + } + bucket = nextBucket; + } + } + } + + /** + * Finds the entry corresponding to key. Since it returns an Entry + * it will also prevent the key from being removed under us. + * @param key the key, may be null + * @return The WeakBucket.WeakEntry or null, if the key wasn't found. + */ + private WeakBucket.WeakEntry internalGet(Object key) + { + if (key == null) + key = NULL_KEY; + int slot = hash(key); + WeakBucket bucket = buckets[slot]; + while (bucket != null) + { + WeakBucket.WeakEntry entry = bucket.getEntry(); + if (entry != null && equals(key, entry.key)) + return entry; + + bucket = bucket.next; + } + return null; + } + + /** + * Adds a new key/value pair to the hash map. + * @param key the key. This mustn't exists in the map. It may be null. + * @param value the value. + */ + private void internalAdd(Object key, Object value) + { + if (key == null) + key = NULL_KEY; + int slot = hash(key); + WeakBucket bucket = new WeakBucket(key, queue, value, slot); + bucket.next = buckets[slot]; + buckets[slot] = bucket; + size++; + } + + /** + * Removes a bucket from this hash map, if it wasn't removed before + * (e.g. one time through rehashing and one time through reference queue). + * Package visible for use in nested classes. + * + * @param bucket the bucket to remove. + */ + void internalRemove(WeakBucket bucket) + { + int slot = bucket.slot; + if (slot == -1) + // This bucket was already removed. + return; + + // Mark the bucket as removed. This is necessary, since the + // bucket may be enqueued later by the garbage collection, and + // internalRemove will be called a second time. + bucket.slot = -1; + + WeakBucket prev = null; + WeakBucket next = buckets[slot]; + while (next != bucket) + { + if (next == null) throw new InternalError("WeakHashMap in incosistent state"); + prev = next; + next = prev.next; + } + if (prev == null) + buckets[slot] = bucket.next; + else + prev.next = bucket.next; + + size--; + } + + /** + * Returns the size of this hash map. Note that the size() may shrink + * spontaneously, if the some of the keys were only weakly reachable. + * @return the number of entries in this hash map. + */ + public int size() + { + cleanQueue(); + return size; + } + + /** + * Tells if the map is empty. Note that the result may change + * spontanously, if all of the keys were only weakly reachable. + * @return true, iff the map is empty. + */ + public boolean isEmpty() + { + cleanQueue(); + return size == 0; + } + + /** + * Tells if the map contains the given key. Note that the result + * may change spontanously, if the key was only weakly + * reachable. + * @param key the key to look for + * @return true, iff the map contains an entry for the given key. + */ + public boolean containsKey(Object key) + { + cleanQueue(); + return internalGet(key) != null; + } + + /** + * Gets the value the key is mapped to. + * @return the value the key was mapped to. It returns null if + * the key wasn't in this map, or if the mapped value was + * explicitly set to null. + */ + public V get(Object key) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + return entry == null ? null : entry.getValue(); + } + + /** + * Adds a new key/value mapping to this map. + * @param key the key, may be null + * @param value the value, may be null + * @return the value the key was mapped to previously. It returns + * null if the key wasn't in this map, or if the mapped value + * was explicitly set to null. + */ + public V put(K key, V value) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + if (entry != null) + return entry.setValue(value); + + modCount++; + if (size >= threshold) + rehash(); + + internalAdd(key, value); + return null; + } + + /** + * Removes the key and the corresponding value from this map. + * @param key the key. This may be null. + * @return the value the key was mapped to previously. It returns + * null if the key wasn't in this map, or if the mapped value was + * explicitly set to null. + */ + public V remove(Object key) + { + cleanQueue(); + WeakBucket.WeakEntry entry = internalGet(key); + if (entry == null) + return null; + + modCount++; + internalRemove(entry.getBucket()); + return entry.getValue(); + } + + /** + * Returns a set representation of the entries in this map. This + * set will not have strong references to the keys, so they can be + * silently removed. The returned set has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a set representation of the entries. + */ + public Set> entrySet() + { + cleanQueue(); + return theEntrySet; + } + + /** + * Clears all entries from this map. + */ + public void clear() + { + super.clear(); + } + + /** + * Returns true if the map contains at least one key which points to + * the specified object as a value. Note that the result + * may change spontanously, if its key was only weakly reachable. + * @param value the value to search for + * @return true if it is found in the set. + */ + public boolean containsValue(Object value) + { + cleanQueue(); + return super.containsValue(value); + } + + /** + * Returns a set representation of the keys in this map. This + * set will not have strong references to the keys, so they can be + * silently removed. The returned set has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a set representation of the keys. + */ + public Set keySet() + { + cleanQueue(); + return super.keySet(); + } + + /** + * Puts all of the mappings from the given map into this one. If the + * key already exists in this map, its value is replaced. + * @param m the map to copy in + */ + public void putAll(Map m) + { + super.putAll(m); + } + + /** + * Returns a collection representation of the values in this map. This + * collection will not have strong references to the keys, so mappings + * can be silently removed. The returned collection has therefore the same + * strange behaviour (shrinking size(), disappearing entries) as + * this weak hash map. + * @return a collection representation of the values. + */ + public Collection values() + { + cleanQueue(); + return super.values(); + } +} // class WeakHashMap diff --git a/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java b/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java new file mode 100644 index 000000000..f9f1ac1b4 --- /dev/null +++ b/libjava/classpath/java/util/concurrent/CopyOnWriteArrayList.java @@ -0,0 +1,1463 @@ +/* CopyOnWriteArrayList.java + Copyright (C) 2006 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.concurrent; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import java.lang.reflect.Array; + +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.RandomAccess; + +/** + * A thread-safe implementation of an ArrayList. A CopyOnWriteArrayList is + * as special ArrayList which performs copies of the underlying storage + * each time a write (remove, add etc..) operation + * is performed.
        + *
        + * The update operation in this class run usually in O(n) or worse, + * but traversal operations are fast and efficient, especially when running in + * a multi-thread environment without the need to design complex synchronize + * mechanisms.
        + *
        + * Iterators in this class work on a snapshot of the backing store + * at the moment the iterator itself was created, hence the iterator will not + * reflect changes in the underlying storage. Thus, update operation on the + * Iterators are not supported, but as interferences from other + * threads are impossible, no ConcurrentModificationException + * will be ever thrown from within the Iterator. + *

        + * This class is especially useful when used with event handling, like the + * following code demonstrates:
        + *

        + *
        + * CopyOnWriteArrayList listeners =
        + *   new CopyOnWriteArrayList();
        + *
        + * [...]
        + *
        + * for (final EventListener listener : listeners)
        + *   {
        + *     Runnable dispatcher = new Runnable() {
        + *       public void run()
        + *       {
        + *         listener.preferenceChange(event);
        + *       }
        + *     };
        + *
        + *     Executor executor = Executors.newSingleThreadExecutor();
        + *     executor.execute(dispatcher);
        + *   }
        + * 
        + * + * @since 1.5 + */ +public class CopyOnWriteArrayList + implements List, RandomAccess, Cloneable, Serializable +{ + /** + * + */ + private static final long serialVersionUID = 8673264195747942595L; + + /** + * Where the data is stored. + */ + private transient E[] data; + + /** + * Construct a new ArrayList with the default capacity (16). + */ + public CopyOnWriteArrayList() + { + data = (E[]) new Object[0]; + } + + /** + * Construct a new ArrayList, and initialize it with the elements in the + * supplied Collection. The initial capacity is 110% of the Collection's size. + * + * @param c + * the collection whose elements will initialize this list + * @throws NullPointerException + * if c is null + */ + public CopyOnWriteArrayList(Collection< ? extends E> c) + { + // FIXME ... correct? use c.toArray() + data = (E[]) new Object[c.size()]; + int index = 0; + for (E value : c) + data[index++] = value; + } + + /** + * Construct a new ArrayList, and initialize it with the elements in the + * supplied array. + * + * @param array + * the array used to initialize this list + * @throws NullPointerException + * if array is null + */ + public CopyOnWriteArrayList(E[] array) + { + data = (E[]) array.clone(); + } + + /** + * Returns the number of elements in this list. + * + * @return the list size + */ + public int size() + { + return data.length; + } + + /** + * Checks if the list is empty. + * + * @return true if there are no elements + */ + public boolean isEmpty() + { + return data.length == 0; + } + + /** + * Returns true if element is in this ArrayList. + * + * @param e + * the element whose inclusion in the List is being tested + * @return true if the list contains e + */ + public boolean contains(Object e) + { + return indexOf(e) != -1; + } + + /** + * Tests whether this collection contains all the elements in a given + * collection. This implementation iterates over the given collection, + * testing whether each element is contained in this collection. If any one + * is not, false is returned. Otherwise true is returned. + * + * @param c the collection to test against + * @return true if this collection contains all the elements in the given + * collection + * @throws NullPointerException if the given collection is null + * @see #contains(Object) + */ + public boolean containsAll(Collection c) + { + Iterator itr = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (!contains(itr.next())) + return false; + return true; + } + + /** + * Returns the lowest index at which element appears in this List, or -1 if it + * does not appear. + * + * @param e + * the element whose inclusion in the List is being tested + * @return the index where e was found + */ + public int indexOf(Object e) + { + E[] data = this.data; + for (int i = 0; i < data.length; i++) + if (equals(e, data[i])) + return i; + return -1; + } + + /** + * Return the lowest index greater equal index at which + * e appears in this List, or -1 if it does not + * appear. + * + * @param e the element whose inclusion in the list is being tested + * @param index the index at which the search begins + * @return the index where e was found + */ + public int indexOf(E e, int index) + { + E[] data = this.data; + + for (int i = index; i < data.length; i++) + if (equals(e, data[i])) + return i; + return -1; + } + + /** + * Returns the highest index at which element appears in this List, or -1 if + * it does not appear. + * + * @param e + * the element whose inclusion in the List is being tested + * @return the index where e was found + */ + public int lastIndexOf(Object e) + { + E[] data = this.data; + for (int i = data.length - 1; i >= 0; i--) + if (equals(e, data[i])) + return i; + return -1; + } + + /** + * Returns the highest index lesser equal index at + * which e appears in this List, or -1 if it does not + * appear. + * + * @param e the element whose inclusion in the list is being tested + * @param index the index at which the search begins + * @return the index where e was found + */ + public int lastIndexOf(E e, int index) + { + E[] data = this.data; + + for (int i = index; i >= 0; i--) + if (equals(e, data[i])) + return i; + return -1; + } + + /** + * Creates a shallow copy of this ArrayList (elements are not cloned). + * + * @return the cloned object + */ + public Object clone() + { + CopyOnWriteArrayList clone = null; + try + { + clone = (CopyOnWriteArrayList) super.clone(); + } + catch (CloneNotSupportedException e) + { + // Impossible to get here. + } + return clone; + } + + /** + * Returns an Object array containing all of the elements in this ArrayList. + * The array is independent of this list. + * + * @return an array representation of this list + */ + public Object[] toArray() + { + E[] data = this.data; + E[] array = (E[]) new Object[data.length]; + System.arraycopy(data, 0, array, 0, data.length); + return array; + } + + /** + * Returns an Array whose component type is the runtime component type of the + * passed-in Array. The returned Array is populated with all of the elements + * in this ArrayList. If the passed-in Array is not large enough to store all + * of the elements in this List, a new Array will be created and returned; if + * the passed-in Array is larger than the size of this List, then + * size() index will be set to null. + * + * @param a + * the passed-in Array + * @return an array representation of this list + * @throws ArrayStoreException + * if the runtime type of a does not allow an element in this list + * @throws NullPointerException + * if a is null + */ + public T[] toArray(T[] a) + { + E[] data = this.data; + if (a.length < data.length) + a = (T[]) Array.newInstance(a.getClass().getComponentType(), data.length); + else if (a.length > data.length) + a[data.length] = null; + System.arraycopy(data, 0, a, 0, data.length); + return a; + } + + /** + * Retrieves the element at the user-supplied index. + * + * @param index + * the index of the element we are fetching + * @throws IndexOutOfBoundsException + * if index < 0 || index >= size() + */ + public E get(int index) + { + return data[index]; + } + + /** + * Sets the element at the specified index. The new element, e, can be an + * object of any type or null. + * + * @param index + * the index at which the element is being set + * @param e + * the element to be set + * @return the element previously at the specified index + * @throws IndexOutOfBoundsException + * if index < 0 || index >= 0 + */ + public synchronized E set(int index, E e) + { + E result = data[index]; + E[] newData = (E[]) data.clone(); + newData[index] = e; + data = newData; + return result; + } + + /** + * Appends the supplied element to the end of this list. The element, e, can + * be an object of any type or null. + * + * @param e + * the element to be appended to this list + * @return true, the add will always succeed + */ + public synchronized boolean add(E e) + { + E[] data = this.data; + E[] newData = (E[]) new Object[data.length + 1]; + System.arraycopy(data, 0, newData, 0, data.length); + newData[data.length] = e; + this.data = newData; + return true; + } + + /** + * Adds the supplied element at the specified index, shifting all elements + * currently at that index or higher one to the right. The element, e, can be + * an object of any type or null. + * + * @param index + * the index at which the element is being added + * @param e + * the item being added + * @throws IndexOutOfBoundsException + * if index < 0 || index > size() + */ + public synchronized void add(int index, E e) + { + E[] data = this.data; + E[] newData = (E[]) new Object[data.length + 1]; + System.arraycopy(data, 0, newData, 0, index); + newData[index] = e; + System.arraycopy(data, index, newData, index + 1, data.length - index); + this.data = newData; + } + + /** + * Removes the element at the user-supplied index. + * + * @param index + * the index of the element to be removed + * @return the removed Object + * @throws IndexOutOfBoundsException + * if index < 0 || index >= size() + */ + public synchronized E remove(int index) + { + if (index < 0 || index >= this.size()) + throw new IndexOutOfBoundsException("index = " + index); + + E[] snapshot = this.data; + E[] newData = (E[]) new Object[snapshot.length - 1]; + + E result = snapshot[index]; + + if (index > 0) + System.arraycopy(snapshot, 0, newData, 0, index); + + System.arraycopy(snapshot, index + 1, newData, index, + snapshot.length - index - 1); + + this.data = newData; + + return result; + } + + /** + * Remove the first occurrence, if any, of the given object from this list, + * returning true if the object was removed, false + * otherwise. + * + * @param element the object to be removed. + * @return true if element was removed, false otherwise. false means also that + * the underlying storage was unchanged after this operation concluded. + */ + public synchronized boolean remove(Object element) + { + E[] snapshot = this.data; + int len = snapshot.length; + + if (len == 0) + return false; + + E[] newData = (E[]) new Object[len - 1]; + + // search the element to remove while filling the backup array + // this way we can run this method in O(n) + int elementIndex = -1; + for (int i = 0; i < snapshot.length; i++) + { + if (equals(element, snapshot[i])) + { + elementIndex = i; + break; + } + + if (i < newData.length) + newData[i] = snapshot[i]; + } + + if (elementIndex < 0) + return false; + + System.arraycopy(snapshot, elementIndex + 1, newData, elementIndex, + snapshot.length - elementIndex - 1); + this.data = newData; + + return true; + } + + /** + * Removes all the elements contained in the given collection. + * This method removes the elements that are contained in both + * this list and in the given collection. + * + * @param c the collection containing the elements to be removed from this + * list. + * @return true if at least one element was removed, indicating that + * the list internal storage changed as a result, false otherwise. + */ + public synchronized boolean removeAll(Collection c) + { + if (c.size() == 0) + return false; + + E [] snapshot = this.data; + E [] storage = (E[]) new Object[this.data.length]; + boolean changed = false; + + int length = 0; + for (E element : snapshot) + { + // copy all the elements, including null values + // if the collection can hold it + // FIXME: slow operation + if (c.contains(element)) + changed = true; + else + storage[length++] = element; + } + + if (!changed) + return false; + + E[] newData = (E[]) new Object[length]; + System.arraycopy(storage, 0, newData, 0, length); + + this.data = newData; + + return true; + } + + /** + * Removes all the elements that are not in the passed collection. + * If the collection is void, this method has the same effect of + * clear(). + * Please, note that this method is extremely slow (unless the argument has + * size == 0) and has bad performance is both space and time + * usage. + * + * @param c the collection containing the elements to be retained by this + * list. + * @return true the list internal storage changed as a result of this + * operation, false otherwise. + */ + public synchronized boolean retainAll(Collection c) + { + // if the given collection does not contain elements + // we remove all the elements from our storage + if (c.size() == 0) + { + this.clear(); + return true; + } + + E [] snapshot = this.data; + E [] storage = (E[]) new Object[this.data.length]; + + int length = 0; + for (E element : snapshot) + { + if (c.contains(element)) + storage[length++] = element; + } + + // means we retained all the elements previously in our storage + // we are running already slow here, but at least we avoid copying + // another array and changing the internal storage + if (length == snapshot.length) + return false; + + E[] newData = (E[]) new Object[length]; + System.arraycopy(storage, 0, newData, 0, length); + + this.data = newData; + + return true; + } + + /** + * Removes all elements from this List + */ + public synchronized void clear() + { + data = (E[]) new Object[0]; + } + + /** + * Add each element in the supplied Collection to this List. It is undefined + * what happens if you modify the list while this is taking place; for + * example, if the collection contains this list. c can contain objects of any + * type, as well as null values. + * + * @param c + * a Collection containing elements to be added to this List + * @return true if the list was modified, in other words c is not empty + * @throws NullPointerException + * if c is null + */ + public synchronized boolean addAll(Collection< ? extends E> c) + { + return addAll(data.length, c); + } + + /** + * Add all elements in the supplied collection, inserting them beginning at + * the specified index. c can contain objects of any type, as well as null + * values. + * + * @param index + * the index at which the elements will be inserted + * @param c + * the Collection containing the elements to be inserted + * @throws IndexOutOfBoundsException + * if index < 0 || index > 0 + * @throws NullPointerException + * if c is null + */ + public synchronized boolean addAll(int index, Collection< ? extends E> c) + { + if (index < 0 || index > this.size()) + throw new IndexOutOfBoundsException("index = " + index); + + int csize = c.size(); + if (csize == 0) + return false; + + E[] data = this.data; + Iterator itr = c.iterator(); + + E[] newData = (E[]) new Object[data.length + csize]; + + // avoid this call at all if we were asked to put the elements at the + // beginning of our storage + if (index != 0) + System.arraycopy(data, 0, newData, 0, index); + + int itemsLeft = index; + + for (E value : c) + newData[index++] = value; + + // now copy the remaining elements + System.arraycopy(data, itemsLeft, newData, 0, data.length - itemsLeft); + + this.data = newData; + + return true; + } + + /** + * Adds an element if the list does not contains it already. + * + * @param val the element to add to the list. + * @return true if the element was added, false otherwise. + */ + public synchronized boolean addIfAbsent(E val) + { + if (contains(val)) + return false; + add(val); + return true; + } + + /** + * Adds all the element from the given collection that are not already + * in this list. + * + * @param c the Collection containing the elements to be inserted + * @return true the list internal storage changed as a result of this + * operation, false otherwise. + */ + public synchronized int addAllAbsent(Collection c) + { + int size = c.size(); + if (size == 0) + return 0; + + E [] snapshot = this.data; + E [] storage = (E[]) new Object[size]; + + size = 0; + for (E val : c) + { + if (!this.contains(val)) + storage[size++] = val; + } + + if (size == 0) + return 0; + + // append storage to data + E [] newData = (E[]) new Object[snapshot.length + size]; + + System.arraycopy(snapshot, 0, newData, 0, snapshot.length); + System.arraycopy(storage, 0, newData, snapshot.length, size); + + this.data = newData; + + return size; + } + + public String toString() + { + return Arrays.toString(this.data); + } + + public boolean equals(Object o) + { + if (o == null) + return false; + + if (this == o) + return true; + + // let's see if 'o' is a list, if so, we need to compare the elements + // as returned by the iterator + if (o instanceof List) + { + List source = (List) o; + + if (source.size() != this.size()) + return false; + + Iterator sourceIterator = source.iterator(); + for (E element : this) + { + if (!element.equals(sourceIterator.next())) + return false; + } + + return true; + } + + return false; + } + + public int hashCode() + { + // see http://java.sun.com/6/docs/api/java/util/List.html#hashcode() + int hashcode = 1; + for (E element : this) + { + hashcode = 31 * hashcode + (element == null ? 0 : element.hashCode()); + } + return hashcode; + } + + /** + * Return an Iterator containing the elements of this list. + * The Iterator uses a snapshot of the state of the internal storage + * at the moment this method is called and does not support + * update operations, so no synchronization is needed to traverse the + * iterator. + * + * @return an Iterator containing the elements of this list in sequence. + */ + public Iterator iterator() + { + return new Iterator() + { + E [] iteratorData = CopyOnWriteArrayList.this.data; + int currentElement = 0; + + public boolean hasNext() + { + return (currentElement < iteratorData.length); + } + + public E next() + { + return iteratorData[currentElement++]; + } + + public void remove() + { + throw new UnsupportedOperationException("updating of elements in " + + "iterators is not supported " + + "by this class"); + } + }; + } + + /** + * Return a ListIterator containing the elements of this list. + * The Iterator uses a snapshot of the state of the internal storage + * at the moment this method is called and does not support + * update operations, so no synchronization is needed to traverse the + * iterator. + * + * @return a ListIterator containing the elements of this list in sequence. + */ + public ListIterator listIterator() + { + return listIterator(0); + } + + /** + * Return a ListIterator over the elements of this list starting at + * the specified index. An initial call to {@code next()} will thus + * return the element at {@code index}, while an initial call to + * {@code previous()} will return the element at {@code index-1}. The + * Iterator uses a snapshot of the state of the internal storage + * at the moment this method is called and does not support + * update operations, so no synchronization is needed to traverse the + * iterator. + * + * @param index the index at which to start iterating. + * @return a ListIterator containing the elements of this list in sequence. + */ + public ListIterator listIterator(final int index) + { + if (index < 0 || index > size()) + throw new IndexOutOfBoundsException("Index: " + index + ", Size:" + + size()); + + return new ListIterator() + { + E [] iteratorData = CopyOnWriteArrayList.this.data; + int currentElement = index; + + public void add(E o) + { + throw new UnsupportedOperationException("updating of elements in " + + "iterators is not supported " + + "by this class"); + } + + public boolean hasNext() + { + return (currentElement < iteratorData.length); + } + + public boolean hasPrevious() + { + return (currentElement > 0); + } + + public E next() + { + if (hasNext() == false) + throw new java.util.NoSuchElementException(); + + return iteratorData[currentElement++]; + } + + public int nextIndex() + { + return (currentElement + 1); + } + + public E previous() + { + if (hasPrevious() == false) + throw new java.util.NoSuchElementException(); + + return iteratorData[--currentElement]; + } + + public int previousIndex() + { + return (currentElement - 1); + } + + public void remove() + { + throw new UnsupportedOperationException("updating of elements in " + + "iterators is not supported " + + "by this class"); + } + + public void set(E o) + { + throw new UnsupportedOperationException("updating of elements in " + + "iterators is not supported " + + "by this class"); + } + + }; + } + + /** + * Obtain a List view of a subsection of this list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. + *

        + * + * This implementation returns a subclass of AbstractList. It stores, in + * private fields, the offset and size of the sublist, and the expected + * modCount of the backing list. If the backing list implements RandomAccess, + * the sublist will also. + *

        + * + * The subclass's set(int, Object), get(int), + * add(int, Object), remove(int), + * addAll(int, Collection) and + * removeRange(int, int) methods all delegate to the + * corresponding methods on the backing abstract list, after + * bounds-checking the index and adjusting for the offset. The + * addAll(Collection c) method merely returns addAll(size, c). + * The listIterator(int) method returns a "wrapper object" + * over a list iterator on the backing list, which is created with the + * corresponding method on the backing list. The iterator() + * method merely returns listIterator(), and the size() method + * merely returns the subclass's size field. + *

        + * + * All methods first check to see if the actual modCount of the backing + * list is equal to its expected value, and throw a + * ConcurrentModificationException if it is not. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() + * @throws IndexOutOfBoundsException if fromIndex > toIndex + * @see ConcurrentModificationException + * @see RandomAccess + */ + public synchronized List subList(int fromIndex, int toIndex) + { + // This follows the specification of AbstractList, but is inconsistent + // with the one in List. Don't you love Sun's inconsistencies? + if (fromIndex > toIndex) + throw new IndexOutOfBoundsException(fromIndex + " > " + toIndex); + if (fromIndex < 0 || toIndex > size()) + throw new IndexOutOfBoundsException(); + + if (this instanceof RandomAccess) + return new RandomAccessSubList(this, fromIndex, toIndex); + return new SubList(this, fromIndex, toIndex); + } + + /** + * This class follows the implementation requirements set forth in + * {@link AbstractList#subList(int, int)}. It matches Sun's implementation + * by using a non-public top-level class in the same package. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SubList + extends AbstractList + { + // Package visible, for use by iterator. + /** The original list. */ + final CopyOnWriteArrayList backingList; + /** The index of the first element of the sublist. */ + final int offset; + /** The size of the sublist. */ + int size; + /** The backing data */ + E[] data; + + /** + * Construct the sublist. + * + * @param backing the list this comes from + * @param fromIndex the lower bound, inclusive + * @param toIndex the upper bound, exclusive + */ + SubList(CopyOnWriteArrayList backing, int fromIndex, int toIndex) + { + backingList = backing; + data = backing.data; + offset = fromIndex; + size = toIndex - fromIndex; + } + + /** + * This method checks the two modCount fields to ensure that there has + * not been a concurrent modification, returning if all is okay. + * + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + */ + // This can be inlined. Package visible, for use by iterator. + void checkMod() + { + if (data != backingList.data) + throw new ConcurrentModificationException(); + } + + /** + * This method checks that a value is between 0 and size (inclusive). If + * it is not, an exception is thrown. + * + * @param index the value to check + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + // This will get inlined, since it is private. + private void checkBoundsInclusive(int index) + { + if (index < 0 || index > size) + throw new IndexOutOfBoundsException("Index: " + index + + ", Size:" + size); + } + + /** + * This method checks that a value is between 0 (inclusive) and size + * (exclusive). If it is not, an exception is thrown. + * + * @param index the value to check + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + // This will get inlined, since it is private. + private void checkBoundsExclusive(int index) + { + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException("Index: " + index + + ", Size:" + size); + } + + /** + * Specified by AbstractList.subList to return the private field size. + * + * @return the sublist size + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + */ + public int size() + { + synchronized (backingList) + { + checkMod(); + return size; + } + } + + public void clear() + { + synchronized (backingList) + { + E[] snapshot = backingList.data; + E[] newData = (E[]) new Object[snapshot.length - size]; + + int toIndex = size + offset; + + System.arraycopy(snapshot, 0, newData, 0, offset); + System.arraycopy(snapshot, toIndex, newData, offset, + snapshot.length - toIndex); + + backingList.data = newData; + this.data = backingList.data; + this.size = 0; + } + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to modify + * @param o the new value + * @return the old value + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws UnsupportedOperationException if the backing list does not + * support the set operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to the backing list due + * to its type + * @throws IllegalArgumentException if o cannot be added to the backing list + * for some other reason + */ + public E set(int index, E o) + { + synchronized (backingList) + { + checkMod(); + checkBoundsExclusive(index); + + E el = backingList.set(index + offset, o); + this.data = backingList.data; + + return el; + } + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to get from + * @return the object at that location + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public E get(int index) + { + synchronized (backingList) + { + checkMod(); + checkBoundsExclusive(index); + + return backingList.get(index + offset); + } + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the index to insert at + * @param o the object to add + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws UnsupportedOperationException if the backing list does not + * support the add operation. + * @throws ClassCastException if o cannot be added to the backing list due + * to its type. + * @throws IllegalArgumentException if o cannot be added to the backing + * list for some other reason. + */ + public void add(int index, E o) + { + synchronized (backingList) + { + checkMod(); + checkBoundsInclusive(index); + + backingList.add(index + offset, o); + + this.data = backingList.data; + size++; + } + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the index to remove + * @return the removed object + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws UnsupportedOperationException if the backing list does not + * support the remove operation + */ + public E remove(int index) + { + synchronized (backingList) + { + checkMod(); + checkBoundsExclusive(index); + E o = backingList.remove(index + offset); + + this.data = backingList.data; + size--; + + return o; + } + } + + /** + * Specified by AbstractList.subList to delegate to the backing list. + * + * @param index the location to insert at + * @param c the collection to insert + * @return true if this list was modified, in other words, c is non-empty + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(int index, Collection c) + { + synchronized (backingList) + { + checkMod(); + checkBoundsInclusive(index); + int csize = c.size(); + boolean result = backingList.addAll(offset + index, c); + + this.data = backingList.data; + size += csize; + + return result; + } + } + + /** + * Specified by AbstractList.subList to return addAll(size, c). + * + * @param c the collection to insert + * @return true if this list was modified, in other words, c is non-empty + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws UnsupportedOperationException if this list does not support the + * addAll operation + * @throws ClassCastException if some element of c cannot be added to this + * list due to its type + * @throws IllegalArgumentException if some element of c cannot be added + * to this list for some other reason + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(Collection c) + { + synchronized (backingList) + { + return addAll(size, c); + } + } + + /** + * Specified by AbstractList.subList to return listIterator(). + * + * @return an iterator over the sublist + */ + public Iterator iterator() + { + return listIterator(); + } + + /** + * Specified by AbstractList.subList to return a wrapper around the + * backing list's iterator. + * + * @param index the start location of the iterator + * @return a list iterator over the sublist + * @throws ConcurrentModificationException if the backing list has been + * modified externally to this sublist + * @throws IndexOutOfBoundsException if the value is out of range + */ + public ListIterator listIterator(final int index) + { + checkMod(); + checkBoundsInclusive(index); + + return new ListIterator() + { + private final ListIterator i = + backingList.listIterator(index + offset); + private int position = index; + + /** + * Tests to see if there are any more objects to + * return. + * + * @return True if the end of the list has not yet been + * reached. + */ + public boolean hasNext() + { + return position < size; + } + + /** + * Tests to see if there are objects prior to the + * current position in the list. + * + * @return True if objects exist prior to the current + * position of the iterator. + */ + public boolean hasPrevious() + { + return position > 0; + } + + /** + * Retrieves the next object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * more objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public E next() + { + if (position == size) + throw new NoSuchElementException(); + position++; + return i.next(); + } + + /** + * Retrieves the previous object from the list. + * + * @return The next object. + * @throws NoSuchElementException if there are no + * previous objects to retrieve. + * @throws ConcurrentModificationException if the + * list has been modified elsewhere. + */ + public E previous() + { + if (position == 0) + throw new NoSuchElementException(); + position--; + return i.previous(); + } + + /** + * Returns the index of the next element in the + * list, which will be retrieved by next() + * + * @return The index of the next element. + */ + public int nextIndex() + { + return i.nextIndex() - offset; + } + + /** + * Returns the index of the previous element in the + * list, which will be retrieved by previous() + * + * @return The index of the previous element. + */ + public int previousIndex() + { + return i.previousIndex() - offset; + } + + /** + * Removes the last object retrieved by next() + * from the list, if the list supports object removal. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list does + * not support removing elements. + */ + public void remove() + { + throw new UnsupportedOperationException("Modification not supported " + + "on CopyOnWriteArrayList iterators"); + } + + /** + * Replaces the last object retrieved by next() + * or previous with o, if the list supports object + * replacement and an add or remove operation has not already + * been performed. + * + * @throws IllegalStateException if the iterator is positioned + * before the start of the list or the last object has already + * been removed. + * @throws UnsupportedOperationException if the list doesn't support + * the addition or removal of elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void set(E o) + { + throw new UnsupportedOperationException("Modification not supported " + + "on CopyOnWriteArrayList iterators"); + } + + /** + * Adds the supplied object before the element that would be returned + * by a call to next(), if the list supports addition. + * + * @param o The object to add to the list. + * @throws UnsupportedOperationException if the list doesn't support + * the addition of new elements. + * @throws ClassCastException if the type of o is not a valid type + * for this list. + * @throws IllegalArgumentException if something else related to o + * prevents its addition. + * @throws ConcurrentModificationException if the list + * has been modified elsewhere. + */ + public void add(E o) + { + throw new UnsupportedOperationException("Modification not supported " + + "on CopyOnWriteArrayList iterators"); + } + }; + } + } // class SubList + + /** + * This class is a RandomAccess version of SubList, as required by + * {@link AbstractList#subList(int, int)}. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class RandomAccessSubList extends SubList + implements RandomAccess + { + /** + * Construct the sublist. + * + * @param backing the list this comes from + * @param fromIndex the lower bound, inclusive + * @param toIndex the upper bound, exclusive + */ + RandomAccessSubList(CopyOnWriteArrayList backing, int fromIndex, int toIndex) + { + super(backing, fromIndex, toIndex); + } + } // class RandomAccessSubList + + /** + * Serializes this object to the given stream. + * + * @param s + * the stream to write to + * @throws IOException + * if the underlying stream fails + * @serialData the size field (int), the length of the backing array (int), + * followed by its elements (Objects) in proper order. + */ + private void writeObject(ObjectOutputStream s) throws IOException + { + // The 'size' field. + s.defaultWriteObject(); + // We serialize unused list entries to preserve capacity. + int len = data.length; + s.writeInt(len); + // it would be more efficient to just write "size" items, + // this need readObject read "size" items too. + for (int i = 0; i < data.length; i++) + s.writeObject(data[i]); + } + + /** + * Deserializes this object from the given stream. + * + * @param s + * the stream to read from + * @throws ClassNotFoundException + * if the underlying stream fails + * @throws IOException + * if the underlying stream fails + * @serialData the size field (int), the length of the backing array (int), + * followed by its elements (Objects) in proper order. + */ + private void readObject(ObjectInputStream s) throws IOException, + ClassNotFoundException + { + // the `size' field. + s.defaultReadObject(); + int capacity = s.readInt(); + data = (E[]) new Object[capacity]; + for (int i = 0; i < capacity; i++) + data[i] = (E) s.readObject(); + } + + static final boolean equals(Object o1, Object o2) + { + return o1 == null ? o2 == null : o1.equals(o2); + } + + Object[] getArray() + { + return data; + } +} diff --git a/libjava/classpath/java/util/jar/Attributes.java b/libjava/classpath/java/util/jar/Attributes.java new file mode 100644 index 000000000..88800294c --- /dev/null +++ b/libjava/classpath/java/util/jar/Attributes.java @@ -0,0 +1,629 @@ +/* Attributes.java -- Represents attribute name/value pairs from a Manifest + Copyright (C) 2000, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import gnu.java.util.jar.JarUtils; + +import java.util.Collection; +import java.util.Hashtable; +import java.util.Map; +import java.util.Set; + +/** + * Represents attribute name/value pairs from a Manifest as a Map. + * The names of an attribute are represented by the + * Attributes.Name class and should confirm to the restrictions + * described in that class. Note that the Map interface that Attributes + * implements allows you to put names and values into the attribute that don't + * follow these restriction (and are not really Atrribute.Names, but if you do + * that it might cause undefined behaviour later). + *

        + * If you use the constants defined in the inner class Name then you can be + * sure that you always access the right attribute names. This makes + * manipulating the Attributes more or less type safe. + *

        + * Most of the methods are wrappers to implement the Map interface. The really + * useful and often used methods are getValue(Name) and + * getValue(String). If you actually want to set attributes you + * may want to use the putValue(String, String) method + * (sorry there is no public type safe putValue(Name, String) + * method). + * + * @see java.util.jar.Attributes.Name + * @author Mark Wielaard (mark@klomp.org) + */ +public class Attributes + implements Cloneable, Map +{ + + // Fields + + /** + * The map that holds all the attribute name/value pairs. In this + * implementation it is actually a Hashtable, but that can be different in + * other implementations. + */ + protected Map map; + + // Inner class + + /** + * Represents a name of a Manifest Attribute. Defines a couple of well + * know names for the general main attributes, stand alone application + * attributes, applet attributes, extension identification attributes, + * package versioning and sealing attributes, file contents attributes, + * bean objects attribute and signing attributes. See the + * + *

        The characters of a Name must obey the following restrictions:

        + * + *
          + *
        • Must contain at least one character
        • + *
        • The first character must be alphanumeric (a-z, A-Z, 0-9)
        • + *
        • All other characters must be alphanumeric, a '-' or a '_'
        • + *
        + * + *

        When comparing Names (with equals) all characters are + * converted to lowercase. But you can get the original case sensitive + * string with the toString() method.

        + * + *

        Most important attributes have a constant defined in this + * class. Some other attributes used in Manifest files are: + *

          + *
        • "Created-By" - General main attribute, tool and version + * that created this Manifest file.
        • + *
        • "Java-Bean" - Bean objects attribute, whether the entry is a Bean. + * Value is either "true" or "false".
        • + *
        • "Magic" - Signing attribute, application specific signing attribute. + * Must be understood by the manifest parser when present to validate the + * jar (entry).
        • + *
        + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ + public static class Name + { + // General Main Attributes + + /** + * General main attribute - + * the version of this Manifest file. + */ + public static final Name MANIFEST_VERSION = new Name(JarUtils.MANIFEST_VERSION); + + /** + * General main attribute - + * the version of the jar file signature. + */ + public static final Name SIGNATURE_VERSION = new Name(JarUtils.SIGNATURE_VERSION); + + /** + * General main attribute - + * (relative) file paths of the libraries/classpaths that the Classes in + * this jar file depend on. Paths are separated by spaces. + */ + public static final Name CLASS_PATH = new Name("Class-Path"); + + /** + * Stand alone application attribute - + * the entry (without the .class ending) that is the main + * class of this jar file. + */ + public static final Name MAIN_CLASS = new Name("Main-Class"); + + /** + * Applet attribute - + * a list of extension libraries that the applet in this + * jar file depends on. + * For every named extension there should be some Attributes in the + * Manifest manifest file with the following Names: + *
          + *
        • <extension>-Extension-Name: + * unique name of the extension
        • + *
        • <extension>-Specification-Version: + * minimum specification version
        • + *
        • <extension>-Implementation-Version: + * minimum implementation version
        • + *
        • <extension>-Implementation-Vendor-Id: + * unique id of implementation vendor
        • + *
        • <extension>-Implementation-URL: + * where the latest version of the extension library can be found
        • + *
        + */ + public static final Name EXTENSION_LIST = new Name("Extension-List"); + + /** + * Extension identification attribute - + * the name if the extension library contained in the jar. + */ + public static final Name EXTENSION_NAME = new Name("Extension-Name"); + + /** + * Extension identification attribute - + * synonym for EXTENSTION_NAME. + */ + public static final Name EXTENSION_INSTALLATION = EXTENSION_NAME; + + // Package versioning and sealing attributes + + /** + * Package versioning - + * name of extension library contained in this jar. + */ + public static final Name IMPLEMENTATION_TITLE + = new Name("Implementation-Title"); + + /** + * Package versioning - + * version of the extension library contained in this jar. + */ + public static final Name IMPLEMENTATION_VERSION + = new Name("Implementation-Version"); + + /** + * Package versioning - + * name of extension library creator contained in this jar. + */ + public static final Name IMPLEMENTATION_VENDOR + = new Name("Implementation-Vendor"); + + /** + * Package versioning - + * unique id of extension library creator. + */ + public static final Name IMPLEMENTATION_VENDOR_ID + = new Name("Implementation-Vendor-Id"); + + /** + * Package versioning - + * location where this implementation can be downloaded. + */ + public static final Name IMPLEMENTATION_URL + = new Name("Implementation-URL"); + + /** + * Package versioning - + * title of the specification contained in this jar. + */ + public static final Name SPECIFICATION_TITLE + = new Name("Specification-Title"); + + /** + * Package versioning - + * version of the specification contained in this jar. + */ + public static final Name SPECIFICATION_VERSION + = new Name("Specification-Version"); + + /** + * Package versioning - + * organisation that maintains the specification contains in this + * jar. + */ + public static final Name SPECIFICATION_VENDOR + = new Name("Specification-Vendor"); + + /** + * Package sealing - + * whether (all) package(s) is(/are) sealed. Value is either "true" + * or "false". + */ + public static final Name SEALED = new Name("Sealed"); + + /** + * File contents attribute - + * Mime type and subtype for the jar entry. + */ + public static final Name CONTENT_TYPE = new Name("Content-Type"); + + /** The (lowercase) String representation of this Name */ + private final String name; + + /** The original String given to the constructor */ + private final String origName; + + // Constructor + + /** + * Creates a new Name from the given String. + * Throws an IllegalArgumentException if the given String is empty or + * contains any illegal Name characters. + * + * @param name the name of the new Name + * @exception IllegalArgumentException if name isn't a valid String + * representation of a Name + * @exception NullPointerException if name is null + */ + public Name(String name) throws IllegalArgumentException, + NullPointerException + { + // name must not be null + // this will throw a NullPointerException if it is + char chars[] = name.toCharArray(); + + // there must be at least one character + if (chars.length == 0) + throw new + IllegalArgumentException + ("There must be at least one character in a name"); + + // first character must be alphanum + char c = chars[0]; + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) + throw new + IllegalArgumentException("First character must be alphanum"); + + // all other characters must be alphanums, '-' or '_' + for (int i = 1; i < chars.length; i++) + { + c = chars[i]; + if (!((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || (c == '-') || (c == '_'))) + throw new + IllegalArgumentException + ("Characters must be alphanums, '-' or '_'"); + } + + // Still here? Then convert to lower case and be done. + // Store the original name for toString(); + this.origName = name; + this.name = name.toLowerCase(); + } + + /** + * Returns the hash code of the (lowercase) String representation of + * this Name. + */ + public int hashCode() + { + return name.hashCode(); + } + + /** + * Checks if another object is equal to this Name object. + * Another object is equal to this Name object if it is an instance of + * Name and the (lowercase) string representation of the name is equal. + */ + public boolean equals(Object o) + { + // Quick and dirty check + if (name == o) + return true; + + try + { + // Note that the constructor already converts the strings to + // lowercase. + String otherName = ((Name) o).name; + return name.equals(otherName); + } + catch (ClassCastException cce) + { + return false; + } + catch (NullPointerException npe) + { + return false; + } + } + + /** + * Returns the string representation of this Name as given to the + * constructor (not neccesarily the lower case representation). + */ + public String toString() + { + return origName; + } + } + + // Constructors + + /** + * Creates an empty Attributes map. + */ + public Attributes() + { + map = new Hashtable(); + } + + /** + * Creates an empty Attributes map with the given initial size. + * @param size the initial size of the underlying map + */ + public Attributes(int size) + { + map = new Hashtable(size); + } + + /** + * Creates an Attributes map with the initial values taken from another + * Attributes map. + * @param attr Attributes map to take the initial values from + */ + public Attributes(Attributes attr) + { + map = new Hashtable(attr.map); + } + + // Methods + + /** + * Gets the value of an attribute name given as a String. + * + * @param name a String describing the Name to look for + * @return the value gotten from the map of null when not found + */ + public String getValue(String name) + { + return (String) get(new Name(name)); + } + + /** + * Gets the value of the given attribute name. + * + * @param name the Name to look for + * @return the value gotten from the map of null when not found + */ + public String getValue(Name name) + { + return (String) get(name); + } + + /** + * Stores an attribute name (represented by a String) and value in this + * Attributes map. + * When the (case insensitive string) name already exists the value is + * replaced and the old value is returned. + * + * @param name a (case insensitive) String representation of the attribite + * name to add/replace + * @param value the (new) value of the attribute name + * @returns the old value of the attribute name or null if it didn't exist + * yet + */ + public String putValue(String name, String value) + { + return putValue(new Name(name), value); + } + + /** + * Stores an attribute name (represented by a String) and value in this + * Attributes map. + * When the name already exists the value is replaced and the old value + * is returned. + * + * @param name the attribite name to add/replace + * @param value the (new) value of the attribute name + * @returns the old value of the attribute name or null if it didn't exist + * yet + */ + private String putValue(Name name, String value) + { + return (String) put(name, value); + } + + // Methods from Cloneable interface + + /** + * Return a clone of this attribute map. + */ + public Object clone() + { + return new Attributes(this); + } + + // Methods from Map interface + + /** + * Removes all attributes. + */ + public void clear() + { + map.clear(); + } + + /** + * Checks to see if there is an attribute with the specified name. + * XXX - what if the object is a String? + * + * @param attrName the name of the attribute to check + * @return true if there is an attribute with the specified name, false + * otherwise + */ + public boolean containsKey(Object attrName) + { + return map.containsKey(attrName); + } + + /** + * Checks to see if there is an attribute name with the specified value. + * + * @param attrValue the value of a attribute to check + * @return true if there is an attribute name with the specified value, + * false otherwise + */ + public boolean containsValue(Object attrValue) + { + return map.containsValue(attrValue); + } + + /** + * Gives a Set of attribute name and values pairs as MapEntries. + * @see java.util.Map.Entry + * @see java.util.Map#entrySet() + * + * @return a set of attribute name value pairs + */ + public Set> entrySet() + { + return map.entrySet(); + } + + /** + * Checks to see if two Attributes are equal. The supplied object must be + * a real instance of Attributes and contain the same attribute name/value + * pairs. + * + * @param o another Attribute object which should be checked for equality + * @return true if the object is an instance of Attributes and contains the + * same name/value pairs, false otherwise + */ + public boolean equals(Object o) + { + // quick and dirty check + if (this == o) + return true; + + try + { + return map.equals(((Attributes) o).map); + } + catch (ClassCastException cce) + { + return false; + } + catch (NullPointerException npe) + { + return false; + } + } + + /** + * Gets the value of a specified attribute name. + * XXX - what if the object is a String? + * + * @param attrName the name of the attribute we want the value of + * @return the value of the specified attribute name or null when there is + * no such attribute name + */ + public Object get(Object attrName) + { + return map.get(attrName); + } + + /** + * Returns the hashcode of the attribute name/value map. + */ + public int hashCode() + { + return map.hashCode(); + } + + /** + * Returns true if there are no attributes set, false otherwise. + */ + public boolean isEmpty() + { + return map.isEmpty(); + } + + /** + * Gives a Set of all the values of defined attribute names. + */ + public Set keySet() + { + return map.keySet(); + } + + /** + * Adds or replaces a attribute name/value pair. + * XXX - What if the name is a string? What if the name is neither a Name + * nor a String? What if the value is not a string? + * + * @param name the name of the attribute + * @param value the (new) value of the attribute + * @return the old value of the attribute or null when there was no old + * attribute with this name + */ + public Object put(Object name, Object value) + { + return map.put(name, value); + } + + /** + * Adds or replaces all attribute name/value pairs from another + * Attributes object to this one. The supplied Map must be an instance of + * Attributes. + * + * @param attr the Attributes object to merge with this one + * @exception ClassCastException if the supplied map is not an instance of + * Attributes + */ + public void putAll(Map attr) + { + if (!(attr instanceof Attributes)) + { + throw new + ClassCastException("Supplied Map is not an instance of Attributes"); + } + map.putAll(attr); + } + + /** + * Remove a attribute name/value pair. + * XXX - What if the name is a String? + * + * @param name the name of the attribute name/value pair to remove + * @return the old value of the attribute or null if the attribute didn't + * exist + */ + public Object remove(Object name) + { + return map.remove(name); + } + + /** + * Returns the number of defined attribute name/value pairs. + */ + public int size() + { + return map.size(); + } + + /** + * Returns all the values of the defined attribute name/value pairs as a + * Collection. + */ + public Collection values() + { + return map.values(); + } +} diff --git a/libjava/classpath/java/util/jar/JarEntry.java b/libjava/classpath/java/util/jar/JarEntry.java new file mode 100644 index 000000000..52cb2c31c --- /dev/null +++ b/libjava/classpath/java/util/jar/JarEntry.java @@ -0,0 +1,172 @@ +/* JarEntry.java - Represents an entry in a jar file + Copyright (C) 2000, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.util.Set; +import java.util.zip.ZipEntry; + +/** + * Extension to a ZipEntry that contains manifest attributes and certificates. + * Both the Atrributes and the Certificates can be null when not set. + * Note that the getCertificates() method only returns a + * valid value after all of the data of the entry has been read. + *

        + * There are no public methods to set the attributes or certificate of an + * Entru. Only JarEntries created by the classes in java.util.jar + * will have these properties set. + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ + +public class JarEntry extends ZipEntry +{ + // (Package local) fields + + Attributes attr; + JarFile jarfile; + + // Constructors + + /** + * Creates a new JarEntry with the specified name and no attributes or + * or certificates. Calls super(name) so all other (zip)entry + * fields are null or -1. + * + * @param name the name of the new jar entry + * @exception NullPointerException when the supplied name is null + * @exception IllegalArgumentException when the supplied name is longer + * than 65535 bytes + */ + public JarEntry(String name) throws NullPointerException, + IllegalArgumentException + { + super(name); + attr = null; + jarfile = null; + } + + /** + * Creates a new JarEntry with the specified ZipEntry as template for + * all properties of the entry. Both attributes and certificates will be + * null. + * + * @param entry the ZipEntry whose fields should be copied + */ + public JarEntry(ZipEntry entry) + { + super(entry); + attr = null; + jarfile = null; + } + + /** + * Creates a new JarEntry with the specified JarEntry as template for + * all properties of the entry. + * + * @param entry the jarEntry whose fields should be copied + */ + public JarEntry(JarEntry entry) + { + super(entry); + try + { + attr = entry.getAttributes(); + } + catch (IOException _) + { + } + jarfile = entry.jarfile; + } + + // Methods + + /** + * Returns a copy of the Attributes set for this entry. + * When no Attributes are set in the manifest null is returned. + * + * @return a copy of the Attributes set for this entry + * @exception IOException This will never be thrown. It is here for + * binary compatibility. + */ + public Attributes getAttributes() throws IOException + { + if (attr != null) + { + return (Attributes) attr.clone(); + } + else + { + return null; + } + } + + /** + * Returns a copy of the certificates set for this entry. + * When no certificates are set or when not all data of this entry has + * been read null is returned. + *

        + * To make sure that this call returns a valid value you must read all + * data from the JarInputStream for this entry. + * When you don't need the data for an entry but want to know the + * certificates that are set for the entry then you can skip all data by + * calling skip(entry.getSize()) on the JarInputStream for + * the entry. + * + * @return a copy of the certificates set for this entry + */ + public Certificate[] getCertificates() + { + if (jarfile != null) + { + synchronized (jarfile) + { + if (jarfile.entryCerts != null) + { + Set certs = (Set) jarfile.entryCerts.get(getName()); + if (certs != null + && jarfile.verified.get(getName()) == Boolean.TRUE) + return (Certificate[]) certs.toArray(new Certificate[certs.size()]); + } + } + } + return null; + } +} diff --git a/libjava/classpath/java/util/jar/JarException.java b/libjava/classpath/java/util/jar/JarException.java new file mode 100644 index 000000000..d6f0634fe --- /dev/null +++ b/libjava/classpath/java/util/jar/JarException.java @@ -0,0 +1,77 @@ +/* JarException.java -- thrown to indicate an problem with a jar file + Copyright (C) 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.util.zip.ZipException; + +/** + * This exception is thrown to indicate an problem with a jar file. + * Note that none of the methods in the java.util.jar package actually declare + * to throw this exception, most just declare that they throw an IOException + * which is super class of JarException. + * + * @author Mark Wielaard (mark@klomp.org) + * @since 1.2 + */ +public class JarException extends ZipException +{ + /** + * Compatible with JDK 1.2+. + */ + private static final long serialVersionUID = 7159778400963954473L; + + /** + * Create a new JarException without a descriptive error message. + */ + public JarException() + { + } + + /** + * Create a new JarException with a descriptive error message indicating + * what went wrong. This message can later be retrieved by calling the + * getMessage() method. + * + * @param message The descriptive error message + * @see #getMessage() + */ + public JarException(String message) + { + super(message); + } +} diff --git a/libjava/classpath/java/util/jar/JarFile.java b/libjava/classpath/java/util/jar/JarFile.java new file mode 100644 index 000000000..b67c95346 --- /dev/null +++ b/libjava/classpath/java/util/jar/JarFile.java @@ -0,0 +1,981 @@ +/* JarFile.java - Representation of a jar file + Copyright (C) 2000, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.jar; + +import gnu.java.io.Base64InputStream; +import gnu.java.security.OID; +import gnu.java.security.pkcs.PKCS7SignedData; +import gnu.java.security.pkcs.SignerInfo; +import gnu.java.security.provider.Gnu; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CRLException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +/** + * Representation of a jar file. + *

        + * Note that this class is not a subclass of java.io.File but a subclass of + * java.util.zip.ZipFile and you can only read JarFiles with it (although + * there are constructors that take a File object). + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + * @author Casey Marshall (csm@gnu.org) wrote the certificate and entry + * verification code. + */ +public class JarFile extends ZipFile +{ + // Fields + + /** The name of the manifest entry: META-INF/MANIFEST.MF */ + public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; + + /** The META-INF directory entry. */ + private static final String META_INF = "META-INF/"; + + /** The suffix for PKCS7 DSA signature entries. */ + private static final String PKCS7_DSA_SUFFIX = ".DSA"; + + /** The suffix for PKCS7 RSA signature entries. */ + private static final String PKCS7_RSA_SUFFIX = ".RSA"; + + /** The suffix for digest attributes. */ + private static final String DIGEST_KEY_SUFFIX = "-Digest"; + + /** The suffix for signature files. */ + private static final String SF_SUFFIX = ".SF"; + + /** + * The security provider to use for signature verification. + * We need a known fallback to be able to read any signed jar file + * (which might contain the user selected security provider). + * This is package-private to avoid accessor methods for inner classes. + */ + static final Gnu provider = new Gnu(); + + // Signature OIDs. + private static final OID MD2_OID = new OID("1.2.840.113549.2.2"); + private static final OID MD4_OID = new OID("1.2.840.113549.2.4"); + private static final OID MD5_OID = new OID("1.2.840.113549.2.5"); + private static final OID SHA1_OID = new OID("1.3.14.3.2.26"); + private static final OID DSA_ENCRYPTION_OID = new OID("1.2.840.10040.4.1"); + private static final OID RSA_ENCRYPTION_OID = new OID("1.2.840.113549.1.1.1"); + + /** + * The manifest of this file, if any, otherwise null. + * Read when first needed. + */ + private Manifest manifest; + + /** Whether to verify the manifest and all entries. */ + boolean verify; + + /** Whether the has already been loaded. */ + private boolean manifestRead = false; + + /** Whether the signature files have been loaded. */ + boolean signaturesRead = false; + + /** + * A map between entry names and booleans, signaling whether or + * not that entry has been verified. + * Only be accessed with lock on this JarFile*/ + HashMap verified = new HashMap(); + + /** + * A mapping from entry name to certificates, if any. + * Only accessed with lock on this JarFile. + */ + HashMap entryCerts; + + /** + * A {@link Map} of message digest algorithm names to their implementation. + * Used to reduce object (algorithm implementation) instantiation. + */ + private HashMap digestAlgorithms = new HashMap(); + + static boolean DEBUG = false; + static void debug(Object msg) + { + System.err.print(JarFile.class.getName()); + System.err.print(" >>> "); + System.err.println(msg); + } + + // Constructors + + /** + * Creates a new JarFile. All jar entries are verified (when a Manifest file + * for this JarFile exists). You need to actually open and read the complete + * jar entry (with getInputStream()) to check its signature. + * + * @param fileName the name of the file to open + * @exception FileNotFoundException if the fileName cannot be found + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(String fileName) throws FileNotFoundException, IOException + { + this(fileName, true); + } + + /** + * Creates a new JarFile. If verify is true then all jar entries are + * verified (when a Manifest file for this JarFile exists). You need to + * actually open and read the complete jar entry + * (with getInputStream()) to check its signature. + * + * @param fileName the name of the file to open + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @exception FileNotFoundException if the fileName cannot be found + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(String fileName, boolean verify) throws + FileNotFoundException, IOException + { + super(fileName); + if (verify) + { + manifest = readManifest(); + verify(); + } + } + + /** + * Creates a new JarFile. All jar entries are verified (when a Manifest file + * for this JarFile exists). You need to actually open and read the complete + * jar entry (with getInputStream()) to check its signature. + * + * @param file the file to open as a jar file + * @exception FileNotFoundException if the file does not exits + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(File file) throws FileNotFoundException, IOException + { + this(file, true); + } + + /** + * Creates a new JarFile. If verify is true then all jar entries are + * verified (when a Manifest file for this JarFile exists). You need to + * actually open and read the complete jar entry + * (with getInputStream()) to check its signature. + * + * @param file the file to open to open as a jar file + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @exception FileNotFoundException if file does not exist + * @exception IOException if another IO exception occurs while reading + */ + public JarFile(File file, boolean verify) throws FileNotFoundException, + IOException + { + super(file); + if (verify) + { + manifest = readManifest(); + verify(); + } + } + + /** + * Creates a new JarFile with the indicated mode. If verify is true then + * all jar entries are verified (when a Manifest file for this JarFile + * exists). You need to actually open and read the complete jar entry + * (with getInputStream()) to check its signature. + * manifest and if the manifest exists and verify is true verfies it. + * + * @param file the file to open to open as a jar file + * @param verify checks manifest and entries when true and a manifest + * exists, when false no checks are made + * @param mode either ZipFile.OPEN_READ or + * (ZipFile.OPEN_READ | ZipFile.OPEN_DELETE) + * @exception FileNotFoundException if the file does not exist + * @exception IOException if another IO exception occurs while reading + * @exception IllegalArgumentException when given an illegal mode + * + * @since 1.3 + */ + public JarFile(File file, boolean verify, int mode) throws + FileNotFoundException, IOException, IllegalArgumentException + { + super(file, mode); + if (verify) + { + manifest = readManifest(); + verify(); + } + } + + // Methods + + /** + * XXX - should verify the manifest file + */ + private void verify() + { + // only check if manifest is not null + if (manifest == null) + { + verify = false; + return; + } + + verify = true; + // XXX - verify manifest + } + + /** + * Parses and returns the manifest if it exists, otherwise returns null. + */ + private Manifest readManifest() + { + try + { + ZipEntry manEntry = super.getEntry(MANIFEST_NAME); + if (manEntry != null) + { + InputStream in = super.getInputStream(manEntry); + manifestRead = true; + return new Manifest(in); + } + else + { + manifestRead = true; + return null; + } + } + catch (IOException ioe) + { + manifestRead = true; + return null; + } + } + + /** + * Returns a enumeration of all the entries in the JarFile. + * Note that also the Jar META-INF entries are returned. + * + * @exception IllegalStateException when the JarFile is already closed + */ + public Enumeration entries() throws IllegalStateException + { + return new JarEnumeration(super.entries(), this); + } + + /** + * Wraps a given Zip Entries Enumeration. For every zip entry a + * JarEntry is created and the corresponding Attributes are looked up. + */ + private static class JarEnumeration implements Enumeration + { + + private final Enumeration entries; + private final JarFile jarfile; + + JarEnumeration(Enumeration e, JarFile f) + { + entries = e; + jarfile = f; + } + + public boolean hasMoreElements() + { + return entries.hasMoreElements(); + } + + public JarEntry nextElement() + { + ZipEntry zip = (ZipEntry) entries.nextElement(); + JarEntry jar = new JarEntry(zip); + Manifest manifest; + try + { + manifest = jarfile.getManifest(); + } + catch (IOException ioe) + { + manifest = null; + } + + if (manifest != null) + { + jar.attr = manifest.getAttributes(jar.getName()); + } + + synchronized(jarfile) + { + if (jarfile.verify && !jarfile.signaturesRead) + try + { + jarfile.readSignatures(); + } + catch (IOException ioe) + { + if (JarFile.DEBUG) + { + JarFile.debug(ioe); + ioe.printStackTrace(); + } + jarfile.signaturesRead = true; // fudge it. + } + } + jar.jarfile = jarfile; + return jar; + } + } + + /** + * XXX + * It actually returns a JarEntry not a zipEntry + * @param name XXX + */ + public synchronized ZipEntry getEntry(String name) + { + ZipEntry entry = super.getEntry(name); + if (entry != null) + { + JarEntry jarEntry = new JarEntry(entry); + Manifest manifest; + try + { + manifest = getManifest(); + } + catch (IOException ioe) + { + manifest = null; + } + + if (manifest != null) + { + jarEntry.attr = manifest.getAttributes(name); + } + + if (verify && !signaturesRead) + try + { + readSignatures(); + } + catch (IOException ioe) + { + if (DEBUG) + { + debug(ioe); + ioe.printStackTrace(); + } + signaturesRead = true; + } + jarEntry.jarfile = this; + return jarEntry; + } + return null; + } + + /** + * Returns an input stream for the given entry. If configured to + * verify entries, the input stream returned will verify them while + * the stream is read, but only on the first time. + * + * @param entry The entry to get the input stream for. + * @exception ZipException XXX + * @exception IOException XXX + */ + public synchronized InputStream getInputStream(ZipEntry entry) throws + ZipException, IOException + { + // If we haven't verified the hash, do it now. + if (!verified.containsKey(entry.getName()) && verify) + { + if (DEBUG) + debug("reading and verifying " + entry); + return new EntryInputStream(entry, super.getInputStream(entry), this); + } + else + { + if (DEBUG) + debug("reading already verified entry " + entry); + if (verify && verified.get(entry.getName()) == Boolean.FALSE) + throw new ZipException("digest for " + entry + " is invalid"); + return super.getInputStream(entry); + } + } + + /** + * Returns the JarEntry that belongs to the name if such an entry + * exists in the JarFile. Returns null otherwise + * Convenience method that just casts the result from getEntry + * to a JarEntry. + * + * @param name the jar entry name to look up + * @return the JarEntry if it exists, null otherwise + */ + public JarEntry getJarEntry(String name) + { + return (JarEntry) getEntry(name); + } + + /** + * Returns the manifest for this JarFile or null when the JarFile does not + * contain a manifest file. + */ + public synchronized Manifest getManifest() throws IOException + { + if (!manifestRead) + manifest = readManifest(); + + return manifest; + } + + // Only called with lock on this JarFile. + // Package private for use in inner classes. + void readSignatures() throws IOException + { + Map pkcs7Dsa = new HashMap(); + Map pkcs7Rsa = new HashMap(); + Map sigFiles = new HashMap(); + + // Phase 1: Read all signature files. These contain the user + // certificates as well as the signatures themselves. + for (Enumeration e = super.entries(); e.hasMoreElements(); ) + { + ZipEntry ze = (ZipEntry) e.nextElement(); + String name = ze.getName(); + if (name.startsWith(META_INF)) + { + String alias = name.substring(META_INF.length()); + if (alias.lastIndexOf('.') >= 0) + alias = alias.substring(0, alias.lastIndexOf('.')); + + if (name.endsWith(PKCS7_DSA_SUFFIX) || name.endsWith(PKCS7_RSA_SUFFIX)) + { + if (DEBUG) + debug("reading PKCS7 info from " + name + ", alias=" + alias); + PKCS7SignedData sig = null; + try + { + sig = new PKCS7SignedData(super.getInputStream(ze)); + } + catch (CertificateException ce) + { + IOException ioe = new IOException("certificate parsing error"); + ioe.initCause(ce); + throw ioe; + } + catch (CRLException crle) + { + IOException ioe = new IOException("CRL parsing error"); + ioe.initCause(crle); + throw ioe; + } + if (name.endsWith(PKCS7_DSA_SUFFIX)) + pkcs7Dsa.put(alias, sig); + else if (name.endsWith(PKCS7_RSA_SUFFIX)) + pkcs7Rsa.put(alias, sig); + } + else if (name.endsWith(SF_SUFFIX)) + { + if (DEBUG) + debug("reading signature file for " + alias + ": " + name); + Manifest sf = new Manifest(super.getInputStream(ze)); + sigFiles.put(alias, sf); + if (DEBUG) + debug("result: " + sf); + } + } + } + + // Phase 2: verify the signatures on any signature files. + Set validCerts = new HashSet(); + Map entryCerts = new HashMap(); + for (Iterator it = sigFiles.entrySet().iterator(); it.hasNext(); ) + { + int valid = 0; + Map.Entry e = (Map.Entry) it.next(); + String alias = (String) e.getKey(); + + PKCS7SignedData sig = (PKCS7SignedData) pkcs7Dsa.get(alias); + if (sig != null) + { + Certificate[] certs = sig.getCertificates(); + Set signerInfos = sig.getSignerInfos(); + for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); ) + verify(certs, (SignerInfo) it2.next(), alias, validCerts); + } + + sig = (PKCS7SignedData) pkcs7Rsa.get(alias); + if (sig != null) + { + Certificate[] certs = sig.getCertificates(); + Set signerInfos = sig.getSignerInfos(); + for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); ) + verify(certs, (SignerInfo) it2.next(), alias, validCerts); + } + + // It isn't a signature for anything. Punt it. + if (validCerts.isEmpty()) + { + it.remove(); + continue; + } + + entryCerts.put(e.getValue(), new HashSet(validCerts)); + validCerts.clear(); + } + + // Read the manifest into a HashMap (String fileName, String entry) + // The fileName might be split into multiple lines in the manifest. + // Such additional lines will start with a space. + InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME)); + ByteArrayOutputStream baStream = new ByteArrayOutputStream(); + byte[] ba = new byte[1024]; + while (true) + { + int len = in.read(ba); + if (len < 0) + break; + baStream.write(ba, 0, len); + } + in.close(); + + HashMap hmManifestEntries = new HashMap(); + Pattern p = Pattern.compile("Name: (.+?\r?\n(?: .+?\r?\n)*)" + + ".+?-Digest: .+?\r?\n\r?\n"); + Matcher m = p.matcher(baStream.toString()); + while (m.find()) + { + String fileName = m.group(1).replaceAll("\r?\n ?", ""); + hmManifestEntries.put(fileName, m.group()); + } + + // Phase 3: verify the signature file signatures against the manifest, + // mapping the entry name to the target certificates. + this.entryCerts = new HashMap(); + for (Iterator it = entryCerts.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry e = (Map.Entry) it.next(); + Manifest sigfile = (Manifest) e.getKey(); + Map entries = sigfile.getEntries(); + Set certificates = (Set) e.getValue(); + + for (Iterator it2 = entries.entrySet().iterator(); it2.hasNext(); ) + { + Map.Entry e2 = (Map.Entry) it2.next(); + String entryname = String.valueOf(e2.getKey()); + Attributes attr = (Attributes) e2.getValue(); + if (verifyHashes(entryname, attr, hmManifestEntries)) + { + if (DEBUG) + debug("entry " + entryname + " has certificates " + certificates); + Set s = (Set) this.entryCerts.get(entryname); + if (s != null) + s.addAll(certificates); + else + this.entryCerts.put(entryname, new HashSet(certificates)); + } + } + } + + signaturesRead = true; + } + + /** + * Tell if the given signer info is over the given alias's signature file, + * given one of the certificates specified. + */ + private void verify(Certificate[] certs, SignerInfo signerInfo, + String alias, Set validCerts) + { + Signature sig = null; + try + { + OID alg = signerInfo.getDigestEncryptionAlgorithmId(); + if (alg.equals(DSA_ENCRYPTION_OID)) + { + if (!signerInfo.getDigestAlgorithmId().equals(SHA1_OID)) + return; + sig = Signature.getInstance("SHA1withDSA", provider); + } + else if (alg.equals(RSA_ENCRYPTION_OID)) + { + OID hash = signerInfo.getDigestAlgorithmId(); + if (hash.equals(MD2_OID)) + sig = Signature.getInstance("md2WithRsaEncryption", provider); + else if (hash.equals(MD4_OID)) + sig = Signature.getInstance("md4WithRsaEncryption", provider); + else if (hash.equals(MD5_OID)) + sig = Signature.getInstance("md5WithRsaEncryption", provider); + else if (hash.equals(SHA1_OID)) + sig = Signature.getInstance("sha1WithRsaEncryption", provider); + else + return; + } + else + { + if (DEBUG) + debug("unsupported signature algorithm: " + alg); + return; + } + } + catch (NoSuchAlgorithmException nsae) + { + if (DEBUG) + { + debug(nsae); + nsae.printStackTrace(); + } + return; + } + ZipEntry sigFileEntry = super.getEntry(META_INF + alias + SF_SUFFIX); + if (sigFileEntry == null) + return; + for (int i = 0; i < certs.length; i++) + { + if (!(certs[i] instanceof X509Certificate)) + continue; + X509Certificate cert = (X509Certificate) certs[i]; + if (!cert.getIssuerX500Principal().equals(signerInfo.getIssuer()) || + !cert.getSerialNumber().equals(signerInfo.getSerialNumber())) + continue; + try + { + sig.initVerify(cert.getPublicKey()); + InputStream in = super.getInputStream(sigFileEntry); + if (in == null) + continue; + byte[] buf = new byte[1024]; + int len = 0; + while ((len = in.read(buf)) != -1) + sig.update(buf, 0, len); + if (sig.verify(signerInfo.getEncryptedDigest())) + { + if (DEBUG) + debug("signature for " + cert.getSubjectDN() + " is good"); + validCerts.add(cert); + } + } + catch (IOException ioe) + { + continue; + } + catch (InvalidKeyException ike) + { + continue; + } + catch (SignatureException se) + { + continue; + } + } + } + + /** + * Verifies that the digest(s) in a signature file were, in fact, made over + * the manifest entry for ENTRY. + * + * @param entry The entry name. + * @param attr The attributes from the signature file to verify. + * @param hmManifestEntries Mappings of Jar file entry names to their manifest + * entry text; i.e. the base-64 encoding of their + */ + private boolean verifyHashes(String entry, Attributes attr, + HashMap hmManifestEntries) + { + int verified = 0; + + String stringEntry = (String) hmManifestEntries.get(entry); + if (stringEntry == null) + { + if (DEBUG) + debug("could not find " + entry + " in manifest"); + return false; + } + // The bytes for ENTRY's manifest entry, which are signed in the + // signature file. + byte[] entryBytes = stringEntry.getBytes(); + + for (Iterator it = attr.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry e = (Map.Entry) it.next(); + String key = String.valueOf(e.getKey()); + if (!key.endsWith(DIGEST_KEY_SUFFIX)) + continue; + String alg = key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length()); + try + { + byte[] hash = Base64InputStream.decode((String) e.getValue()); + MessageDigest md = (MessageDigest) digestAlgorithms.get(alg); + if (md == null) + { + md = MessageDigest.getInstance(alg, provider); + digestAlgorithms.put(alg, md); + } + md.reset(); + byte[] hash2 = md.digest(entryBytes); + if (DEBUG) + debug("verifying SF entry " + entry + " alg: " + md.getAlgorithm() + + " expect=" + new java.math.BigInteger(hash).toString(16) + + " comp=" + new java.math.BigInteger(hash2).toString(16)); + if (!Arrays.equals(hash, hash2)) + return false; + verified++; + } + catch (IOException ioe) + { + if (DEBUG) + { + debug(ioe); + ioe.printStackTrace(); + } + return false; + } + catch (NoSuchAlgorithmException nsae) + { + if (DEBUG) + { + debug(nsae); + nsae.printStackTrace(); + } + return false; + } + } + + // We have to find at least one valid digest. + return verified > 0; + } + + /** + * A utility class that verifies jar entries as they are read. + */ + private static class EntryInputStream extends FilterInputStream + { + private final JarFile jarfile; + private final long length; + private long pos; + private final ZipEntry entry; + private final byte[][] hashes; + private final MessageDigest[] md; + private boolean checked; + + EntryInputStream(final ZipEntry entry, + final InputStream in, + final JarFile jar) + throws IOException + { + super(in); + this.entry = entry; + this.jarfile = jar; + + length = entry.getSize(); + pos = 0; + checked = false; + + Attributes attr; + Manifest manifest = jarfile.getManifest(); + if (manifest != null) + attr = manifest.getAttributes(entry.getName()); + else + attr = null; + if (DEBUG) + debug("verifying entry " + entry + " attr=" + attr); + if (attr == null) + { + hashes = new byte[0][]; + md = new MessageDigest[0]; + } + else + { + List hashes = new LinkedList(); + List md = new LinkedList(); + for (Iterator it = attr.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry e = (Map.Entry) it.next(); + String key = String.valueOf(e.getKey()); + if (key == null) + continue; + if (!key.endsWith(DIGEST_KEY_SUFFIX)) + continue; + hashes.add(Base64InputStream.decode((String) e.getValue())); + try + { + int length = key.length() - DIGEST_KEY_SUFFIX.length(); + String alg = key.substring(0, length); + md.add(MessageDigest.getInstance(alg, provider)); + } + catch (NoSuchAlgorithmException nsae) + { + IOException ioe = new IOException("no such message digest: " + key); + ioe.initCause(nsae); + throw ioe; + } + } + if (DEBUG) + debug("digests=" + md); + this.hashes = (byte[][]) hashes.toArray(new byte[hashes.size()][]); + this.md = (MessageDigest[]) md.toArray(new MessageDigest[md.size()]); + } + } + + public boolean markSupported() + { + return false; + } + + public void mark(int readLimit) + { + } + + public void reset() + { + } + + public int read() throws IOException + { + int b = super.read(); + if (b == -1) + { + eof(); + return -1; + } + for (int i = 0; i < md.length; i++) + md[i].update((byte) b); + pos++; + if (length > 0 && pos >= length) + eof(); + return b; + } + + public int read(byte[] buf, int off, int len) throws IOException + { + int count = super.read(buf, off, (int) Math.min(len, (length != 0 + ? length - pos + : Integer.MAX_VALUE))); + if (count == -1 || (length > 0 && pos >= length)) + { + eof(); + return -1; + } + for (int i = 0; i < md.length; i++) + md[i].update(buf, off, count); + pos += count; + if (length != 0 && pos >= length) + eof(); + return count; + } + + public int read(byte[] buf) throws IOException + { + return read(buf, 0, buf.length); + } + + public long skip(long bytes) throws IOException + { + byte[] b = new byte[1024]; + long amount = 0; + while (amount < bytes) + { + int l = read(b, 0, (int) Math.min(b.length, bytes - amount)); + if (l == -1) + break; + amount += l; + } + return amount; + } + + private void eof() throws IOException + { + if (checked) + return; + checked = true; + for (int i = 0; i < md.length; i++) + { + byte[] hash = md[i].digest(); + if (DEBUG) + debug("verifying " + md[i].getAlgorithm() + " expect=" + + new java.math.BigInteger(hashes[i]).toString(16) + + " comp=" + new java.math.BigInteger(hash).toString(16)); + if (!Arrays.equals(hash, hashes[i])) + { + synchronized(jarfile) + { + if (DEBUG) + debug(entry + " could NOT be verified"); + jarfile.verified.put(entry.getName(), Boolean.FALSE); + } + return; + // XXX ??? what do we do here? + // throw new ZipException("message digest mismatch"); + } + } + + synchronized(jarfile) + { + if (DEBUG) + debug(entry + " has been VERIFIED"); + jarfile.verified.put(entry.getName(), Boolean.TRUE); + } + } + } +} diff --git a/libjava/classpath/java/util/jar/JarInputStream.java b/libjava/classpath/java/util/jar/JarInputStream.java new file mode 100644 index 000000000..4de6609a5 --- /dev/null +++ b/libjava/classpath/java/util/jar/JarInputStream.java @@ -0,0 +1,200 @@ +/* JarInputStream.java - InputStream for reading jar files + Copyright (C) 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * InputStream for reading jar files. + * XXX - verification of the signatures in the Manifest file is not yet + * implemented. + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ + +public class JarInputStream extends ZipInputStream +{ + // Fields + + /** The manifest for this file or null when there was no manifest. */ + private Manifest manifest; + + /** The first real JarEntry for this file. Used by readManifest() to store + an entry that isn't the manifest but that should be returned by + getNextEntry next time it is called. Null when no firstEntry was read + while searching for the manifest entry, or when it has already been + returned by getNextEntry(). */ + private JarEntry firstEntry; + + // Constructors + + /** + * Creates a new JarInputStream and tries to read the manifest. + * If such a manifest is present the JarInputStream tries to verify all + * the entry signatures while reading. + * + * @param in InputStream to read the jar from + * @exception IOException when an error occurs when opening or reading + */ + public JarInputStream(InputStream in) throws IOException + { + this(in, true); + } + + /** + * Creates a new JarInputStream and tries to read the manifest. + * If such a manifest is present and verify is true, the JarInputStream + * tries to verify all the entry signatures while reading. + * + * @param in InputStream to read the jar from + * @param verify whether or not to verify the manifest entries + * @exception IOException when an error occurs when opening or reading + */ + public JarInputStream(InputStream in, boolean verify) throws IOException + { + super(in); + readManifest(verify); + } + + // Methods + + /** + * Set the manifest if found. Skips all entries that start with "META-INF/" + * + * @param verify when true (and a Manifest is found) checks the Manifest, + * when false no check is performed + * @exception IOException if an error occurs while reading + */ + private void readManifest(boolean verify) throws IOException + { + firstEntry = (JarEntry) super.getNextEntry(); + while ((firstEntry != null) && + firstEntry.getName().startsWith("META-INF/")) + { + if (firstEntry.getName().equals(JarFile.MANIFEST_NAME)) + { + manifest = new Manifest(this); + } + firstEntry = (JarEntry) super.getNextEntry(); + } + + if (verify) + { + // XXX + } + } + + /** + * Creates a JarEntry for a particular name and consults the manifest + * for the Attributes of the entry. + * Used by ZipEntry.getNextEntry() + * + * @param name the name of the new entry + */ + protected ZipEntry createZipEntry(String name) + { + ZipEntry zipEntry = super.createZipEntry(name); + JarEntry jarEntry = new JarEntry(zipEntry); + if (manifest != null) + { + jarEntry.attr = manifest.getAttributes(name); + } + return jarEntry; + } + + /** + * Returns the Manifest for the jar file or null if there was no Manifest. + */ + public Manifest getManifest() + { + return manifest; + } + + /** + * Returns the next entry or null when there are no more entries. + * Does actually return a JarEntry, if you don't want to cast it yourself + * use getNextJarEntry(). Does not return any entries found + * at the beginning of the ZipFile that are special + * (those that start with "META-INF/"). + * + * @exception IOException if an IO error occurs when reading the entry + */ + public ZipEntry getNextEntry() throws IOException + { + ZipEntry entry; + if (firstEntry != null) + { + entry = firstEntry; + firstEntry = null; + } + else + { + entry = super.getNextEntry(); + } + return entry; + } + + /** + * Returns the next jar entry or null when there are no more entries. + * + * @exception IOException if an IO error occurs when reading the entry + */ + public JarEntry getNextJarEntry() throws IOException + { + return (JarEntry) getNextEntry(); + } + + /** + * XXX + * + * @param buf XXX + * @param off XXX + * @param len XXX + * @return XXX + * @exception IOException XXX + */ + public int read(byte[]buf, int off, int len) throws IOException + { + // XXX if (verify) {} + return super.read(buf, off, len); + } +} diff --git a/libjava/classpath/java/util/jar/JarOutputStream.java b/libjava/classpath/java/util/jar/JarOutputStream.java new file mode 100644 index 000000000..0ba6002cb --- /dev/null +++ b/libjava/classpath/java/util/jar/JarOutputStream.java @@ -0,0 +1,113 @@ +/* JarOutputStream.java - OutputStream for writing jar files + Copyright (C) 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * OutputStream for writing jar files. + * A special ZipOutputStream that can take JarEntries and can have a optional + * Manifest as first entry. + * + * @author Mark Wielaard (mark@klomp.org) + */ + +public class JarOutputStream extends ZipOutputStream +{ + // Constructors + + /** + * Creates a new JarOutputStream without a manifest entry. + * + * @param out the stream to create the new jar on + * @exception IOException if something unexpected happend + */ + public JarOutputStream(OutputStream out) throws IOException + { + this(out, null); + } + + /** + * Creates a new JarOutputStream with a manifest entry. + * The manifest will be the first entry in the jar. + * + * @param out the stream to create the new jar on + * @param man the manifest that should be put in the jar file or null + * for no manifest entry + * @exception IOException if something unexpected happend + */ + public JarOutputStream(OutputStream out, Manifest man) throws IOException + { + super(out); + if (man != null) + writeManifest(man); + } + + // Methods + + /** + * Writes the manifest to a new JarEntry in this JarOutputStream with as + * name JarFile.MANIFEST_NAME. + * + * @param manifest the non null manifest to be written + * @exception IOException if something unexpected happend + */ + private void writeManifest(Manifest manifest) throws IOException + { + // Create a new Jar Entry for the Manifest + JarEntry entry = new JarEntry(JarFile.MANIFEST_NAME); + putNextEntry(entry); + manifest.write(this); + closeEntry(); + } + + /** + * Prepares the JarOutputStream for writing the next entry. + * This implementation just calls super.putNextEntry(). + * + * @param entry The information for the next entry + * @exception IOException when some unexpected I/O exception occurred + */ + public void putNextEntry(ZipEntry entry) throws IOException + { + super.putNextEntry(entry); // XXX + } +} diff --git a/libjava/classpath/java/util/jar/Manifest.java b/libjava/classpath/java/util/jar/Manifest.java new file mode 100644 index 000000000..f266d823e --- /dev/null +++ b/libjava/classpath/java/util/jar/Manifest.java @@ -0,0 +1,213 @@ +/* Manifest.java -- Reads, writes and manipulates jar manifest files + Copyright (C) 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.jar; + +import gnu.java.util.jar.JarUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Hashtable; +import java.util.Map; + +/** + * Reads, writes and manipulaties jar manifest files. + * XXX + * + * @since 1.2 + * @author Mark Wielaard (mark@klomp.org) + */ +public class Manifest implements Cloneable +{ + // Fields + + /** The main attributes of the manifest (jar file). */ + private final Attributes mainAttr; + + /** A map of atrributes for all entries described in this Manifest. */ + private final Map entries; + + // Constructors + + /** + * Creates a new empty Manifest. + */ + public Manifest() + { + mainAttr = new Attributes(); + entries = new Hashtable(); + } + + /** + * Creates a Manifest from the supplied input stream. + * + * @see #read(InputStream) + * @see #write(OutputStream) + * + * @param in the input stream to read the manifest from + * @exception IOException when an i/o exception occurs or the input stream + * does not describe a valid manifest + */ + public Manifest(InputStream in) throws IOException + { + this(); + read(in); + } + + /** + * Creates a Manifest from another Manifest. + * Makes a deep copy of the main attributes, but a shallow copy of + * the other entries. This means that you can freely add, change or remove + * the main attributes or the entries of the new manifest without effecting + * the original manifest, but adding, changing or removing attributes from + * a particular entry also changes the attributes of that entry in the + * original manifest. + * + * @see #clone() + * @param man the Manifest to copy from + */ + public Manifest(Manifest man) + { + mainAttr = new Attributes(man.getMainAttributes()); + entries = new Hashtable(man.getEntries()); + } + + // Methods + + /** + * Gets the main attributes of this Manifest. + */ + public Attributes getMainAttributes() + { + return mainAttr; + } + + /** + * Gets a map of entry Strings to Attributes for all the entries described + * in this manifest. Adding, changing or removing from this entries map + * changes the entries of this manifest. + */ + public Map getEntries() + { + return entries; + } + + /** + * Returns the Attributes associated with the Entry. + *

        + * Implemented as: + * return (Attributes)getEntries().get(entryName) + * + * @param entryName the name of the entry to look up + * @return the attributes associated with the entry or null when none + */ + public Attributes getAttributes(String entryName) + { + return getEntries().get(entryName); + } + + /** + * Clears the main attributes and removes all the entries from the + * manifest. + */ + public void clear() + { + mainAttr.clear(); + entries.clear(); + } + + /** + * Read and merge a Manifest from the designated input stream. + * + * @param in the input stream to read from. + * @throws IOException if an I/O related exception occurs during the process. + */ + public void read(InputStream in) throws IOException + { + JarUtils.readMFManifest(getMainAttributes(), getEntries(), in); + } + + /** + * Writes the contents of this Manifest to the designated + * output stream. Line-endings are platform-independent and consist of the + * 2-codepoint sequence 0x0D and 0x0A. + * + * @param out the output stream to write this Manifest to. + * @throws IOException if an I/O related exception occurs during the process. + */ + public void write(OutputStream out) throws IOException + { + JarUtils.writeMFManifest(getMainAttributes(), getEntries(), out); + } + + /** + * Makes a deep copy of the main attributes, but a shallow copy of + * the other entries. This means that you can freely add, change or remove + * the main attributes or the entries of the new manifest without effecting + * the original manifest, but adding, changing or removing attributes from + * a particular entry also changes the attributes of that entry in the + * original manifest. Calls new Manifest(this). + */ + public Object clone() + { + return new Manifest(this); + } + + /** + * Checks if another object is equal to this Manifest object. + * Another Object is equal to this Manifest object if it is an instance of + * Manifest and the main attributes and the entries of the other manifest + * are equal to this one. + */ + public boolean equals(Object o) + { + return (o instanceof Manifest) && + (mainAttr.equals(((Manifest) o).mainAttr)) && + (entries.equals(((Manifest) o).entries)); + } + + /** + * Calculates the hash code of the manifest. Implemented by a xor of the + * hash code of the main attributes with the hash code of the entries map. + */ + public int hashCode() + { + return mainAttr.hashCode() ^ entries.hashCode(); + } + +} diff --git a/libjava/classpath/java/util/jar/package.html b/libjava/classpath/java/util/jar/package.html new file mode 100644 index 000000000..7fd87878d --- /dev/null +++ b/libjava/classpath/java/util/jar/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.util.jar + + +

        Utility classes for manipulating java archives +(zip files with a manifest file with attributes).

        + + + diff --git a/libjava/classpath/java/util/logging/ConsoleHandler.java b/libjava/classpath/java/util/logging/ConsoleHandler.java new file mode 100644 index 000000000..9f6bb7240 --- /dev/null +++ b/libjava/classpath/java/util/logging/ConsoleHandler.java @@ -0,0 +1,125 @@ +/* ConsoleHandler.java -- a class for publishing log messages to System.err + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +/** + * A ConsoleHandler publishes log records to + * System.err. + * + *

        Configuration: Values of the subsequent + * LogManager properties are taken into consideration + * when a ConsoleHandler is initialized. + * If a property is not defined, or if it has an invalid + * value, a default is taken without an exception being thrown. + * + *

          + * + *
        • java.util.logging.ConsoleHandler.level - specifies + * the initial severity level threshold. Default value: + * Level.INFO.
        • + * + *
        • java.util.logging.ConsoleHandler.filter - specifies + * the name of a Filter class. Default value: No Filter.
        • + * + *
        • java.util.logging.ConsoleHandler.formatter - specifies + * the name of a Formatter class. Default value: + * java.util.logging.SimpleFormatter.
        • + * + *
        • java.util.logging.ConsoleHandler.encoding - specifies + * the name of the character encoding. Default value: + * the default platform encoding.
        • + * + *
        + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class ConsoleHandler + extends StreamHandler +{ + /** + * Constructs a StreamHandler that publishes + * log records to System.err. The initial + * configuration is determined by the LogManager + * properties described above. + */ + public ConsoleHandler() + { + super(System.err, "java.util.logging.ConsoleHandler", Level.INFO, + /* formatter */ null, SimpleFormatter.class); + } + + + /** + * Forces any data that may have been buffered to the underlying + * output device, but does not close System.err. + * + *

        In case of an I/O failure, the ErrorManager + * of this ConsoleHandler will be informed, but the caller + * of this method will not receive an exception. + */ + public void close() + { + flush(); + } + + + /** + * Publishes a LogRecord to the console, provided the + * record passes all tests for being loggable. + * + *

        Most applications do not need to call this method directly. + * Instead, they will use use a Logger, which will + * create LogRecords and distribute them to registered handlers. + * + *

        In case of an I/O failure, the ErrorManager + * of this SocketHandler will be informed, but the caller + * of this method will not receive an exception. + * + *

        The GNU implementation of ConsoleHandler.publish + * calls flush() for every request to publish a record, so + * they appear immediately on the console. + * + * @param record the log event to be published. + */ + public void publish(LogRecord record) + { + super.publish(record); + flush(); + } +} diff --git a/libjava/classpath/java/util/logging/ErrorManager.java b/libjava/classpath/java/util/logging/ErrorManager.java new file mode 100644 index 000000000..291efb2f1 --- /dev/null +++ b/libjava/classpath/java/util/logging/ErrorManager.java @@ -0,0 +1,193 @@ +/* ErrorManager.java -- + A class for dealing with errors that a Handler encounters + during logging + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +/** + * An ErrorManager deals with errors that a Handler + * encounters while logging. + * + * @see Handler#setErrorManager(ErrorManager) + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class ErrorManager +{ + /* The values have been taken from Sun's public J2SE 1.4 API + * documentation. + * See http://java.sun.com/j2se/1.4/docs/api/constant-values.html + */ + + /** + * Indicates that there was a failure that does not readily + * fall into any of the other categories. + */ + public static final int GENERIC_FAILURE = 0; + + + /** + * Indicates that there was a problem upon writing to + * an output stream. + */ + public static final int WRITE_FAILURE = 1; + + + /** + * Indicates that there was a problem upon flushing + * an output stream. + */ + public static final int FLUSH_FAILURE = 2; + + + /** + * Indicates that there was a problem upon closing + * an output stream. + */ + public static final int CLOSE_FAILURE = 3; + + + /** + * Indicates that there was a problem upon opening + * an output stream. + */ + public static final int OPEN_FAILURE = 4; + + + /** + * Indicates that there was a problem upon formatting + * the message of a log record. + */ + public static final int FORMAT_FAILURE = 5; + + + /** + * Indicates whether the {@link #error} method of this ErrorManager + * has ever been used. + * + * Declared volatile in order to correctly support the + * double-checked locking idiom (once the revised Java Memory Model + * gets adopted); see Classpath bug #2944. + */ + private volatile boolean everUsed = false; + + + public ErrorManager() + { + } + + + /** + * Reports an error that occured upon logging. The default implementation + * emits the very first error to System.err, ignoring subsequent errors. + * + * @param message a message describing the error, or null if + * there is no suitable description. + * + * @param ex an exception, or null if the error is not + * related to an exception. + * + * @param errorCode one of the defined error codes, for example + * ErrorManager.CLOSE_FAILURE. + */ + public void error(String message, Exception ex, int errorCode) + { + if (everUsed) + return; + + synchronized (this) + { + /* The double check is intentional. If the first check was + * omitted, the monitor would have to be entered every time + * error() method was called. If the second check was + * omitted, the code below could be executed by multiple + * threads simultaneously. + * + * This is the 'double-checked locking' idiom, which is broken + * with the current version of the Java memory model. However, + * we assume that JVMs will have adopted a revised version of + * the Java Memory Model by the time GNU Classpath gains + * widespread acceptance. See Classpath bug #2944. + */ + if (everUsed) + return; + + everUsed = true; + } + + String codeMsg; + switch (errorCode) + { + case GENERIC_FAILURE: + codeMsg = "GENERIC_FAILURE"; + break; + + case WRITE_FAILURE: + codeMsg = "WRITE_FAILURE"; + break; + + case FLUSH_FAILURE: + codeMsg = "FLUSH_FAILURE"; + break; + + case CLOSE_FAILURE: + codeMsg = "CLOSE_FAILURE"; + break; + + case OPEN_FAILURE: + codeMsg = "OPEN_FAILURE"; + break; + + case FORMAT_FAILURE: + codeMsg = "FORMAT_FAILURE"; + break; + + default: + codeMsg = String.valueOf(errorCode); + break; + } + + System.err.println("Error upon logging: " + codeMsg); + if ((message != null) && (message.length() > 0)) + System.err.println(message); + + if (ex != null) + ex.printStackTrace(); + } +} diff --git a/libjava/classpath/java/util/logging/FileHandler.java b/libjava/classpath/java/util/logging/FileHandler.java new file mode 100644 index 000000000..00d967f57 --- /dev/null +++ b/libjava/classpath/java/util/logging/FileHandler.java @@ -0,0 +1,665 @@ +/* FileHandler.java -- a class for publishing log messages to log files + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import gnu.java.lang.CPStringBuilder; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.LinkedList; +import java.util.ListIterator; + +/** + * A FileHandler publishes log records to a set of log + * files. A maximum file size can be specified; as soon as a log file + * reaches the size limit, it is closed and the next file in the set + * is taken. + * + *

        Configuration: Values of the subsequent + * LogManager properties are taken into consideration + * when a FileHandler is initialized. If a property is + * not defined, or if it has an invalid value, a default is taken + * without an exception being thrown. + * + *

          + * + *
        • java.util.FileHandler.level - specifies + * the initial severity level threshold. Default value: + * Level.ALL.
        • + * + *
        • java.util.FileHandler.filter - specifies + * the name of a Filter class. Default value: No Filter.
        • + * + *
        • java.util.FileHandler.formatter - specifies + * the name of a Formatter class. Default value: + * java.util.logging.XMLFormatter.
        • + * + *
        • java.util.FileHandler.encoding - specifies + * the name of the character encoding. Default value: + * the default platform encoding.
        • + * + *
        • java.util.FileHandler.limit - specifies the number + * of bytes a log file is approximately allowed to reach before it + * is closed and the handler switches to the next file in the + * rotating set. A value of zero means that files can grow + * without limit. Default value: 0 (unlimited growth).
        • + * + *
        • java.util.FileHandler.count - specifies the number + * of log files through which this handler cycles. Default value: + * 1.
        • + * + *
        • java.util.FileHandler.pattern - specifies a + * pattern for the location and name of the produced log files. + * See the section on file name + * patterns for details. Default value: + * "%h/java%u.log".
        • + * + *
        • java.util.FileHandler.append - specifies + * whether the handler will append log records to existing + * files, or whether the handler will clear log files + * upon switching to them. Default value: false, + * indicating that files will be cleared.
        • + * + *
        + * + *

        File Name Patterns: + * The name and location and log files are specified with pattern + * strings. The handler will replace the following character sequences + * when opening log files: + * + *

          + *
        • / - replaced by the platform-specific path name + * separator. This value is taken from the system property + * file.separator.
        • + * + *
        • %t - replaced by the platform-specific location of + * the directory intended for temporary files. This value is + * taken from the system property java.io.tmpdir.
        • + * + *
        • %h - replaced by the location of the home + * directory of the current user. This value is taken from the + * system property user.home.
        • + * + *
        • %g - replaced by a generation number for + * distinguisthing the individual items in the rotating set + * of log files. The generation number cycles through the + * sequence 0, 1, ..., count - 1.
        • + * + *
        • %u - replaced by a unique number for + * distinguisthing the output files of several concurrently + * running processes. The FileHandler starts + * with 0 when it tries to open a log file. If the file + * cannot be opened because it is currently in use, + * the unique number is incremented by one and opening + * is tried again. These steps are repeated until the + * opening operation succeeds. + * + *

          FIXME: Is the following correct? Please review. The unique + * number is determined for each log file individually when it is + * opened upon switching to the next file. Therefore, it is not + * correct to assume that all log files in a rotating set bear the + * same unique number. + * + *

          FIXME: The Javadoc for the Sun reference implementation + * says: "Note that the use of unique ids to avoid conflicts is + * only guaranteed to work reliably when using a local disk file + * system." Why? This needs to be mentioned as well, in case + * the reviewers decide the statement is true. Otherwise, + * file a bug report with Sun.

        • + * + *
        • %% - replaced by a single percent sign.
        • + *
        + * + *

        If the pattern string does not contain %g and + * count is greater than one, the handler will append + * the string .%g to the specified pattern. + * + *

        If the handler attempts to open a log file, this log file + * is being used at the time of the attempt, and the pattern string + * does not contain %u, the handler will append + * the string .%u to the specified pattern. This + * step is performed after any generation number has been + * appended. + * + *

        Examples for the GNU platform: + * + *

          + * + *
        • %h/java%u.log will lead to a single log file + * /home/janet/java0.log, assuming count + * equals 1, the user's home directory is + * /home/janet, and the attempt to open the file + * succeeds.
        • + * + *
        • %h/java%u.log will lead to three log files + * /home/janet/java0.log.0, + * /home/janet/java0.log.1, and + * /home/janet/java0.log.2, + * assuming count equals 3, the user's home + * directory is /home/janet, and all attempts + * to open files succeed.
        • + * + *
        • %h/java%u.log will lead to three log files + * /home/janet/java0.log.0, + * /home/janet/java1.log.1, and + * /home/janet/java0.log.2, + * assuming count equals 3, the user's home + * directory is /home/janet, and the attempt + * to open /home/janet/java0.log.1 fails.
        • + * + *
        + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class FileHandler + extends StreamHandler +{ + /** + * A literal that prefixes all file-handler related properties in the + * logging.properties file. + */ + private static final String PROPERTY_PREFIX = "java.util.logging.FileHandler"; + /** + * The name of the property to set for specifying a file naming (incl. path) + * pattern to use with rotating log files. + */ + private static final String PATTERN_KEY = PROPERTY_PREFIX + ".pattern"; + /** + * The default pattern to use when the PATTERN_KEY property was + * not specified in the logging.properties file. + */ + private static final String DEFAULT_PATTERN = "%h/java%u.log"; + /** + * The name of the property to set for specifying an approximate maximum + * amount, in bytes, to write to any one log output file. A value of zero + * (which is the default) implies a no limit. + */ + private static final String LIMIT_KEY = PROPERTY_PREFIX + ".limit"; + private static final int DEFAULT_LIMIT = 0; + /** + * The name of the property to set for specifying how many output files to + * cycle through. The default value is 1. + */ + private static final String COUNT_KEY = PROPERTY_PREFIX + ".count"; + private static final int DEFAULT_COUNT = 1; + /** + * The name of the property to set for specifying whether this handler should + * append, or not, its output to existing files. The default value is + * false meaning NOT to append. + */ + private static final String APPEND_KEY = PROPERTY_PREFIX + ".append"; + private static final boolean DEFAULT_APPEND = false; + + /** + * The number of bytes a log file is approximately allowed to reach + * before it is closed and the handler switches to the next file in + * the rotating set. A value of zero means that files can grow + * without limit. + */ + private final int limit; + + + /** + * The number of log files through which this handler cycles. + */ + private final int count; + + + /** + * The pattern for the location and name of the produced log files. + * See the section on file name patterns + * for details. + */ + private final String pattern; + + + /** + * Indicates whether the handler will append log records to existing + * files (true), or whether the handler will clear log files + * upon switching to them (false). + */ + private final boolean append; + + + /** + * The number of bytes that have currently been written to the stream. + * Package private for use in inner classes. + */ + long written; + + + /** + * A linked list of files we are, or have written to. The entries + * are file path strings, kept in the order + */ + private LinkedList logFiles; + + + /** + * Constructs a FileHandler, taking all property values + * from the current {@link LogManager LogManager} configuration. + * + * @throws java.io.IOException FIXME: The Sun Javadoc says: "if + * there are IO problems opening the files." This conflicts + * with the general principle that configuration errors do + * not prohibit construction. Needs review. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public FileHandler() + throws IOException, SecurityException + { + this(LogManager.getLogManager().getProperty(PATTERN_KEY), + LogManager.getIntProperty(LIMIT_KEY, DEFAULT_LIMIT), + LogManager.getIntProperty(COUNT_KEY, DEFAULT_COUNT), + LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND)); + } + + + /* FIXME: Javadoc missing. */ + public FileHandler(String pattern) + throws IOException, SecurityException + { + this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, DEFAULT_APPEND); + } + + + /* FIXME: Javadoc missing. */ + public FileHandler(String pattern, boolean append) + throws IOException, SecurityException + { + this(pattern, DEFAULT_LIMIT, DEFAULT_COUNT, append); + } + + + /* FIXME: Javadoc missing. */ + public FileHandler(String pattern, int limit, int count) + throws IOException, SecurityException + { + this(pattern, limit, count, + LogManager.getBooleanProperty(APPEND_KEY, DEFAULT_APPEND)); + } + + + /** + * Constructs a FileHandler given the pattern for the + * location and name of the produced log files, the size limit, the + * number of log files thorough which the handler will rotate, and + * the append property. All other property values are + * taken from the current {@link LogManager LogManager} + * configuration. + * + * @param pattern The pattern for the location and name of the + * produced log files. See the section on file name patterns for details. + * If pattern is null, the value is + * taken from the {@link LogManager LogManager} configuration + * property + * java.util.logging.FileHandler.pattern. + * However, this is a pecularity of the GNU implementation, + * and Sun's API specification does not mention what behavior + * is to be expected for null. Therefore, + * applications should not rely on this feature. + * + * @param limit specifies the number of bytes a log file is + * approximately allowed to reach before it is closed and the + * handler switches to the next file in the rotating set. A + * value of zero means that files can grow without limit. + * + * @param count specifies the number of log files through which this + * handler cycles. + * + * @param append specifies whether the handler will append log + * records to existing files (true), or whether the + * handler will clear log files upon switching to them + * (false). + * + * @throws java.io.IOException FIXME: The Sun Javadoc says: "if + * there are IO problems opening the files." This conflicts + * with the general principle that configuration errors do + * not prohibit construction. Needs review. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + *

        FIXME: This seems in contrast to all other handler + * constructors -- verify this by running tests against + * the Sun reference implementation. + */ + public FileHandler(String pattern, + int limit, + int count, + boolean append) + throws IOException, SecurityException + { + super(/* output stream, created below */ null, + PROPERTY_PREFIX, + /* default level */ Level.ALL, + /* formatter */ null, + /* default formatter */ XMLFormatter.class); + + if ((limit <0) || (count < 1)) + throw new IllegalArgumentException(); + + this.pattern = pattern != null ? pattern : DEFAULT_PATTERN; + this.limit = limit; + this.count = count; + this.append = append; + this.written = 0; + this.logFiles = new LinkedList (); + + setOutputStream (createFileStream (this.pattern, limit, count, append, + /* generation */ 0)); + } + + + /* FIXME: Javadoc missing. */ + private OutputStream createFileStream(String pattern, + int limit, + int count, + boolean append, + int generation) + { + String path; + int unique = 0; + + /* Throws a SecurityException if the caller does not have + * LoggingPermission("control"). + */ + LogManager.getLogManager().checkAccess(); + + /* Default value from the java.util.logging.FileHandler.pattern + * LogManager configuration property. + */ + if (pattern == null) + pattern = LogManager.getLogManager().getProperty(PATTERN_KEY); + if (pattern == null) + pattern = DEFAULT_PATTERN; + + if (count > 1 && !has (pattern, 'g')) + pattern = pattern + ".%g"; + + do + { + path = replaceFileNameEscapes(pattern, generation, unique, count); + + try + { + File file = new File(path); + if (!file.exists () || append) + { + FileOutputStream fout = new FileOutputStream (file, append); + // FIXME we need file locks for this to work properly, but they + // are not implemented yet in Classpath! Madness! +// FileChannel channel = fout.getChannel (); +// FileLock lock = channel.tryLock (); +// if (lock != null) // We've locked the file. +// { + if (logFiles.isEmpty ()) + logFiles.addFirst (path); + return new ostr (fout); +// } + } + } + catch (Exception ex) + { + reportError (null, ex, ErrorManager.OPEN_FAILURE); + } + + unique = unique + 1; + if (!has (pattern, 'u')) + pattern = pattern + ".%u"; + } + while (true); + } + + + /** + * Replaces the substrings "/" by the value of the + * system property "file.separator", "%t" + * by the value of the system property + * "java.io.tmpdir", "%h" by the value of + * the system property "user.home", "%g" + * by the value of generation, "%u" by the + * value of uniqueNumber, and "%%" by a + * single percent character. If pattern does + * not contain the sequence "%g", + * the value of generation will be appended to + * the result. + * + * @throws NullPointerException if one of the system properties + * "file.separator", + * "java.io.tmpdir", or + * "user.home" has no value and the + * corresponding escape sequence appears in + * pattern. + */ + private static String replaceFileNameEscapes(String pattern, + int generation, + int uniqueNumber, + int count) + { + CPStringBuilder buf = new CPStringBuilder(pattern); + String replaceWith; + boolean foundGeneration = false; + + int pos = 0; + do + { + // Uncomment the next line for finding bugs. + // System.out.println(buf.substring(0,pos) + '|' + buf.substring(pos)); + + if (buf.charAt(pos) == '/') + { + /* The same value is also provided by java.io.File.separator. */ + replaceWith = System.getProperty("file.separator"); + buf.replace(pos, pos + 1, replaceWith); + pos = pos + replaceWith.length() - 1; + continue; + } + + if (buf.charAt(pos) == '%') + { + switch (buf.charAt(pos + 1)) + { + case 't': + replaceWith = System.getProperty("java.io.tmpdir"); + break; + + case 'h': + replaceWith = System.getProperty("user.home"); + break; + + case 'g': + replaceWith = Integer.toString(generation); + foundGeneration = true; + break; + + case 'u': + replaceWith = Integer.toString(uniqueNumber); + break; + + case '%': + replaceWith = "%"; + break; + + default: + replaceWith = "??"; + break; // FIXME: Throw exception? + } + + buf.replace(pos, pos + 2, replaceWith); + pos = pos + replaceWith.length() - 1; + continue; + } + } + while (++pos < buf.length() - 1); + + if (!foundGeneration && (count > 1)) + { + buf.append('.'); + buf.append(generation); + } + + return buf.toString(); + } + + + /* FIXME: Javadoc missing. */ + public void publish(LogRecord record) + { + if (limit > 0 && written >= limit) + rotate (); + super.publish(record); + flush (); + } + + /** + * Rotates the current log files, possibly removing one if we + * exceed the file count. + */ + private synchronized void rotate () + { + if (logFiles.size () > 0) + { + File f1 = null; + ListIterator lit = null; + + // If we reach the file count, ditch the oldest file. + if (logFiles.size () == count) + { + f1 = new File ((String) logFiles.getLast ()); + f1.delete (); + lit = logFiles.listIterator (logFiles.size () - 1); + } + // Otherwise, move the oldest to a new location. + else + { + String path = replaceFileNameEscapes (pattern, logFiles.size (), + /* unique */ 0, count); + f1 = new File (path); + logFiles.addLast (path); + lit = logFiles.listIterator (logFiles.size () - 1); + } + + // Now rotate the files. + while (lit.hasPrevious ()) + { + String s = (String) lit.previous (); + File f2 = new File (s); + f2.renameTo (f1); + f1 = f2; + } + } + + setOutputStream (createFileStream (pattern, limit, count, append, + /* generation */ 0)); + + // Reset written count. + written = 0; + } + + /** + * Tell if pattern contains the pattern sequence + * with character escape. That is, if escape + * is 'g', this method returns true if the given pattern contains + * "%g", and not just the substring "%g" (for example, in the case of + * "%%g"). + * + * @param pattern The pattern to test. + * @param escape The escape character to search for. + * @return True iff the pattern contains the escape sequence with the + * given character. + */ + private static boolean has (final String pattern, final char escape) + { + final int len = pattern.length (); + boolean sawPercent = false; + for (int i = 0; i < len; i++) + { + char c = pattern.charAt (i); + if (sawPercent) + { + if (c == escape) + return true; + if (c == '%') // Double percent + { + sawPercent = false; + continue; + } + } + sawPercent = (c == '%'); + } + return false; + } + + /** + * An output stream that tracks the number of bytes written to it. + */ + private final class ostr extends FilterOutputStream + { + private ostr (OutputStream out) + { + super (out); + } + + public void write (final int b) throws IOException + { + out.write (b); + FileHandler.this.written++; // FIXME: synchronize? + } + + public void write (final byte[] b) throws IOException + { + write (b, 0, b.length); + } + + public void write (final byte[] b, final int offset, final int length) + throws IOException + { + out.write (b, offset, length); + FileHandler.this.written += length; // FIXME: synchronize? + } + } +} diff --git a/libjava/classpath/java/util/logging/Filter.java b/libjava/classpath/java/util/logging/Filter.java new file mode 100644 index 000000000..ec4597670 --- /dev/null +++ b/libjava/classpath/java/util/logging/Filter.java @@ -0,0 +1,64 @@ +/* Filter.java -- an interface for filters that decide whether a + LogRecord should be published or discarded + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +/** + * By implementing the Filter interface, applications + * can control what is being logged based on arbitrary properties, + * not just the severity level. Both Handler and + * Logger allow to register Filters whose + * isLoggable method will be called when a + * LogRecord has passed the test based on the + * severity level. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public interface Filter +{ + /** + * Determines whether a LogRecord should be published or discarded. + * + * @param record the LogRecord to be inspected. + * + * @return true if the record should be published, + * false if it should be discarded. + */ + boolean isLoggable(LogRecord record); +} diff --git a/libjava/classpath/java/util/logging/Formatter.java b/libjava/classpath/java/util/logging/Formatter.java new file mode 100644 index 000000000..feaf55315 --- /dev/null +++ b/libjava/classpath/java/util/logging/Formatter.java @@ -0,0 +1,171 @@ +/* Formatter.java -- + A class for formatting log messages by localizing message texts + and performing substitution of parameters + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +/** + * A Formatter supports handlers by localizing + * message texts and by subsituting parameter values for their + * placeholders. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public abstract class Formatter +{ + /** + * Constructs a new Formatter. + */ + protected Formatter() + { + } + + + /** + * Formats a LogRecord into a string. Usually called by handlers + * which need a string for a log record, for example to append + * a record to a log file or to transmit a record over the network. + * + * @param record the log record for which a string form is requested. + */ + public abstract String format(LogRecord record); + + + /** + * Returns a string that handlers are supposed to emit before + * the first log record. The base implementation returns an + * empty string, but subclasses such as {@link XMLFormatter} + * override this method in order to provide a suitable header. + * + * @return a string for the header. + * + * @param handler the handler which will prepend the returned + * string in front of the first log record. This method + * may inspect certain properties of the handler, for + * example its encoding, in order to construct the header. + */ + public String getHead(Handler handler) + { + return ""; + } + + + /** + * Returns a string that handlers are supposed to emit after + * the last log record. The base implementation returns an + * empty string, but subclasses such as {@link XMLFormatter} + * override this method in order to provide a suitable tail. + * + * @return a string for the header. + * + * @param handler the handler which will append the returned + * string after the last log record. This method + * may inspect certain properties of the handler + * in order to construct the tail. + */ + public String getTail(Handler handler) + { + return ""; + } + + + /** + * Formats the message part of a log record. + * + *

        First, the Formatter localizes the record message to the + * default locale by looking up the message in the record's + * localization resource bundle. If this step fails because there + * is no resource bundle associated with the record, or because the + * record message is not a key in the bundle, the raw message is + * used instead. + * + *

        Second, the Formatter substitutes appropriate strings for + * the message parameters. If the record returns a non-empty + * array for getParameters() and the localized + * message string contains the character sequence "{0", the + * formatter uses java.text.MessageFormat to format + * the message. Otherwise, no parameter substitution is performed. + * + * @param record the log record to be localized and formatted. + * + * @return the localized message text where parameters have been + * substituted by suitable strings. + * + * @throws NullPointerException if record + * is null. + */ + public String formatMessage(LogRecord record) + { + String msg; + ResourceBundle bundle; + Object[] params; + + /* This will throw a NullPointerExceptionif record is null. */ + msg = record.getMessage(); + if (msg == null) + msg = ""; + + /* Try to localize the message. */ + bundle = record.getResourceBundle(); + if (bundle != null) + { + try + { + msg = bundle.getString(msg); + } + catch (java.util.MissingResourceException _) + { + } + } + + /* Format the message if there are parameters. */ + params = record.getParameters(); + if ((params != null) + && (params.length > 0) + && (msg.indexOf("{0") >= 0)) + { + msg = MessageFormat.format(msg, params); + } + + return msg; + } +} diff --git a/libjava/classpath/java/util/logging/Handler.java b/libjava/classpath/java/util/logging/Handler.java new file mode 100644 index 000000000..e254b4d35 --- /dev/null +++ b/libjava/classpath/java/util/logging/Handler.java @@ -0,0 +1,386 @@ +/* Handler.java -- a class for publishing log messages + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.io.UnsupportedEncodingException; + +/** + * A Handler publishes LogRecords to + * a sink, for example a file, the console or a network socket. + * There are different subclasses of Handler + * to deal with different kinds of sinks. + * + *

        FIXME: Are handlers thread-safe, or is the assumption that only + * loggers are, and a handler can belong only to one single logger? If + * the latter, should we enforce it? (Spec not clear). In any + * case, it needs documentation. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public abstract class Handler +{ + Formatter formatter; + Filter filter; + Level level; + ErrorManager errorManager; + String encoding; + + /** + * Constructs a Handler with a logging severity level of + * Level.ALL, no formatter, no filter, and + * an instance of ErrorManager managing errors. + * + *

        Specification Note: The specification of the + * JavaTM Logging API does not mention which character + * encoding is to be used by freshly constructed Handlers. The GNU + * implementation uses the default platform encoding, but other + * Java implementations might behave differently. + * + *

        Specification Note: While a freshly constructed + * Handler is required to have no filter according to the + * specification, null is not a valid parameter for + * Handler.setFormatter. Therefore, the following + * code will throw a java.lang.NullPointerException: + * + *

        Handler h = new MyConcreteSubclassOfHandler();
        +h.setFormatter(h.getFormatter());
        + * + * It seems strange that a freshly constructed Handler is not + * supposed to provide a Formatter, but this is what the specification + * says. + */ + protected Handler() + { + level = Level.ALL; + } + + + /** + * Publishes a LogRecord to an appropriate sink, + * provided the record passes all tests for being loggable. The + * Handler will localize the message of the log + * record and substitute any message parameters. + * + *

        Most applications do not need to call this method directly. + * Instead, they will use use a {@link Logger}, which will + * create LogRecords and distribute them to registered handlers. + * + *

        In case of an I/O failure, the ErrorManager + * of this Handler will be informed, but the caller + * of this method will not receive an exception. + * + * @param record the log event to be published. + */ + public abstract void publish(LogRecord record); + + + /** + * Forces any data that may have been buffered to the underlying + * output device. + * + *

        In case of an I/O failure, the ErrorManager + * of this Handler will be informed, but the caller + * of this method will not receive an exception. + */ + public abstract void flush(); + + + /** + * Closes this Handler after having flushed + * the buffers. As soon as close has been called, + * a Handler should not be used anymore. Attempts + * to publish log records, to flush buffers, or to modify the + * Handler in any other way may throw runtime + * exceptions after calling close. + * + *

        In case of an I/O failure, the ErrorManager + * of this Handler will be informed, but the caller + * of this method will not receive an exception. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public abstract void close() + throws SecurityException; + + + /** + * Returns the Formatter which will be used to + * localize the text of log messages and to substitute + * message parameters. A Handler is encouraged, + * but not required to actually use an assigned + * Formatter. + * + * @return the Formatter being used, or + * null if this Handler + * does not use formatters and no formatter has + * ever been set by calling setFormatter. + */ + public Formatter getFormatter() + { + return formatter; + } + + + /** + * Sets the Formatter which will be used to + * localize the text of log messages and to substitute + * message parameters. A Handler is encouraged, + * but not required to actually use an assigned + * Formatter. + * + * @param formatter the new Formatter to use. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @throws NullPointerException if formatter is + * null. + */ + public void setFormatter(Formatter formatter) + throws SecurityException + { + LogManager.getLogManager().checkAccess(); + + /* Throws a NullPointerException if formatter is null. */ + formatter.getClass(); + + this.formatter = formatter; + } + + + /** + * Returns the character encoding which this handler uses for publishing + * log records. + * + * @return the name of a character encoding, or null + * for the default platform encoding. + */ + public String getEncoding() + { + return encoding; + } + + + /** + * Sets the character encoding which this handler uses for publishing + * log records. The encoding of a Handler must be + * set before any log records have been published. + * + * @param encoding the name of a character encoding, or null + * for the default encoding. + * + * @exception SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + */ + public void setEncoding(String encoding) + throws SecurityException, UnsupportedEncodingException + { + /* Should any developer ever change this implementation, they are + * advised to have a look at StreamHandler.setEncoding(String), + * which overrides this method without calling super.setEncoding. + */ + LogManager.getLogManager().checkAccess(); + + /* Simple check for supported encodings. This is more expensive + * than it could be, but this method is overwritten by StreamHandler + * anyway. + */ + if (encoding != null) + new String(new byte[0], encoding); + + this.encoding = encoding; + } + + + /** + * Returns the Filter that currently controls which + * log records are being published by this Handler. + * + * @return the currently active Filter, or + * null if no filter has been associated. + * In the latter case, log records are filtered purely + * based on their severity level. + */ + public Filter getFilter() + { + return filter; + } + + + /** + * Sets the Filter for controlling which + * log records will be published by this Handler. + * + * @param filter the Filter to use, or + * null to filter log records purely based + * on their severity level. + */ + public void setFilter(Filter filter) + throws SecurityException + { + LogManager.getLogManager().checkAccess(); + this.filter = filter; + } + + + /** + * Returns the ErrorManager that currently deals + * with errors originating from this Handler. + * + * @exception SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public ErrorManager getErrorManager() + { + LogManager.getLogManager().checkAccess(); + + /* Developers wanting to change the subsequent code should + * have a look at Handler.reportError -- it also can create + * an ErrorManager, but does so without checking permissions + * to control the logging infrastructure. + */ + if (errorManager == null) + errorManager = new ErrorManager(); + + return errorManager; + } + + + public void setErrorManager(ErrorManager manager) + { + LogManager.getLogManager().checkAccess(); + + /* Make sure manager is not null. */ + manager.getClass(); + + this.errorManager = manager; + } + + + protected void reportError(String message, Exception ex, int code) + { + if (errorManager == null) + errorManager = new ErrorManager(); + + errorManager.error(message, ex, code); + } + + + /** + * Returns the severity level threshold for this Handler + * All log records with a lower severity level will be discarded; + * a log record of the same or a higher level will be published + * unless an installed Filter decides to discard it. + * + * @return the severity level below which all log messages + * will be discarded. + */ + public Level getLevel() + { + return level; + } + + + /** + * Sets the severity level threshold for this Handler. + * All log records with a lower severity level will be discarded; + * a log record of the same or a higher level will be published + * unless an installed Filter decides to discard it. + * + * @param level the severity level below which all log messages + * will be discarded. + * + * @exception SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @exception NullPointerException if level is + * null. + */ + public void setLevel(Level level) + { + LogManager.getLogManager().checkAccess(); + + /* Throw NullPointerException if level is null. */ + level.getClass(); + this.level = level; + } + + + /** + * Checks whether a LogRecord would be logged + * if it was passed to this Handler for publication. + * + *

        The Handler implementation considers a record as + * loggable if its level is greater than or equal to the severity + * level threshold. In a second step, if a {@link Filter} has + * been installed, its {@link Filter#isLoggable(LogRecord) isLoggable} + * method is invoked. Subclasses of Handler can override + * this method to impose their own constraints. + * + * @param record the LogRecord to be checked. + * + * @return true if record would + * be published by {@link #publish(LogRecord) publish}, + * false if it would be discarded. + * + * @see #setLevel(Level) + * @see #setFilter(Filter) + * @see Filter#isLoggable(LogRecord) + * + * @throws NullPointerException if record + * is null. + */ + public boolean isLoggable(LogRecord record) + { + if (record.getLevel().intValue() < level.intValue()) + return false; + + if (filter != null) + return filter.isLoggable(record); + else + return true; + } +} diff --git a/libjava/classpath/java/util/logging/Level.java b/libjava/classpath/java/util/logging/Level.java new file mode 100644 index 000000000..ea2c83fb2 --- /dev/null +++ b/libjava/classpath/java/util/logging/Level.java @@ -0,0 +1,416 @@ +/* Level.java -- a class for indicating logging levels + Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.io.Serializable; +import java.util.ResourceBundle; + +/** + * A class for indicating logging levels. A number of commonly used + * levels is pre-defined (such as java.util.logging.Level.INFO), + * and applications should utilize those whenever possible. For specialized + * purposes, however, applications can sub-class Level in order to define + * custom logging levels. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class Level implements Serializable +{ + /* The integer values are the same as in the Sun J2SE 1.4. + * They have been obtained with a test program. In J2SE 1.4.1, + * Sun has amended the API documentation; these values are now + * publicly documented. + */ + + /** + * The OFF level is used as a threshold for filtering + * log records, meaning that no message should be logged. + * + * @see Logger#setLevel(java.util.logging.Level) + */ + public static final Level OFF = new Level ("OFF", Integer.MAX_VALUE); + + /** + * Log records whose level is SEVERE indicate a serious + * failure that prevents normal program execution. Messages at this + * level should be understandable to an inexperienced, non-technical + * end user. Ideally, they explain in simple words what actions the + * user can take in order to resolve the problem. + */ + public static final Level SEVERE = new Level ("SEVERE", 1000); + + + /** + * Log records whose level is WARNING indicate a + * potential problem that does not prevent normal program execution. + * Messages at this level should be understandable to an + * inexperienced, non-technical end user. Ideally, they explain in + * simple words what actions the user can take in order to resolve + * the problem. + */ + public static final Level WARNING = new Level ("WARNING", 900); + + + /** + * Log records whose level is INFO are used in purely + * informational situations that do not constitute serious errors or + * potential problems. In the default logging configuration, INFO + * messages will be written to the system console. For this reason, + * the INFO level should be used only for messages that are + * important to end users and system administrators. Messages at + * this level should be understandable to an inexperienced, + * non-technical user. + */ + public static final Level INFO = new Level ("INFO", 800); + + + /** + * Log records whose level is CONFIG are used for + * describing the static configuration, for example the windowing + * environment, the operating system version, etc. + */ + public static final Level CONFIG = new Level ("CONFIG", 700); + + + /** + * Log records whose level is FINE are typically used + * for messages that are relevant for developers using + * the component generating log messages. Examples include minor, + * recoverable failures, or possible inefficiencies. + */ + public static final Level FINE = new Level ("FINE", 500); + + + /** + * Log records whose level is FINER are intended for + * rather detailed tracing, for example entering a method, returning + * from a method, or throwing an exception. + */ + public static final Level FINER = new Level ("FINER", 400); + + + /** + * Log records whose level is FINEST are used for + * highly detailed tracing, for example to indicate that a certain + * point inside the body of a method has been reached. + */ + public static final Level FINEST = new Level ("FINEST", 300); + + + /** + * The ALL level is used as a threshold for filtering + * log records, meaning that every message should be logged. + * + * @see Logger#setLevel(java.util.logging.Level) + */ + public static final Level ALL = new Level ("ALL", Integer.MIN_VALUE); + + + private static final Level[] knownLevels = { + ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, OFF + }; + + + /** + * The name of the Level without localizing it, for example + * "WARNING". + */ + private String name; + + + /** + * The integer value of this Level. + */ + private int value; + + + /** + * The name of the resource bundle used for localizing the level + * name, or null if the name does not undergo + * localization. + */ + private String resourceBundleName; + + + /** + * Creates a logging level given a name and an integer value. + * It rarely is necessary to create custom levels, + * as most applications should be well served with one of the + * standard levels such as Level.CONFIG, + * Level.INFO, or Level.FINE. + * + * @param name the name of the level. + * + * @param value the integer value of the level. Please note + * that the JavaTM + * Logging API does not specify integer + * values for standard levels (such as + * Level.FINE). Therefore, a custom + * level should pass an integer value that + * is calculated at run-time, e.g. + * (Level.FINE.intValue() + Level.CONFIG.intValue()) + * / 2 for a level between FINE and CONFIG. + */ + protected Level(String name, int value) + { + this(name, value, null); + } + + + /** + * Create a logging level given a name, an integer value and a name + * of a resource bundle for localizing the level name. It rarely + * is necessary to create custom levels, as most applications + * should be well served with one of the standard levels such as + * Level.CONFIG, Level.INFO, or + * Level.FINE. + * + * @param name the name of the level. + * + * @param value the integer value of the level. Please note + * that the JavaTM + * Logging API does not specify integer + * values for standard levels (such as + * Level.FINE). Therefore, a custom + * level should pass an integer value that + * is calculated at run-time, e.g. + * (Level.FINE.intValue() + Level.CONFIG.intValue()) + * / 2 for a level between FINE and CONFIG. + * + * @param resourceBundleName the name of a resource bundle + * for localizing the level name, or null + * if the name does not need to be localized. + */ + protected Level(String name, int value, String resourceBundleName) + { + this.name = name; + this.value = value; + this.resourceBundleName = resourceBundleName; + } + + + static final long serialVersionUID = -8176160795706313070L; + + + /** + * Checks whether the Level has the same intValue as one of the + * pre-defined levels. If so, the pre-defined level object is + * returned. + * + *
        Since the resource bundle name is not taken into + * consideration, it is possible to resolve Level objects that have + * been de-serialized by another implementation, even if the other + * implementation uses a different resource bundle for localizing + * the names of pre-defined levels. + */ + private Object readResolve() + { + for (int i = 0; i < knownLevels.length; i++) + if (value == knownLevels[i].intValue()) + return knownLevels[i]; + + return this; + } + + + /** + * Returns the name of the resource bundle used for localizing the + * level name. + * + * @return the name of the resource bundle used for localizing the + * level name, or null if the name does not undergo + * localization. + */ + public String getResourceBundleName() + { + return resourceBundleName; + } + + + /** + * Returns the name of the Level without localizing it, for example + * "WARNING". + */ + public String getName() + { + return name; + } + + + /** + * Returns the name of the Level after localizing it, for example + * "WARNUNG". + */ + public String getLocalizedName() + { + String localizedName = null; + + if (resourceBundleName != null) + { + try + { + ResourceBundle b = ResourceBundle.getBundle(resourceBundleName); + localizedName = b.getString(name); + } + catch (Exception _) + { + } + } + + if (localizedName != null) + return localizedName; + else + return name; + } + + + /** + * Returns the name of the Level without localizing it, for example + * "WARNING". + */ + public final String toString() + { + return getName(); + } + + + /** + * Returns the integer value of the Level. + */ + public final int intValue() + { + return value; + } + + + /** + * Returns one of the standard Levels given either its name or its + * integer value. Custom subclasses of Level will not be returned + * by this method. + * + * @throws IllegalArgumentException if name is neither + * the name nor the integer value of one of the pre-defined standard + * logging levels. + * + * @throws NullPointerException if name is null. + * + */ + public static Level parse(String name) + throws IllegalArgumentException + { + /* This will throw a NullPointerException if name is null, + * as required by the API specification. + */ + name = name.intern(); + + for (int i = 0; i < knownLevels.length; i++) + { + // It's safe to use == instead of .equals here because only the + // standard logging levels will be returned by this method, and + // they are all created using string literals. + if (name == knownLevels[i].name) + return knownLevels[i]; + } + + try + { + int num = Integer.parseInt(name); + for (int i = 0; i < knownLevels.length; i++) + if (num == knownLevels[i].value) + return knownLevels[i]; + } + catch (NumberFormatException _) + { + } + + String msg = "Not the name of a standard logging level: \"" + name + "\""; + throw new IllegalArgumentException(msg); + } + + + /** + * Checks whether this Level's integer value is equal to that of + * another object. + * + * @return true if other is an instance of + * java.util.logging.Level and has the same integer + * value, false otherwise. + */ + public boolean equals(Object other) + { + if (!(other instanceof Level)) + return false; + + return value == ((Level) other).value; + } + + + /** + * Returns a hash code for this Level which is based on its numeric + * value. + */ + public int hashCode() + { + return value; + } + + + /** + * Determines whether or not this Level is one of the standard + * levels specified in the Logging API. + * + *

        This method is package-private because it is not part + * of the logging API specification. However, an XMLFormatter + * is supposed to emit the numeric value for a custom log + * level, but the name for a pre-defined level. It seems + * cleaner to put this method to Level than to write some + * procedural code for XMLFormatter. + * + * @return true if this Level is a standard level, + * false otherwise. + */ + final boolean isStandardLevel() + { + for (int i = 0; i < knownLevels.length; i++) + if (knownLevels[i] == this) + return true; + + return false; + } +} diff --git a/libjava/classpath/java/util/logging/LogManager.java b/libjava/classpath/java/util/logging/LogManager.java new file mode 100644 index 000000000..dffa44d9c --- /dev/null +++ b/libjava/classpath/java/util/logging/LogManager.java @@ -0,0 +1,986 @@ +/* LogManager.java -- a class for maintaining Loggers and managing + configuration properties + Copyright (C) 2002, 2005, 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import gnu.classpath.SystemProperties; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * The LogManager maintains a hierarchical namespace + * of Logger objects and manages properties for configuring the logging + * framework. There exists only one single LogManager + * per virtual machine. This instance can be retrieved using the + * static method {@link #getLogManager()}. + * + *

        Configuration Process: The global LogManager + * object is created and configured when the class + * java.util.logging.LogManager is initialized. + * The configuration process includes the subsequent steps: + * + *

          + *
        • If the system property java.util.logging.manager + * is set to the name of a subclass of + * java.util.logging.LogManager, an instance of + * that subclass is created and becomes the global LogManager. + * Otherwise, a new instance of LogManager is created.
        • + *
        • The LogManager constructor tries to create + * a new instance of the class specified by the system + * property java.util.logging.config.class. + * Typically, the constructor of this class will call + * LogManager.getLogManager().readConfiguration(java.io.InputStream) + * for configuring the logging framework. + * The configuration process stops at this point if + * the system property java.util.logging.config.class + * is set (irrespective of whether the class constructor + * could be called or an exception was thrown).
        • + * + *
        • If the system property java.util.logging.config.class + * is not set, the configuration parameters are read in from + * a file and passed to + * {@link #readConfiguration(java.io.InputStream)}. + * The name and location of this file are specified by the system + * property java.util.logging.config.file.
        • + *
        • If the system property java.util.logging.config.file + * is not set, however, the contents of the URL + * "{gnu.classpath.home.url}/logging.properties" are passed to + * {@link #readConfiguration(java.io.InputStream)}. + * Here, "{gnu.classpath.home.url}" stands for the value of + * the system property gnu.classpath.home.url.
        • + *
        + * + *

        The LogManager has a level of INFO by + * default, and this will be inherited by Loggers unless they + * override it either by properties or programmatically. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class LogManager +{ + /** + * The object name for the logging management bean. + * @since 1.5 + */ + public static final String LOGGING_MXBEAN_NAME + = "java.util.logging:type=Logging"; + + /** + * The singleton LogManager instance. + */ + private static LogManager logManager; + + /** + * The singleton logging bean. + */ + private static LoggingMXBean loggingBean; + + /** + * The registered named loggers; maps the name of a Logger to + * a WeakReference to it. + */ + private Map> loggers; + + /** + * The properties for the logging framework which have been + * read in last. + */ + private Properties properties; + + /** + * A delegate object that provides support for handling + * PropertyChangeEvents. The API specification does not + * mention which bean should be the source in the distributed + * PropertyChangeEvents, but Mauve test code has determined that + * the Sun J2SE 1.4 reference implementation uses the LogManager + * class object. This is somewhat strange, as the class object + * is not the bean with which listeners have to register, but + * there is no reason for the GNU Classpath implementation to + * behave differently from the reference implementation in + * this case. + */ + private final PropertyChangeSupport pcs = new PropertyChangeSupport( /* source bean */ + LogManager.class); + + protected LogManager() + { + loggers = new HashMap(); + } + + /** + * Returns the globally shared LogManager instance. + */ + public static synchronized LogManager getLogManager() + { + if (logManager == null) + { + logManager = makeLogManager(); + initLogManager(); + } + return logManager; + } + + private static final String MANAGER_PROPERTY = "java.util.logging.manager"; + + private static LogManager makeLogManager() + { + String managerClassName = SystemProperties.getProperty(MANAGER_PROPERTY); + LogManager manager = (LogManager) createInstance + (managerClassName, LogManager.class, MANAGER_PROPERTY); + if (manager == null) + manager = new LogManager(); + return manager; + } + + private static final String CONFIG_PROPERTY = "java.util.logging.config.class"; + + private static void initLogManager() + { + LogManager manager = getLogManager(); + Logger.root.setLevel(Level.INFO); + manager.addLogger(Logger.root); + + /* The Javadoc description of the class explains + * what is going on here. + */ + Object configurator = createInstance(System.getProperty(CONFIG_PROPERTY), + /* must be instance of */ Object.class, + CONFIG_PROPERTY); + + try + { + if (configurator == null) + manager.readConfiguration(); + } + catch (IOException ex) + { + /* FIXME: Is it ok to ignore exceptions here? */ + } + } + + /** + * Registers a listener which will be notified when the + * logging properties are re-read. + */ + public synchronized void addPropertyChangeListener(PropertyChangeListener listener) + { + /* do not register null. */ + listener.getClass(); + + pcs.addPropertyChangeListener(listener); + } + + /** + * Unregisters a listener. + * + * If listener has not been registered previously, + * nothing happens. Also, no exception is thrown if + * listener is null. + */ + public synchronized void removePropertyChangeListener(PropertyChangeListener listener) + { + if (listener != null) + pcs.removePropertyChangeListener(listener); + } + + /** + * Adds a named logger. If a logger with the same name has + * already been registered, the method returns false + * without adding the logger. + * + *

        The LogManager only keeps weak references + * to registered loggers. Therefore, names can become available + * after automatic garbage collection. + * + * @param logger the logger to be added. + * + * @return trueif logger was added, + * false otherwise. + * + * @throws NullPointerException if name is + * null. + */ + public synchronized boolean addLogger(Logger logger) + { + /* To developers thinking about to remove the 'synchronized' + * declaration from this method: Please read the comment + * in java.util.logging.Logger.getLogger(String, String) + * and make sure that whatever you change wrt. synchronization + * does not endanger thread-safety of Logger.getLogger. + * The current implementation of Logger.getLogger assumes + * that LogManager does its synchronization on the globally + * shared instance of LogManager. + */ + String name; + WeakReference ref; + + /* This will throw a NullPointerException if logger is null, + * as required by the API specification. + */ + name = logger.getName(); + + ref = loggers.get(name); + if (ref != null) + { + if (ref.get() != null) + return false; + + /* There has been a logger under this name in the past, + * but it has been garbage collected. + */ + loggers.remove(ref); + } + + /* Adding a named logger requires a security permission. */ + if ((name != null) && ! name.equals("")) + checkAccess(); + + Logger parent = findAncestor(logger); + loggers.put(name, new WeakReference(logger)); + if (parent != logger.getParent()) + logger.setParent(parent); + + // The level of the newly added logger must be specified. + // The easiest case is if there is a level for exactly this logger + // in the properties. If no such level exists the level needs to be + // searched along the hirachy. So if there is a new logger 'foo.blah.blub' + // and an existing parent logger 'foo' the properties 'foo.blah.blub.level' + // and 'foo.blah.level' need to be checked. If both do not exist in the + // properties the level of the new logger is set to 'null' (i.e. it uses the + // level of its parent 'foo'). + Level logLevel = logger.getLevel(); + String searchName = name; + String parentName = parent != null ? parent.getName() : ""; + while (logLevel == null && ! searchName.equals(parentName)) + { + logLevel = getLevelProperty(searchName + ".level", logLevel); + int index = searchName.lastIndexOf('.'); + if(index > -1) + searchName = searchName.substring(0,index); + else + searchName = ""; + } + logger.setLevel(logLevel); + + /* It can happen that existing loggers should be children of + * the newly added logger. For example, assume that there + * already exist loggers under the names "", "foo", and "foo.bar.baz". + * When adding "foo.bar", the logger "foo.bar.baz" should change + * its parent to "foo.bar". + */ + for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();) + { + Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next())) + .get(); + if ((possChild == null) || (possChild == logger) + || (possChild.getParent() != parent)) + continue; + + if (! possChild.getName().startsWith(name)) + continue; + + if (possChild.getName().charAt(name.length()) != '.') + continue; + + possChild.setParent(logger); + } + + return true; + } + + /** + * Finds the closest ancestor for a logger among the currently + * registered ones. For example, if the currently registered + * loggers have the names "", "foo", and "foo.bar", the result for + * "foo.bar.baz" will be the logger whose name is "foo.bar". + * + * @param child a logger for whose name no logger has been + * registered. + * + * @return the closest ancestor for child, + * or null if child + * is the root logger. + * + * @throws NullPointerException if child + * is null. + */ + private synchronized Logger findAncestor(Logger child) + { + String childName = child.getName(); + int childNameLength = childName.length(); + Logger best = Logger.root; + int bestNameLength = 0; + + Logger cand; + int candNameLength; + + if (child == Logger.root) + return null; + + for (String candName : loggers.keySet()) + { + candNameLength = candName.length(); + + if (candNameLength > bestNameLength + && childNameLength > candNameLength + && childName.startsWith(candName) + && childName.charAt(candNameLength) == '.') + { + cand = loggers.get(candName).get(); + if ((cand == null) || (cand == child)) + continue; + + bestNameLength = candName.length(); + best = cand; + } + } + + return best; + } + + /** + * Returns a Logger given its name. + * + * @param name the name of the logger. + * + * @return a named Logger, or null if there is no + * logger with that name. + * + * @throw java.lang.NullPointerException if name + * is null. + */ + public synchronized Logger getLogger(String name) + { + WeakReference ref; + + /* Throw a NullPointerException if name is null. */ + name.getClass(); + + ref = loggers.get(name); + if (ref != null) + return ref.get(); + else + return null; + } + + /** + * Returns an Enumeration of currently registered Logger names. + * Since other threads can register loggers at any time, the + * result could be different any time this method is called. + * + * @return an Enumeration with the names of the currently + * registered Loggers. + */ + public synchronized Enumeration getLoggerNames() + { + return Collections.enumeration(loggers.keySet()); + } + + /** + * Resets the logging configuration by removing all handlers for + * registered named loggers and setting their level to null. + * The level of the root logger will be set to Level.INFO. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public synchronized void reset() throws SecurityException + { + /* Throw a SecurityException if the caller does not have the + * permission to control the logging infrastructure. + */ + checkAccess(); + + properties = new Properties(); + + Iterator> iter = loggers.values().iterator(); + while (iter.hasNext()) + { + WeakReference ref; + Logger logger; + + ref = iter.next(); + if (ref != null) + { + logger = ref.get(); + + if (logger == null) + iter.remove(); + else if (logger != Logger.root) + { + logger.resetLogger(); + logger.setLevel(null); + } + } + } + + Logger.root.setLevel(Level.INFO); + Logger.root.resetLogger(); + } + + /** + * Configures the logging framework by reading a configuration file. + * The name and location of this file are specified by the system + * property java.util.logging.config.file. If this + * property is not set, the URL + * "{gnu.classpath.home.url}/logging.properties" is taken, where + * "{gnu.classpath.home.url}" stands for the value of the system + * property gnu.classpath.home.url. + * + *

        The task of configuring the framework is then delegated to + * {@link #readConfiguration(java.io.InputStream)}, which will + * notify registered listeners after having read the properties. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure, or if the caller is + * not granted the permission to read the configuration + * file. + * + * @throws IOException if there is a problem reading in the + * configuration file. + */ + public synchronized void readConfiguration() + throws IOException, SecurityException + { + String path; + InputStream inputStream; + + path = System.getProperty("java.util.logging.config.file"); + if ((path == null) || (path.length() == 0)) + { + String url = (System.getProperty("gnu.classpath.home.url") + + "/logging.properties"); + try + { + inputStream = new URL(url).openStream(); + } + catch (Exception e) + { + inputStream=null; + } + + // If no config file could be found use a default configuration. + if(inputStream == null) + { + String defaultConfig = "handlers = java.util.logging.ConsoleHandler \n" + + ".level=INFO \n"; + inputStream = new ByteArrayInputStream(defaultConfig.getBytes()); + } + } + else + inputStream = new java.io.FileInputStream(path); + + try + { + readConfiguration(inputStream); + } + finally + { + // Close the stream in order to save + // resources such as file descriptors. + inputStream.close(); + } + } + + public synchronized void readConfiguration(InputStream inputStream) + throws IOException, SecurityException + { + Properties newProperties; + Enumeration keys; + + checkAccess(); + newProperties = new Properties(); + newProperties.load(inputStream); + reset(); + this.properties = newProperties; + keys = newProperties.propertyNames(); + + while (keys.hasMoreElements()) + { + String key = ((String) keys.nextElement()).trim(); + String value = newProperties.getProperty(key); + + if (value == null) + continue; + + value = value.trim(); + + if ("handlers".equals(key)) + { + // In Java 5 and earlier this was specified to be + // whitespace-separated, but in reality it also accepted + // commas (tomcat relied on this), and in Java 6 the + // documentation was updated to fit the implementation. + StringTokenizer tokenizer = new StringTokenizer(value, + " \t\n\r\f,"); + while (tokenizer.hasMoreTokens()) + { + String handlerName = tokenizer.nextToken(); + Handler handler = (Handler) + createInstance(handlerName, Handler.class, key); + // Tomcat also relies on the implementation ignoring + // items in 'handlers' which are not class names. + if (handler != null) + Logger.root.addHandler(handler); + } + } + + if (key.endsWith(".level")) + { + String loggerName = key.substring(0, key.length() - 6); + Logger logger = getLogger(loggerName); + + if (logger == null) + { + logger = Logger.getLogger(loggerName); + addLogger(logger); + } + Level level = null; + try + { + level = Level.parse(value); + } + catch (IllegalArgumentException e) + { + warn("bad level \'" + value + "\'", e); + } + if (level != null) + { + logger.setLevel(level); + } + continue; + } + } + + /* The API specification does not talk about the + * property name that is distributed with the + * PropertyChangeEvent. With test code, it could + * be determined that the Sun J2SE 1.4 reference + * implementation uses null for the property name. + */ + pcs.firePropertyChange(null, null, null); + } + + /** + * Returns the value of a configuration property as a String. + */ + public synchronized String getProperty(String name) + { + if (properties != null) + return properties.getProperty(name); + else + return null; + } + + /** + * Returns the value of a configuration property as an integer. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is not specified in the + * logging API. + * + * @param name the name of the configuration property. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if its value is not an integer + * number. + */ + static int getIntProperty(String name, int defaultValue) + { + try + { + return Integer.parseInt(getLogManager().getProperty(name)); + } + catch (Exception ex) + { + return defaultValue; + } + } + + /** + * Returns the value of a configuration property as an integer, + * provided it is inside the acceptable range. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is not specified in the + * logging API. + * + * @param name the name of the configuration property. + * + * @param minValue the lowest acceptable value. + * + * @param maxValue the highest acceptable value. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if its value is not an integer + * number, or if it is less than the minimum value, + * or if it is greater than the maximum value. + */ + static int getIntPropertyClamped(String name, int defaultValue, + int minValue, int maxValue) + { + int val = getIntProperty(name, defaultValue); + if ((val < minValue) || (val > maxValue)) + val = defaultValue; + return val; + } + + /** + * Returns the value of a configuration property as a boolean. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is not specified in the + * logging API. + * + * @param name the name of the configuration property. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if its value is neither + * "true" nor "false". + */ + static boolean getBooleanProperty(String name, boolean defaultValue) + { + try + { + return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue(); + } + catch (Exception ex) + { + return defaultValue; + } + } + + /** + * Returns the value of a configuration property as a Level. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is not specified in the + * logging API. + * + * @param propertyName the name of the configuration property. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if + * {@link Level#parse(java.lang.String)} does not like + * the property value. + */ + static Level getLevelProperty(String propertyName, Level defaultValue) + { + try + { + String value = getLogManager().getProperty(propertyName); + if (value != null) + return Level.parse(getLogManager().getProperty(propertyName)); + else + return defaultValue; + } + catch (Exception ex) + { + return defaultValue; + } + } + + /** + * Returns the value of a configuration property as a Class. + * This function is a helper used by the Classpath implementation + * of java.util.logging, it is not specified in the + * logging API. + * + * @param propertyName the name of the configuration property. + * + * @param defaultValue the value that will be returned if the + * property is not defined, or if it does not specify + * the name of a loadable class. + */ + static final Class getClassProperty(String propertyName, Class defaultValue) + { + String propertyValue = logManager.getProperty(propertyName); + + if (propertyValue != null) + try + { + return locateClass(propertyValue); + } + catch (ClassNotFoundException e) + { + warn(propertyName + " = " + propertyValue, e); + } + + return defaultValue; + } + + static final Object getInstanceProperty(String propertyName, Class ofClass, + Class defaultClass) + { + Class klass = getClassProperty(propertyName, defaultClass); + if (klass == null) + return null; + + try + { + Object obj = klass.newInstance(); + if (ofClass.isInstance(obj)) + return obj; + } + catch (InstantiationException e) + { + warn(propertyName + " = " + klass.getName(), e); + } + catch (IllegalAccessException e) + { + warn(propertyName + " = " + klass.getName(), e); + } + + if (defaultClass == null) + return null; + + try + { + return defaultClass.newInstance(); + } + catch (java.lang.InstantiationException ex) + { + throw new RuntimeException(ex.getMessage()); + } + catch (java.lang.IllegalAccessException ex) + { + throw new RuntimeException(ex.getMessage()); + } + } + + /** + * An instance of LoggingPermission("control") + * that is shared between calls to checkAccess(). + */ + private static final LoggingPermission controlPermission = new LoggingPermission("control", + null); + + /** + * Checks whether the current security context allows changing + * the configuration of the logging framework. For the security + * context to be trusted, it has to be granted + * a LoggingPermission("control"). + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public void checkAccess() throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(controlPermission); + } + + /** + * Creates a new instance of a class specified by name and verifies + * that it is an instance (or subclass of) a given type. + * + * @param className the name of the class of which a new instance + * should be created. + * + * @param type the object created must be an instance of + * type or any subclass of type + * + * @param property the system property to reference in error + * messages + * + * @return the new instance, or null if + * className is null, if no class + * with that name could be found, if there was an error + * loading that class, or if the constructor of the class + * has thrown an exception. + */ + private static final Object createInstance(String className, Class type, + String property) + { + Class klass = null; + + if ((className == null) || (className.length() == 0)) + return null; + + try + { + klass = locateClass(className); + if (type.isAssignableFrom(klass)) + return klass.newInstance(); + warn(property, className, "not an instance of " + type.getName()); + } + catch (ClassNotFoundException e) + { + warn(property, className, "class not found", e); + } + catch (IllegalAccessException e) + { + warn(property, className, "illegal access", e); + } + catch (InstantiationException e) + { + warn(property, className, e); + } + catch (java.lang.LinkageError e) + { + warn(property, className, "linkage error", e); + } + + return null; + } + + private static final void warn(String property, String klass, Throwable t) + { + warn(property, klass, null, t); + } + + private static final void warn(String property, String klass, String msg) + { + warn(property, klass, msg, null); + } + + private static final void warn(String property, String klass, String msg, + Throwable t) + { + warn("error instantiating '" + klass + "' referenced by " + property + + (msg == null ? "" : ", " + msg), t); + } + + /** + * All debug warnings go through this method. + */ + + private static final void warn(String msg, Throwable t) + { + System.err.println("WARNING: " + msg); + if (t != null) + t.printStackTrace(System.err); + } + + /** + * Locates a class by first checking the system class loader and + * then checking the context class loader. + * + * @param name the fully qualified name of the Class to locate + * @return Class the located Class + */ + + private static Class locateClass(String name) throws ClassNotFoundException + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try + { + return Class.forName(name, true, loader); + } + catch (ClassNotFoundException e) + { + loader = ClassLoader.getSystemClassLoader(); + return Class.forName(name, true, loader); + } + } + + /** + * Return the logging bean. There is a single logging bean per + * VM instance. + * @since 1.5 + */ + public static synchronized LoggingMXBean getLoggingMXBean() + { + if (loggingBean == null) + { + loggingBean = new LoggingMXBean() + { + public String getLoggerLevel(String logger) + { + LogManager mgr = getLogManager(); + Logger l = mgr.getLogger(logger); + if (l == null) + return null; + Level lev = l.getLevel(); + if (lev == null) + return ""; + return lev.getName(); + } + + public List getLoggerNames() + { + LogManager mgr = getLogManager(); + // This is inefficient, but perhaps better for maintenance. + return Collections.list(mgr.getLoggerNames()); + } + + public String getParentLoggerName(String logger) + { + LogManager mgr = getLogManager(); + Logger l = mgr.getLogger(logger); + if (l == null) + return null; + l = l.getParent(); + if (l == null) + return ""; + return l.getName(); + } + + public void setLoggerLevel(String logger, String level) + { + LogManager mgr = getLogManager(); + Logger l = mgr.getLogger(logger); + if (l == null) + throw new IllegalArgumentException("no logger named " + logger); + Level newLevel; + if (level == null) + newLevel = null; + else + newLevel = Level.parse(level); + l.setLevel(newLevel); + } + }; + } + return loggingBean; + } +} diff --git a/libjava/classpath/java/util/logging/LogRecord.java b/libjava/classpath/java/util/logging/LogRecord.java new file mode 100644 index 000000000..ee99ee69a --- /dev/null +++ b/libjava/classpath/java/util/logging/LogRecord.java @@ -0,0 +1,672 @@ +/* LogRecord.java -- + A class for the state associated with individual logging events + Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.util.ResourceBundle; + + +/** + * A LogRecord contains the state for an individual + * event to be logged. + * + *

        As soon as a LogRecord instance has been handed over to the + * logging framework, applications should not manipulate it anymore. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class LogRecord + implements java.io.Serializable +{ + /** + * The severity level of this LogRecord. + */ + private Level level; + + + /** + * The sequence number of this LogRecord. + */ + private long sequenceNumber; + + + /** + * The name of the class that issued the logging request, or + * null if this information could not be obtained. + */ + private String sourceClassName; + + + /** + * The name of the method that issued the logging request, or + * null if this information could not be obtained. + */ + private String sourceMethodName; + + + /** + * The message for this LogRecord before + * any localization or formatting. + */ + private String message; + + + /** + * An identifier for the thread in which this LogRecord + * was created. The identifier is not necessarily related to any + * thread identifiers used by the operating system. + */ + private int threadID; + + + /** + * The time when this LogRecord was created, + * in milliseconds since the beginning of January 1, 1970. + */ + private long millis; + + + /** + * The Throwable associated with this LogRecord, or + * null if the logged event is not related to an + * exception or error. + */ + private Throwable thrown; + + + /** + * The name of the logger where this LogRecord has + * originated, or null if this LogRecord + * does not originate from a Logger. + */ + private String loggerName; + + + /** + * The name of the resource bundle used for localizing log messages, + * or null if no bundle has been specified. + */ + private String resourceBundleName; + + private transient Object[] parameters; + + private transient ResourceBundle bundle; + + + /** + * Constructs a LogRecord given a severity level and + * an unlocalized message text. In addition, the sequence number, + * creation time (as returned by getMillis()) and + * thread ID are assigned. All other properties are set to + * null. + * + * @param level the severity level, for example Level.WARNING. + * + * @param message the message text (which will be used as key + * for looking up the localized message text + * if a resource bundle has been associated). + */ + public LogRecord(Level level, String message) + { + this.level = level; + this.message = message; + this.millis = System.currentTimeMillis(); + + /* A subclass of java.lang.Thread could override hashCode(), + * in which case the result would not be guaranteed anymore + * to be unique among all threads. While System.identityHashCode + * is not necessarily unique either, it at least cannot be + * overridden by user code. However, is might be a good idea + * to use something better for generating thread IDs. + */ + this.threadID = System.identityHashCode(Thread.currentThread()); + + sequenceNumber = allocateSeqNum(); + } + + + /** + * Determined with the serialver tool of the Sun J2SE 1.4. + */ + static final long serialVersionUID = 5372048053134512534L; + + private void readObject(java.io.ObjectInputStream in) + throws java.io.IOException, java.lang.ClassNotFoundException + { + in.defaultReadObject(); + + /* We assume that future versions will be downwards compatible, + * so we can ignore the versions. + */ + byte majorVersion = in.readByte(); + byte minorVersion = in.readByte(); + + int numParams = in.readInt(); + if (numParams >= 0) + { + parameters = new Object[numParams]; + for (int i = 0; i < numParams; i++) + parameters[i] = in.readObject(); + } + } + + + /** + * @serialData The default fields, followed by a major byte version + * number, followed by a minor byte version number, followed by + * information about the log record parameters. If + * parameters is null, the integer -1 is + * written, otherwise the length of the parameters + * array (which can be zero), followed by the result of calling + * {@link Object#toString() toString()} on the parameter (or + * null if the parameter is null). + * + *

        Specification Note: The Javadoc for the + * Sun reference implementation does not specify the version + * number. FIXME: Reverse-engineer the JDK and file a bug + * report with Sun, asking for amendment of the specification. + */ + private void writeObject(java.io.ObjectOutputStream out) + throws java.io.IOException + { + out.defaultWriteObject(); + + /* Major, minor version number: The Javadoc for J2SE1.4 does not + * specify the values. + */ + out.writeByte(0); + out.writeByte(0); + + if (parameters == null) + out.writeInt(-1); + else + { + out.writeInt(parameters.length); + for (int i = 0; i < parameters.length; i++) + { + if (parameters[i] == null) + out.writeObject(null); + else + out.writeObject(parameters[i].toString()); + } + } + } + + + /** + * Returns the name of the logger where this LogRecord + * has originated. + * + * @return the name of the source {@link Logger}, or + * null if this LogRecord + * does not originate from a Logger. + */ + public String getLoggerName() + { + return loggerName; + } + + + /** + * Sets the name of the logger where this LogRecord + * has originated. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param name the name of the source logger, or null to + * indicate that this LogRecord does not + * originate from a Logger. + */ + public void setLoggerName(String name) + { + loggerName = name; + } + + + /** + * Returns the resource bundle that is used when the message + * of this LogRecord needs to be localized. + * + * @return the resource bundle used for localization, + * or null if this message does not need + * to be localized. + */ + public ResourceBundle getResourceBundle() + { + return bundle; + } + + + /** + * Sets the resource bundle that is used when the message + * of this LogRecord needs to be localized. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param bundle the resource bundle to be used, or + * null to indicate that this + * message does not need to be localized. + */ + public void setResourceBundle(ResourceBundle bundle) + { + this.bundle = bundle; + + /* FIXME: Is there a way to infer the name + * of a resource bundle from a ResourceBundle object? + */ + this.resourceBundleName = null; + } + + + /** + * Returns the name of the resource bundle that is used when the + * message of this LogRecord needs to be localized. + * + * @return the name of the resource bundle used for localization, + * or null if this message does not need + * to be localized. + */ + public String getResourceBundleName() + { + return resourceBundleName; + } + + + /** + * Sets the name of the resource bundle that is used when the + * message of this LogRecord needs to be localized. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param name the name of the resource bundle to be used, or + * null to indicate that this message + * does not need to be localized. + */ + public void setResourceBundleName(String name) + { + resourceBundleName = name; + bundle = null; + + try + { + if (resourceBundleName != null) + bundle = ResourceBundle.getBundle(resourceBundleName); + } + catch (java.util.MissingResourceException _) + { + } + } + + + /** + * Returns the level of the LogRecord. + * + *

        Applications should be aware of the possibility that the + * result is not necessarily one of the standard logging levels, + * since the logging framework allows to create custom subclasses + * of java.util.logging.Level. Therefore, filters + * should perform checks like theRecord.getLevel().intValue() + * == Level.INFO.intValue() instead of theRecord.getLevel() + * == Level.INFO. + */ + public Level getLevel() + { + return level; + } + + + /** + * Sets the severity level of this LogRecord to a new + * value. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param level the new severity level, for example + * Level.WARNING. + */ + public void setLevel(Level level) + { + this.level = level; + } + + + /** + * The last used sequence number for any LogRecord. + */ + private static long lastSeqNum; + + + /** + * Allocates a sequence number for a new LogRecord. This class + * method is only called by the LogRecord constructor. + */ + private static synchronized long allocateSeqNum() + { + lastSeqNum += 1; + return lastSeqNum; + } + + + /** + * Returns the sequence number of this LogRecord. + */ + public long getSequenceNumber() + { + return sequenceNumber; + } + + + /** + * Sets the sequence number of this LogRecord to a new + * value. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param seqNum the new sequence number. + */ + public void setSequenceNumber(long seqNum) + { + this.sequenceNumber = seqNum; + } + + + /** + * Returns the name of the class where the event being logged + * has had its origin. This information can be passed as + * parameter to some logging calls, and in certain cases, the + * logging framework tries to determine an approximation + * (which may or may not be accurate). + * + * @return the name of the class that issued the logging request, + * or null if this information could not + * be obtained. + */ + public String getSourceClassName() + { + if (sourceClassName != null) + return sourceClassName; + + /* FIXME: Should infer this information from the call stack. */ + return null; + } + + + /** + * Sets the name of the class where the event being logged + * has had its origin. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param sourceClassName the name of the class that issued the + * logging request, or null to indicate that + * this information could not be obtained. + */ + public void setSourceClassName(String sourceClassName) + { + this.sourceClassName = sourceClassName; + } + + + /** + * Returns the name of the method where the event being logged + * has had its origin. This information can be passed as + * parameter to some logging calls, and in certain cases, the + * logging framework tries to determine an approximation + * (which may or may not be accurate). + * + * @return the name of the method that issued the logging request, + * or null if this information could not + * be obtained. + */ + public String getSourceMethodName() + { + if (sourceMethodName != null) + return sourceMethodName; + + /* FIXME: Should infer this information from the call stack. */ + return null; + } + + + /** + * Sets the name of the method where the event being logged + * has had its origin. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param sourceMethodName the name of the method that issued the + * logging request, or null to indicate that + * this information could not be obtained. + */ + public void setSourceMethodName(String sourceMethodName) + { + this.sourceMethodName = sourceMethodName; + } + + + /** + * Returns the message for this LogRecord before + * any localization or parameter substitution. + * + *

        A {@link Logger} will try to localize the message + * if a resource bundle has been associated with this + * LogRecord. In this case, the logger will call + * getMessage() and use the result as the key + * for looking up the localized message in the bundle. + * If no bundle has been associated, or if the result of + * getMessage() is not a valid key in the + * bundle, the logger will use the raw message text as + * returned by this method. + * + * @return the message text, or null if there + * is no message text. + */ + public String getMessage() + { + return message; + } + + + /** + * Sets the message for this LogRecord. + * + *

        A Logger will try to localize the message + * if a resource bundle has been associated with this + * LogRecord. In this case, the logger will call + * getMessage() and use the result as the key + * for looking up the localized message in the bundle. + * If no bundle has been associated, or if the result of + * getMessage() is not a valid key in the + * bundle, the logger will use the raw message text as + * returned by this method. + * + *

        It is possible to set the message to either an empty String or + * null, although this does not make the the message + * very helpful to human users. + * + * @param message the message text (which will be used as key + * for looking up the localized message text + * if a resource bundle has been associated). + */ + public void setMessage(String message) + { + this.message = message; + } + + + /** + * Returns the parameters to the log message. + * + * @return the parameters to the message, or null if + * the message has no parameters. + */ + public Object[] getParameters() + { + return parameters; + } + + + /** + * Sets the parameters to the log message. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param parameters the parameters to the message, or null + * to indicate that the message has no parameters. + */ + public void setParameters(Object[] parameters) + { + this.parameters = parameters; + } + + + /** + * Returns an identifier for the thread in which this + * LogRecord was created. The identifier is not + * necessarily related to any thread identifiers used by the + * operating system. + * + * @return an identifier for the source thread. + */ + public int getThreadID() + { + return threadID; + } + + + /** + * Sets the identifier indicating in which thread this + * LogRecord was created. The identifier is not + * necessarily related to any thread identifiers used by the + * operating system. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param threadID the identifier for the source thread. + */ + public void setThreadID(int threadID) + { + this.threadID = threadID; + } + + + /** + * Returns the time when this LogRecord was created. + * + * @return the time of creation in milliseconds since the beginning + * of January 1, 1970. + */ + public long getMillis() + { + return millis; + } + + + /** + * Sets the time when this LogRecord was created. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param millis the time of creation in milliseconds since the + * beginning of January 1, 1970. + */ + public void setMillis(long millis) + { + this.millis = millis; + } + + + /** + * Returns the Throwable associated with this LogRecord, + * or null if the logged event is not related to an exception + * or error. + */ + public Throwable getThrown() + { + return thrown; + } + + + /** + * Associates this LogRecord with an exception or error. + * + *

        As soon as a LogRecord has been handed over + * to the logging framework, applications should not modify it + * anymore. Therefore, this method should only be called on + * freshly constructed LogRecords. + * + * @param thrown the exception or error to associate with, or + * null if this LogRecord + * should be made unrelated to an exception or error. + */ + public void setThrown(Throwable thrown) + { + this.thrown = thrown; + } +} diff --git a/libjava/classpath/java/util/logging/Logger.java b/libjava/classpath/java/util/logging/Logger.java new file mode 100644 index 000000000..c55e133e5 --- /dev/null +++ b/libjava/classpath/java/util/logging/Logger.java @@ -0,0 +1,1193 @@ +/* Logger.java -- a class for logging messages + Copyright (C) 2002, 2004, 2006, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import gnu.java.lang.CPStringBuilder; + +import java.util.List; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * A Logger is used for logging information about events. Usually, there is a + * seprate logger for each subsystem or component, although there is a shared + * instance for components that make only occasional use of the logging + * framework. + *

        + * It is common to name a logger after the name of a corresponding Java package. + * Loggers are organized into a hierarchical namespace; for example, the logger + * "org.gnu.foo" is the parent of logger + * "org.gnu.foo.bar". + *

        + * A logger for a named subsystem can be obtained through {@link + * java.util.logging.Logger#getLogger(java.lang.String)}. However, only code + * which has been granted the permission to control the logging infrastructure + * will be allowed to customize that logger. Untrusted code can obtain a + * private, anonymous logger through {@link #getAnonymousLogger()} if it wants + * to perform any modifications to the logger. + *

        + * FIXME: Write more documentation. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class Logger +{ + static final Logger root = new Logger("", null); + + /** + * A logger provided to applications that make only occasional use of the + * logging framework, typically early prototypes. Serious products are + * supposed to create and use their own Loggers, so they can be controlled + * individually. + */ + public static final Logger global; + + /** + * Use to lock methods on this class instead of calling synchronize on methods + * to avoid deadlocks. Yeah, no kidding, we got them :) + */ + private static final Object[] lock = new Object[0]; + + static + { + // Our class might be initialized from an unprivileged context + global = (Logger) AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + return getLogger("global"); + } + }); + } + + /** + * The name of the Logger, or null if the logger is anonymous. + *

        + * A previous version of the GNU Classpath implementation granted untrusted + * code the permission to control any logger whose name was null. However, + * test code revealed that the Sun J2SE 1.4 reference implementation enforces + * the security control for any logger that was not created through + * getAnonymousLogger, even if it has a null name. Therefore, a separate flag + * {@link Logger#anonymous} was introduced. + */ + private final String name; + + /** + * The name of the resource bundle used for localization. + *

        + * This variable cannot be declared as final because its value + * can change as a result of calling getLogger(String,String). + */ + private String resourceBundleName; + + /** + * The resource bundle used for localization. + *

        + * This variable cannot be declared as final because its value + * can change as a result of calling getLogger(String,String). + */ + private ResourceBundle resourceBundle; + + private Filter filter; + + private final List handlerList = new java.util.ArrayList(4); + + private Handler[] handlers = new Handler[0]; + + /** + * Indicates whether or not this logger is anonymous. While a + * LoggingPermission is required for any modifications to a normal logger, + * untrusted code can obtain an anonymous logger and modify it according to + * its needs. + *

        + * A previous version of the GNU Classpath implementation granted access to + * every logger whose name was null. However, test code revealed that the Sun + * J2SE 1.4 reference implementation enforces the security control for any + * logger that was not created through getAnonymousLogger, even if it has a + * null name. + */ + private boolean anonymous; + + private boolean useParentHandlers; + + private Level level; + + private Logger parent; + + /** + * Constructs a Logger for a subsystem. Most applications do not need to + * create new Loggers explicitly; instead, they should call the static factory + * methods {@link #getLogger(java.lang.String,java.lang.String) getLogger} + * (with ResourceBundle for localization) or + * {@link #getLogger(java.lang.String) getLogger} (without ResourceBundle), + * respectively. + * + * @param name the name for the logger, for example "java.awt" or + * "com.foo.bar". The name should be based on the name of the + * package issuing log records and consist of dot-separated Java + * identifiers. + * @param resourceBundleName the name of a resource bundle for localizing + * messages, or null to indicate that messages do + * not need to be localized. + * @throws java.util.MissingResourceException if + * resourceBundleName is not null + * and no such bundle could be located. + */ + protected Logger(String name, String resourceBundleName) + throws MissingResourceException + { + this.name = name; + this.resourceBundleName = resourceBundleName; + + if (resourceBundleName == null) + resourceBundle = null; + else + resourceBundle = ResourceBundle.getBundle(resourceBundleName); + + level = null; + + /* + * This is null when the root logger is being constructed, and the root + * logger afterwards. + */ + parent = root; + + useParentHandlers = (parent != null); + } + + /** + * Finds a registered logger for a subsystem, or creates one in case no logger + * has been registered yet. + * + * @param name the name for the logger, for example "java.awt" or + * "com.foo.bar". The name should be based on the name of the + * package issuing log records and consist of dot-separated Java + * identifiers. + * @throws IllegalArgumentException if a logger for the subsystem identified + * by name has already been created, but uses a a + * resource bundle for localizing messages. + * @throws NullPointerException if name is null. + * @return a logger for the subsystem specified by name that + * does not localize messages. + */ + public static Logger getLogger(String name) + { + return getLogger(name, null); + } + + /** + * Finds a registered logger for a subsystem, or creates one in case no logger + * has been registered yet. + *

        + * If a logger with the specified name has already been registered, the + * behavior depends on the resource bundle that is currently associated with + * the existing logger. + *

          + *
        • If the existing logger uses the same resource bundle as specified by + * resourceBundleName, the existing logger is returned.
        • + *
        • If the existing logger currently does not localize messages, the + * existing logger is modified to use the bundle specified by + * resourceBundleName. The existing logger is then returned. + * Therefore, all subsystems currently using this logger will produce + * localized messages from now on.
        • + *
        • If the existing logger already has an associated resource bundle, but + * a different one than specified by resourceBundleName, an + * IllegalArgumentException is thrown.
        • + *
        + * + * @param name the name for the logger, for example "java.awt" or + * "org.gnu.foo". The name should be based on the name of the + * package issuing log records and consist of dot-separated Java + * identifiers. + * @param resourceBundleName the name of a resource bundle for localizing + * messages, or null to indicate that messages do + * not need to be localized. + * @return a logger for the subsystem specified by name. + * @throws java.util.MissingResourceException if + * resourceBundleName is not null + * and no such bundle could be located. + * @throws IllegalArgumentException if a logger for the subsystem identified + * by name has already been created, but uses a + * different resource bundle for localizing messages. + * @throws NullPointerException if name is null. + */ + public static Logger getLogger(String name, String resourceBundleName) + { + LogManager lm = LogManager.getLogManager(); + Logger result; + + if (name == null) + throw new NullPointerException(); + + /* + * Without synchronized(lm), it could happen that another thread would + * create a logger between our calls to getLogger and addLogger. While + * addLogger would indicate this by returning false, we could not be sure + * that this other logger was still existing when we called getLogger a + * second time in order to retrieve it -- note that LogManager is only + * allowed to keep weak references to registered loggers, so Loggers can be + * garbage collected at any time in general, and between our call to + * addLogger and our second call go getLogger in particular. Of course, we + * assume here that LogManager.addLogger etc. are synchronizing on the + * global LogManager object. There is a comment in the implementation of + * LogManager.addLogger referring to this comment here, so that any change + * in the synchronization of LogManager will be reflected here. + */ + synchronized (lock) + { + synchronized (lm) + { + result = lm.getLogger(name); + if (result == null) + { + boolean couldBeAdded; + + result = new Logger(name, resourceBundleName); + couldBeAdded = lm.addLogger(result); + if (! couldBeAdded) + throw new IllegalStateException("cannot register new logger"); + } + else + { + /* + * The logger already exists. Make sure it uses the same + * resource bundle for localizing messages. + */ + String existingBundleName = result.getResourceBundleName(); + + /* + * The Sun J2SE 1.4 reference implementation will return the + * registered logger object, even if it does not have a resource + * bundle associated with it. However, it seems to change the + * resourceBundle of the registered logger to the bundle whose + * name was passed to getLogger. + */ + if ((existingBundleName == null) && + (resourceBundleName != null)) + { + /* + * If ResourceBundle.getBundle throws an exception, the + * existing logger will be unchanged. This would be + * different if the assignment to resourceBundleName came + * first. + */ + result.resourceBundle = + ResourceBundle.getBundle(resourceBundleName); + + result.resourceBundleName = resourceBundleName; + return result; + } + + if ((existingBundleName != resourceBundleName) + && ((existingBundleName == null) + || !existingBundleName.equals(resourceBundleName))) + { + throw new IllegalArgumentException(); + } + } + } + } + + return result; + } + + /** + * Creates a new, unnamed logger. Unnamed loggers are not registered in the + * namespace of the LogManager, and no special security permission is required + * for changing their state. Therefore, untrusted applets are able to modify + * their private logger instance obtained through this method. + *

        + * The parent of the newly created logger will the the root logger, from which + * the level threshold and the handlers are inherited. + */ + public static Logger getAnonymousLogger() + { + return getAnonymousLogger(null); + } + + /** + * Creates a new, unnamed logger. Unnamed loggers are not registered in the + * namespace of the LogManager, and no special security permission is required + * for changing their state. Therefore, untrusted applets are able to modify + * their private logger instance obtained through this method. + *

        + * The parent of the newly created logger will the the root logger, from which + * the level threshold and the handlers are inherited. + * + * @param resourceBundleName the name of a resource bundle for localizing + * messages, or null to indicate that messages do + * not need to be localized. + * @throws java.util.MissingResourceException if + * resourceBundleName is not null + * and no such bundle could be located. + */ + public static Logger getAnonymousLogger(String resourceBundleName) + throws MissingResourceException + { + Logger result; + + result = new Logger(null, resourceBundleName); + result.anonymous = true; + return result; + } + + /** + * Returns the name of the resource bundle that is being used for localizing + * messages. + * + * @return the name of the resource bundle used for localizing messages, or + * null if the parent's resource bundle is used for + * this purpose. + */ + public String getResourceBundleName() + { + synchronized (lock) + { + return resourceBundleName; + } + } + + /** + * Returns the resource bundle that is being used for localizing messages. + * + * @return the resource bundle used for localizing messages, or + * null if the parent's resource bundle is used for + * this purpose. + */ + public ResourceBundle getResourceBundle() + { + synchronized (lock) + { + return resourceBundle; + } + } + + /** + * Returns the severity level threshold for this Handler. All + * log records with a lower severity level will be discarded; a log record of + * the same or a higher level will be published unless an installed + * Filter decides to discard it. + * + * @return the severity level below which all log messages will be discarded, + * or null if the logger inherits the threshold from + * its parent. + */ + public Level getLevel() + { + synchronized (lock) + { + return level; + } + } + + /** + * Returns whether or not a message of the specified level would be logged by + * this logger. + * + * @throws NullPointerException if level is null. + */ + public boolean isLoggable(Level level) + { + synchronized (lock) + { + if (this.level != null) + return this.level.intValue() <= level.intValue(); + + if (parent != null) + return parent.isLoggable(level); + else + return false; + } + } + + /** + * Sets the severity level threshold for this Handler. All log + * records with a lower severity level will be discarded immediately. A log + * record of the same or a higher level will be published unless an installed + * Filter decides to discard it. + * + * @param level the severity level below which all log messages will be + * discarded, or null to indicate that the logger + * should inherit the threshold from its parent. + * @throws SecurityException if this logger is not anonymous, a security + * manager exists, and the caller is not granted the permission to + * control the logging infrastructure by having + * LoggingPermission("control"). Untrusted code can obtain an + * anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public void setLevel(Level level) + { + synchronized (lock) + { + /* + * An application is allowed to control an anonymous logger without + * having the permission to control the logging infrastructure. + */ + if (! anonymous) + LogManager.getLogManager().checkAccess(); + + this.level = level; + } + } + + public Filter getFilter() + { + synchronized (lock) + { + return filter; + } + } + + /** + * @throws SecurityException if this logger is not anonymous, a security + * manager exists, and the caller is not granted the permission to + * control the logging infrastructure by having + * LoggingPermission("control"). Untrusted code can obtain an + * anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public void setFilter(Filter filter) throws SecurityException + { + synchronized (lock) + { + /* + * An application is allowed to control an anonymous logger without + * having the permission to control the logging infrastructure. + */ + if (! anonymous) + LogManager.getLogManager().checkAccess(); + + this.filter = filter; + } + } + + /** + * Returns the name of this logger. + * + * @return the name of this logger, or null if the logger is + * anonymous. + */ + public String getName() + { + /* + * Note that the name of a logger cannot be changed during its lifetime, so + * no synchronization is needed. + */ + return name; + } + + /** + * Passes a record to registered handlers, provided the record is considered + * as loggable both by {@link #isLoggable(Level)} and a possibly installed + * custom {@link #setFilter(Filter) filter}. + *

        + * If the logger has been configured to use parent handlers, the record will + * be forwarded to the parent of this logger in addition to being processed by + * the handlers registered with this logger. + *

        + * The other logging methods in this class are convenience methods that merely + * create a new LogRecord and pass it to this method. Therefore, subclasses + * usually just need to override this single method for customizing the + * logging behavior. + * + * @param record the log record to be inspected and possibly forwarded. + */ + public void log(LogRecord record) + { + synchronized (lock) + { + if (!isLoggable(record.getLevel())) + return; + + if ((filter != null) && ! filter.isLoggable(record)) + return; + + /* + * If no logger name has been set for the log record, use the name of + * this logger. + */ + if (record.getLoggerName() == null) + record.setLoggerName(name); + + /* + * Avoid that some other thread is changing the logger hierarchy while + * we are traversing it. + */ + synchronized (LogManager.getLogManager()) + { + Logger curLogger = this; + + do + { + /* + * The Sun J2SE 1.4 reference implementation seems to call the + * filter only for the logger whose log method is called, never + * for any of its parents. Also, parent loggers publish log + * record whatever their level might be. This is pretty weird, + * but GNU Classpath tries to be as compatible as possible to + * the reference implementation. + */ + for (int i = 0; i < curLogger.handlers.length; i++) + curLogger.handlers[i].publish(record); + + if (curLogger.getUseParentHandlers() == false) + break; + + curLogger = curLogger.getParent(); + } + while (parent != null); + } + } + } + + public void log(Level level, String message) + { + if (isLoggable(level)) + log(level, message, (Object[]) null); + } + + public void log(Level level, String message, Object param) + { + synchronized (lock) + { + if (isLoggable(level)) + { + StackTraceElement caller = getCallerStackFrame(); + logp(level, caller != null ? caller.getClassName() : "", + caller != null ? caller.getMethodName() : "", + message, param); + } + } + } + + public void log(Level level, String message, Object[] params) + { + synchronized (lock) + { + if (isLoggable(level)) + { + StackTraceElement caller = getCallerStackFrame(); + logp(level, caller != null ? caller.getClassName() : "", + caller != null ? caller.getMethodName() : "", + message, params); + + } + } + } + + public void log(Level level, String message, Throwable thrown) + { + synchronized (lock) + { + if (isLoggable(level)) + { + StackTraceElement caller = getCallerStackFrame(); + logp(level, caller != null ? caller.getClassName() : "", + caller != null ? caller.getMethodName() : "", + message, thrown); + } + } + } + + public void logp(Level level, String sourceClass, String sourceMethod, + String message) + { + synchronized (lock) + { + logp(level, sourceClass, sourceMethod, message, (Object[]) null); + } + } + + public void logp(Level level, String sourceClass, String sourceMethod, + String message, Object param) + { + synchronized (lock) + { + logp(level, sourceClass, sourceMethod, message, new Object[] { param }); + } + + } + + private ResourceBundle findResourceBundle() + { + synchronized (lock) + { + if (resourceBundle != null) + return resourceBundle; + + if (parent != null) + return parent.findResourceBundle(); + + return null; + } + } + + private void logImpl(Level level, String sourceClass, String sourceMethod, + String message, Object[] params) + { + synchronized (lock) + { + LogRecord rec = new LogRecord(level, message); + + rec.setResourceBundle(findResourceBundle()); + rec.setSourceClassName(sourceClass); + rec.setSourceMethodName(sourceMethod); + rec.setParameters(params); + + log(rec); + } + } + + public void logp(Level level, String sourceClass, String sourceMethod, + String message, Object[] params) + { + synchronized (lock) + { + logImpl(level, sourceClass, sourceMethod, message, params); + } + } + + public void logp(Level level, String sourceClass, String sourceMethod, + String message, Throwable thrown) + { + synchronized (lock) + { + LogRecord rec = new LogRecord(level, message); + + rec.setResourceBundle(resourceBundle); + rec.setSourceClassName(sourceClass); + rec.setSourceMethodName(sourceMethod); + rec.setThrown(thrown); + + log(rec); + } + } + + public void logrb(Level level, String sourceClass, String sourceMethod, + String bundleName, String message) + { + synchronized (lock) + { + logrb(level, sourceClass, sourceMethod, bundleName, message, + (Object[]) null); + } + } + + public void logrb(Level level, String sourceClass, String sourceMethod, + String bundleName, String message, Object param) + { + synchronized (lock) + { + logrb(level, sourceClass, sourceMethod, bundleName, message, + new Object[] { param }); + } + } + + public void logrb(Level level, String sourceClass, String sourceMethod, + String bundleName, String message, Object[] params) + { + synchronized (lock) + { + LogRecord rec = new LogRecord(level, message); + + rec.setResourceBundleName(bundleName); + rec.setSourceClassName(sourceClass); + rec.setSourceMethodName(sourceMethod); + rec.setParameters(params); + + log(rec); + } + } + + public void logrb(Level level, String sourceClass, String sourceMethod, + String bundleName, String message, Throwable thrown) + { + synchronized (lock) + { + LogRecord rec = new LogRecord(level, message); + + rec.setResourceBundleName(bundleName); + rec.setSourceClassName(sourceClass); + rec.setSourceMethodName(sourceMethod); + rec.setThrown(thrown); + + log(rec); + } + } + + public void entering(String sourceClass, String sourceMethod) + { + synchronized (lock) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "ENTRY"); + } + } + + public void entering(String sourceClass, String sourceMethod, Object param) + { + synchronized (lock) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "ENTRY {0}", param); + } + } + + public void entering(String sourceClass, String sourceMethod, Object[] params) + { + synchronized (lock) + { + if (isLoggable(Level.FINER)) + { + CPStringBuilder buf = new CPStringBuilder(80); + buf.append("ENTRY"); + for (int i = 0; i < params.length; i++) + { + buf.append(" {"); + buf.append(i); + buf.append('}'); + } + + logp(Level.FINER, sourceClass, sourceMethod, buf.toString(), params); + } + } + } + + public void exiting(String sourceClass, String sourceMethod) + { + synchronized (lock) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "RETURN"); + } + } + + public void exiting(String sourceClass, String sourceMethod, Object result) + { + synchronized (lock) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "RETURN {0}", result); + } + } + + public void throwing(String sourceClass, String sourceMethod, Throwable thrown) + { + synchronized (lock) + { + if (isLoggable(Level.FINER)) + logp(Level.FINER, sourceClass, sourceMethod, "THROW", thrown); + } + } + + /** + * Logs a message with severity level SEVERE, indicating a serious failure + * that prevents normal program execution. Messages at this level should be + * understandable to an inexperienced, non-technical end user. Ideally, they + * explain in simple words what actions the user can take in order to resolve + * the problem. + * + * @see Level#SEVERE + * @param message the message text, also used as look-up key if the logger is + * localizing messages with a resource bundle. While it is possible + * to pass null, this is not recommended, since a + * logging message without text is unlikely to be helpful. + */ + public void severe(String message) + { + synchronized (lock) + { + if (isLoggable(Level.SEVERE)) + log(Level.SEVERE, message); + } + } + + /** + * Logs a message with severity level WARNING, indicating a potential problem + * that does not prevent normal program execution. Messages at this level + * should be understandable to an inexperienced, non-technical end user. + * Ideally, they explain in simple words what actions the user can take in + * order to resolve the problem. + * + * @see Level#WARNING + * @param message the message text, also used as look-up key if the logger is + * localizing messages with a resource bundle. While it is possible + * to pass null, this is not recommended, since a + * logging message without text is unlikely to be helpful. + */ + public void warning(String message) + { + synchronized (lock) + { + if (isLoggable(Level.WARNING)) + log(Level.WARNING, message); + } + } + + /** + * Logs a message with severity level INFO. {@link Level#INFO} is intended for + * purely informational messages that do not indicate error or warning + * situations. In the default logging configuration, INFO messages will be + * written to the system console. For this reason, the INFO level should be + * used only for messages that are important to end users and system + * administrators. Messages at this level should be understandable to an + * inexperienced, non-technical user. + * + * @param message the message text, also used as look-up key if the logger is + * localizing messages with a resource bundle. While it is possible + * to pass null, this is not recommended, since a + * logging message without text is unlikely to be helpful. + */ + public void info(String message) + { + synchronized (lock) + { + if (isLoggable(Level.INFO)) + log(Level.INFO, message); + } + } + + /** + * Logs a message with severity level CONFIG. {@link Level#CONFIG} is intended + * for static configuration messages, for example about the windowing + * environment, the operating system version, etc. + * + * @param message the message text, also used as look-up key if the logger is + * localizing messages with a resource bundle. While it is possible + * to pass null, this is not recommended, since a + * logging message without text is unlikely to be helpful. + */ + public void config(String message) + { + synchronized (lock) + { + if (isLoggable(Level.CONFIG)) + log(Level.CONFIG, message); + } + } + + /** + * Logs a message with severity level FINE. {@link Level#FINE} is intended for + * messages that are relevant for developers using the component generating + * log messages. Examples include minor, recoverable failures, or possible + * inefficiencies. + * + * @param message the message text, also used as look-up key if the logger is + * localizing messages with a resource bundle. While it is possible + * to pass null, this is not recommended, since a + * logging message without text is unlikely to be helpful. + */ + public void fine(String message) + { + synchronized (lock) + { + if (isLoggable(Level.FINE)) + log(Level.FINE, message); + } + } + + /** + * Logs a message with severity level FINER. {@link Level#FINER} is intended + * for rather detailed tracing, for example entering a method, returning from + * a method, or throwing an exception. + * + * @param message the message text, also used as look-up key if the logger is + * localizing messages with a resource bundle. While it is possible + * to pass null, this is not recommended, since a + * logging message without text is unlikely to be helpful. + */ + public void finer(String message) + { + synchronized (lock) + { + if (isLoggable(Level.FINER)) + log(Level.FINER, message); + } + } + + /** + * Logs a message with severity level FINEST. {@link Level#FINEST} is intended + * for highly detailed tracing, for example reaching a certain point inside + * the body of a method. + * + * @param message the message text, also used as look-up key if the logger is + * localizing messages with a resource bundle. While it is possible + * to pass null, this is not recommended, since a + * logging message without text is unlikely to be helpful. + */ + public void finest(String message) + { + synchronized (lock) + { + if (isLoggable(Level.FINEST)) + log(Level.FINEST, message); + } + } + + /** + * Adds a handler to the set of handlers that get notified when a log record + * is to be published. + * + * @param handler the handler to be added. + * @throws NullPointerException if handler is null. + * @throws SecurityException if this logger is not anonymous, a security + * manager exists, and the caller is not granted the permission to + * control the logging infrastructure by having + * LoggingPermission("control"). Untrusted code can obtain an + * anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public void addHandler(Handler handler) throws SecurityException + { + synchronized (lock) + { + if (handler == null) + throw new NullPointerException(); + + /* + * An application is allowed to control an anonymous logger without + * having the permission to control the logging infrastructure. + */ + if (! anonymous) + LogManager.getLogManager().checkAccess(); + + if (! handlerList.contains(handler)) + { + handlerList.add(handler); + handlers = getHandlers(); + } + } + } + + /** + * Removes a handler from the set of handlers that get notified when a log + * record is to be published. + * + * @param handler the handler to be removed. + * @throws SecurityException if this logger is not anonymous, a security + * manager exists, and the caller is not granted the permission to + * control the logging infrastructure by having + * LoggingPermission("control"). Untrusted code can obtain an + * anonymous logger through the static factory method {@link + * #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + * @throws NullPointerException if handler is null. + */ + public void removeHandler(Handler handler) throws SecurityException + { + synchronized (lock) + { + /* + * An application is allowed to control an anonymous logger without + * having the permission to control the logging infrastructure. + */ + if (! anonymous) + LogManager.getLogManager().checkAccess(); + + if (handler == null) + throw new NullPointerException(); + + handlerList.remove(handler); + handlers = getHandlers(); + } + } + + /** + * Returns the handlers currently registered for this Logger. When a log + * record has been deemed as being loggable, it will be passed to all + * registered handlers for publication. In addition, if the logger uses parent + * handlers (see {@link #getUseParentHandlers() getUseParentHandlers} and + * {@link #setUseParentHandlers(boolean) setUseParentHandlers}, the log + * record will be passed to the parent's handlers. + */ + public Handler[] getHandlers() + { + synchronized (lock) + { + /* + * We cannot return our internal handlers array because we do not have + * any guarantee that the caller would not change the array entries. + */ + return (Handler[]) handlerList.toArray(new Handler[handlerList.size()]); + } + } + + /** + * Returns whether or not this Logger forwards log records to handlers + * registered for its parent loggers. + * + * @return false if this Logger sends log records merely to + * Handlers registered with itself; true if this Logger + * sends log records not only to Handlers registered with itself, but + * also to those Handlers registered with parent loggers. + */ + public boolean getUseParentHandlers() + { + synchronized (lock) + { + return useParentHandlers; + } + } + + /** + * Sets whether or not this Logger forwards log records to handlers registered + * for its parent loggers. + * + * @param useParentHandlers false to let this Logger send log + * records merely to Handlers registered with itself; + * true to let this Logger send log records not only + * to Handlers registered with itself, but also to those Handlers + * registered with parent loggers. + * @throws SecurityException if this logger is not anonymous, a security + * manager exists, and the caller is not granted the permission to + * control the logging infrastructure by having + * LoggingPermission("control"). Untrusted code can obtain an + * anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public void setUseParentHandlers(boolean useParentHandlers) + { + synchronized (lock) + { + /* + * An application is allowed to control an anonymous logger without + * having the permission to control the logging infrastructure. + */ + if (! anonymous) + LogManager.getLogManager().checkAccess(); + + this.useParentHandlers = useParentHandlers; + } + } + + /** + * Returns the parent of this logger. By default, the parent is assigned by + * the LogManager by inspecting the logger's name. + * + * @return the parent of this logger (as detemined by the LogManager by + * inspecting logger names), the root logger if no other logger has a + * name which is a prefix of this logger's name, or null + * for the root logger. + */ + public Logger getParent() + { + synchronized (lock) + { + return parent; + } + } + + /** + * Sets the parent of this logger. Usually, applications do not call this + * method directly. Instead, the LogManager will ensure that the tree of + * loggers reflects the hierarchical logger namespace. Basically, this method + * should not be public at all, but the GNU implementation follows the API + * specification. + * + * @throws NullPointerException if parent is null. + * @throws SecurityException if this logger is not anonymous, a security + * manager exists, and the caller is not granted the permission to + * control the logging infrastructure by having + * LoggingPermission("control"). Untrusted code can obtain an + * anonymous logger through the static factory method + * {@link #getAnonymousLogger(java.lang.String) getAnonymousLogger}. + */ + public void setParent(Logger parent) + { + synchronized (lock) + { + if (parent == null) + throw new NullPointerException(); + + if (this == root) + throw new IllegalArgumentException( + "the root logger can only have a null parent"); + + /* + * An application is allowed to control an anonymous logger without + * having the permission to control the logging infrastructure. + */ + if (! anonymous) + LogManager.getLogManager().checkAccess(); + + this.parent = parent; + } + } + + /** + * Gets the StackTraceElement of the first class that is not this class. That + * should be the initial caller of a logging method. + * + * @return caller of the initial logging method or null if unknown. + */ + private StackTraceElement getCallerStackFrame() + { + Throwable t = new Throwable(); + StackTraceElement[] stackTrace = t.getStackTrace(); + int index = 0; + + // skip to stackentries until this class + while (index < stackTrace.length + && ! stackTrace[index].getClassName().equals(getClass().getName())) + index++; + + // skip the stackentries of this class + while (index < stackTrace.length + && stackTrace[index].getClassName().equals(getClass().getName())) + index++; + + return index < stackTrace.length ? stackTrace[index] : null; + } + + /** + * Reset and close handlers attached to this logger. This function is package + * private because it must only be available to the LogManager. + */ + void resetLogger() + { + for (int i = 0; i < handlers.length; i++) + { + handlers[i].close(); + handlerList.remove(handlers[i]); + } + handlers = getHandlers(); + } +} diff --git a/libjava/classpath/java/util/logging/LoggingMXBean.java b/libjava/classpath/java/util/logging/LoggingMXBean.java new file mode 100644 index 000000000..24d8834c7 --- /dev/null +++ b/libjava/classpath/java/util/logging/LoggingMXBean.java @@ -0,0 +1,85 @@ +/* LoggingMxBean.java -- Management interface for logging + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.util.List; + +/** + * This interface represents the management interface for logging. + * There is a single logging bean per VM instance, which can be + * retrieved via {@link LogManager#getLoggingMXBean()}. + * + * @since 1.5 + */ +public interface LoggingMXBean +{ + /** + * Return the name of the logging level given the name of + * a logger. Returns null if no such logger exists. + * @param logger the logger's name + * @return the logging level's name, or null + */ + String getLoggerLevel(String logger); + + /** + * Return a list of all logger names. + */ + List getLoggerNames(); + + /** + * Return the name of the parent of the indicated logger. + * If no such logger exists, returns null. If the logger + * is the root logger, returns the empty string. + * @param logger the logger's name + * @return the name of the logger's parent, or null + */ + String getParentLoggerName(String logger); + + /** + * Sets the logging level for a particular logger. + * + * @param logger the name of the logger + * @param level the name of the new logging level, or null + * @throws IllegalArgumentException if the level is not + * recognized, or if the logger does not exist + * @throws SecurityException if access is denied; + * see {@link Logger#setLevel(Level)} + */ + void setLoggerLevel(String logger, String level); +} diff --git a/libjava/classpath/java/util/logging/LoggingPermission.java b/libjava/classpath/java/util/logging/LoggingPermission.java new file mode 100644 index 000000000..804fb9401 --- /dev/null +++ b/libjava/classpath/java/util/logging/LoggingPermission.java @@ -0,0 +1,75 @@ +/* LoggingPermission.java -- a class for logging permissions. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +public final class LoggingPermission + extends java.security.BasicPermission +{ + private static final long serialVersionUID = 63564341580231582L; + + /** + * Creates a new LoggingPermission. + * + * @param name the name of the permission, which must be "control". + * + * @param actions the list of actions for the permission, which + * must be either null or an empty + * string. + * + * @exception IllegalArgumentException if name + * is not "control", or actions is + * neither null nor empty. + */ + public LoggingPermission(String name, String actions) + { + super("control", ""); + + if (!"control".equals(name)) + { + throw new IllegalArgumentException( + "name of LoggingPermission must be \"control\""); + } + + if ((actions != null) && (actions.length() != 0)) + { + throw new IllegalArgumentException( + "actions of LoggingPermissions must be null or empty"); + } + } +} diff --git a/libjava/classpath/java/util/logging/MemoryHandler.java b/libjava/classpath/java/util/logging/MemoryHandler.java new file mode 100644 index 000000000..e5c258bbf --- /dev/null +++ b/libjava/classpath/java/util/logging/MemoryHandler.java @@ -0,0 +1,345 @@ +/* MemoryHandler.java -- a class for buffering log messages in a memory buffer + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.logging; + +/** + * A MemoryHandler maintains a circular buffer of + * log records. + * + *

        Configuration: Values of the subsequent + * LogManager properties are taken into consideration + * when a MemoryHandler is initialized. + * If a property is not defined, or if it has an invalid + * value, a default is taken without an exception being thrown. + * + *

          + *
        • java.util.MemoryHandler.level - specifies + * the initial severity level threshold. Default value: + * Level.ALL.
        • + *
        • java.util.MemoryHandler.filter - specifies + * the name of a Filter class. Default value: No Filter.
        • + *
        • java.util.MemoryHandler.size - specifies the + * maximum number of log records that are kept in the circular + * buffer. Default value: 1000.
        • + *
        • java.util.MemoryHandler.push - specifies the + * pushLevel. Default value: + * Level.SEVERE.
        • + *
        • java.util.MemoryHandler.target - specifies the + * name of a subclass of {@link Handler} that will be used as the + * target handler. There is no default value for this property; + * if it is not set, the no-argument MemoryHandler constructor + * will throw an exception.
        • + *
        + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class MemoryHandler + extends Handler +{ + /** + * The storage area used for buffering the unpushed log records in + * memory. + */ + private final LogRecord[] buffer; + + + /** + * The current position in the circular buffer. For a new + * MemoryHandler, or immediately after {@link #push()} was called, + * the value of this variable is zero. Each call to {@link + * #publish(LogRecord)} will store the published LogRecord into + * buffer[position] before position is incremented by + * one. If position becomes greater than the size of the buffer, it + * is reset to zero. + */ + private int position; + + + /** + * The number of log records which have been published, but not + * pushed yet to the target handler. + */ + private int numPublished; + + + /** + * The push level threshold for this Handler. When a + * record is published whose severity level is greater than or equal + * to the pushLevel of this MemoryHandler, + * the {@link #push()} method will be invoked for pushing the buffer + * contents to the target Handler. + */ + private Level pushLevel; + + + /** + * The Handler to which log records are forwarded for actual + * publication. + */ + private final Handler target; + + + /** + * Constructs a MemoryHandler for keeping a circular + * buffer of LogRecords; the initial configuration is determined by + * the LogManager properties described above. + */ + public MemoryHandler() + { + this((Handler) LogManager.getInstanceProperty( + "java.util.logging.MemoryHandler.target", + Handler.class, /* default */ null), + LogManager.getIntPropertyClamped( + "java.util.logging.MemoryHandler.size", + /* default */ 1000, + /* minimum value */ 1, + /* maximum value */ Integer.MAX_VALUE), + LogManager.getLevelProperty( + "java.util.logging.MemoryHandler.push", + /* default push level */ Level.SEVERE)); + } + + + /** + * Constructs a MemoryHandler for keeping a circular + * buffer of LogRecords, given some parameters. The values of the + * other parameters are taken from LogManager properties, as + * described above. + * + * @param target the target handler that will receive those + * log records that are passed on for publication. + * + * @param size the number of log records that are kept in the buffer. + * The value must be a at least one. + * + * @param pushLevel the push level threshold for this + * MemoryHandler. When a record is published whose + * severity level is greater than or equal to + * pushLevel, the {@link #push()} method will be + * invoked in order to push the bufffer contents to + * target. + * + * @throws java.lang.IllegalArgumentException if size + * is negative or zero. The GNU implementation also throws + * an IllegalArgumentException if target or + * pushLevel are null, but the + * API specification does not prescribe what should happen + * in those cases. + */ + public MemoryHandler(Handler target, int size, Level pushLevel) + { + if ((target == null) || (size <= 0) || (pushLevel == null)) + throw new IllegalArgumentException(); + + buffer = new LogRecord[size]; + this.pushLevel = pushLevel; + this.target = target; + + setLevel(LogManager.getLevelProperty( + "java.util.logging.MemoryHandler.level", + /* default value */ Level.ALL)); + + setFilter((Filter) LogManager.getInstanceProperty( + "java.util.logging.MemoryHandler.filter", + /* must be instance of */ Filter.class, + /* default value */ null)); + } + + + /** + * Stores a LogRecord in a fixed-size circular buffer, + * provided the record passes all tests for being loggable. If the + * buffer is full, the oldest record will be discarded. + * + *

        If the record has a severity level which is greater than or + * equal to the pushLevel of this + * MemoryHandler, the {@link #push()} method will be + * invoked for pushing the buffer contents to the target + * Handler. + * + *

        Most applications do not need to call this method directly. + * Instead, they will use use a {@link Logger}, which will create + * LogRecords and distribute them to registered handlers. + * + * @param record the log event to be published. + */ + public void publish(LogRecord record) + { + if (!isLoggable(record)) + return; + + buffer[position] = record; + position = (position + 1) % buffer.length; + numPublished = numPublished + 1; + + if (record.getLevel().intValue() >= pushLevel.intValue()) + push(); + } + + + /** + * Pushes the contents of the memory buffer to the target + * Handler and clears the buffer. Note that + * the target handler will discard those records that do + * not satisfy its own severity level threshold, or that are + * not considered loggable by an installed {@link Filter}. + * + *

        In case of an I/O failure, the {@link ErrorManager} of the + * target Handler will be notified, but the caller of + * this method will not receive an exception. + */ + public void push() + { + int i; + + if (numPublished < buffer.length) + { + for (i = 0; i < position; i++) + target.publish(buffer[i]); + } + else + { + for (i = position; i < buffer.length; i++) + target.publish(buffer[i]); + for (i = 0; i < position; i++) + target.publish(buffer[i]); + } + + numPublished = 0; + position = 0; + } + + + /** + * Forces any data that may have been buffered by the target + * Handler to the underlying output device, but + * does not push the contents of the circular memory + * buffer to the target handler. + * + *

        In case of an I/O failure, the {@link ErrorManager} of the + * target Handler will be notified, but the caller of + * this method will not receive an exception. + * + * @see #push() + */ + public void flush() + { + target.flush(); + } + + + /** + * Closes this MemoryHandler and its associated target + * handler, discarding the contents of the memory buffer. However, + * any data that may have been buffered by the target + * Handler is forced to the underlying output device. + * + *

        As soon as close has been called, + * a Handler should not be used anymore. Attempts + * to publish log records, to flush buffers, or to modify the + * Handler in any other way may throw runtime + * exceptions after calling close.

        + * + *

        In case of an I/O failure, the ErrorManager of + * the associated target Handler will be informed, but + * the caller of this method will not receive an exception.

        + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @see #push() + */ + public void close() + throws SecurityException + { + push(); + + /* This will check for LoggingPermission("control"). If the + * current security context does not grant this permission, + * push() has been executed, but this does not impose a + * security risk. + */ + target.close(); + } + + + + /** + * Returns the push level threshold for this Handler. + * When a record is published whose severity level is greater + * than or equal to the pushLevel of this + * MemoryHandler, the {@link #push()} method will be + * invoked for pushing the buffer contents to the target + * Handler. + * + * @return the push level threshold for automatic pushing. + */ + public Level getPushLevel() + { + return pushLevel; + } + + + /** + * Sets the push level threshold for this Handler. + * When a record is published whose severity level is greater + * than or equal to the pushLevel of this + * MemoryHandler, the {@link #push()} method will be + * invoked for pushing the buffer contents to the target + * Handler. + * + * @param pushLevel the push level threshold for automatic pushing. + * + * @exception SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @exception NullPointerException if pushLevel is + * null. + */ + public void setPushLevel(Level pushLevel) + { + LogManager.getLogManager().checkAccess(); + + /* Throws a NullPointerException if pushLevel is null. */ + pushLevel.getClass(); + + this.pushLevel = pushLevel; + } +} diff --git a/libjava/classpath/java/util/logging/SimpleFormatter.java b/libjava/classpath/java/util/logging/SimpleFormatter.java new file mode 100644 index 000000000..da731f2b1 --- /dev/null +++ b/libjava/classpath/java/util/logging/SimpleFormatter.java @@ -0,0 +1,131 @@ +/* SimpleFormatter.java -- + A class for formatting log records into short human-readable messages + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import gnu.java.lang.CPStringBuilder; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.DateFormat; +import java.util.Date; + +/** + * A SimpleFormatter formats log records into + * short human-readable messages, typically one or two lines. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class SimpleFormatter + extends Formatter +{ + /** + * Constructs a SimpleFormatter. + */ + public SimpleFormatter() + { + } + + + /** + * An instance of a DateFormatter that is used for formatting + * the time of a log record into a human-readable string, + * according to the rules of the current locale. The value + * is set after the first invocation of format, since it is + * common that a JVM will instantiate a SimpleFormatter without + * ever using it. + */ + private DateFormat dateFormat; + + /** + * The character sequence that is used to separate lines in the + * generated stream. Somewhat surprisingly, the Sun J2SE 1.4 + * reference implementation always uses UNIX line endings, even on + * platforms that have different line ending conventions (i.e., + * DOS). The GNU implementation does not replicate this bug. + * + * @see Sun bug parade, bug #4462871, + * "java.util.logging.SimpleFormatter uses hard-coded line separator". + */ + static final String lineSep = System.getProperty("line.separator"); + + + /** + * Formats a log record into a String. + * + * @param record the log record to be formatted. + * + * @return a short human-readable message, typically one or two + * lines. Lines are separated using the default platform line + * separator. + * + * @throws NullPointerException if record + * is null. + */ + public String format(LogRecord record) + { + CPStringBuilder buf = new CPStringBuilder(180); + + if (dateFormat == null) + dateFormat = DateFormat.getDateTimeInstance(); + + buf.append(dateFormat.format(new Date(record.getMillis()))); + buf.append(' '); + buf.append(record.getSourceClassName()); + buf.append(' '); + buf.append(record.getSourceMethodName()); + buf.append(lineSep); + + buf.append(record.getLevel()); + buf.append(": "); + buf.append(formatMessage(record)); + + buf.append(lineSep); + + Throwable throwable = record.getThrown(); + if (throwable != null) + { + StringWriter sink = new StringWriter(); + throwable.printStackTrace(new PrintWriter(sink, true)); + buf.append(sink.toString()); + } + + return buf.toString(); + } +} diff --git a/libjava/classpath/java/util/logging/SocketHandler.java b/libjava/classpath/java/util/logging/SocketHandler.java new file mode 100644 index 000000000..3c17b9bbc --- /dev/null +++ b/libjava/classpath/java/util/logging/SocketHandler.java @@ -0,0 +1,220 @@ +/* SocketHandler.java -- a class for publishing log messages to network sockets + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + + +/** + * A SocketHandler publishes log records to + * a TCP/IP socket. + * + *

        Configuration: Values of the subsequent + * LogManager properties are taken into consideration + * when a SocketHandler is initialized. + * If a property is not defined, or if it has an invalid + * value, a default is taken without an exception being thrown. + * + *

          + * + *
        • java.util.SocketHandler.level - specifies + * the initial severity level threshold. Default value: + * Level.ALL.
        • + * + *
        • java.util.SocketHandler.filter - specifies + * the name of a Filter class. Default value: No Filter.
        • + * + *
        • java.util.SocketHandler.formatter - specifies + * the name of a Formatter class. Default value: + * java.util.logging.XMLFormatter.
        • + * + *
        • java.util.SocketHandler.encoding - specifies + * the name of the character encoding. Default value: + * the default platform encoding.
        • + * + *
        • java.util.SocketHandler.host - specifies + * the name of the host to which records are published. + * There is no default value for this property; if it is + * not set, the SocketHandler constructor will throw + * an exception.
        • + * + *
        • java.util.SocketHandler.port - specifies + * the TCP/IP port to which records are published. + * There is no default value for this property; if it is + * not set, the SocketHandler constructor will throw + * an exception.
        • + * + *
        + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class SocketHandler + extends StreamHandler +{ + /** + * Constructs a SocketHandler that publishes log + * records to a TCP/IP socket. Tthe initial configuration is + * determined by the LogManager properties described + * above. + * + * @throws java.io.IOException if the connection to the specified + * network host and port cannot be established. + * + * @throws java.lang.IllegalArgumentException if either the + * java.util.logging.SocketHandler.host + * or java.util.logging.SocketHandler.port + * LogManager properties is not defined, or specifies + * an invalid value. + */ + public SocketHandler() + throws java.io.IOException + { + this(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.host"), + getPortNumber()); + } + + + /** + * Constructs a SocketHandler that publishes log + * records to a TCP/IP socket. With the exception of the internet + * host and port, the initial configuration is determined by the + * LogManager properties described above. + * + * @param host the Internet host to which log records will be + * forwarded. + * + * @param port the port at the host which will accept a request + * for a TCP/IP connection. + * + * @throws java.io.IOException if the connection to the specified + * network host and port cannot be established. + * + * @throws java.lang.IllegalArgumentException if either + * host or port specify + * an invalid value. + */ + public SocketHandler(String host, int port) + throws java.io.IOException + { + super(createSocket(host, port), + "java.util.logging.SocketHandler", + /* default level */ Level.ALL, + /* formatter */ null, + /* default formatter */ XMLFormatter.class); + } + + + /** + * Retrieves the port number from the java.util.logging.SocketHandler.port + * LogManager property. + * + * @throws IllegalArgumentException if the property is not defined or + * does not specify an integer value. + */ + private static int getPortNumber() + { + try { + return Integer.parseInt(LogManager.getLogManager().getProperty("java.util.logging.SocketHandler.port")); + } catch (Exception ex) { + throw new IllegalArgumentException(); + } + } + + + /** + * Creates an OutputStream for publishing log records to an Internet + * host and port. This private method is a helper for use by the + * constructor of SocketHandler. + * + * @param host the Internet host to which log records will be + * forwarded. + * + * @param port the port at the host which will accept a request + * for a TCP/IP connection. + * + * @throws java.io.IOException if the connection to the specified + * network host and port cannot be established. + * + * @throws java.lang.IllegalArgumentException if either + * host or port specify + * an invalid value. + */ + private static java.io.OutputStream createSocket(String host, int port) + throws java.io.IOException, java.lang.IllegalArgumentException + { + java.net.Socket socket; + + if ((host == null) || (port < 1)) + throw new IllegalArgumentException(); + + socket = new java.net.Socket(host, port); + + socket.shutdownInput(); + + /* The architecture of the logging framework provides replaceable + * formatters. Because these formatters perform their task by + * returning one single String for each LogRecord to be formatted, + * there is no need to buffer. + */ + socket.setTcpNoDelay(true); + + return socket.getOutputStream(); + } + + + /** + * Publishes a LogRecord to the network socket, + * provided the record passes all tests for being loggable. + * In addition, all data that may have been buffered will + * be forced to the network stream. + * + *

        Most applications do not need to call this method directly. + * Instead, they will use a {@link Logger} instance, which will + * create LogRecords and distribute them to registered handlers. + * + *

        In case of an I/O failure, the ErrorManager + * of this SocketHandler will be informed, but the caller + * of this method will not receive an exception. + * + * @param record the log event to be published. + */ + public void publish(LogRecord record) + { + super.publish(record); + flush(); + } +} diff --git a/libjava/classpath/java/util/logging/StreamHandler.java b/libjava/classpath/java/util/logging/StreamHandler.java new file mode 100644 index 000000000..d74dfac0c --- /dev/null +++ b/libjava/classpath/java/util/logging/StreamHandler.java @@ -0,0 +1,521 @@ +/* StreamHandler.java -- + A class for publishing log messages to instances of java.io.OutputStream + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +/** + * A StreamHandler publishes LogRecords to + * a instances of java.io.OutputStream. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class StreamHandler + extends Handler +{ + private OutputStream out; + private Writer writer; + + + /** + * Indicates the current state of this StreamHandler. The value + * should be one of STATE_FRESH, STATE_PUBLISHED, or STATE_CLOSED. + */ + private int streamState = STATE_FRESH; + + + /** + * streamState having this value indicates that the StreamHandler + * has been created, but the publish(LogRecord) method has not been + * called yet. If the StreamHandler has been constructed without an + * OutputStream, writer will be null, otherwise it is set to a + * freshly created OutputStreamWriter. + */ + private static final int STATE_FRESH = 0; + + + /** + * streamState having this value indicates that the publish(LocRecord) + * method has been called at least once. + */ + private static final int STATE_PUBLISHED = 1; + + + /** + * streamState having this value indicates that the close() method + * has been called. + */ + private static final int STATE_CLOSED = 2; + + + /** + * Creates a StreamHandler without an output stream. + * Subclasses can later use {@link + * #setOutputStream(java.io.OutputStream)} to associate an output + * stream with this StreamHandler. + */ + public StreamHandler() + { + this(null, null); + } + + + /** + * Creates a StreamHandler that formats log messages + * with the specified Formatter and publishes them to the specified + * output stream. + * + * @param out the output stream to which the formatted log messages + * are published. + * + * @param formatter the Formatter that will be used + * to format log messages. + */ + public StreamHandler(OutputStream out, Formatter formatter) + { + this(out, "java.util.logging.StreamHandler", Level.INFO, + formatter, SimpleFormatter.class); + } + + + StreamHandler( + OutputStream out, + String propertyPrefix, + Level defaultLevel, + Formatter formatter, Class defaultFormatterClass) + { + this.level = LogManager.getLevelProperty(propertyPrefix + ".level", + defaultLevel); + + this.filter = (Filter) LogManager.getInstanceProperty( + propertyPrefix + ".filter", + /* must be instance of */ Filter.class, + /* default: new instance of */ null); + + if (formatter != null) + this.formatter = formatter; + else + this.formatter = (Formatter) LogManager.getInstanceProperty( + propertyPrefix + ".formatter", + /* must be instance of */ Formatter.class, + /* default: new instance of */ defaultFormatterClass); + + try + { + String enc = LogManager.getLogManager().getProperty(propertyPrefix + + ".encoding"); + + /* make sure enc actually is a valid encoding */ + if ((enc != null) && (enc.length() > 0)) + new String(new byte[0], enc); + + this.encoding = enc; + } + catch (Exception _) + { + } + + if (out != null) + { + try + { + changeWriter(out, getEncoding()); + } + catch (UnsupportedEncodingException uex) + { + /* This should never happen, since the validity of the encoding + * name has been checked above. + */ + throw new RuntimeException(uex.getMessage()); + } + } + } + + + private void checkOpen() + { + if (streamState == STATE_CLOSED) + throw new IllegalStateException(this.toString() + " has been closed"); + } + + private void checkFresh() + { + checkOpen(); + if (streamState != STATE_FRESH) + throw new IllegalStateException("some log records have been published to " + this); + } + + + private void changeWriter(OutputStream out, String encoding) + throws UnsupportedEncodingException + { + OutputStreamWriter writer; + + /* The logging API says that a null encoding means the default + * platform encoding. However, java.io.OutputStreamWriter needs + * another constructor for the default platform encoding, + * passing null would throw an exception. + */ + if (encoding == null) + writer = new OutputStreamWriter(out); + else + writer = new OutputStreamWriter(out, encoding); + + /* Closing the stream has side effects -- do this only after + * creating a new writer has been successful. + */ + if ((streamState != STATE_FRESH) || (this.writer != null)) + close(); + + this.writer = writer; + this.out = out; + this.encoding = encoding; + streamState = STATE_FRESH; + } + + + /** + * Sets the character encoding which this handler uses for publishing + * log records. The encoding of a StreamHandler must be + * set before any log records have been published. + * + * @param encoding the name of a character encoding, or null + * for the default encoding. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control the + * the logging infrastructure. + * + * @exception IllegalStateException if any log records have been + * published to this StreamHandler before. Please + * be aware that this is a pecularity of the GNU implementation. + * While the API specification indicates that it is an error + * if the encoding is set after records have been published, + * it does not mandate any specific behavior for that case. + */ + public void setEncoding(String encoding) + throws SecurityException, UnsupportedEncodingException + { + /* The inherited implementation first checks whether the invoking + * code indeed has the permission to control the logging infra- + * structure, and throws a SecurityException if this was not the + * case. + * + * Next, it verifies that the encoding is supported and throws + * an UnsupportedEncodingExcpetion otherwise. Finally, it remembers + * the name of the encoding. + */ + super.setEncoding(encoding); + + checkFresh(); + + /* If out is null, setEncoding is being called before an output + * stream has been set. In that case, we need to check that the + * encoding is valid, and remember it if this is the case. Since + * this is exactly what the inherited implementation of + * Handler.setEncoding does, we can delegate. + */ + if (out != null) + { + /* The logging API says that a null encoding means the default + * platform encoding. However, java.io.OutputStreamWriter needs + * another constructor for the default platform encoding, passing + * null would throw an exception. + */ + if (encoding == null) + writer = new OutputStreamWriter(out); + else + writer = new OutputStreamWriter(out, encoding); + } + } + + + /** + * Changes the output stream to which this handler publishes + * logging records. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + * + * @throws NullPointerException if out + * is null. + */ + protected void setOutputStream(OutputStream out) + throws SecurityException + { + LogManager.getLogManager().checkAccess(); + + /* Throw a NullPointerException if out is null. */ + out.getClass(); + + try + { + changeWriter(out, getEncoding()); + } + catch (UnsupportedEncodingException ex) + { + /* This seems quite unlikely to happen, unless the underlying + * implementation of java.io.OutputStreamWriter changes its + * mind (at runtime) about the set of supported character + * encodings. + */ + throw new RuntimeException(ex.getMessage()); + } + } + + + /** + * Publishes a LogRecord to the associated output + * stream, provided the record passes all tests for being loggable. + * The StreamHandler will localize the message of the + * log record and substitute any message parameters. + * + *

        Most applications do not need to call this method directly. + * Instead, they will use use a {@link Logger}, which will create + * LogRecords and distribute them to registered handlers. + * + *

        In case of an I/O failure, the ErrorManager + * of this Handler will be informed, but the caller + * of this method will not receive an exception. + * + *

        If a log record is being published to a + * StreamHandler that has been closed earlier, the Sun + * J2SE 1.4 reference can be observed to silently ignore the + * call. The GNU implementation, however, intentionally behaves + * differently by informing the ErrorManager associated + * with this StreamHandler. Since the condition + * indicates a programming error, the programmer should be + * informed. It also seems extremely unlikely that any application + * would depend on the exact behavior in this rather obscure, + * erroneous case -- especially since the API specification does not + * prescribe what is supposed to happen. + * + * @param record the log event to be published. + */ + public void publish(LogRecord record) + { + String formattedMessage; + + if (!isLoggable(record)) + return; + + if (streamState == STATE_FRESH) + { + try + { + writer.write(formatter.getHead(this)); + } + catch (java.io.IOException ex) + { + reportError(null, ex, ErrorManager.WRITE_FAILURE); + return; + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.GENERIC_FAILURE); + return; + } + + streamState = STATE_PUBLISHED; + } + + try + { + formattedMessage = formatter.format(record); + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.FORMAT_FAILURE); + return; + } + + try + { + writer.write(formattedMessage); + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.WRITE_FAILURE); + } + } + + + /** + * Checks whether or not a LogRecord would be logged + * if it was passed to this StreamHandler for publication. + * + *

        The StreamHandler implementation first checks + * whether a writer is present and the handler's level is greater + * than or equal to the severity level threshold. In a second step, + * if a {@link Filter} has been installed, its {@link + * Filter#isLoggable(LogRecord) isLoggable} method is + * invoked. Subclasses of StreamHandler can override + * this method to impose their own constraints. + * + * @param record the LogRecord to be checked. + * + * @return true if record would + * be published by {@link #publish(LogRecord) publish}, + * false if it would be discarded. + * + * @see #setLevel(Level) + * @see #setFilter(Filter) + * @see Filter#isLoggable(LogRecord) + * + * @throws NullPointerException if record is + * null. */ + public boolean isLoggable(LogRecord record) + { + return (writer != null) && super.isLoggable(record); + } + + + /** + * Forces any data that may have been buffered to the underlying + * output device. + * + *

        In case of an I/O failure, the ErrorManager + * of this Handler will be informed, but the caller + * of this method will not receive an exception. + * + *

        If a StreamHandler that has been closed earlier + * is closed a second time, the Sun J2SE 1.4 reference can be + * observed to silently ignore the call. The GNU implementation, + * however, intentionally behaves differently by informing the + * ErrorManager associated with this + * StreamHandler. Since the condition indicates a + * programming error, the programmer should be informed. It also + * seems extremely unlikely that any application would depend on the + * exact behavior in this rather obscure, erroneous case -- + * especially since the API specification does not prescribe what is + * supposed to happen. + */ + public void flush() + { + try + { + checkOpen(); + if (writer != null) + writer.flush(); + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.FLUSH_FAILURE); + } + } + + + /** + * Closes this StreamHandler after having forced any + * data that may have been buffered to the underlying output + * device. + * + *

        As soon as close has been called, + * a Handler should not be used anymore. Attempts + * to publish log records, to flush buffers, or to modify the + * Handler in any other way may throw runtime + * exceptions after calling close.

        + * + *

        In case of an I/O failure, the ErrorManager + * of this Handler will be informed, but the caller + * of this method will not receive an exception.

        + * + *

        If a StreamHandler that has been closed earlier + * is closed a second time, the Sun J2SE 1.4 reference can be + * observed to silently ignore the call. The GNU implementation, + * however, intentionally behaves differently by informing the + * ErrorManager associated with this + * StreamHandler. Since the condition indicates a + * programming error, the programmer should be informed. It also + * seems extremely unlikely that any application would depend on the + * exact behavior in this rather obscure, erroneous case -- + * especially since the API specification does not prescribe what is + * supposed to happen. + * + * @throws SecurityException if a security manager exists and + * the caller is not granted the permission to control + * the logging infrastructure. + */ + public void close() + throws SecurityException + { + LogManager.getLogManager().checkAccess(); + + try + { + /* Although flush also calls checkOpen, it catches + * any exceptions and reports them to the ErrorManager + * as flush failures. However, we want to report + * a closed stream as a close failure, not as a + * flush failure here. Therefore, we call checkOpen() + * before flush(). + */ + checkOpen(); + flush(); + + if (writer != null) + { + if (formatter != null) + { + /* Even if the StreamHandler has never published a record, + * it emits head and tail upon closing. An earlier version + * of the GNU Classpath implementation did not emitted + * anything. However, this had caused XML log files to be + * entirely empty instead of containing no log records. + */ + if (streamState == STATE_FRESH) + writer.write(formatter.getHead(this)); + if (streamState != STATE_CLOSED) + writer.write(formatter.getTail(this)); + } + streamState = STATE_CLOSED; + writer.close(); + } + } + catch (Exception ex) + { + reportError(null, ex, ErrorManager.CLOSE_FAILURE); + } + } +} diff --git a/libjava/classpath/java/util/logging/XMLFormatter.java b/libjava/classpath/java/util/logging/XMLFormatter.java new file mode 100644 index 000000000..da7c6ef29 --- /dev/null +++ b/libjava/classpath/java/util/logging/XMLFormatter.java @@ -0,0 +1,389 @@ +/* XMLFormatter.java -- + A class for formatting log messages into a standard XML format + Copyright (C) 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.logging; + +import gnu.java.lang.CPStringBuilder; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.ResourceBundle; + +/** + * An XMLFormatter formats LogRecords into + * a standard XML format. + * + * @author Sascha Brawer (brawer@acm.org) + */ +public class XMLFormatter + extends Formatter +{ + /** + * Constructs a new XMLFormatter. + */ + public XMLFormatter() + { + } + + + /** + * The character sequence that is used to separate lines in the + * generated XML stream. Somewhat surprisingly, the Sun J2SE 1.4 + * reference implementation always uses UNIX line endings, even on + * platforms that have different line ending conventions (i.e., + * DOS). The GNU Classpath implementation does not replicates this + * bug. + * + * See also the Sun bug parade, bug #4462871, + * "java.util.logging.SimpleFormatter uses hard-coded line separator". + */ + private static final String lineSep = SimpleFormatter.lineSep; + + + /** + * A DateFormat for emitting time in the ISO 8601 format. + * Since the API specification of SimpleDateFormat does not talk + * about its thread-safety, we cannot share a singleton instance. + */ + private final SimpleDateFormat iso8601 + = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + + /** + * Appends a line consisting of indentation, opening element tag, + * element content, closing element tag and line separator to + * a CPStringBuilder, provided that the element content is + * actually existing. + * + * @param buf the CPStringBuilder to which the line will be appended. + * + * @param indent the indentation level. + * + * @param tag the element tag name, for instance method. + * + * @param content the element content, or null to + * have no output whatsoever appended to buf. + */ + private static void appendTag(CPStringBuilder buf, int indent, + String tag, String content) + { + int i; + + if (content == null) + return; + + for (i = 0; i < indent * 2; i++) + buf.append(' '); + + buf.append("<"); + buf.append(tag); + buf.append('>'); + + /* Append the content, but escape for XML by replacing + * '&', '<', '>' and all non-ASCII characters with + * appropriate escape sequences. + * The Sun J2SE 1.4 reference implementation does not + * escape non-ASCII characters. This is a bug in their + * implementation which has been reported in the Java + * bug parade as bug number (FIXME: Insert number here). + */ + for (i = 0; i < content.length(); i++) + { + char c = content.charAt(i); + switch (c) + { + case '&': + buf.append("&"); + break; + + case '<': + buf.append("<"); + break; + + case '>': + buf.append(">"); + break; + + default: + if (((c >= 0x20) && (c <= 0x7e)) + || (c == /* line feed */ 10) + || (c == /* carriage return */ 13)) + buf.append(c); + else + { + buf.append("&#"); + buf.append((int) c); + buf.append(';'); + } + break; + } /* switch (c) */ + } /* for i */ + + buf.append(""); + buf.append(lineSep); + } + + + /** + * Appends a line consisting of indentation, opening element tag, + * numeric element content, closing element tag and line separator + * to a CPStringBuilder. + * + * @param buf the CPStringBuilder to which the line will be appended. + * + * @param indent the indentation level. + * + * @param tag the element tag name, for instance method. + * + * @param content the element content. + */ + private static void appendTag(CPStringBuilder buf, int indent, + String tag, long content) + { + appendTag(buf, indent, tag, Long.toString(content)); + } + + + public String format(LogRecord record) + { + CPStringBuilder buf = new CPStringBuilder(400); + Level level = record.getLevel(); + long millis = record.getMillis(); + Object[] params = record.getParameters(); + ResourceBundle bundle = record.getResourceBundle(); + String message; + + buf.append(""); + buf.append(lineSep); + + + appendTag(buf, 1, "date", iso8601.format(new Date(millis))); + appendTag(buf, 1, "millis", millis); + appendTag(buf, 1, "sequence", record.getSequenceNumber()); + appendTag(buf, 1, "logger", record.getLoggerName()); + + if (level.isStandardLevel()) + appendTag(buf, 1, "level", level.toString()); + else + appendTag(buf, 1, "level", level.intValue()); + + appendTag(buf, 1, "class", record.getSourceClassName()); + appendTag(buf, 1, "method", record.getSourceMethodName()); + appendTag(buf, 1, "thread", record.getThreadID()); + + /* The Sun J2SE 1.4 reference implementation does not emit the + * message in localized form. This is in violation of the API + * specification. The GNU Classpath implementation intentionally + * replicates the buggy behavior of the Sun implementation, as + * different log files might be a big nuisance to users. + */ + try + { + record.setResourceBundle(null); + message = formatMessage(record); + } + finally + { + record.setResourceBundle(bundle); + } + appendTag(buf, 1, "message", message); + + /* The Sun J2SE 1.4 reference implementation does not + * emit key, catalog and param tags. This is in violation + * of the API specification. The Classpath implementation + * intentionally replicates the buggy behavior of the + * Sun implementation, as different log files might be + * a big nuisance to users. + * + * FIXME: File a bug report with Sun. Insert bug number here. + * + * + * key = record.getMessage(); + * if (key == null) + * key = ""; + * + * if ((bundle != null) && !key.equals(message)) + * { + * appendTag(buf, 1, "key", key); + * appendTag(buf, 1, "catalog", record.getResourceBundleName()); + * } + * + * if (params != null) + * { + * for (int i = 0; i < params.length; i++) + * appendTag(buf, 1, "param", params[i].toString()); + * } + */ + + /* FIXME: We have no way to obtain the stacktrace before free JVMs + * support the corresponding method in java.lang.Throwable. Well, + * it would be possible to parse the output of printStackTrace, + * but this would be pretty kludgy. Instead, we postpose the + * implementation until Throwable has made progress. + */ + Throwable thrown = record.getThrown(); + if (thrown != null) + { + buf.append(" "); + buf.append(lineSep); + + /* The API specification is not clear about what exactly + * goes into the XML record for a thrown exception: It + * could be the result of getMessage(), getLocalizedMessage(), + * or toString(). Therefore, it was necessary to write a + * Mauve testlet and run it with the Sun J2SE 1.4 reference + * implementation. It turned out that the we need to call + * toString(). + * + * FIXME: File a bug report with Sun, asking for clearer + * specs. + */ + appendTag(buf, 2, "message", thrown.toString()); + + /* FIXME: The Logging DTD specifies: + * + * + * + * However, java.lang.Throwable.getStackTrace() is + * allowed to return an empty array. So, what frame should + * be emitted for an empty stack trace? We probably + * should file a bug report with Sun, asking for the DTD + * to be changed. + */ + + buf.append(" "); + buf.append(lineSep); + } + + + buf.append(""); + buf.append(lineSep); + + return buf.toString(); + } + + + /** + * Returns a string that handlers are supposed to emit before + * the first log record. The base implementation returns an + * empty string, but subclasses such as {@link XMLFormatter} + * override this method in order to provide a suitable header. + * + * @return a string for the header. + * + * @param h the handler which will prepend the returned + * string in front of the first log record. This method + * will inspect certain properties of the handler, for + * example its encoding, in order to construct the header. + */ + public String getHead(Handler h) + { + CPStringBuilder buf; + String encoding; + + buf = new CPStringBuilder(80); + buf.append(" 2) && encoding.startsWith("Cp")) + encoding = "windows-" + encoding.substring(2); + + buf.append(encoding); + + buf.append("\" standalone=\"no\"?>"); + buf.append(lineSep); + + /* SYSTEM is not a fully qualified URL so that validating + * XML parsers do not need to connect to the Internet in + * order to read in a log file. See also the Sun Bug Parade, + * bug #4372790, "Logging APIs: need to use relative URL for XML + * doctype". + */ + buf.append(""); + buf.append(lineSep); + buf.append(""); + buf.append(lineSep); + + return buf.toString(); + } + + + public String getTail(Handler h) + { + return "" + lineSep; + } +} diff --git a/libjava/classpath/java/util/logging/package.html b/libjava/classpath/java/util/logging/package.html new file mode 100644 index 000000000..31f0494fc --- /dev/null +++ b/libjava/classpath/java/util/logging/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.util.logging + + +

        Utility classes for logging events.

        + + + diff --git a/libjava/classpath/java/util/package.html b/libjava/classpath/java/util/package.html new file mode 100644 index 000000000..ff2919c55 --- /dev/null +++ b/libjava/classpath/java/util/package.html @@ -0,0 +1,48 @@ + + + + +GNU Classpath - java.util + + +

        Utility classes such as collections (maps, sets, lists, dictionaries and +stacks), calendars, dates, locales, properties, timers, resource bundles and +event objects.

        + + + diff --git a/libjava/classpath/java/util/prefs/AbstractPreferences.java b/libjava/classpath/java/util/prefs/AbstractPreferences.java new file mode 100644 index 000000000..14fed384a --- /dev/null +++ b/libjava/classpath/java/util/prefs/AbstractPreferences.java @@ -0,0 +1,1392 @@ +/* AbstractPreferences -- Partial implementation of a Preference node + Copyright (C) 2001, 2003, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.prefs; + +import gnu.classpath.toolkit.DefaultDaemonThreadFactory; +import gnu.java.lang.CPStringBuilder; +import gnu.java.util.prefs.NodeWriter; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.TreeSet; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** + * Partial implementation of a Preference node. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public abstract class AbstractPreferences extends Preferences { + + // protected fields + + /** + * Object used to lock this preference node. Any thread only locks nodes + * downwards when it has the lock on the current node. No method should + * synchronize on the lock of any of its parent nodes while holding the + * lock on the current node. + */ + protected final Object lock = new Object(); + + /** + * Set to true in the contructor if the node did not exist in the backing + * store when this preference node object was created. Should be set in + * the constructor of a subclass. Defaults to false. Used to fire node + * changed events. + */ + protected boolean newNode = false; + + // private fields + + /** + * The parent preferences node or null when this is the root node. + */ + private final AbstractPreferences parent; + + /** + * The name of this node. + * Only when this is a root node (parent == null) the name is empty. + * It has a maximum of 80 characters and cannot contain any '/' characters. + */ + private final String name; + + /** True when this node has been remove, false otherwise. */ + private boolean removed = false; + + /** + * Holds all the child names and nodes of this node that have been + * accessed by earlier getChild() or childSpi() + * invocations and that have not been removed. + */ + private HashMap childCache + = new HashMap(); + + /** + * A list of all the registered NodeChangeListener objects. + */ + private ArrayList nodeListeners; + + /** + * A list of all the registered PreferenceChangeListener objects. + */ + private ArrayList preferenceListeners; + + // constructor + + /** + * Creates a new AbstractPreferences node with the given parent and name. + * + * @param parent the parent of this node or null when this is the root node + * @param name the name of this node, can not be null, only 80 characters + * maximum, must be empty when parent is null and cannot + * contain any '/' characters + * @exception IllegalArgumentException when name is null, greater then 80 + * characters, not the empty string but parent is null or + * contains a '/' character + */ + protected AbstractPreferences(AbstractPreferences parent, String name) { + if ( (name == null) // name should be given + || (name.length() > MAX_NAME_LENGTH) // 80 characters max + || (parent == null && name.length() != 0) // root has no name + || (parent != null && name.length() == 0) // all other nodes do + || (name.indexOf('/') != -1)) // must not contain '/' + throw new IllegalArgumentException("Illegal name argument '" + + name + + "' (parent is " + + (parent == null ? "" : "not ") + + "null)"); + this.parent = parent; + this.name = name; + } + + // identification methods + + /** + * Returns the absolute path name of this preference node. + * The absolute path name of a node is the path name of its parent node + * plus a '/' plus its own name. If the node is the root node and has no + * parent then its path name is "" and its absolute path name is "/". + */ + public String absolutePath() { + if (parent == null) + return "/"; + else + return parent.path() + '/' + name; + } + + /** + * Private helper method for absolutePath. Returns the empty string for a + * root node and otherwise the parentPath of its parent plus a '/'. + */ + private String path() { + if (parent == null) + return ""; + else + return parent.path() + '/' + name; + } + + /** + * Returns true if this node comes from the user preferences tree, false + * if it comes from the system preferences tree. + */ + public boolean isUserNode() { + AbstractPreferences root = this; + while (root.parent != null) + root = root.parent; + return root == Preferences.userRoot(); + } + + /** + * Returns the name of this preferences node. The name of the node cannot + * be null, can be mostly 80 characters and cannot contain any '/' + * characters. The root node has as name "". + */ + public String name() { + return name; + } + + /** + * Returns the String given by + * + * (isUserNode() ? "User":"System") + " Preference Node: " + absolutePath() + * + */ + public String toString() { + return (isUserNode() ? "User":"System") + + " Preference Node: " + + absolutePath(); + } + + /** + * Returns all known unremoved children of this node. + * + * @return All known unremoved children of this node + */ + protected final AbstractPreferences[] cachedChildren() + { + Collection vals = childCache.values(); + return vals.toArray(new AbstractPreferences[vals.size()]); + } + + /** + * Returns all the direct sub nodes of this preferences node. + * Needs access to the backing store to give a meaningfull answer. + *

        + * This implementation locks this node, checks if the node has not yet + * been removed and throws an IllegalStateException when it + * has been. Then it creates a new TreeSet and adds any + * already cached child nodes names. To get any uncached names it calls + * childrenNamesSpi() and adds the result to the set. Finally + * it calls toArray() on the created set. When the call to + * childrenNamesSpi thows an BackingStoreException + * this method will not catch that exception but propagate the exception + * to the caller. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException when this node has been removed + */ + public String[] childrenNames() throws BackingStoreException { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + TreeSet childrenNames = new TreeSet(); + + // First get all cached node names + childrenNames.addAll(childCache.keySet()); + + // Then add any others + String names[] = childrenNamesSpi(); + for (int i = 0; i < names.length; i++) { + childrenNames.add(names[i]); + } + + // And return the array of names + String[] children = new String[childrenNames.size()]; + childrenNames.toArray(children); + return children; + + } + } + + /** + * Returns a sub node of this preferences node if the given path is + * relative (does not start with a '/') or a sub node of the root + * if the path is absolute (does start with a '/'). + *

        + * This method first locks this node and checks if the node has not been + * removed, if it has been removed it throws an exception. Then if the + * path is relative (does not start with a '/') it checks if the path is + * legal (does not end with a '/' and has no consecutive '/' characters). + * Then it recursively gets a name from the path, gets the child node + * from the child-cache of this node or calls the childSpi() + * method to create a new child sub node. This is done recursively on the + * newly created sub node with the rest of the path till the path is empty. + * If the path is absolute (starts with a '/') the lock on this node is + * droped and this method is called on the root of the preferences tree + * with as argument the complete path minus the first '/'. + * + * @exception IllegalStateException if this node has been removed + * @exception IllegalArgumentException if the path contains two or more + * consecutive '/' characters, ends with a '/' charactor and is not the + * string "/" (indicating the root node) or any name on the path is more + * than 80 characters long + */ + public Preferences node(String path) { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + // Is it a relative path? + if (!path.startsWith("/")) { + + // Check if it is a valid path + if (path.indexOf("//") != -1 || path.endsWith("/")) + throw new IllegalArgumentException(path); + + return getNode(path); + } + } + + // path started with a '/' so it is absolute + // we drop the lock and start from the root (omitting the first '/') + Preferences root = isUserNode() ? userRoot() : systemRoot(); + return root.node(path.substring(1)); + + } + + /** + * Private helper method for node(). Called with this node + * locked. Returns this node when path is the empty string, if it is not + * empty the next node name is taken from the path (all chars till the + * next '/' or end of path string) and the node is either taken from the + * child-cache of this node or the childSpi() method is called + * on this node with the name as argument. Then this method is called + * recursively on the just constructed child node with the rest of the + * path. + * + * @param path should not end with a '/' character and should not contain + * consecutive '/' characters + * @exception IllegalArgumentException if path begins with a name that is + * larger then 80 characters. + */ + private Preferences getNode(String path) { + // if mark is dom then goto end + + // Empty String "" indicates this node + if (path.length() == 0) + return this; + + // Calculate child name and rest of path + String childName; + String childPath; + int nextSlash = path.indexOf('/'); + if (nextSlash == -1) { + childName = path; + childPath = ""; + } else { + childName = path.substring(0, nextSlash); + childPath = path.substring(nextSlash+1); + } + + // Get the child node + AbstractPreferences child; + child = (AbstractPreferences)childCache.get(childName); + if (child == null) { + + if (childName.length() > MAX_NAME_LENGTH) + throw new IllegalArgumentException(childName); + + // Not in childCache yet so create a new sub node + child = childSpi(childName); + childCache.put(childName, child); + if (child.newNode && nodeListeners != null) + fire(new NodeChangeEvent(this, child), true); + } + + // Lock the child and go down + synchronized(child.lock) { + return child.getNode(childPath); + } + } + + /** + * Returns true if the node that the path points to exists in memory or + * in the backing store. Otherwise it returns false or an exception is + * thrown. When this node is removed the only valid parameter is the + * empty string (indicating this node), the return value in that case + * will be false. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + * and the path is not the empty string (indicating this node) + * @exception IllegalArgumentException if the path contains two or more + * consecutive '/' characters, ends with a '/' charactor and is not the + * string "/" (indicating the root node) or any name on the path is more + * then 80 characters long + */ + public boolean nodeExists(String path) throws BackingStoreException { + synchronized(lock) { + if (isRemoved() && path.length() != 0) + throw new IllegalStateException("Node removed"); + + // Is it a relative path? + if (!path.startsWith("/")) { + + // Check if it is a valid path + if (path.indexOf("//") != -1 || path.endsWith("/")) + throw new IllegalArgumentException(path); + + return existsNode(path); + } + } + + // path started with a '/' so it is absolute + // we drop the lock and start from the root (omitting the first '/') + Preferences root = isUserNode() ? userRoot() : systemRoot(); + return root.nodeExists(path.substring(1)); + + } + + private boolean existsNode(String path) throws BackingStoreException { + + // Empty String "" indicates this node + if (path.length() == 0) + return(!isRemoved()); + + // Calculate child name and rest of path + String childName; + String childPath; + int nextSlash = path.indexOf('/'); + if (nextSlash == -1) { + childName = path; + childPath = ""; + } else { + childName = path.substring(0, nextSlash); + childPath = path.substring(nextSlash+1); + } + + // Get the child node + AbstractPreferences child; + child = (AbstractPreferences)childCache.get(childName); + if (child == null) { + + if (childName.length() > MAX_NAME_LENGTH) + throw new IllegalArgumentException(childName); + + // Not in childCache yet so create a new sub node + child = getChild(childName); + + if (child == null) + return false; + + childCache.put(childName, child); + } + + // Lock the child and go down + synchronized(child.lock) { + return child.existsNode(childPath); + } + } + + /** + * Returns the child sub node if it exists in the backing store or null + * if it does not exist. Called (indirectly) by nodeExists() + * when a child node name can not be found in the cache. + *

        + * Gets the lock on this node, calls childrenNamesSpi() to + * get an array of all (possibly uncached) children and compares the + * given name with the names in the array. If the name is found in the + * array childSpi() is called to get an instance, otherwise + * null is returned. + * + * @exception BackingStoreException when the backing store cannot be + * reached + */ + protected AbstractPreferences getChild(String name) + throws BackingStoreException + { + synchronized(lock) { + // Get all the names (not yet in the cache) + String[] names = childrenNamesSpi(); + for (int i=0; i < names.length; i++) + if (name.equals(names[i])) + return childSpi(name); + + // No child with that name found + return null; + } + } + + /** + * Returns true if this node has been removed with the + * removeNode() method, false otherwise. + *

        + * Gets the lock on this node and then returns a boolean field set by + * removeNode methods. + */ + protected boolean isRemoved() { + synchronized(lock) { + return removed; + } + } + + /** + * Returns the parent preferences node of this node or null if this is + * the root of the preferences tree. + *

        + * Gets the lock on this node, checks that the node has not been removed + * and returns the parent given to the constructor. + * + * @exception IllegalStateException if this node has been removed + */ + public Preferences parent() { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + return parent; + } + } + + // export methods + + // Inherit javadoc. + public void exportNode(OutputStream os) + throws BackingStoreException, + IOException + { + NodeWriter nodeWriter = new NodeWriter(this, os); + nodeWriter.writePrefs(); + } + + // Inherit javadoc. + public void exportSubtree(OutputStream os) + throws BackingStoreException, + IOException + { + NodeWriter nodeWriter = new NodeWriter(this, os); + nodeWriter.writePrefsTree(); + } + + // preference entry manipulation methods + + /** + * Returns an (possibly empty) array with all the keys of the preference + * entries of this node. + *

        + * This method locks this node and checks if the node has not been + * removed, if it has been removed it throws an exception, then it returns + * the result of calling keysSpi(). + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public String[] keys() throws BackingStoreException { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + return keysSpi(); + } + } + + + /** + * Returns the value associated with the key in this preferences node. If + * the default value of the key cannot be found in the preferences node + * entries or something goes wrong with the backing store the supplied + * default value is returned. + *

        + * Checks that key is not null and not larger then 80 characters, + * locks this node, and checks that the node has not been removed. + * Then it calls keySpi() and returns + * the result of that method or the given default value if it returned + * null or throwed an exception. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public String get(String key, String defaultVal) { + if (key.length() > MAX_KEY_LENGTH) + throw new IllegalArgumentException(key); + + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + String value; + try { + value = getSpi(key); + } catch (ThreadDeath death) { + throw death; + } catch (Throwable t) { + value = null; + } + + if (value != null) { + return value; + } else { + return defaultVal; + } + } + } + + /** + * Convenience method for getting the given entry as a boolean. + * When the string representation of the requested entry is either + * "true" or "false" (ignoring case) then that value is returned, + * otherwise the given default boolean value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public boolean getBoolean(String key, boolean defaultVal) { + String value = get(key, null); + + if ("true".equalsIgnoreCase(value)) + return true; + + if ("false".equalsIgnoreCase(value)) + return false; + + return defaultVal; + } + + /** + * Convenience method for getting the given entry as a byte array. + * When the string representation of the requested entry is a valid + * Base64 encoded string (without any other characters, such as newlines) + * then the decoded Base64 string is returned as byte array, + * otherwise the given default byte array value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public byte[] getByteArray(String key, byte[] defaultVal) { + String value = get(key, null); + + byte[] b = null; + if (value != null) { + b = decode64(value); + } + + if (b != null) + return b; + else + return defaultVal; + } + + /** + * Helper method for decoding a Base64 string as an byte array. + * Returns null on encoding error. This method does not allow any other + * characters present in the string then the 65 special base64 chars. + */ + private static byte[] decode64(String s) { + ByteArrayOutputStream bs = new ByteArrayOutputStream((s.length()/4)*3); + char[] c = new char[s.length()]; + s.getChars(0, s.length(), c, 0); + + // Convert from base64 chars + int endchar = -1; + for(int j = 0; j < c.length && endchar == -1; j++) { + if (c[j] >= 'A' && c[j] <= 'Z') { + c[j] -= 'A'; + } else if (c[j] >= 'a' && c[j] <= 'z') { + c[j] = (char) (c[j] + 26 - 'a'); + } else if (c[j] >= '0' && c[j] <= '9') { + c[j] = (char) (c[j] + 52 - '0'); + } else if (c[j] == '+') { + c[j] = 62; + } else if (c[j] == '/') { + c[j] = 63; + } else if (c[j] == '=') { + endchar = j; + } else { + return null; // encoding exception + } + } + + int remaining = endchar == -1 ? c.length : endchar; + int i = 0; + while (remaining > 0) { + // Four input chars (6 bits) are decoded as three bytes as + // 000000 001111 111122 222222 + + byte b0 = (byte) (c[i] << 2); + if (remaining >= 2) { + b0 += (c[i+1] & 0x30) >> 4; + } + bs.write(b0); + + if (remaining >= 3) { + byte b1 = (byte) ((c[i+1] & 0x0F) << 4); + b1 += (byte) ((c[i+2] & 0x3C) >> 2); + bs.write(b1); + } + + if (remaining >= 4) { + byte b2 = (byte) ((c[i+2] & 0x03) << 6); + b2 += c[i+3]; + bs.write(b2); + } + + i += 4; + remaining -= 4; + } + + return bs.toByteArray(); + } + + /** + * Convenience method for getting the given entry as a double. + * When the string representation of the requested entry can be decoded + * with Double.parseDouble() then that double is returned, + * otherwise the given default double value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public double getDouble(String key, double defaultVal) { + String value = get(key, null); + + if (value != null) { + try { + return Double.parseDouble(value); + } catch (NumberFormatException nfe) { /* ignore */ } + } + + return defaultVal; + } + + /** + * Convenience method for getting the given entry as a float. + * When the string representation of the requested entry can be decoded + * with Float.parseFloat() then that float is returned, + * otherwise the given default float value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public float getFloat(String key, float defaultVal) { + String value = get(key, null); + + if (value != null) { + try { + return Float.parseFloat(value); + } catch (NumberFormatException nfe) { /* ignore */ } + } + + return defaultVal; + } + + /** + * Convenience method for getting the given entry as an integer. + * When the string representation of the requested entry can be decoded + * with Integer.parseInt() then that integer is returned, + * otherwise the given default integer value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public int getInt(String key, int defaultVal) { + String value = get(key, null); + + if (value != null) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException nfe) { /* ignore */ } + } + + return defaultVal; + } + + /** + * Convenience method for getting the given entry as a long. + * When the string representation of the requested entry can be decoded + * with Long.parseLong() then that long is returned, + * otherwise the given default long value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public long getLong(String key, long defaultVal) { + String value = get(key, null); + + if (value != null) { + try { + return Long.parseLong(value); + } catch (NumberFormatException nfe) { /* ignore */ } + } + + return defaultVal; + } + + /** + * Sets the value of the given preferences entry for this node. + * Key and value cannot be null, the key cannot exceed 80 characters + * and the value cannot exceed 8192 characters. + *

        + * The result will be immediately visible in this VM, but may not be + * immediately written to the backing store. + *

        + * Checks that key and value are valid, locks this node, and checks that + * the node has not been removed. Then it calls putSpi(). + * + * @exception NullPointerException if either key or value are null + * @exception IllegalArgumentException if either key or value are to large + * @exception IllegalStateException when this node has been removed + */ + public void put(String key, String value) { + if (key.length() > MAX_KEY_LENGTH + || value.length() > MAX_VALUE_LENGTH) + throw new IllegalArgumentException("key (" + + key.length() + ")" + + " or value (" + + value.length() + ")" + + " to large"); + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + putSpi(key, value); + + if (preferenceListeners != null) + fire(new PreferenceChangeEvent(this, key, value)); + } + + } + + /** + * Convenience method for setting the given entry as a boolean. + * The boolean is converted with Boolean.toString(value) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putBoolean(String key, boolean value) { + put(key, Boolean.toString(value)); + } + + /** + * Convenience method for setting the given entry as an array of bytes. + * The byte array is converted to a Base64 encoded string + * and then stored in the preference entry as that string. + *

        + * Note that a byte array encoded as a Base64 string will be about 1.3 + * times larger then the original length of the byte array, which means + * that the byte array may not be larger about 6 KB. + * + * @exception NullPointerException if either key or value are null + * @exception IllegalArgumentException if either key or value are to large + * @exception IllegalStateException when this node has been removed + */ + public void putByteArray(String key, byte[] value) { + put(key, encode64(value)); + } + + /** + * Helper method for encoding an array of bytes as a Base64 String. + */ + private static String encode64(byte[] b) { + CPStringBuilder sb = new CPStringBuilder((b.length/3)*4); + + int i = 0; + int remaining = b.length; + char c[] = new char[4]; + while (remaining > 0) { + // Three input bytes are encoded as four chars (6 bits) as + // 00000011 11112222 22333333 + + c[0] = (char) ((b[i] & 0xFC) >> 2); + c[1] = (char) ((b[i] & 0x03) << 4); + if (remaining >= 2) { + c[1] += (char) ((b[i+1] & 0xF0) >> 4); + c[2] = (char) ((b[i+1] & 0x0F) << 2); + if (remaining >= 3) { + c[2] += (char) ((b[i+2] & 0xC0) >> 6); + c[3] = (char) (b[i+2] & 0x3F); + } else { + c[3] = 64; + } + } else { + c[2] = 64; + c[3] = 64; + } + + // Convert to base64 chars + for(int j = 0; j < 4; j++) { + if (c[j] < 26) { + c[j] += 'A'; + } else if (c[j] < 52) { + c[j] = (char) (c[j] - 26 + 'a'); + } else if (c[j] < 62) { + c[j] = (char) (c[j] - 52 + '0'); + } else if (c[j] == 62) { + c[j] = '+'; + } else if (c[j] == 63) { + c[j] = '/'; + } else { + c[j] = '='; + } + } + + sb.append(c); + i += 3; + remaining -= 3; + } + + return sb.toString(); + } + + /** + * Convenience method for setting the given entry as a double. + * The double is converted with Double.toString(double) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putDouble(String key, double value) { + put(key, Double.toString(value)); + } + + /** + * Convenience method for setting the given entry as a float. + * The float is converted with Float.toString(float) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putFloat(String key, float value) { + put(key, Float.toString(value)); + } + + /** + * Convenience method for setting the given entry as an integer. + * The integer is converted with Integer.toString(int) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putInt(String key, int value) { + put(key, Integer.toString(value)); + } + + /** + * Convenience method for setting the given entry as a long. + * The long is converted with Long.toString(long) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void putLong(String key, long value) { + put(key, Long.toString(value)); + } + + /** + * Removes the preferences entry from this preferences node. + *

        + * The result will be immediately visible in this VM, but may not be + * immediately written to the backing store. + *

        + * This implementation checks that the key is not larger then 80 + * characters, gets the lock of this node, checks that the node has + * not been removed and calls removeSpi with the given key. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public void remove(String key) { + if (key.length() > MAX_KEY_LENGTH) + throw new IllegalArgumentException(key); + + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node removed"); + + removeSpi(key); + + if (preferenceListeners != null) + fire(new PreferenceChangeEvent(this, key, null)); + } + } + + /** + * Removes all entries from this preferences node. May need access to the + * backing store to get and clear all entries. + *

        + * The result will be immediately visible in this VM, but may not be + * immediatly written to the backing store. + *

        + * This implementation locks this node, checks that the node has not been + * removed and calls keys() to get a complete array of keys + * for this node. For every key found removeSpi() is called. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public void clear() throws BackingStoreException { + synchronized(lock) { + if (isRemoved()) + throw new IllegalStateException("Node Removed"); + + String[] keys = keys(); + for (int i = 0; i < keys.length; i++) { + removeSpi(keys[i]); + } + } + } + + /** + * Writes all preference changes on this and any subnode that have not + * yet been written to the backing store. This has no effect on the + * preference entries in this VM, but it makes sure that all changes + * are visible to other programs (other VMs might need to call the + * sync() method to actually see the changes to the backing + * store. + *

        + * Locks this node, calls the flushSpi() method, gets all + * the (cached - already existing in this VM) subnodes and then calls + * flushSpi() on every subnode with this node unlocked and + * only that particular subnode locked. + * + * @exception BackingStoreException when the backing store cannot be + * reached + */ + public void flush() throws BackingStoreException { + flushNode(false); + } + + /** + * Writes and reads all preference changes to and from this and any + * subnodes. This makes sure that all local changes are written to the + * backing store and that all changes to the backing store are visible + * in this preference node (and all subnodes). + *

        + * Checks that this node is not removed, locks this node, calls the + * syncSpi() method, gets all the subnodes and then calls + * syncSpi() on every subnode with this node unlocked and + * only that particular subnode locked. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public void sync() throws BackingStoreException { + flushNode(true); + } + + + /** + * Private helper method that locks this node and calls either + * flushSpi() if sync is false, or + * flushSpi() if sync is true. Then it gets all + * the currently cached subnodes. For every subnode it calls this method + * recursively with this node no longer locked. + *

        + * Called by either flush() or sync() + */ + private void flushNode(boolean sync) throws BackingStoreException { + String[] keys = null; + synchronized(lock) { + if (sync) { + syncSpi(); + } else { + flushSpi(); + } + keys = (String[]) childCache.keySet().toArray(new String[]{}); + } + + if (keys != null) { + for (int i = 0; i < keys.length; i++) { + // Have to lock this node again to access the childCache + AbstractPreferences subNode; + synchronized(lock) { + subNode = (AbstractPreferences) childCache.get(keys[i]); + } + + // The child could already have been removed from the cache + if (subNode != null) { + subNode.flushNode(sync); + } + } + } + } + + /** + * Removes this and all subnodes from the backing store and clears all + * entries. After removal this instance will not be useable (except for + * a few methods that don't throw a InvalidStateException), + * even when a new node with the same path name is created this instance + * will not be usable again. + *

        + * Checks that this is not a root node. If not it locks the parent node, + * then locks this node and checks that the node has not yet been removed. + * Then it makes sure that all subnodes of this node are in the child cache, + * by calling childSpi() on any children not yet in the cache. + * Then for all children it locks the subnode and removes it. After all + * subnodes have been purged the child cache is cleared, this nodes removed + * flag is set and any listeners are called. Finally this node is removed + * from the child cache of the parent node. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has already been removed + * @exception UnsupportedOperationException if this is a root node + */ + public void removeNode() throws BackingStoreException { + // Check if it is a root node + if (parent == null) + throw new UnsupportedOperationException("Cannot remove root node"); + + synchronized (parent.lock) { + synchronized(this.lock) { + if (isRemoved()) + throw new IllegalStateException("Node Removed"); + + purge(); + } + parent.childCache.remove(name); + } + } + + /** + * Private helper method used to completely remove this node. + * Called by removeNode with the parent node and this node + * locked. + *

        + * Makes sure that all subnodes of this node are in the child cache, + * by calling childSpi() on any children not yet in the + * cache. Then for all children it locks the subnode and calls this method + * on that node. After all subnodes have been purged the child cache is + * cleared, this nodes removed flag is set and any listeners are called. + */ + private void purge() throws BackingStoreException + { + // Make sure all children have an AbstractPreferences node in cache + String children[] = childrenNamesSpi(); + for (int i = 0; i < children.length; i++) { + if (childCache.get(children[i]) == null) + childCache.put(children[i], childSpi(children[i])); + } + + // purge all children + Iterator i = childCache.values().iterator(); + while (i.hasNext()) { + AbstractPreferences node = (AbstractPreferences) i.next(); + synchronized(node.lock) { + node.purge(); + } + } + + // Cache is empty now + childCache.clear(); + + // remove this node + removeNodeSpi(); + removed = true; + + if (nodeListeners != null) + fire(new NodeChangeEvent(parent, this), false); + } + + // listener methods + + /** + * Add a listener which is notified when a sub-node of this node + * is added or removed. + * @param listener the listener to add + */ + public void addNodeChangeListener(NodeChangeListener listener) + { + synchronized (lock) + { + if (isRemoved()) + throw new IllegalStateException("node has been removed"); + if (listener == null) + throw new NullPointerException("listener is null"); + if (nodeListeners == null) + nodeListeners = new ArrayList(); + nodeListeners.add(listener); + } + } + + /** + * Add a listener which is notified when a value in this node + * is added, changed, or removed. + * @param listener the listener to add + */ + public void addPreferenceChangeListener(PreferenceChangeListener listener) + { + synchronized (lock) + { + if (isRemoved()) + throw new IllegalStateException("node has been removed"); + if (listener == null) + throw new NullPointerException("listener is null"); + if (preferenceListeners == null) + preferenceListeners = new ArrayList(); + preferenceListeners.add(listener); + } + } + + /** + * Remove the indicated node change listener from the list of + * listeners to notify. + * @param listener the listener to remove + */ + public void removeNodeChangeListener(NodeChangeListener listener) + { + synchronized (lock) + { + if (isRemoved()) + throw new IllegalStateException("node has been removed"); + if (listener == null) + throw new NullPointerException("listener is null"); + if (nodeListeners != null) + nodeListeners.remove(listener); + } + } + + /** + * Remove the indicated preference change listener from the list of + * listeners to notify. + * @param listener the listener to remove + */ + public void removePreferenceChangeListener (PreferenceChangeListener listener) + { + synchronized (lock) + { + if (isRemoved()) + throw new IllegalStateException("node has been removed"); + if (listener == null) + throw new NullPointerException("listener is null"); + if (preferenceListeners != null) + preferenceListeners.remove(listener); + } + } + + /** + * Send a preference change event to all listeners. Note that + * the caller is responsible for holding the node's lock, and + * for checking that the list of listeners is not null. + * @param event the event to send + */ + private void fire(final PreferenceChangeEvent event) + { + for (final PreferenceChangeListener listener : preferenceListeners) + { + Runnable dispatcher = new Runnable() { + public void run() + { + listener.preferenceChange(event); + } + }; + + Executor executor = + Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory()); + executor.execute(dispatcher); + } + } + + /** + * Send a node change event to all listeners. Note that + * the caller is responsible for holding the node's lock, and + * for checking that the list of listeners is not null. + * @param event the event to send + */ + private void fire(final NodeChangeEvent event, final boolean added) + { + for (final NodeChangeListener listener : nodeListeners) + { + Runnable dispatcher = new Runnable() { + public void run() + { + if (added) + listener.childAdded(event); + else + listener.childRemoved(event); + } + }; + + Executor executor = + Executors.newSingleThreadExecutor(new DefaultDaemonThreadFactory()); + executor.execute(dispatcher); + } + } + + // abstract spi methods + + /** + * Returns the names of the sub nodes of this preference node. + * This method only has to return any not yet cached child names, + * but may return all names if that is easier. It must not return + * null when there are no children, it has to return an empty array + * in that case. Since this method must consult the backing store to + * get all the sub node names it may throw a BackingStoreException. + *

        + * Called by childrenNames() with this node locked. + */ + protected abstract String[] childrenNamesSpi() throws BackingStoreException; + + /** + * Returns a child note with the given name. + * This method is called by the node() method (indirectly + * through the getNode() helper method) with this node locked + * if a sub node with this name does not already exist in the child cache. + * If the child node did not aleady exist in the backing store the boolean + * field newNode of the returned node should be set. + *

        + * Note that this method should even return a non-null child node if the + * backing store is not available since it may not throw a + * BackingStoreException. + */ + protected abstract AbstractPreferences childSpi(String name); + + /** + * Returns an (possibly empty) array with all the keys of the preference + * entries of this node. + *

        + * Called by keys() with this node locked if this node has + * not been removed. May throw an exception when the backing store cannot + * be accessed. + * + * @exception BackingStoreException when the backing store cannot be + * reached + */ + protected abstract String[] keysSpi() throws BackingStoreException; + + /** + * Returns the value associated with the key in this preferences node or + * null when the key does not exist in this preferences node. + *

        + * Called by key() with this node locked after checking that + * key is valid, not null and that the node has not been removed. + * key() will catch any exceptions that this method throws. + */ + protected abstract String getSpi(String key); + + /** + * Sets the value of the given preferences entry for this node. + * The implementation is not required to propagate the change to the + * backing store immediately. It may not throw an exception when it tries + * to write to the backing store and that operation fails, the failure + * should be registered so a later invocation of flush() + * or sync() can signal the failure. + *

        + * Called by put() with this node locked after checking that + * key and value are valid and non-null. + */ + protected abstract void putSpi(String key, String value); + + /** + * Removes the given key entry from this preferences node. + * The implementation is not required to propagate the change to the + * backing store immediately. It may not throw an exception when it tries + * to write to the backing store and that operation fails, the failure + * should be registered so a later invocation of flush() + * or sync() can signal the failure. + *

        + * Called by remove() with this node locked after checking + * that the key is valid and non-null. + */ + protected abstract void removeSpi(String key); + + /** + * Writes all entries of this preferences node that have not yet been + * written to the backing store and possibly creates this node in the + * backing store, if it does not yet exist. Should only write changes to + * this node and not write changes to any subnodes. + * Note that the node can be already removed in this VM. To check if + * that is the case the implementation can call isRemoved(). + *

        + * Called (indirectly) by flush() with this node locked. + */ + protected abstract void flushSpi() throws BackingStoreException; + + /** + * Writes all entries of this preferences node that have not yet been + * written to the backing store and reads any entries that have changed + * in the backing store but that are not yet visible in this VM. + * Should only sync this node and not change any of the subnodes. + * Note that the node can be already removed in this VM. To check if + * that is the case the implementation can call isRemoved(). + *

        + * Called (indirectly) by sync() with this node locked. + */ + protected abstract void syncSpi() throws BackingStoreException; + + /** + * Clears this node from this VM and removes it from the backing store. + * After this method has been called the node is marked as removed. + *

        + * Called (indirectly) by removeNode() with this node locked + * after all the sub nodes of this node have already been removed. + */ + protected abstract void removeNodeSpi() throws BackingStoreException; +} diff --git a/libjava/classpath/java/util/prefs/BackingStoreException.java b/libjava/classpath/java/util/prefs/BackingStoreException.java new file mode 100644 index 000000000..0ba358a56 --- /dev/null +++ b/libjava/classpath/java/util/prefs/BackingStoreException.java @@ -0,0 +1,104 @@ +/* BackingStoreException.java - chained exception thrown when backing store + fails + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Chained exception thrown when backing store fails. This exception is + * only thrown from methods that actually have to access the backing store, + * such as clear(), keys(), childrenNames(), nodeExists(), removeNode(), + * flush(), sync(), exportNode(), exportSubTree(); normal operations + * do not throw BackingStoreExceptions. + * + *

        Note that although this class inherits the Serializable interface, an + * attempt to serialize will fail with a NotSerializableException. + * + * @author Mark Wielaard (mark@klomp.org) + * @since 1.4 + * @status updated to 1.4 + */ +public class BackingStoreException extends Exception +{ + static final long serialVersionUID = 859796500401108469L; + + /** + * Creates a new exception with a descriptive message. + * + * @param message the message + */ + public BackingStoreException(String message) + { + super(message); + } + + /** + * Create a new exception with the given cause. + * + * @param cause the cause + */ + public BackingStoreException(Throwable cause) + { + super(cause); + } + + /** + * This class should not be serialized. + * + * @param o the output stream + */ + private void writeObject(ObjectOutputStream o) throws NotSerializableException + { + throw new NotSerializableException + ("java.util.prefs.BackingStoreException"); + } + + /** + * This class should not be serialized. + * + * @param i the input stream + */ + private void readObject(ObjectInputStream i) throws NotSerializableException + { + throw new NotSerializableException + ("java.util.prefs.BackingStoreException"); + } +} diff --git a/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java b/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java new file mode 100644 index 000000000..f929b56f6 --- /dev/null +++ b/libjava/classpath/java/util/prefs/InvalidPreferencesFormatException.java @@ -0,0 +1,116 @@ +/* InvalidPreferencesFormatException - indicates reading prefs from stream + failed + Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Indicates reading prefs from stream failed. Thrown by the + * importPreferences() method when the supplied input stream + * could not be read because it was not in the correct XML format. + * + *

        Note that although this class inherits the Serializable interface, an + * attempt to serialize will fail with a NotSerializableException. + *

        + * + * @author Mark Wielaard (mark@klomp.org) + * @see Preferences + * @since 1.4 + * @status updated to 1.4 + */ +public class InvalidPreferencesFormatException extends Exception +{ + static final long serialVersionUID = -791715184232119669L; + + /** + * Creates a new exception with a descriptive message. The cause remains + * uninitialized. + * + * @param message the message + */ + public InvalidPreferencesFormatException(String message) + { + super(message); + } + + /** + * Creates a new exception with the given cause. + * + * @param cause the cause + */ + public InvalidPreferencesFormatException(Throwable cause) + { + super(cause); + } + + /** + * Creates a new exception with a descriptive message and a cause. + * + * @param message the message + * @param cause the cause + */ + public InvalidPreferencesFormatException(String message, Throwable cause) + { + super(message, cause); + } + + /** + * This class should not be serialized. + * + * @param o the output stream + */ + private void writeObject(ObjectOutputStream o) throws NotSerializableException + { + throw new NotSerializableException + ("java.util.prefs.InvalidPreferencesFormatException"); + } + + /** + * This class should not be serialized. + * + * @param i the input stream + */ + private void readObject(ObjectInputStream i) throws NotSerializableException + { + throw new NotSerializableException + ("java.util.prefs.InvalidPreferencesFormatException"); + } +} diff --git a/libjava/classpath/java/util/prefs/NodeChangeEvent.java b/libjava/classpath/java/util/prefs/NodeChangeEvent.java new file mode 100644 index 000000000..5bdeb3346 --- /dev/null +++ b/libjava/classpath/java/util/prefs/NodeChangeEvent.java @@ -0,0 +1,111 @@ +/* NodeChangeEvent - ObjectEvent fired when a Preference node is added/removed + Copyright (C) 2001, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EventObject; + +/** + * ObjectEvent fired when a Preference node is added/removed. + * This event is only generated when a new subnode is added or a subnode is + * removed from a preference node. Changes in the entries of a preference node + * are indicated with a PreferenceChangeEvent. + *

        + * Note that although this class is marked as serializable, attempts to + * serialize it will fail with NotSerializableException. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public class NodeChangeEvent extends EventObject { + + // We have this to placate the compiler. + private static final long serialVersionUID =8068949086596572957L; + + /** + * The sub node that was added or removed. + * Defined transient just like EventObject.source since + * this object should be serializable, but Preferences is in general not + * serializable. + */ + private final transient Preferences child; + + /** + * Creates a new NodeChangeEvent. + * + * @param parentNode The source preference node from which a subnode was + * added or removed + * @param childNode The preference node that was added or removed + */ + public NodeChangeEvent(Preferences parentNode, Preferences childNode) { + super(parentNode); + child = childNode; + } + + /** + * Returns the source parent preference node from which a subnode was + * added or removed. + */ + public Preferences getParent() { + return (Preferences) source; + } + + /** + * Returns the child preference subnode that was added or removed. + * To see wether it is still a valid preference node one has to call + * event.getChild().nodeExists(""). + */ + public Preferences getChild() { + return child; + } + + private void readObject(ObjectInputStream ois) + throws IOException + { + throw new NotSerializableException("LineEvent is not serializable"); + } + + private void writeObject(ObjectOutputStream oos) + throws IOException + { + throw new NotSerializableException("LineEvent is not serializable"); + } +} diff --git a/libjava/classpath/java/util/prefs/NodeChangeListener.java b/libjava/classpath/java/util/prefs/NodeChangeListener.java new file mode 100644 index 000000000..8d018ab67 --- /dev/null +++ b/libjava/classpath/java/util/prefs/NodeChangeListener.java @@ -0,0 +1,64 @@ +/* NodeChangeListener - EventListener for Preferences node addition/removal + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.util.EventListener; + +/** + * EventListener for Preferences node addition/removal. + *

        + * Note that these events are only generated for the addition and removal + * of sub nodes from the preference node. Entry changes in the preference + * node can be monitored with a PreferenceChangeListener. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public interface NodeChangeListener extends EventListener { + + /** + * Fired when a sub node is added to the preference node. + */ + void childAdded(NodeChangeEvent event); + + /** + * Fired when a sub node is removed from the preference node. + */ + void childRemoved(NodeChangeEvent event); + +} diff --git a/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java b/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java new file mode 100644 index 000000000..f273361e1 --- /dev/null +++ b/libjava/classpath/java/util/prefs/PreferenceChangeEvent.java @@ -0,0 +1,125 @@ +/* PreferenceChangeEvent - ObjectEvent fired when a Preferences entry changes + Copyright (C) 2001, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EventObject; + +/** + * ObjectEvent fired when a Preferences entry changes. + * This event is generated when a entry is added, changed or removed. + * When an entry is removed then getNewValue will return null. + *

        + * Preference change events are only generated for entries in one particular + * preference node. Notification of subnode addition/removal is given by a + * NodeChangeEvent. + *

        + * Note that although this class is marked as serializable, attempts to + * serialize it will fail with NotSerializableException. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public class PreferenceChangeEvent extends EventObject { + + // We have this to placate the compiler. + private static final long serialVersionUID = 793724513368024975L; + + /** + * The key of the changed entry. + */ + private final String key; + + /** + * The new value of the changed entry, or null when the entry was removed. + */ + private final String newValue; + + /** + * Creates a new PreferenceChangeEvent. + * + * @param node The source preference node for which an entry was added, + * changed or removed + * @param key The key of the entry that was added, changed or removed + * @param value The new value of the entry that was added or changed, or + * null when the entry was removed + */ + public PreferenceChangeEvent(Preferences node, String key, String value) { + super(node); + this.key = key; + this.newValue = value; + } + + /** + * Returns the source Preference node from which an entry was added, + * changed or removed. + */ + public Preferences getNode() { + return (Preferences) source; + } + + /** + * Returns the key of the entry that was added, changed or removed. + */ + public String getKey() { + return key; + } + + /** + * Returns the new value of the entry that was added or changed, or + * returns null when the entry was removed. + */ + public String getNewValue() { + return newValue; + } + + private void readObject(ObjectInputStream ois) + throws IOException + { + throw new NotSerializableException("LineEvent is not serializable"); + } + + private void writeObject(ObjectOutputStream oos) + throws IOException + { + throw new NotSerializableException("LineEvent is not serializable"); + } +} diff --git a/libjava/classpath/java/util/prefs/PreferenceChangeListener.java b/libjava/classpath/java/util/prefs/PreferenceChangeListener.java new file mode 100644 index 000000000..e6118bc31 --- /dev/null +++ b/libjava/classpath/java/util/prefs/PreferenceChangeListener.java @@ -0,0 +1,60 @@ +/* PreferenceChangeListener - EventListener for Preferences entry changes + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import java.util.EventListener; + +/** + * EventListener for Preferences entry addition, change or removal. + *

        + * Preference change events are only generated for entries in one particular + * preference node. Notification of subnode addition/removal can be monitored + * with a NodeChangeListener. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public interface PreferenceChangeListener extends EventListener { + + /** + * Fired when a entry has been added, changed or removed from the + * preference node. + */ + void preferenceChange(PreferenceChangeEvent event); + +} diff --git a/libjava/classpath/java/util/prefs/Preferences.java b/libjava/classpath/java/util/prefs/Preferences.java new file mode 100644 index 000000000..3ff9c85e3 --- /dev/null +++ b/libjava/classpath/java/util/prefs/Preferences.java @@ -0,0 +1,696 @@ +/* Preferences -- Preference node containing key value entries and subnodes + Copyright (C) 2001, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.prefs; + +import gnu.classpath.ServiceFactory; +import gnu.java.util.prefs.NodeReader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.AccessController; +import java.security.Permission; +import java.security.PrivilegedAction; +import java.util.Iterator; + +/** + * Preference node containing key value entries and subnodes. + *

        + * There are two preference node trees, a system tree which can be accessed + * by calling systemRoot() containing system preferences usefull + * for all users, and a user tree that can be accessed by calling + * userRoot() containing preferences that can differ between + * different users. How different users are identified is implementation + * depended. It can be determined by Thread, Access Control Context or Subject. + *

        + * This implementation uses the "java.util.prefs.PreferencesFactory" system + * property to find a class that implement PreferencesFactory + * and initialized that class (if it has a public no arguments contructor) + * to get at the actual system or user root. If the system property is not set, + * or the class cannot be initialized it uses the default implementation + * gnu.java.util.prefs.FileBasedFactory. + *

        + * Besides the two static method above to get the roots of the system and user + * preference node trees there are also two convenience methods to access the + * default preference node for a particular package an object is in. These are + * userNodeForPackage() and systemNodeForPackage(). + * Both methods take an Object as an argument so accessing preferences values + * can be as easy as calling Preferences.userNodeForPackage(this). + *

        + * Note that if a security manager is installed all static methods check for + * RuntimePermission("preferences"). But if this permission is + * given to the code then it can access and change all (user) preference nodes + * and entries. So you should be carefull not to store to sensitive information + * or make security decissions based on preference values since there is no + * more fine grained control over what preference values can be changed once + * code has been given the correct runtime permission. + *

        + * XXX + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public abstract class Preferences { + + // Static Fields + + /** + * Default PreferencesFactory class used when the system property + * "java.util.prefs.PreferencesFactory" is not set. + */ + private static final String defaultFactoryClass + = "gnu.java.util.prefs.FileBasedFactory"; + + /** Permission needed to access system or user root. */ + private static final Permission prefsPermission + = new RuntimePermission("preferences"); + + /** + * The preferences factory object that supplies the system and user root. + * Set and returned by the getFactory() method. + */ + private static PreferencesFactory factory; + + /** Maximum node name length. 80 characters. */ + public static final int MAX_NAME_LENGTH = 80; + + /** Maximum entry key length. 80 characters. */ + public static final int MAX_KEY_LENGTH = 80; + + /** Maximum entry value length. 8192 characters. */ + public static final int MAX_VALUE_LENGTH = 8192; + + // Constructors + + /** + * Creates a new Preferences node. Can only be used by subclasses. + * Empty implementation. + */ + protected Preferences() {} + + // Static methods + + /** + * Returns the system preferences root node containing usefull preferences + * for all users. It is save to cache this value since it should always + * return the same preference node. + * + * @return the root system preference node + * @exception SecurityException when a security manager is installed and + * the caller does not have RuntimePermission("preferences"). + */ + public static Preferences systemRoot() throws SecurityException { + // Get the preferences factory and check for permission + PreferencesFactory factory = getFactory(); + + return factory.systemRoot(); + } + + /** + * Returns the user preferences root node containing preferences for the + * the current user. How different users are identified is implementation + * depended. It can be determined by Thread, Access Control Context or + * Subject. + * + * @return the root user preference node + * @exception SecurityException when a security manager is installed and + * the caller does not have RuntimePermission("preferences"). + */ + public static Preferences userRoot() throws SecurityException { + // Get the preferences factory and check for permission + PreferencesFactory factory = getFactory(); + return factory.userRoot(); + } + + /** + * Private helper method for systemRoot() and + * userRoot(). Checks security permission and instantiates the + * correct factory if it has not yet been set. + *

        + * When the preferences factory has not yet been set this method first + * tries to get the system propery "java.util.prefs.PreferencesFactory" + * and tries to initializes that class. If the system property is not set + * or initialization fails it returns an instance of the default factory + * gnu.java.util.prefs.FileBasedPreferencesFactory. + * + * @return the preferences factory to use + * @exception SecurityException when a security manager is installed and + * the caller does not have RuntimePermission("preferences"). + */ + private static PreferencesFactory getFactory() throws SecurityException { + + // First check for permission + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(prefsPermission); + } + + // Get the factory + if (factory == null) { + // Caller might not have enough permissions + factory = AccessController.doPrivileged( + new PrivilegedAction() { + public PreferencesFactory run() { + PreferencesFactory pf = null; + String className = System.getProperty + ("java.util.prefs.PreferencesFactory"); + if (className != null) { + try { + Class fc = Class.forName(className); + Object o = fc.newInstance(); + pf = (PreferencesFactory) o; + } catch (ClassNotFoundException cnfe) + {/*ignore*/} + catch (InstantiationException ie) + {/*ignore*/} + catch (IllegalAccessException iae) + {/*ignore*/} + catch (ClassCastException cce) + {/*ignore*/} + } + return pf; + } + }); + + // Still no factory? Try to see if we have one defined + // as a System Preference + if (factory == null) + { + Iterator iter = ServiceFactory.lookupProviders + (PreferencesFactory.class, null); + + if (iter != null && iter.hasNext()) + factory = (PreferencesFactory) iter.next(); + } + + // Still no factory? Use our default. + if (factory == null) + { + try + { + Class cls = Class.forName (defaultFactoryClass); + factory = (PreferencesFactory) cls.newInstance(); + } + catch (Exception e) + { + throw new RuntimeException ("Couldn't load default factory" + + " '"+ defaultFactoryClass +"'", e); + } + } + + } + + return factory; + } + + /** + * Returns the system preferences node for the package of a class. + * The package node name of the class is determined by dropping the + * final component of the fully qualified class name and + * changing all '.' to '/' in the package name. If the class of the + * object has no package then the package node name is "<unnamed>". + * The returned node is systemRoot().node(packageNodeName). + * + * @param c Object whose default system preference node is requested + * @returns system preferences node that should be used by class c + * @exception SecurityException when a security manager is installed and + * the caller does not have RuntimePermission("preferences"). + */ + public static Preferences systemNodeForPackage(Class c) + throws SecurityException + { + return nodeForPackage(c, systemRoot()); + } + + /** + * Returns the user preferences node for the package of a class. + * The package node name of the class is determined by dropping the + * final component of the fully qualified class name and + * changing all '.' to '/' in the package name. If the class of the + * object has no package then the package node name is "<unnamed>". + * The returned node is userRoot().node(packageNodeName). + * + * @param c Object whose default userpreference node is requested + * @returns userpreferences node that should be used by class c + * @exception SecurityException when a security manager is installed and + * the caller does not have RuntimePermission("preferences"). + */ + public static Preferences userNodeForPackage(Class c) + throws SecurityException + { + return nodeForPackage(c, userRoot()); + } + + /** + * Private helper method for systemNodeForPackage() and + * userNodeForPackage(). Given the correct system or user + * root it returns the correct Preference node for the package node name + * of the given object. + */ + private static Preferences nodeForPackage(Class c, Preferences root) { + // Get the package path + String className = c.getName(); + String packagePath; + int index = className.lastIndexOf('.'); + if(index == -1) { + packagePath = ""; + } else { + packagePath = className.substring(0,index).replace('.','/'); + } + + return root.node(packagePath); + } + + /** + * Import preferences from the given input stream. This expects + * preferences to be represented in XML as emitted by + * {@link #exportNode(OutputStream)} and + * {@link #exportSubtree(OutputStream)}. + * @throws IOException if there is an error while reading + * @throws InvalidPreferencesFormatException if the XML is not properly + * formatted + */ + public static void importPreferences(InputStream is) + throws InvalidPreferencesFormatException, + IOException + { + PreferencesFactory factory = getFactory(); + NodeReader reader = new NodeReader(is, factory); + reader.importPreferences(); + } + + // abstract methods (identification) + + /** + * Returns the absolute path name of this preference node. + * The absolute path name of a node is the path name of its parent node + * plus a '/' plus its own name. If the node is the root node and has no + * parent then its name is "" and its absolute path name is "/". + */ + public abstract String absolutePath(); + + /** + * Returns true if this node comes from the user preferences tree, false + * if it comes from the system preferences tree. + */ + public abstract boolean isUserNode(); + + /** + * Returns the name of this preferences node. The name of the node cannot + * be null, can be mostly 80 characters and cannot contain any '/' + * characters. The root node has as name "". + */ + public abstract String name(); + + /** + * Returns the String given by + * + * (isUserNode() ? "User":"System") + " Preference Node: " + absolutePath() + * + */ + public abstract String toString(); + + // abstract methods (navigation) + + /** + * Returns all the direct sub nodes of this preferences node. + * Needs access to the backing store to give a meaningfull answer. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException when this node has been removed + */ + public abstract String[] childrenNames() throws BackingStoreException; + + /** + * Returns a sub node of this preferences node if the given path is + * relative (does not start with a '/') or a sub node of the root + * if the path is absolute (does start with a '/'). + * + * @exception IllegalStateException if this node has been removed + * @exception IllegalArgumentException if the path contains two or more + * consecutive '/' characters, ends with a '/' charactor and is not the + * string "/" (indicating the root node) or any name on the path is more + * then 80 characters long + */ + public abstract Preferences node(String path); + + /** + * Returns true if the node that the path points to exists in memory or + * in the backing store. Otherwise it returns false or an exception is + * thrown. When this node is removed the only valid parameter is the + * empty string (indicating this node), the return value in that case + * will be false. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + * and the path is not the empty string (indicating this node) + * @exception IllegalArgumentException if the path contains two or more + * consecutive '/' characters, ends with a '/' charactor and is not the + * string "/" (indicating the root node) or any name on the path is more + * then 80 characters long + */ + public abstract boolean nodeExists(String path) + throws BackingStoreException; + + /** + * Returns the parent preferences node of this node or null if this is + * the root of the preferences tree. + * + * @exception IllegalStateException if this node has been removed + */ + public abstract Preferences parent(); + + // abstract methods (export) + + /** + * Export this node, but not its descendants, as XML to the + * indicated output stream. The XML will be encoded using UTF-8 + * and will use a specified document type:
        + * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
        + * @param os the output stream to which the XML is sent + * @throws BackingStoreException if preference data cannot be read + * @throws IOException if an error occurs while writing the XML + * @throws IllegalStateException if this node or an ancestor has been removed + */ + public abstract void exportNode(OutputStream os) + throws BackingStoreException, + IOException; + + /** + * Export this node and all its descendants as XML to the + * indicated output stream. The XML will be encoded using UTF-8 + * and will use a specified document type:
        + * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
        + * @param os the output stream to which the XML is sent + * @throws BackingStoreException if preference data cannot be read + * @throws IOException if an error occurs while writing the XML + * @throws IllegalStateException if this node or an ancestor has been removed + */ + public abstract void exportSubtree(OutputStream os) + throws BackingStoreException, + IOException; + + // abstract methods (preference entry manipulation) + + /** + * Returns an (possibly empty) array with all the keys of the preference + * entries of this node. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public abstract String[] keys() throws BackingStoreException; + + /** + * Returns the value associated with the key in this preferences node. If + * the default value of the key cannot be found in the preferences node + * entries or something goes wrong with the backing store the supplied + * default value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract String get(String key, String defaultVal); + + /** + * Convenience method for getting the given entry as a boolean. + * When the string representation of the requested entry is either + * "true" or "false" (ignoring case) then that value is returned, + * otherwise the given default boolean value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract boolean getBoolean(String key, boolean defaultVal); + + /** + * Convenience method for getting the given entry as a byte array. + * When the string representation of the requested entry is a valid + * Base64 encoded string (without any other characters, such as newlines) + * then the decoded Base64 string is returned as byte array, + * otherwise the given default byte array value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract byte[] getByteArray(String key, byte[] defaultVal); + + /** + * Convenience method for getting the given entry as a double. + * When the string representation of the requested entry can be decoded + * with Double.parseDouble() then that double is returned, + * otherwise the given default double value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract double getDouble(String key, double defaultVal); + + /** + * Convenience method for getting the given entry as a float. + * When the string representation of the requested entry can be decoded + * with Float.parseFloat() then that float is returned, + * otherwise the given default float value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract float getFloat(String key, float defaultVal); + + /** + * Convenience method for getting the given entry as an integer. + * When the string representation of the requested entry can be decoded + * with Integer.parseInt() then that integer is returned, + * otherwise the given default integer value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract int getInt(String key, int defaultVal); + + /** + * Convenience method for getting the given entry as a long. + * When the string representation of the requested entry can be decoded + * with Long.parseLong() then that long is returned, + * otherwise the given default long value is returned. + * + * @exception IllegalArgumentException if key is larger then 80 characters + * @exception IllegalStateException if this node has been removed + * @exception NullPointerException if key is null + */ + public abstract long getLong(String key, long defaultVal); + + /** + * Sets the value of the given preferences entry for this node. + * Key and value cannot be null, the key cannot exceed 80 characters + * and the value cannot exceed 8192 characters. + *

        + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * + * @exception NullPointerException if either key or value are null + * @exception IllegalArgumentException if either key or value are to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void put(String key, String value); + + /** + * Convenience method for setting the given entry as a boolean. + * The boolean is converted with Boolean.toString(value) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putBoolean(String key, boolean value); + + /** + * Convenience method for setting the given entry as an array of bytes. + * The byte array is converted to a Base64 encoded string + * and then stored in the preference entry as that string. + *

        + * Note that a byte array encoded as a Base64 string will be about 1.3 + * times larger then the original length of the byte array, which means + * that the byte array may not be larger about 6 KB. + * + * @exception NullPointerException if either key or value are null + * @exception IllegalArgumentException if either key or value are to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putByteArray(String key, byte[] value); + + /** + * Convenience method for setting the given entry as a double. + * The double is converted with Double.toString(double) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putDouble(String key, double value); + + /** + * Convenience method for setting the given entry as a float. + * The float is converted with Float.toString(float) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putFloat(String key, float value); + + /** + * Convenience method for setting the given entry as an integer. + * The integer is converted with Integer.toString(int) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putInt(String key, int value); + + /** + * Convenience method for setting the given entry as a long. + * The long is converted with Long.toString(long) + * and then stored in the preference entry as that string. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void putLong(String key, long value); + + /** + * Removes the preferences entry from this preferences node. + *

        + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * + * @exception NullPointerException if the key is null + * @exception IllegalArgumentException if the key length is to large + * @exception IllegalStateException when this node has been removed + */ + public abstract void remove(String key); + + // abstract methods (preference node manipulation) + + /** + * Removes all entries from this preferences node. May need access to the + * backing store to get and clear all entries. + *

        + * The result will be immediatly visible in this VM, but may not be + * immediatly written to the backing store. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public abstract void clear() throws BackingStoreException; + + /** + * Writes all preference changes on this and any subnode that have not + * yet been written to the backing store. This has no effect on the + * preference entries in this VM, but it makes sure that all changes + * are visible to other programs (other VMs might need to call the + * sync() method to actually see the changes to the backing + * store. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public abstract void flush() throws BackingStoreException; + + /** + * Writes and reads all preference changes to and from this and any + * subnodes. This makes sure that all local changes are written to the + * backing store and that all changes to the backing store are visible + * in this preference node (and all subnodes). + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has been removed + */ + public abstract void sync() throws BackingStoreException; + + /** + * Removes this and all subnodes from the backing store and clears all + * entries. After removal this instance will not be useable (except for + * a few methods that don't throw a InvalidStateException), + * even when a new node with the same path name is created this instance + * will not be usable again. The root (system or user) may never be removed. + *

        + * Note that according to the specification an implementation may delay + * removal of the node from the backing store till the flush() + * method is called. But the flush() method may throw a + * IllegalStateException when the node has been removed. + * So most implementations will actually remove the node and any subnodes + * from the backing store immediatly. + * + * @exception BackingStoreException when the backing store cannot be + * reached + * @exception IllegalStateException if this node has already been removed + * @exception UnsupportedOperationException if this is a root node + */ + public abstract void removeNode() throws BackingStoreException; + + // abstract methods (listeners) + + public abstract void addNodeChangeListener(NodeChangeListener listener); + + public abstract void addPreferenceChangeListener + (PreferenceChangeListener listener); + + public abstract void removeNodeChangeListener(NodeChangeListener listener); + + public abstract void removePreferenceChangeListener + (PreferenceChangeListener listener); +} diff --git a/libjava/classpath/java/util/prefs/PreferencesFactory.java b/libjava/classpath/java/util/prefs/PreferencesFactory.java new file mode 100644 index 000000000..5674f806d --- /dev/null +++ b/libjava/classpath/java/util/prefs/PreferencesFactory.java @@ -0,0 +1,65 @@ +/* PreferencesFactory - Preferences system and user root factory interface + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.prefs; + +/** + * Preferences system and user root factory interface. Defines how to get + * to the system and user root preferences objects. Should be implemented by + * new preferences backends. + * + * @since 1.4 + * @author Mark Wielaard (mark@klomp.org) + */ +public interface PreferencesFactory { + + /** + * Returns the system root preferences node. Should always return the + * same object. + */ + Preferences systemRoot(); + + /** + * Returns the user root preferences node. May return different objects + * depending on the user that called this method. The user may for example + * be determined by the current Thread or the Subject associated with the + * current AccessControllContext. + */ + Preferences userRoot(); + +} diff --git a/libjava/classpath/java/util/prefs/package.html b/libjava/classpath/java/util/prefs/package.html new file mode 100644 index 000000000..65fc1ac79 --- /dev/null +++ b/libjava/classpath/java/util/prefs/package.html @@ -0,0 +1,46 @@ + + + + +GNU Classpath - java.util.prefs + + +

        Utility classes for storing and retrieving user and system preferences.

        + + + diff --git a/libjava/classpath/java/util/regex/MatchResult.java b/libjava/classpath/java/util/regex/MatchResult.java new file mode 100644 index 000000000..605873dd0 --- /dev/null +++ b/libjava/classpath/java/util/regex/MatchResult.java @@ -0,0 +1,81 @@ +/* MatchResult.java -- Result of a regular expression match. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.regex; + +/** + * This interface represents the result of a regular expression match. + * It can be used to query the contents of the match, but not to modify + * them. + * @since 1.5 + */ +public interface MatchResult +{ + /** Returns the index just after the last matched character. */ + int end(); + + /** + * Returns the index just after the last matched character of the + * given sub-match group. + * @param group the sub-match group + */ + int end(int group); + + /** Returns the substring of the input which was matched. */ + String group(); + + /** + * Returns the substring of the input which was matched by the + * given sub-match group. + * @param group the sub-match group + */ + String group(int group); + + /** Returns the number of sub-match groups in the matching pattern. */ + int groupCount(); + + /** Returns the index of the first character of the match. */ + int start(); + + /** + * Returns the index of the first character of the given sub-match + * group. + * @param group the sub-match group + */ + int start(int group); +} diff --git a/libjava/classpath/java/util/regex/Matcher.java b/libjava/classpath/java/util/regex/Matcher.java new file mode 100644 index 000000000..be57471de --- /dev/null +++ b/libjava/classpath/java/util/regex/Matcher.java @@ -0,0 +1,611 @@ +/* Matcher.java -- Instance of a regular expression applied to a char sequence. + Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.regex; + +import gnu.java.lang.CPStringBuilder; + +import gnu.java.util.regex.CharIndexed; +import gnu.java.util.regex.RE; +import gnu.java.util.regex.REMatch; + +/** + * Instance of a regular expression applied to a char sequence. + * + * @since 1.4 + */ +public final class Matcher implements MatchResult +{ + private Pattern pattern; + private CharSequence input; + // We use CharIndexed as an input object to the getMatch method in order + // that /\G/ (the end of the previous match) may work. The information + // of the previous match is stored in the CharIndexed object. + private CharIndexed inputCharIndexed; + private int position; + private int appendPosition; + private REMatch match; + + /** + * The start of the region of the input on which to match. + */ + private int regionStart; + + /** + * The end of the region of the input on which to match. + */ + private int regionEnd; + + /** + * True if the match process should look beyond the + * region marked by regionStart to regionEnd when + * performing lookAhead, lookBehind and boundary + * matching. + */ + private boolean transparentBounds; + + /** + * The flags that affect the anchoring bounds. + * If {@link #hasAnchoringBounds()} is {@code true}, + * the match process will honour the + * anchoring bounds: ^, \A, \Z, \z and $. If + * {@link #hasAnchoringBounds()} is {@code false}, + * the anchors are ignored and appropriate flags, + * stored in this variable, are used to provide this + * behaviour. + */ + private int anchoringBounds; + + Matcher(Pattern pattern, CharSequence input) + { + this.pattern = pattern; + this.input = input; + this.inputCharIndexed = RE.makeCharIndexed(input, 0); + regionStart = 0; + regionEnd = input.length(); + transparentBounds = false; + anchoringBounds = 0; + } + + /** + * @param sb The target string buffer + * @param replacement The replacement string + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public Matcher appendReplacement (StringBuffer sb, String replacement) + throws IllegalStateException + { + assertMatchOp(); + sb.append(input.subSequence(appendPosition, + match.getStartIndex()).toString()); + sb.append(RE.getReplacement(replacement, match, + RE.REG_REPLACE_USE_BACKSLASHESCAPE)); + appendPosition = match.getEndIndex(); + return this; + } + + /** + * @param sb The target string buffer + */ + public StringBuffer appendTail (StringBuffer sb) + { + sb.append(input.subSequence(appendPosition, input.length()).toString()); + return sb; + } + + /** + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + */ + public int end () + throws IllegalStateException + { + assertMatchOp(); + return match.getEndIndex(); + } + + /** + * @param group The index of a capturing group in this matcher's pattern + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public int end (int group) + throws IllegalStateException + { + assertMatchOp(); + return match.getEndIndex(group); + } + + public boolean find () + { + boolean first = (match == null); + if (transparentBounds || (regionStart == 0 && regionEnd == input.length())) + match = pattern.getRE().getMatch(inputCharIndexed, position, anchoringBounds); + else + match = pattern.getRE().getMatch(input.subSequence(regionStart, regionEnd), + position, anchoringBounds); + if (match != null) + { + int endIndex = match.getEndIndex(); + // Are we stuck at the same position? + if (!first && endIndex == position) + { + match = null; + // Not at the end of the input yet? + if (position < input.length() - 1) + { + position++; + return find(position); + } + else + return false; + } + position = endIndex; + return true; + } + return false; + } + + /** + * @param start The index to start the new pattern matching + * + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public boolean find (int start) + { + if (transparentBounds || (regionStart == 0 && regionEnd == input.length())) + match = pattern.getRE().getMatch(inputCharIndexed, start, anchoringBounds); + else + match = pattern.getRE().getMatch(input.subSequence(regionStart, regionEnd), + start, anchoringBounds); + if (match != null) + { + position = match.getEndIndex(); + return true; + } + return false; + } + + /** + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + */ + public String group () + { + assertMatchOp(); + return match.toString(); + } + + /** + * @param group The index of a capturing group in this matcher's pattern + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public String group (int group) + throws IllegalStateException + { + assertMatchOp(); + return match.toString(group); + } + + /** + * @param replacement The replacement string + */ + public String replaceFirst (String replacement) + { + reset(); + // Semantics might not quite match + return pattern.getRE().substitute(input, replacement, position, + RE.REG_REPLACE_USE_BACKSLASHESCAPE); + } + + /** + * @param replacement The replacement string + */ + public String replaceAll (String replacement) + { + reset(); + return pattern.getRE().substituteAll(input, replacement, position, + RE.REG_REPLACE_USE_BACKSLASHESCAPE); + } + + public int groupCount () + { + return pattern.getRE().getNumSubs(); + } + + public boolean lookingAt () + { + if (transparentBounds || (regionStart == 0 && regionEnd == input.length())) + match = pattern.getRE().getMatch(inputCharIndexed, regionStart, + anchoringBounds|RE.REG_FIX_STARTING_POSITION|RE.REG_ANCHORINDEX); + else + match = pattern.getRE().getMatch(input.subSequence(regionStart, regionEnd), 0, + anchoringBounds|RE.REG_FIX_STARTING_POSITION); + if (match != null) + { + if (match.getStartIndex() == 0) + { + position = match.getEndIndex(); + return true; + } + match = null; + } + return false; + } + + /** + * Attempts to match the entire input sequence against the pattern. + * + * If the match succeeds then more information can be obtained via the + * start, end, and group methods. + * + * @see #start() + * @see #end() + * @see #group() + */ + public boolean matches () + { + if (transparentBounds || (regionStart == 0 && regionEnd == input.length())) + match = pattern.getRE().getMatch(inputCharIndexed, regionStart, + anchoringBounds|RE.REG_TRY_ENTIRE_MATCH|RE.REG_FIX_STARTING_POSITION|RE.REG_ANCHORINDEX); + else + match = pattern.getRE().getMatch(input.subSequence(regionStart, regionEnd), 0, + anchoringBounds|RE.REG_TRY_ENTIRE_MATCH|RE.REG_FIX_STARTING_POSITION); + if (match != null) + { + if (match.getStartIndex() == 0) + { + position = match.getEndIndex(); + if (position == input.length()) + return true; + } + match = null; + } + return false; + } + + /** + * Returns the Pattern that is interpreted by this Matcher + */ + public Pattern pattern () + { + return pattern; + } + + /** + * Resets the internal state of the matcher, including + * resetting the region to its default state of encompassing + * the whole input. The state of {@link #hasTransparentBounds()} + * and {@link #hasAnchoringBounds()} are unaffected. + * + * @return a reference to this matcher. + * @see #regionStart() + * @see #regionEnd() + * @see #hasTransparentBounds() + * @see #hasAnchoringBounds() + */ + public Matcher reset () + { + position = 0; + match = null; + regionStart = 0; + regionEnd = input.length(); + appendPosition = 0; + return this; + } + + /** + * Resets the internal state of the matcher, including + * resetting the region to its default state of encompassing + * the whole input. The state of {@link #hasTransparentBounds()} + * and {@link #hasAnchoringBounds()} are unaffected. + * + * @param input The new input character sequence. + * @return a reference to this matcher. + * @see #regionStart() + * @see #regionEnd() + * @see #hasTransparentBounds() + * @see #hasAnchoringBounds() + */ + public Matcher reset (CharSequence input) + { + this.input = input; + this.inputCharIndexed = RE.makeCharIndexed(input, 0); + return reset(); + } + + /** + * @return the index of a capturing group in this matcher's pattern + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + */ + public int start () + throws IllegalStateException + { + assertMatchOp(); + return match.getStartIndex(); + } + + /** + * @param group The index of a capturing group in this matcher's pattern + * + * @exception IllegalStateException If no match has yet been attempted, + * or if the previous match operation failed + * @exception IndexOutOfBoundsException If the replacement string refers + * to a capturing group that does not exist in the pattern + */ + public int start (int group) + throws IllegalStateException + { + assertMatchOp(); + return match.getStartIndex(group); + } + + /** + * @return True if and only if the matcher hit the end of input. + * @since 1.5 + */ + public boolean hitEnd() + { + return inputCharIndexed.hitEnd(); + } + + /** + * @return A string expression of this matcher. + */ + public String toString() + { + CPStringBuilder sb = new CPStringBuilder(); + sb.append(this.getClass().getName()) + .append("[pattern=").append(pattern.pattern()) + .append(" region=").append(regionStart).append(",").append(regionEnd) + .append(" anchoringBounds=").append(anchoringBounds == 0) + .append(" transparentBounds=").append(transparentBounds) + .append(" lastmatch=").append(match == null ? "" : match.toString()) + .append("]"); + return sb.toString(); + } + + private void assertMatchOp() + { + if (match == null) throw new IllegalStateException(); + } + + /** + *

        + * Defines the region of the input on which to match. + * By default, the {@link Matcher} attempts to match + * the whole string (from 0 to the length of the input), + * but a region between {@code start} (inclusive) and + * {@code end} (exclusive) on which to match may instead + * be defined using this method. + *

        + *

        + * The behaviour of region matching is further affected + * by the use of transparent or opaque bounds (see + * {@link #useTransparentBounds(boolean)}) and whether or not + * anchors ({@code ^} and {@code $}) are in use + * (see {@link #useAnchoringBounds(boolean)}). With transparent + * bounds, the matcher is aware of input outside the bounds + * set by this method, whereas, with opaque bounds (the default) + * only the input within the bounds is used. The use of + * anchors are affected by this setting; with transparent + * bounds, anchors will match the beginning of the real input, + * while with opaque bounds they match the beginning of the + * region. {@link #useAnchoringBounds(boolean)} can be used + * to turn on or off the matching of anchors. + *

        + * + * @param start the start of the region (inclusive). + * @param end the end of the region (exclusive). + * @return a reference to this matcher. + * @throws IndexOutOfBoundsException if either {@code start} or + * {@code end} are less than zero, + * if either {@code start} or + * {@code end} are greater than the + * length of the input, or if + * {@code start} is greater than + * {@code end}. + * @see #regionStart() + * @see #regionEnd() + * @see #hasTransparentBounds() + * @see #useTransparentBounds(boolean) + * @see #hasAnchoringBounds() + * @see #useAnchoringBounds(boolean) + * @since 1.5 + */ + public Matcher region(int start, int end) + { + int length = input.length(); + if (start < 0) + throw new IndexOutOfBoundsException("The start position was less than zero."); + if (start >= length) + throw new IndexOutOfBoundsException("The start position is after the end of the input."); + if (end < 0) + throw new IndexOutOfBoundsException("The end position was less than zero."); + if (end > length) + throw new IndexOutOfBoundsException("The end position is after the end of the input."); + if (start > end) + throw new IndexOutOfBoundsException("The start position is after the end position."); + reset(); + regionStart = start; + regionEnd = end; + return this; + } + + /** + * The start of the region on which to perform matches (inclusive). + * + * @return the start index of the region. + * @see #region(int,int) + * #see #regionEnd() + * @since 1.5 + */ + public int regionStart() + { + return regionStart; + } + + /** + * The end of the region on which to perform matches (exclusive). + * + * @return the end index of the region. + * @see #region(int,int) + * @see #regionStart() + * @since 1.5 + */ + public int regionEnd() + { + return regionEnd; + } + + /** + * Returns true if the bounds of the region marked by + * {@link #regionStart()} and {@link #regionEnd()} are + * transparent. When these bounds are transparent, the + * matching process can look beyond them to perform + * lookahead, lookbehind and boundary matching operations. + * By default, the bounds are opaque. + * + * @return true if the bounds of the matching region are + * transparent. + * @see #useTransparentBounds(boolean) + * @see #region(int,int) + * @see #regionStart() + * @see #regionEnd() + * @since 1.5 + */ + public boolean hasTransparentBounds() + { + return transparentBounds; + } + + /** + * Sets the transparency of the bounds of the region + * marked by {@link #regionStart()} and {@link #regionEnd()}. + * A value of {@code true} makes the bounds transparent, + * so the matcher can see beyond them to perform lookahead, + * lookbehind and boundary matching operations. A value + * of {@code false} (the default) makes the bounds opaque, + * restricting the match to the input region denoted + * by {@link #regionStart()} and {@link #regionEnd()}. + * + * @param transparent true if the bounds should be transparent. + * @return a reference to this matcher. + * @see #hasTransparentBounds() + * @see #region(int,int) + * @see #regionStart() + * @see #regionEnd() + * @since 1.5 + */ + public Matcher useTransparentBounds(boolean transparent) + { + transparentBounds = transparent; + return this; + } + + /** + * Returns true if the matcher will honour the use of + * the anchoring bounds: {@code ^}, {@code \A}, {@code \Z}, + * {@code \z} and {@code $}. By default, the anchors + * are used. Note that the effect of the anchors is + * also affected by {@link #hasTransparentBounds()}. + * + * @return true if the matcher will attempt to match + * the anchoring bounds. + * @see #useAnchoringBounds(boolean) + * @see #hasTransparentBounds() + * @since 1.5 + */ + public boolean hasAnchoringBounds() + { + return anchoringBounds == 0; + } + + /** + * Enables or disables the use of the anchoring bounds: + * {@code ^}, {@code \A}, {@code \Z}, {@code \z} and + * {@code $}. By default, their use is enabled. When + * disabled, the matcher will not attempt to match + * the anchors. + * + * @param useAnchors true if anchoring bounds should be used. + * @return a reference to this matcher. + * @since 1.5 + * @see #hasAnchoringBounds() + */ + public Matcher useAnchoringBounds(boolean useAnchors) + { + if (useAnchors) + anchoringBounds = 0; + else + anchoringBounds = RE.REG_NOTBOL|RE.REG_NOTEOL; + return this; + } + + /** + * Returns a read-only snapshot of the current state of + * the {@link Matcher} as a {@link MatchResult}. Any + * subsequent changes to this instance are not reflected + * in the returned {@link MatchResult}. + * + * @return a {@link MatchResult} instance representing the + * current state of the {@link Matcher}. + */ + public MatchResult toMatchResult() + { + Matcher snapshot = new Matcher(pattern, input); + if (match != null) + snapshot.match = (REMatch) match.clone(); + return snapshot; + } + +} diff --git a/libjava/classpath/java/util/regex/Pattern.java b/libjava/classpath/java/util/regex/Pattern.java new file mode 100644 index 000000000..7d1fc84b4 --- /dev/null +++ b/libjava/classpath/java/util/regex/Pattern.java @@ -0,0 +1,271 @@ +/* Pattern.java -- Compiled regular expression ready to be applied. + Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.regex; + +import gnu.java.util.regex.RE; +import gnu.java.util.regex.REException; +import gnu.java.util.regex.RESyntax; + +import java.io.Serializable; +import java.util.ArrayList; + + +/** + * Compiled regular expression ready to be applied. + * + * @since 1.4 + */ +public final class Pattern implements Serializable +{ + private static final long serialVersionUID = 5073258162644648461L; + + public static final int CANON_EQ = 128; + public static final int CASE_INSENSITIVE = 2; + public static final int COMMENTS = 4; + public static final int DOTALL = 32; + public static final int MULTILINE = 8; + public static final int UNICODE_CASE = 64; + public static final int UNIX_LINES = 1; + + private final String regex; + private final int flags; + + private final RE re; + + private Pattern (String regex, int flags) + throws PatternSyntaxException + { + this.regex = regex; + this.flags = flags; + + RESyntax syntax = RESyntax.RE_SYNTAX_JAVA_1_4; + int gnuFlags = 0; + gnuFlags |= RE.REG_ICASE_USASCII; + if ((flags & CASE_INSENSITIVE) != 0) + gnuFlags |= RE.REG_ICASE; + if ((flags & MULTILINE) != 0) + { + gnuFlags |= RE.REG_MULTILINE; + syntax = new RESyntax(syntax); + syntax.setLineSeparator(null); + } + if ((flags & DOTALL) != 0) + gnuFlags |= RE.REG_DOT_NEWLINE; + if ((flags & UNICODE_CASE) != 0) + gnuFlags &= ~RE.REG_ICASE_USASCII; + // not yet supported: + // if ((flags & CANON_EQ) != 0) gnuFlags = + + if ((flags & UNIX_LINES) != 0) + { + // Use a syntax set with \n for linefeeds? + syntax = new RESyntax(syntax); + syntax.setLineSeparator("\n"); + } + + if ((flags & COMMENTS) != 0) + { + gnuFlags |= RE.REG_X_COMMENTS; + } + + try + { + this.re = new RE(regex, gnuFlags, syntax); + } + catch (REException e) + { + PatternSyntaxException pse; + pse = new PatternSyntaxException(e.getMessage(), + regex, e.getPosition()); + pse.initCause(e); + throw pse; + } + } + + // package private accessor method + RE getRE() + { + return re; + } + + /** + * @param regex The regular expression + * + * @exception PatternSyntaxException If the expression's syntax is invalid + */ + public static Pattern compile (String regex) + throws PatternSyntaxException + { + return compile(regex, 0); + } + + /** + * @param regex The regular expression + * @param flags The match flags, a bit mask + * + * @exception PatternSyntaxException If the expression's syntax is invalid + * @exception IllegalArgumentException If bit values other than those + * corresponding to the defined match flags are set in flags + */ + public static Pattern compile (String regex, int flags) + throws PatternSyntaxException + { + // FIXME: check which flags are really accepted + if ((flags & ~0xEF) != 0) + throw new IllegalArgumentException (); + + return new Pattern (regex, flags); + } + + public int flags () + { + return this.flags; + } + + /** + * @param regex The regular expression + * @param input The character sequence to be matched + * + * @exception PatternSyntaxException If the expression's syntax is invalid + */ + public static boolean matches (String regex, CharSequence input) + { + return compile(regex).matcher(input).matches(); + } + + /** + * @param input The character sequence to be matched + */ + public Matcher matcher (CharSequence input) + { + return new Matcher(this, input); + } + + /** + * @param input The character sequence to be matched + */ + public String[] split (CharSequence input) + { + return split(input, 0); + } + + /** + * @param input The character sequence to be matched + * @param limit The result threshold + */ + public String[] split (CharSequence input, int limit) + { + Matcher matcher = new Matcher(this, input); + ArrayList list = new ArrayList(); + int empties = 0; + int count = 0; + int start = 0; + int end; + boolean matched = matcher.find(); + + while (matched && (limit <= 0 || count < limit - 1)) + { + ++count; + end = matcher.start(); + if (start == end) + empties++; + else + { + while (empties > 0) + { + list.add(""); + empties--; + } + + String text = input.subSequence(start, end).toString(); + list.add(text); + } + start = matcher.end(); + matched = matcher.find(); + } + + // We matched nothing. + if (!matched && count == 0) + return new String[] { input.toString() }; + + // Is the last token empty? + boolean emptyLast = (start == input.length()); + + // Can/Must we add empties or an extra last token at the end? + if (list.size() < limit || limit < 0 || (limit == 0 && !emptyLast)) + { + if (limit > list.size()) + { + int max = limit - list.size(); + empties = (empties > max) ? max : empties; + } + while (empties > 0) + { + list.add(""); + empties--; + } + } + + // last token at end + if (limit != 0 || (limit == 0 && !emptyLast)) + { + String t = input.subSequence(start, input.length()).toString(); + if ("".equals(t) && limit == 0) + { /* Don't add. */ } + else + list.add(t); + } + + return list.toArray(new String[list.size()]); + } + + public String pattern () + { + return regex; + } + + /** + * Return the regular expression used to construct this object. + * @specnote Prior to JDK 1.5 this method had a different behavior + * @since 1.5 + */ + public String toString() + { + return regex; + } +} diff --git a/libjava/classpath/java/util/regex/PatternSyntaxException.java b/libjava/classpath/java/util/regex/PatternSyntaxException.java new file mode 100644 index 000000000..db73d06e2 --- /dev/null +++ b/libjava/classpath/java/util/regex/PatternSyntaxException.java @@ -0,0 +1,135 @@ +/* PatternSyntaxException - Indicates illegal pattern for regular expression. + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.regex; + +import gnu.java.lang.CPStringBuilder; + +/** + * Indicates illegal pattern for regular expression. + * Includes state to inspect the pattern and what and where the expression + * was not valid regular expression. + * @since 1.4 + */ +public class PatternSyntaxException extends IllegalArgumentException +{ + private static final long serialVersionUID = -3864639126226059218L; + + /** + * Human readable escription of the syntax error. + */ + private final String desc; + + /** + * The original pattern that contained the syntax error. + */ + private final String pattern; + + /** + * Index of the first character in the String that was probably invalid, + * or -1 when unknown. + */ + private final int index; + + /** + * Creates a new PatternSyntaxException. + * + * @param description Human readable escription of the syntax error. + * @param pattern The original pattern that contained the syntax error. + * @param index Index of the first character in the String that was + * probably invalid, or -1 when unknown. + */ + public PatternSyntaxException(String description, + String pattern, + int index) + { + super(description); + this.desc = description; + this.pattern = pattern; + this.index = index; + } + + /** + * Returns a human readable escription of the syntax error. + */ + public String getDescription() + { + return desc; + } + + /** + * Returns the original pattern that contained the syntax error. + */ + public String getPattern() + { + return pattern; + } + + /** + * Returns the index of the first character in the String that was probably + * invalid, or -1 when unknown. + */ + public int getIndex() + { + return index; + } + + /** + * Returns a string containing a line with the description, a line with + * the original pattern and a line indicating with a ^ which character is + * probably the first invalid character in the pattern if the index is not + * negative. + */ + public String getMessage() + { + String lineSep = System.getProperty("line.separator"); + CPStringBuilder sb = new CPStringBuilder(desc); + sb.append(lineSep); + sb.append('\t'); + sb.append(pattern); + if (index != -1) + { + sb.append(lineSep); + sb.append('\t'); + for (int i=0; i + + + +GNU Classpath - java.util.regex + + +

        Regular expression patterns and matchers.

        + + + diff --git a/libjava/classpath/java/util/spi/CurrencyNameProvider.java b/libjava/classpath/java/util/spi/CurrencyNameProvider.java new file mode 100644 index 000000000..14fae4d87 --- /dev/null +++ b/libjava/classpath/java/util/spi/CurrencyNameProvider.java @@ -0,0 +1,100 @@ +/* CurrencyNameProvider.java -- Providers of localized currency symbols + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.spi; + +import java.util.Locale; + +/** + * A {@link CurrencyNameProvider} provides localized + * versions of the symbols that represent a particular + * currency. Note that currency symbols are regarded + * as names, and thus a null value may + * be returned, which should be treated as a lack of + * support for the specified {@link Locale}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class CurrencyNameProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link CurrencyNameProvider}. + * Provided for implicit invocation by subclasses. + */ + protected CurrencyNameProvider() + { + } + + /** + *

        + * This method returns the symbol which precedes or follows a + * value in this particular currency. The returned value is + * the symbol used to denote the currency in the specified locale. + *

        + *

        + * For example, a supplied locale may specify a different symbol + * for the currency, due to conflicts with its own currency. + * This would be the case with the American currency, the dollar. + * Locales that also use a dollar-based currency (e.g. Canada, Australia) + * need to differentiate the American dollar using 'US$' rather than '$'. + * So, supplying one of these locales to getSymbol() would + * return this value, rather than the standard '$'. + *

        + *

        + * In cases where there is no such symbol for a particular currency, + * null should be returned. + *

        + * + * @param currencyCode the ISO 4217 currency code, consisting + * of three uppercase letters from 'A' to 'Z' + * @param locale the locale to express the symbol in. + * @return the currency symbol, or null if one is + * unavailable. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the currency code is + * not in the correct format + * or the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.Currency#getSymbol(java.util.Locale) + */ + public abstract String getSymbol(String currencyCode, Locale locale); + +} diff --git a/libjava/classpath/java/util/spi/LocaleNameProvider.java b/libjava/classpath/java/util/spi/LocaleNameProvider.java new file mode 100644 index 000000000..44250feb5 --- /dev/null +++ b/libjava/classpath/java/util/spi/LocaleNameProvider.java @@ -0,0 +1,135 @@ +/* LocaleNameProvider.java -- Providers of localized locale names + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.spi; + +import java.util.Locale; + +/** + * A {@link LocaleNameProvider} provides localized + * versions of the names that represent a particular + * locale. Note that a null value may + * be returned, which should be treated as a lack of + * support for the specified {@link Locale}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class LocaleNameProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link LocaleNameProvider}. + * Provided for implicit invocation by subclasses. + */ + protected LocaleNameProvider() + { + } + + /** + * Returns the localized name for the specified ISO 3166 + * country in the supplied {@link java.util.Locale}. + * For example, if the country code is "DE", + * this method will return "Germany" for + * {@link Locale.ENGLISH} but "Deutschland" + * for {@link Locale.GERMANY}. If the name of the country + * in the given locale is not supported, null + * is returned. + * + * @param countryCode the ISO 3166 country code, consisting + * of two uppercase letters from 'A' to 'Z' + * @param locale the locale to express the country in. + * @return the country name, or null if one is + * not available. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the country code is + * not in the correct format + * or the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.Locale#getDisplayCountry(java.util.Locale) + */ + public abstract String getDisplayCountry(String countryCode, + Locale locale); + + /** + * Returns the localized name for the specified ISO 639 + * language in the supplied {@link java.util.Locale}. + * For example, if the language code is "de", + * this method will return "German" for + * {@link Locale.ENGLISH} but "Deutsch" + * for {@link Locale.GERMANY}. If the name of the language + * in the given locale is not supported, null + * is returned. + * + * @param langCode the ISO 639 language code, consisting + * of two lowercase letters from 'a' to 'z' + * @param locale the locale to express the language in. + * @return the country name, or null if one is + * not available. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the language code is + * not in the correct format + * or the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.Locale#getDisplayLanguage(java.util.Locale) + */ + public abstract String getDisplayLanguage(String langCode, + Locale locale); + + /** + * Returns the localized name for the specified variant + * in the supplied {@link java.util.Locale}. If the name + * of the variant in the given locale is not supported, + * null is returned. + * + * @param variant the variant. + * @param locale the locale to express the variant in. + * @return the localized variant, or null if one is + * not available. + * @throws NullPointerException if the locale is null. + * @throws IllegalArgumentException if the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.Locale#getDisplayVariant(java.util.Locale) + */ + public abstract String getDisplayVariant(String variant, + Locale locale); + +} diff --git a/libjava/classpath/java/util/spi/LocaleServiceProvider.java b/libjava/classpath/java/util/spi/LocaleServiceProvider.java new file mode 100644 index 000000000..bb5b68527 --- /dev/null +++ b/libjava/classpath/java/util/spi/LocaleServiceProvider.java @@ -0,0 +1,125 @@ +/* LocaleServiceProvider.java -- Superclass of locale SPIs + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.spi; + +import java.util.Locale; + +/** + *

        + * This is the superclass of all the {@link Locale} service + * provider interfaces or SPIs. The locale SPIs are used + * to allow for the provision of additional support for + * locale-specific data. The runtime environment has its + * own collection of locale data, but these interfaces allow + * this to be extended by external classes. + *

        + *

        + * Service providers are created as concrete implementations + * of these interfaces, and accessed using the extension + * mechanism, realised by {@link ServiceLoader}. When a factory + * method of one of the locale-specific classes (such as + * {@link java.text.DateFormatSymbols} or {@link java.util.Currency}) + * is called, the runtime environment is first asked to + * provide data for the specified locale. If the runtime + * environment fails to provide this, then the offer is + * made to service providers which implement the appropriate + * interface. + *

        + *

        + * Each provider implements the method specified by this + * class, {@link #getAvailableLocales()}. This method is + * called first to determine whether the provider will be of + * any use in providing data for the specified locale. If + * a provider is found to be capable, then a more specific + * method appropriate to the class requiring the data will + * be called. In the case of {@link java.text.DateFormatSymbols}, + * this would be + * {@link java.text.spi.DateFormatSymbols#getInstance(Locale)}. + *

        + *

        + * If neither a service provider nor the runtime environment + * itself can fulfill the request, a fallback procedure is + * engaged. The locale is modified by applying the first + * applicable rule: + *

        + *
          + *
        1. If the variant contains a '_', then + * this and everything following it is trimmed.
        2. + *
        3. If the variant is non-empty, it is converted to + * an empty string.
        4. + *
        5. If the country is non-empty, it is converted to + * an empty string.
        6. + *
        7. If the language is non-empty, it is converted to + * an empty string.
        8. + *
        + *

        + * The modified locale is then used to start the same + * process again. The root locale (@link java.util.Locale#ROOT} + * must be supported by the runtime environment in order + * to terminate this cycle. + *

        + *

        + * Note that any names returned by the providers may + * be null. Returning a null + * name is considered equivalent to not supporting a + * particular locale. + *

        + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class LocaleServiceProvider +{ + + /** + * Constructs a new {@link LocaleServiceProvider}. + * Provided for implicit invocation by subclasses. + */ + protected LocaleServiceProvider() + { + } + + /** + * Returns an array of {@link Locale} instances, + * for which the provider can supply localized data. + * + * @return an array of supported locales. + */ + public abstract Locale[] getAvailableLocales(); + +} diff --git a/libjava/classpath/java/util/spi/TimeZoneNameProvider.java b/libjava/classpath/java/util/spi/TimeZoneNameProvider.java new file mode 100644 index 000000000..afd56eb1b --- /dev/null +++ b/libjava/classpath/java/util/spi/TimeZoneNameProvider.java @@ -0,0 +1,97 @@ +/* TimeZoneNameProvider.java -- Providers of localized currency symbols + Copyright (C) 2007 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.spi; + +import java.util.Locale; + +/** + * A {@link TimeZoneNameProvider} provides localized + * versions of the names that represent a particular + * timezone. A null value may + * be returned, which should be treated as a lack of + * support for the specified {@link Locale}. The names + * from this class are also used by + * {@link DateFormatSymbols#getZoneStrings()}. + * + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + * @since 1.6 + */ +public abstract class TimeZoneNameProvider + extends LocaleServiceProvider +{ + + /** + * Constructs a new {@link TimeZoneNameProvider}. + * Provided for implicit invocation by subclasses. + */ + protected TimeZoneNameProvider() + { + } + + /** + * Returns a name for the specified time zone identifier + * localized to the supplied {@link java.util.Locale}. + * The time zone identifier is either "GMT" + * or one of the identifiers from the public domain "tz + * database" found at + * ftp://elsie.nci.nih.gov/pub. Note that a translated + * name for the daylight savings time variant should be returned, + * even if the timezone has not observed daylight savings + * time in the past. If the name of the timezone + * in the given locale is not supported, null + * is returned. + * + * @param id a time zone identifier. + * @param daylight true if the daylight savings time variant + * should be returned. + * @param style either {@link java.util.TimeZone.LONG} or + * {@link java.util.TimeZone.SHORT} + * @param locale the locale to express the timezone in. + * @return the localized time zone name, or null + * if one is not available. + * @throws NullPointerException if the identifer or locale is null. + * @throws IllegalArgumentException if the style is invalid + * or the locale is not one + * returned by + * {@link getAvailableLocales()} + * @see java.util.TimeZone#getDisplayName(boolean,int,java.util.Locale) + */ + public abstract String getDisplayName(String id, boolean daylight, + int style, Locale locale); + +} diff --git a/libjava/classpath/java/util/spi/package.html b/libjava/classpath/java/util/spi/package.html new file mode 100644 index 000000000..1abdeb8b4 --- /dev/null +++ b/libjava/classpath/java/util/spi/package.html @@ -0,0 +1,50 @@ + + + + +GNU Classpath - java.util.spi + + + +

        +A series of service provider interfaces for use by the +classes in java.util. +

        +

        Since: 1.6

        + + diff --git a/libjava/classpath/java/util/zip/Adler32.java b/libjava/classpath/java/util/zip/Adler32.java new file mode 100644 index 000000000..cc27da927 --- /dev/null +++ b/libjava/classpath/java/util/zip/Adler32.java @@ -0,0 +1,205 @@ +/* Adler32.java - Computes Adler32 data checksum of a data stream + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * The actual Adler32 algorithm is taken from RFC 1950. + * Status: Believed complete and correct. + */ + +/** + * Computes Adler32 checksum for a stream of data. An Adler32 + * checksum is not as reliable as a CRC32 checksum, but a lot faster to + * compute. + *

        + * The specification for Adler32 may be found in RFC 1950. + * (ZLIB Compressed Data Format Specification version 3.3) + *

        + *

        + * From that document: + *

        + * "ADLER32 (Adler-32 checksum) + * This contains a checksum value of the uncompressed data + * (excluding any dictionary data) computed according to Adler-32 + * algorithm. This algorithm is a 32-bit extension and improvement + * of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073 + * standard. + *

        + * Adler-32 is composed of two sums accumulated per byte: s1 is + * the sum of all bytes, s2 is the sum of all s1 values. Both sums + * are done modulo 65521. s1 is initialized to 1, s2 to zero. The + * Adler-32 checksum is stored as s2*65536 + s1 in most- + * significant-byte first (network) order." + *

        + * "8.2. The Adler-32 algorithm + *

        + * The Adler-32 algorithm is much faster than the CRC32 algorithm yet + * still provides an extremely low probability of undetected errors. + *

        + * The modulo on unsigned long accumulators can be delayed for 5552 + * bytes, so the modulo operation time is negligible. If the bytes + * are a, b, c, the second sum is 3a + 2b + c + 3, and so is position + * and order sensitive, unlike the first sum, which is just a + * checksum. That 65521 is prime is important to avoid a possible + * large class of two-byte errors that leave the check unchanged. + * (The Fletcher checksum uses 255, which is not prime and which also + * makes the Fletcher check insensitive to single byte changes 0 <-> + * 255.) + *

        + * The sum s1 is initialized to 1 instead of zero to make the length + * of the sequence part of s2, so that the length does not have to be + * checked separately. (Any sequence of zeroes has a Fletcher + * checksum of zero.)" + * + * @author John Leuner, Per Bothner + * @since JDK 1.1 + * + * @see InflaterInputStream + * @see DeflaterOutputStream + */ +public class Adler32 implements Checksum +{ + + /** largest prime smaller than 65536 */ + private static final int BASE = 65521; + + private int checksum; //we do all in int. + + //Note that java doesn't have unsigned integers, + //so we have to be careful with what arithmetic + //we do. We return the checksum as a long to + //avoid sign confusion. + + /** + * Creates a new instance of the Adler32 class. + * The checksum starts off with a value of 1. + */ + public Adler32 () + { + reset(); + } + + /** + * Resets the Adler32 checksum to the initial value. + */ + public void reset () + { + checksum = 1; //Initialize to 1 + } + + /** + * Updates the checksum with the byte b. + * + * @param bval the data value to add. The high byte of the int is ignored. + */ + public void update (int bval) + { + //We could make a length 1 byte array and call update again, but I + //would rather not have that overhead + int s1 = checksum & 0xffff; + int s2 = checksum >>> 16; + + s1 = (s1 + (bval & 0xFF)) % BASE; + s2 = (s1 + s2) % BASE; + + checksum = (s2 << 16) + s1; + } + + /** + * Updates the checksum with the bytes taken from the array. + * + * @param buffer an array of bytes + */ + public void update (byte[] buffer) + { + update(buffer, 0, buffer.length); + } + + /** + * Updates the checksum with the bytes taken from the array. + * + * @param buf an array of bytes + * @param off the start of the data used for this update + * @param len the number of bytes to use for this update + */ + public void update (byte[] buf, int off, int len) + { + //(By Per Bothner) + int s1 = checksum & 0xffff; + int s2 = checksum >>> 16; + + while (len > 0) + { + // We can defer the modulo operation: + // s1 maximally grows from 65521 to 65521 + 255 * 3800 + // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 + int n = 3800; + if (n > len) + n = len; + len -= n; + while (--n >= 0) + { + s1 = s1 + (buf[off++] & 0xFF); + s2 = s2 + s1; + } + s1 %= BASE; + s2 %= BASE; + } + + /*Old implementation, borrowed from somewhere: + int n; + + while (len-- > 0) { + + s1 = (s1 + (bs[offset++] & 0xff)) % BASE; + s2 = (s2 + s1) % BASE; + }*/ + + checksum = (s2 << 16) | s1; + } + + /** + * Returns the Adler32 data checksum computed so far. + */ + public long getValue() + { + return (long) checksum & 0xffffffffL; + } +} diff --git a/libjava/classpath/java/util/zip/CRC32.java b/libjava/classpath/java/util/zip/CRC32.java new file mode 100644 index 000000000..de2e7083d --- /dev/null +++ b/libjava/classpath/java/util/zip/CRC32.java @@ -0,0 +1,132 @@ +/* CRC32.java - Computes CRC32 data checksum of a data stream + Copyright (C) 1999. 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * The actual CRC32 algorithm is taken from RFC 1952. + * Status: Believed complete and correct. + */ + +/** + * Computes CRC32 data checksum of a data stream. + * The actual CRC32 algorithm is described in RFC 1952 + * (GZIP file format specification version 4.3). + * Can be used to get the CRC32 over a stream if used with checked input/output + * streams. + * + * @see InflaterInputStream + * @see DeflaterOutputStream + * + * @author Per Bothner + * @date April 1, 1999. + */ +public class CRC32 implements Checksum +{ + /** The crc data checksum so far. */ + private int crc = 0; + + /** The fast CRC table. Computed once when the CRC32 class is loaded. */ + private static int[] crc_table = make_crc_table(); + + /** Make the table for a fast CRC. */ + private static int[] make_crc_table () + { + int[] crc_table = new int[256]; + for (int n = 0; n < 256; n++) + { + int c = n; + for (int k = 8; --k >= 0; ) + { + if ((c & 1) != 0) + c = 0xedb88320 ^ (c >>> 1); + else + c = c >>> 1; + } + crc_table[n] = c; + } + return crc_table; + } + + /** + * Returns the CRC32 data checksum computed so far. + */ + public long getValue () + { + return (long) crc & 0xffffffffL; + } + + /** + * Resets the CRC32 data checksum as if no update was ever called. + */ + public void reset () { crc = 0; } + + /** + * Updates the checksum with the int bval. + * + * @param bval (the byte is taken as the lower 8 bits of bval) + */ + + public void update (int bval) + { + int c = ~crc; + c = crc_table[(c ^ bval) & 0xff] ^ (c >>> 8); + crc = ~c; + } + + /** + * Adds the byte array to the data checksum. + * + * @param buf the buffer which contains the data + * @param off the offset in the buffer where the data starts + * @param len the length of the data + */ + public void update (byte[] buf, int off, int len) + { + int c = ~crc; + while (--len >= 0) + c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8); + crc = ~c; + } + + /** + * Adds the complete byte array to the data checksum. + */ + public void update (byte[] buf) { update(buf, 0, buf.length); } +} diff --git a/libjava/classpath/java/util/zip/CheckedInputStream.java b/libjava/classpath/java/util/zip/CheckedInputStream.java new file mode 100644 index 000000000..163a4c4aa --- /dev/null +++ b/libjava/classpath/java/util/zip/CheckedInputStream.java @@ -0,0 +1,135 @@ +/* CheckedInputStream.java - Compute checksum of data being read + Copyright (C) 1999, 2000, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +/** + * InputStream that computes a checksum of the data being read using a + * supplied Checksum object. + * + * @see Checksum + * + * @author Tom Tromey + * @date May 17, 1999 + */ +public class CheckedInputStream extends FilterInputStream +{ + /** + * Creates a new CheckInputStream on top of the supplied OutputStream + * using the supplied Checksum. + */ + public CheckedInputStream (InputStream in, Checksum sum) + { + super (in); + this.sum = sum; + } + + /** + * Returns the Checksum object used. To get the data checksum computed so + * far call getChecksum.getValue(). + */ + public Checksum getChecksum () + { + return sum; + } + + /** + * Reads one byte, updates the checksum and returns the read byte + * (or -1 when the end of file was reached). + */ + public int read () throws IOException + { + int x = in.read(); + if (x != -1) + sum.update(x); + return x; + } + + /** + * Reads at most len bytes in the supplied buffer and updates the checksum + * with it. Returns the number of bytes actually read or -1 when the end + * of file was reached. + */ + public int read (byte[] buf, int off, int len) throws IOException + { + int r = in.read(buf, off, len); + if (r != -1) + sum.update(buf, off, r); + return r; + } + + /** + * Skips n bytes by reading them in a temporary buffer and updating the + * the checksum with that buffer. Returns the actual number of bytes skiped + * which can be less then requested when the end of file is reached. + */ + public long skip (long n) throws IOException + { + if (n == 0) + return 0; + + int min = (int) Math.min(n, 1024); + byte[] buf = new byte[min]; + + long s = 0; + while (n > 0) + { + int r = in.read(buf, 0, min); + if (r == -1) + break; + n -= r; + s += r; + min = (int) Math.min(n, 1024); + sum.update(buf, 0, r); + } + + return s; + } + + /** The checksum object. */ + private Checksum sum; +} diff --git a/libjava/classpath/java/util/zip/CheckedOutputStream.java b/libjava/classpath/java/util/zip/CheckedOutputStream.java new file mode 100644 index 000000000..e0222258a --- /dev/null +++ b/libjava/classpath/java/util/zip/CheckedOutputStream.java @@ -0,0 +1,100 @@ +/* CheckedOutputStream.java - Compute checksum of data being written. + Copyright (C) 1999, 2000 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +/** + * OutputStream that computes a checksum of data being written using a + * supplied Checksum object. + * + * @see Checksum + * + * @author Tom Tromey + * @date May 17, 1999 + */ +public class CheckedOutputStream extends FilterOutputStream +{ + /** + * Creates a new CheckInputStream on top of the supplied OutputStream + * using the supplied Checksum. + */ + public CheckedOutputStream (OutputStream out, Checksum cksum) + { + super (out); + this.sum = cksum; + } + + /** + * Returns the Checksum object used. To get the data checksum computed so + * far call getChecksum.getValue(). + */ + public Checksum getChecksum () + { + return sum; + } + + /** + * Writes one byte to the OutputStream and updates the Checksum. + */ + public void write (int bval) throws IOException + { + out.write(bval); + sum.update(bval); + } + + /** + * Writes the byte array to the OutputStream and updates the Checksum. + */ + public void write (byte[] buf, int off, int len) throws IOException + { + out.write(buf, off, len); + sum.update(buf, off, len); + } + + /** The checksum object. */ + private Checksum sum; +} diff --git a/libjava/classpath/java/util/zip/Checksum.java b/libjava/classpath/java/util/zip/Checksum.java new file mode 100644 index 000000000..3342ba3a6 --- /dev/null +++ b/libjava/classpath/java/util/zip/Checksum.java @@ -0,0 +1,86 @@ +/* Checksum.java - Interface to compute a data checksum + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* + * Written using on-line Java Platform 1.2 API Specification, as well + * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). + * Status: Believed complete and correct. + */ + +/** + * Interface to compute a data checksum used by checked input/output streams. + * A data checksum can be updated by one byte or with a byte array. After each + * update the value of the current checksum can be returned by calling + * getValue. The complete checksum object can also be reset + * so it can be used again with new data. + * + * @see CheckedInputStream + * @see CheckedOutputStream + * + * @author Per Bothner + * @author Jochen Hoenicke + */ +public interface Checksum +{ + /** + * Returns the data checksum computed so far. + */ + long getValue(); + + /** + * Resets the data checksum as if no update was ever called. + */ + void reset(); + + /** + * Adds one byte to the data checksum. + * + * @param bval the data value to add. The high byte of the int is ignored. + */ + void update (int bval); + + /** + * Adds the byte array to the data checksum. + * + * @param buf the buffer which contains the data + * @param off the offset in the buffer where the data starts + * @param len the length of the data + */ + void update (byte[] buf, int off, int len); +} diff --git a/libjava/classpath/java/util/zip/DataFormatException.java b/libjava/classpath/java/util/zip/DataFormatException.java new file mode 100644 index 000000000..dc5b10dec --- /dev/null +++ b/libjava/classpath/java/util/zip/DataFormatException.java @@ -0,0 +1,71 @@ +/* DataformatException.java -- thrown when compressed data is corrupt + Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * Exception thrown when compressed data is corrupt. + * + * @author Tom Tromey + * @author John Leuner + * @since 1.1 + * @status updated to 1.4 + */ +public class DataFormatException extends Exception +{ + /** + * Compatible with JDK 1.1+. + */ + private static final long serialVersionUID = 2219632870893641452L; + + /** + * Create an exception without a message. + */ + public DataFormatException() + { + } + + /** + * Create an exception with a message. + * + * @param msg the message + */ + public DataFormatException(String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/util/zip/Deflater.java b/libjava/classpath/java/util/zip/Deflater.java new file mode 100644 index 000000000..dd81fe748 --- /dev/null +++ b/libjava/classpath/java/util/zip/Deflater.java @@ -0,0 +1,537 @@ +/* Deflater.java - Compress a data stream + Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This is the Deflater class. The deflater class compresses input + * with the deflate algorithm described in RFC 1951. It has several + * compression levels and three different strategies described below. + * + * This class is not thread safe. This is inherent in the API, due + * to the split of deflate and setInput. + * + * @author Jochen Hoenicke + * @author Tom Tromey + */ +public class Deflater +{ + /** + * The best and slowest compression level. This tries to find very + * long and distant string repetitions. + */ + public static final int BEST_COMPRESSION = 9; + /** + * The worst but fastest compression level. + */ + public static final int BEST_SPEED = 1; + /** + * The default compression level. + */ + public static final int DEFAULT_COMPRESSION = -1; + /** + * This level won't compress at all but output uncompressed blocks. + */ + public static final int NO_COMPRESSION = 0; + + /** + * The default strategy. + */ + public static final int DEFAULT_STRATEGY = 0; + /** + * This strategy will only allow longer string repetitions. It is + * useful for random data with a small character set. + */ + public static final int FILTERED = 1; + + /** + * This strategy will not look for string repetitions at all. It + * only encodes with Huffman trees (which means, that more common + * characters get a smaller encoding. + */ + public static final int HUFFMAN_ONLY = 2; + + /** + * The compression method. This is the only method supported so far. + * There is no need to use this constant at all. + */ + public static final int DEFLATED = 8; + + /* + * The Deflater can do the following state transitions: + * + * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. + * / | (2) (5) | + * / v (5) | + * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) + * \ | (3) | ,-------' + * | | | (3) / + * v v (5) v v + * (1) -> BUSY_STATE ----> FINISHING_STATE + * | (6) + * v + * FINISHED_STATE + * \_____________________________________/ + * | (7) + * v + * CLOSED_STATE + * + * (1) If we should produce a header we start in INIT_STATE, otherwise + * we start in BUSY_STATE. + * (2) A dictionary may be set only when we are in INIT_STATE, then + * we change the state as indicated. + * (3) Whether a dictionary is set or not, on the first call of deflate + * we change to BUSY_STATE. + * (4) -- intentionally left blank -- :) + * (5) FINISHING_STATE is entered, when flush() is called to indicate that + * there is no more INPUT. There are also states indicating, that + * the header wasn't written yet. + * (6) FINISHED_STATE is entered, when everything has been flushed to the + * internal pending output buffer. + * (7) At any time (7) + * + */ + + private static final int IS_SETDICT = 0x01; + private static final int IS_FLUSHING = 0x04; + private static final int IS_FINISHING = 0x08; + + private static final int INIT_STATE = 0x00; + private static final int SETDICT_STATE = 0x01; + private static final int INIT_FINISHING_STATE = 0x08; + private static final int SETDICT_FINISHING_STATE = 0x09; + private static final int BUSY_STATE = 0x10; + private static final int FLUSHING_STATE = 0x14; + private static final int FINISHING_STATE = 0x1c; + private static final int FINISHED_STATE = 0x1e; + private static final int CLOSED_STATE = 0x7f; + + /** Compression level. */ + private int level; + + /** should we include a header. */ + private boolean noHeader; + + /** The current state. */ + private int state; + + /** The total bytes of output written. */ + private long totalOut; + + /** The pending output. */ + private DeflaterPending pending; + + /** The deflater engine. */ + private DeflaterEngine engine; + + /** + * Creates a new deflater with default compression level. + */ + public Deflater() + { + this(DEFAULT_COMPRESSION, false); + } + + /** + * Creates a new deflater with given compression level. + * @param lvl the compression level, a value between NO_COMPRESSION + * and BEST_COMPRESSION, or DEFAULT_COMPRESSION. + * @exception IllegalArgumentException if lvl is out of range. + */ + public Deflater(int lvl) + { + this(lvl, false); + } + + /** + * Creates a new deflater with given compression level. + * @param lvl the compression level, a value between NO_COMPRESSION + * and BEST_COMPRESSION. + * @param nowrap true, iff we should suppress the deflate header at the + * beginning and the adler checksum at the end of the output. This is + * useful for the GZIP format. + * @exception IllegalArgumentException if lvl is out of range. + */ + public Deflater(int lvl, boolean nowrap) + { + if (lvl == DEFAULT_COMPRESSION) + lvl = 6; + else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) + throw new IllegalArgumentException(); + + pending = new DeflaterPending(); + engine = new DeflaterEngine(pending); + this.noHeader = nowrap; + setStrategy(DEFAULT_STRATEGY); + setLevel(lvl); + reset(); + } + + /** + * Resets the deflater. The deflater acts afterwards as if it was + * just created with the same compression level and strategy as it + * had before. + */ + public void reset() + { + state = (noHeader ? BUSY_STATE : INIT_STATE); + totalOut = 0; + pending.reset(); + engine.reset(); + } + + /** + * Frees all objects allocated by the compressor. There's no + * reason to call this, since you can just rely on garbage + * collection. Exists only for compatibility against Sun's JDK, + * where the compressor allocates native memory. + * If you call any method (even reset) afterwards the behaviour is + * undefined. + */ + public void end() + { + engine = null; + pending = null; + state = CLOSED_STATE; + } + + /** + * Gets the current adler checksum of the data that was processed so + * far. + */ + public int getAdler() + { + return engine.getAdler(); + } + + /** + * Gets the number of input bytes processed so far. + */ + public int getTotalIn() + { + return (int) engine.getTotalIn(); + } + + /** + * Gets the number of input bytes processed so far. + * @since 1.5 + */ + public long getBytesRead() + { + return engine.getTotalIn(); + } + + /** + * Gets the number of output bytes so far. + */ + public int getTotalOut() + { + return (int) totalOut; + } + + /** + * Gets the number of output bytes so far. + * @since 1.5 + */ + public long getBytesWritten() + { + return totalOut; + } + + /** + * Finalizes this object. + */ + protected void finalize() + { + /* Exists solely for compatibility. We don't have any native state. */ + } + + /** + * Flushes the current input block. Further calls to deflate() will + * produce enough output to inflate everything in the current input + * block. This is not part of Sun's JDK so I have made it package + * private. It is used by DeflaterOutputStream to implement + * flush(). + */ + void flush() { + state |= IS_FLUSHING; + } + + /** + * Finishes the deflater with the current input block. It is an error + * to give more input after this method was called. This method must + * be called to force all bytes to be flushed. + */ + public void finish() { + state |= IS_FLUSHING | IS_FINISHING; + } + + /** + * Returns true iff the stream was finished and no more output bytes + * are available. + */ + public boolean finished() + { + return state == FINISHED_STATE && pending.isFlushed(); + } + + /** + * Returns true, if the input buffer is empty. + * You should then call setInput().
        + * + * NOTE: This method can also return true when the stream + * was finished. + */ + public boolean needsInput() + { + return engine.needsInput(); + } + + /** + * Sets the data which should be compressed next. This should be only + * called when needsInput indicates that more input is needed. + * If you call setInput when needsInput() returns false, the + * previous input that is still pending will be thrown away. + * The given byte array should not be changed, before needsInput() returns + * true again. + * This call is equivalent to setInput(input, 0, input.length). + * @param input the buffer containing the input data. + * @exception IllegalStateException if the buffer was finished() or ended(). + */ + public void setInput(byte[] input) + { + setInput(input, 0, input.length); + } + + /** + * Sets the data which should be compressed next. This should be + * only called when needsInput indicates that more input is needed. + * The given byte array should not be changed, before needsInput() returns + * true again. + * @param input the buffer containing the input data. + * @param off the start of the data. + * @param len the length of the data. + * @exception IllegalStateException if the buffer was finished() or ended() + * or if previous input is still pending. + */ + public void setInput(byte[] input, int off, int len) + { + if ((state & IS_FINISHING) != 0) + throw new IllegalStateException("finish()/end() already called"); + engine.setInput(input, off, len); + } + + /** + * Sets the compression level. There is no guarantee of the exact + * position of the change, but if you call this when needsInput is + * true the change of compression level will occur somewhere near + * before the end of the so far given input. + * @param lvl the new compression level. + */ + public void setLevel(int lvl) + { + if (lvl == DEFAULT_COMPRESSION) + lvl = 6; + else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION) + throw new IllegalArgumentException(); + + + if (level != lvl) + { + level = lvl; + engine.setLevel(lvl); + } + } + + /** + * Sets the compression strategy. Strategy is one of + * DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact + * position where the strategy is changed, the same as for + * setLevel() applies. + * @param stgy the new compression strategy. + */ + public void setStrategy(int stgy) + { + if (stgy != DEFAULT_STRATEGY && stgy != FILTERED + && stgy != HUFFMAN_ONLY) + throw new IllegalArgumentException(); + engine.setStrategy(stgy); + } + + /** + * Deflates the current input block to the given array. It returns + * the number of bytes compressed, or 0 if either + * needsInput() or finished() returns true or length is zero. + * @param output the buffer where to write the compressed data. + */ + public int deflate(byte[] output) + { + return deflate(output, 0, output.length); + } + + /** + * Deflates the current input block to the given array. It returns + * the number of bytes compressed, or 0 if either + * needsInput() or finished() returns true or length is zero. + * @param output the buffer where to write the compressed data. + * @param offset the offset into the output array. + * @param length the maximum number of bytes that may be written. + * @exception IllegalStateException if end() was called. + * @exception IndexOutOfBoundsException if offset and/or length + * don't match the array length. + */ + public int deflate(byte[] output, int offset, int length) + { + int origLength = length; + + if (state == CLOSED_STATE) + throw new IllegalStateException("Deflater closed"); + + if (state < BUSY_STATE) + { + /* output header */ + int header = (DEFLATED + + ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; + int level_flags = (level - 1) >> 1; + if (level_flags < 0 || level_flags > 3) + level_flags = 3; + header |= level_flags << 6; + if ((state & IS_SETDICT) != 0) + /* Dictionary was set */ + header |= DeflaterConstants.PRESET_DICT; + header += 31 - (header % 31); + + pending.writeShortMSB(header); + if ((state & IS_SETDICT) != 0) + { + int chksum = engine.getAdler(); + engine.resetAdler(); + pending.writeShortMSB(chksum >> 16); + pending.writeShortMSB(chksum & 0xffff); + } + + state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); + } + + for (;;) + { + int count = pending.flush(output, offset, length); + offset += count; + totalOut += count; + length -= count; + if (length == 0 || state == FINISHED_STATE) + break; + + if (!engine.deflate((state & IS_FLUSHING) != 0, + (state & IS_FINISHING) != 0)) + { + if (state == BUSY_STATE) + /* We need more input now */ + return origLength - length; + else if (state == FLUSHING_STATE) + { + if (level != NO_COMPRESSION) + { + /* We have to supply some lookahead. 8 bit lookahead + * are needed by the zlib inflater, and we must fill + * the next byte, so that all bits are flushed. + */ + int neededbits = 8 + ((-pending.getBitCount()) & 7); + while (neededbits > 0) + { + /* write a static tree block consisting solely of + * an EOF: + */ + pending.writeBits(2, 10); + neededbits -= 10; + } + } + state = BUSY_STATE; + } + else if (state == FINISHING_STATE) + { + pending.alignToByte(); + /* We have completed the stream */ + if (!noHeader) + { + int adler = engine.getAdler(); + pending.writeShortMSB(adler >> 16); + pending.writeShortMSB(adler & 0xffff); + } + state = FINISHED_STATE; + } + } + } + + return origLength - length; + } + + /** + * Sets the dictionary which should be used in the deflate process. + * This call is equivalent to setDictionary(dict, 0, + * dict.length). + * @param dict the dictionary. + * @exception IllegalStateException if setInput () or deflate () + * were already called or another dictionary was already set. + */ + public void setDictionary(byte[] dict) + { + setDictionary(dict, 0, dict.length); + } + + /** + * Sets the dictionary which should be used in the deflate process. + * The dictionary should be a byte array containing strings that are + * likely to occur in the data which should be compressed. The + * dictionary is not stored in the compressed output, only a + * checksum. To decompress the output you need to supply the same + * dictionary again. + * @param dict the dictionary. + * @param offset an offset into the dictionary. + * @param length the length of the dictionary. + * @exception IllegalStateException if setInput () or deflate () were + * already called or another dictionary was already set. + */ + public void setDictionary(byte[] dict, int offset, int length) + { + if (state != INIT_STATE) + throw new IllegalStateException(); + + state = SETDICT_STATE; + engine.setDictionary(dict, offset, length); + } +} diff --git a/libjava/classpath/java/util/zip/DeflaterConstants.java b/libjava/classpath/java/util/zip/DeflaterConstants.java new file mode 100644 index 000000000..bfef86344 --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterConstants.java @@ -0,0 +1,78 @@ +/* java.util.zip.DeflaterConstants + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +interface DeflaterConstants +{ + boolean DEBUGGING = false; + + int STORED_BLOCK = 0; + int STATIC_TREES = 1; + int DYN_TREES = 2; + int PRESET_DICT = 0x20; + + int DEFAULT_MEM_LEVEL = 8; + + int MAX_MATCH = 258; + int MIN_MATCH = 3; + + int MAX_WBITS = 15; + int WSIZE = 1 << MAX_WBITS; + int WMASK = WSIZE - 1; + + int HASH_BITS = DEFAULT_MEM_LEVEL + 7; + int HASH_SIZE = 1 << HASH_BITS; + int HASH_MASK = HASH_SIZE - 1; + int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; + + int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + int MAX_DIST = WSIZE - MIN_LOOKAHEAD; + + int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); + int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5); + + int DEFLATE_STORED = 0; + int DEFLATE_FAST = 1; + int DEFLATE_SLOW = 2; + + int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 }; + int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 }; + int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 }; + int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 }; + int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 }; +} diff --git a/libjava/classpath/java/util/zip/DeflaterEngine.java b/libjava/classpath/java/util/zip/DeflaterEngine.java new file mode 100644 index 000000000..287558e3e --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterEngine.java @@ -0,0 +1,698 @@ +/* DeflaterEngine.java -- + Copyright (C) 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +class DeflaterEngine implements DeflaterConstants +{ + private static final int TOO_FAR = 4096; + + private int ins_h; + + /** + * Hashtable, hashing three characters to an index for window, so + * that window[index]..window[index+2] have this hash code. + * Note that the array should really be unsigned short, so you need + * to and the values with 0xffff. + */ + private short[] head; + + /** + * prev[index & WMASK] points to the previous index that has the + * same hash code as the string starting at index. This way + * entries with the same hash code are in a linked list. + * Note that the array should really be unsigned short, so you need + * to and the values with 0xffff. + */ + private short[] prev; + + private int matchStart, matchLen; + private boolean prevAvailable; + private int blockStart; + + /** + * strstart points to the current character in window. + */ + private int strstart; + + /** + * lookahead is the number of characters starting at strstart in + * window that are valid. + * So window[strstart] until window[strstart+lookahead-1] are valid + * characters. + */ + private int lookahead; + + /** + * This array contains the part of the uncompressed stream that + * is of relevance. The current character is indexed by strstart. + */ + private byte[] window; + + private int strategy, max_chain, max_lazy, niceLength, goodLength; + + /** The current compression function. */ + private int comprFunc; + + /** The input data for compression. */ + private byte[] inputBuf; + + /** The total bytes of input read. */ + private long totalIn; + + /** The offset into inputBuf, where input data starts. */ + private int inputOff; + + /** The end offset of the input data. */ + private int inputEnd; + + private DeflaterPending pending; + private DeflaterHuffman huffman; + + /** The adler checksum */ + private Adler32 adler; + + /* DEFLATE ALGORITHM: + * + * The uncompressed stream is inserted into the window array. When + * the window array is full the first half is thrown away and the + * second half is copied to the beginning. + * + * The head array is a hash table. Three characters build a hash value + * and they the value points to the corresponding index in window of + * the last string with this hash. The prev array implements a + * linked list of matches with the same hash: prev[index & WMASK] points + * to the previous index with the same hash. + * + * + */ + + + DeflaterEngine(DeflaterPending pending) { + this.pending = pending; + huffman = new DeflaterHuffman(pending); + adler = new Adler32(); + + window = new byte[2*WSIZE]; + head = new short[HASH_SIZE]; + prev = new short[WSIZE]; + + /* We start at index 1, to avoid a implementation deficiency, that + * we cannot build a repeat pattern at index 0. + */ + blockStart = strstart = 1; + } + + public void reset() + { + huffman.reset(); + adler.reset(); + blockStart = strstart = 1; + lookahead = 0; + totalIn = 0; + prevAvailable = false; + matchLen = MIN_MATCH - 1; + for (int i = 0; i < HASH_SIZE; i++) + head[i] = 0; + for (int i = 0; i < WSIZE; i++) + prev[i] = 0; + } + + public final void resetAdler() + { + adler.reset(); + } + + public final int getAdler() + { + int chksum = (int) adler.getValue(); + return chksum; + } + + public final long getTotalIn() + { + return totalIn; + } + + public final void setStrategy(int strat) + { + strategy = strat; + } + + public void setLevel(int lvl) + { + goodLength = DeflaterConstants.GOOD_LENGTH[lvl]; + max_lazy = DeflaterConstants.MAX_LAZY[lvl]; + niceLength = DeflaterConstants.NICE_LENGTH[lvl]; + max_chain = DeflaterConstants.MAX_CHAIN[lvl]; + + if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc) + { + if (DeflaterConstants.DEBUGGING) + System.err.println("Change from "+comprFunc +" to " + + DeflaterConstants.COMPR_FUNC[lvl]); + switch (comprFunc) + { + case DEFLATE_STORED: + if (strstart > blockStart) + { + huffman.flushStoredBlock(window, blockStart, + strstart - blockStart, false); + blockStart = strstart; + } + updateHash(); + break; + case DEFLATE_FAST: + if (strstart > blockStart) + { + huffman.flushBlock(window, blockStart, strstart - blockStart, + false); + blockStart = strstart; + } + break; + case DEFLATE_SLOW: + if (prevAvailable) + huffman.tallyLit(window[strstart-1] & 0xff); + if (strstart > blockStart) + { + huffman.flushBlock(window, blockStart, strstart - blockStart, + false); + blockStart = strstart; + } + prevAvailable = false; + matchLen = MIN_MATCH - 1; + break; + } + comprFunc = COMPR_FUNC[lvl]; + } + } + + private void updateHash() { + if (DEBUGGING) + System.err.println("updateHash: "+strstart); + ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1]; + } + + /** + * Inserts the current string in the head hash and returns the previous + * value for this hash. + */ + private int insertString() { + short match; + int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)]) + & HASH_MASK; + + if (DEBUGGING) + { + if (hash != (((window[strstart] << (2*HASH_SHIFT)) + ^ (window[strstart + 1] << HASH_SHIFT) + ^ (window[strstart + 2])) & HASH_MASK)) + throw new InternalError("hash inconsistent: "+hash+"/" + +window[strstart]+"," + +window[strstart+1]+"," + +window[strstart+2]+","+HASH_SHIFT); + } + + prev[strstart & WMASK] = match = head[hash]; + head[hash] = (short) strstart; + ins_h = hash; + return match & 0xffff; + } + + private void slideWindow() + { + System.arraycopy(window, WSIZE, window, 0, WSIZE); + matchStart -= WSIZE; + strstart -= WSIZE; + blockStart -= WSIZE; + + /* Slide the hash table (could be avoided with 32 bit values + * at the expense of memory usage). + */ + for (int i = 0; i < HASH_SIZE; i++) + { + int m = head[i] & 0xffff; + head[i] = m >= WSIZE ? (short) (m - WSIZE) : 0; + } + + /* Slide the prev table. + */ + for (int i = 0; i < WSIZE; i++) + { + int m = prev[i] & 0xffff; + prev[i] = m >= WSIZE ? (short) (m - WSIZE) : 0; + } + } + + /** + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * OUT assertions: strstart + lookahead <= 2*WSIZE + * lookahead >= MIN_LOOKAHEAD or inputOff == inputEnd + */ + private void fillWindow() + { + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (strstart >= WSIZE + MAX_DIST) + slideWindow(); + + /* If there is not enough lookahead, but still some input left, + * read in the input + */ + while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) + { + int more = 2*WSIZE - lookahead - strstart; + + if (more > inputEnd - inputOff) + more = inputEnd - inputOff; + + System.arraycopy(inputBuf, inputOff, + window, strstart + lookahead, more); + adler.update(inputBuf, inputOff, more); + inputOff += more; + totalIn += more; + lookahead += more; + } + + if (lookahead >= MIN_MATCH) + updateHash(); + } + + /** + * Find the best (longest) string in the window matching the + * string starting at strstart. + * + * Preconditions: + * strstart + MAX_MATCH <= window.length. + * + * + * @param curMatch + */ + private boolean findLongestMatch(int curMatch) { + int chainLength = this.max_chain; + int niceLength = this.niceLength; + short[] prev = this.prev; + int scan = this.strstart; + int match; + int best_end = this.strstart + matchLen; + int best_len = Math.max(matchLen, MIN_MATCH - 1); + + int limit = Math.max(strstart - MAX_DIST, 0); + + int strend = scan + MAX_MATCH - 1; + byte scan_end1 = window[best_end - 1]; + byte scan_end = window[best_end]; + + /* Do not waste too much time if we already have a good match: */ + if (best_len >= this.goodLength) + chainLength >>= 2; + + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if (niceLength > lookahead) + niceLength = lookahead; + + if (DeflaterConstants.DEBUGGING + && strstart > 2*WSIZE - MIN_LOOKAHEAD) + throw new InternalError("need lookahead"); + + do { + if (DeflaterConstants.DEBUGGING && curMatch >= strstart) + throw new InternalError("future match"); + if (window[curMatch + best_len] != scan_end + || window[curMatch + best_len - 1] != scan_end1 + || window[curMatch] != window[scan] + || window[curMatch+1] != window[scan + 1]) + continue; + + match = curMatch + 2; + scan += 2; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + while (window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && window[++scan] == window[++match] + && scan < strend) + ; + + if (scan > best_end) { +// if (DeflaterConstants.DEBUGGING && ins_h == 0) +// System.err.println("Found match: "+curMatch+"-"+(scan-strstart)); + matchStart = curMatch; + best_end = scan; + best_len = scan - strstart; + if (best_len >= niceLength) + break; + + scan_end1 = window[best_end-1]; + scan_end = window[best_end]; + } + scan = strstart; + } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit + && --chainLength != 0); + + matchLen = Math.min(best_len, lookahead); + return matchLen >= MIN_MATCH; + } + + void setDictionary(byte[] buffer, int offset, int length) { + if (DeflaterConstants.DEBUGGING && strstart != 1) + throw new IllegalStateException("strstart not 1"); + adler.update(buffer, offset, length); + if (length < MIN_MATCH) + return; + if (length > MAX_DIST) { + offset += length - MAX_DIST; + length = MAX_DIST; + } + + System.arraycopy(buffer, offset, window, strstart, length); + + updateHash(); + length--; + while (--length > 0) + { + insertString(); + strstart++; + } + strstart += 2; + blockStart = strstart; + } + + private boolean deflateStored(boolean flush, boolean finish) + { + if (!flush && lookahead == 0) + return false; + + strstart += lookahead; + lookahead = 0; + + int storedLen = strstart - blockStart; + + if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE) + /* Block is full */ + || (blockStart < WSIZE && storedLen >= MAX_DIST) + /* Block may move out of window */ + || flush) + { + boolean lastBlock = finish; + if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE) + { + storedLen = DeflaterConstants.MAX_BLOCK_SIZE; + lastBlock = false; + } + + if (DeflaterConstants.DEBUGGING) + System.err.println("storedBlock["+storedLen+","+lastBlock+"]"); + + huffman.flushStoredBlock(window, blockStart, storedLen, lastBlock); + blockStart += storedLen; + return !lastBlock; + } + return true; + } + + private boolean deflateFast(boolean flush, boolean finish) + { + if (lookahead < MIN_LOOKAHEAD && !flush) + return false; + + while (lookahead >= MIN_LOOKAHEAD || flush) + { + if (lookahead == 0) + { + /* We are flushing everything */ + huffman.flushBlock(window, blockStart, strstart - blockStart, + finish); + blockStart = strstart; + return false; + } + + if (strstart > 2 * WSIZE - MIN_LOOKAHEAD) + { + /* slide window, as findLongestMatch need this. + * This should only happen when flushing and the window + * is almost full. + */ + slideWindow(); + } + + int hashHead; + if (lookahead >= MIN_MATCH + && (hashHead = insertString()) != 0 + && strategy != Deflater.HUFFMAN_ONLY + && strstart - hashHead <= MAX_DIST + && findLongestMatch(hashHead)) + { + /* longestMatch sets matchStart and matchLen */ + if (DeflaterConstants.DEBUGGING) + { + for (int i = 0 ; i < matchLen; i++) + { + if (window[strstart+i] != window[matchStart + i]) + throw new InternalError(); + } + } + boolean full = huffman.tallyDist(strstart - matchStart, matchLen); + + lookahead -= matchLen; + if (matchLen <= max_lazy && lookahead >= MIN_MATCH) + { + while (--matchLen > 0) + { + strstart++; + insertString(); + } + strstart++; + } + else + { + strstart += matchLen; + if (lookahead >= MIN_MATCH - 1) + updateHash(); + } + matchLen = MIN_MATCH - 1; + if (!full) + continue; + } + else + { + /* No match found */ + huffman.tallyLit(window[strstart] & 0xff); + strstart++; + lookahead--; + } + + if (huffman.isFull()) + { + boolean lastBlock = finish && lookahead == 0; + huffman.flushBlock(window, blockStart, strstart - blockStart, + lastBlock); + blockStart = strstart; + return !lastBlock; + } + } + return true; + } + + private boolean deflateSlow(boolean flush, boolean finish) + { + if (lookahead < MIN_LOOKAHEAD && !flush) + return false; + + while (lookahead >= MIN_LOOKAHEAD || flush) + { + if (lookahead == 0) + { + if (prevAvailable) + huffman.tallyLit(window[strstart-1] & 0xff); + prevAvailable = false; + + /* We are flushing everything */ + if (DeflaterConstants.DEBUGGING && !flush) + throw new InternalError("Not flushing, but no lookahead"); + huffman.flushBlock(window, blockStart, strstart - blockStart, + finish); + blockStart = strstart; + return false; + } + + if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD) + { + /* slide window, as findLongestMatch need this. + * This should only happen when flushing and the window + * is almost full. + */ + slideWindow(); + } + + int prevMatch = matchStart; + int prevLen = matchLen; + if (lookahead >= MIN_MATCH) + { + int hashHead = insertString(); + if (strategy != Deflater.HUFFMAN_ONLY + && hashHead != 0 && strstart - hashHead <= MAX_DIST + && findLongestMatch(hashHead)) + { + /* longestMatch sets matchStart and matchLen */ + + /* Discard match if too small and too far away */ + if (matchLen <= 5 + && (strategy == Deflater.FILTERED + || (matchLen == MIN_MATCH + && strstart - matchStart > TOO_FAR))) { + matchLen = MIN_MATCH - 1; + } + } + } + + /* previous match was better */ + if (prevLen >= MIN_MATCH && matchLen <= prevLen) + { + if (DeflaterConstants.DEBUGGING) + { + for (int i = 0 ; i < matchLen; i++) + { + if (window[strstart-1+i] != window[prevMatch + i]) + throw new InternalError(); + } + } + huffman.tallyDist(strstart - 1 - prevMatch, prevLen); + prevLen -= 2; + do + { + strstart++; + lookahead--; + if (lookahead >= MIN_MATCH) + insertString(); + } + while (--prevLen > 0); + strstart ++; + lookahead--; + prevAvailable = false; + matchLen = MIN_MATCH - 1; + } + else + { + if (prevAvailable) + huffman.tallyLit(window[strstart-1] & 0xff); + prevAvailable = true; + strstart++; + lookahead--; + } + + if (huffman.isFull()) + { + int len = strstart - blockStart; + if (prevAvailable) + len--; + boolean lastBlock = (finish && lookahead == 0 && !prevAvailable); + huffman.flushBlock(window, blockStart, len, lastBlock); + blockStart += len; + return !lastBlock; + } + } + return true; + } + + public boolean deflate(boolean flush, boolean finish) + { + boolean progress; + do + { + fillWindow(); + boolean canFlush = flush && inputOff == inputEnd; + if (DeflaterConstants.DEBUGGING) + System.err.println("window: ["+blockStart+","+strstart+"," + +lookahead+"], "+comprFunc+","+canFlush); + switch (comprFunc) + { + case DEFLATE_STORED: + progress = deflateStored(canFlush, finish); + break; + case DEFLATE_FAST: + progress = deflateFast(canFlush, finish); + break; + case DEFLATE_SLOW: + progress = deflateSlow(canFlush, finish); + break; + default: + throw new InternalError(); + } + } + while (pending.isFlushed() /* repeat while we have no pending output */ + && progress); /* and progress was made */ + + return progress; + } + + public void setInput(byte[] buf, int off, int len) + { + if (inputOff < inputEnd) + throw new IllegalStateException + ("Old input was not completely processed"); + + int end = off + len; + + /* We want to throw an ArrayIndexOutOfBoundsException early. The + * check is very tricky: it also handles integer wrap around. + */ + if (0 > off || off > end || end > buf.length) + throw new ArrayIndexOutOfBoundsException(); + + inputBuf = buf; + inputOff = off; + inputEnd = end; + } + + public final boolean needsInput() + { + return inputEnd == inputOff; + } +} diff --git a/libjava/classpath/java/util/zip/DeflaterHuffman.java b/libjava/classpath/java/util/zip/DeflaterHuffman.java new file mode 100644 index 000000000..8da987e0f --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterHuffman.java @@ -0,0 +1,776 @@ +/* DeflaterHuffman.java -- + Copyright (C) 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This is the DeflaterHuffman class. + * + * This class is not thread safe. This is inherent in the API, due + * to the split of deflate and setInput. + * + * @author Jochen Hoenicke + * @date Jan 6, 2000 + */ +class DeflaterHuffman +{ + private static final int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); + private static final int LITERAL_NUM = 286; + private static final int DIST_NUM = 30; + private static final int BITLEN_NUM = 19; + private static final int REP_3_6 = 16; + private static final int REP_3_10 = 17; + private static final int REP_11_138 = 18; + private static final int EOF_SYMBOL = 256; + private static final int[] BL_ORDER = + { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static final String bit4Reverse = + "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017"; + + class Tree { + short[] freqs; + short[] codes; + byte[] length; + int[] bl_counts; + int minNumCodes, numCodes; + int maxLength; + + Tree(int elems, int minCodes, int maxLength) { + this.minNumCodes = minCodes; + this.maxLength = maxLength; + freqs = new short[elems]; + bl_counts = new int[maxLength]; + } + + void reset() { + for (int i = 0; i < freqs.length; i++) + freqs[i] = 0; + codes = null; + length = null; + } + + final void writeSymbol(int code) + { + if (DeflaterConstants.DEBUGGING) + { + freqs[code]--; +// System.err.print("writeSymbol("+freqs.length+","+code+"): "); + } + pending.writeBits(codes[code] & 0xffff, length[code]); + } + + final void checkEmpty() + { + boolean empty = true; + for (int i = 0; i < freqs.length; i++) + if (freqs[i] != 0) + { + System.err.println("freqs["+i+"] == "+freqs[i]); + empty = false; + } + if (!empty) + throw new InternalError(); + System.err.println("checkEmpty suceeded!"); + } + + void setStaticCodes(short[] stCodes, byte[] stLength) + { + codes = stCodes; + length = stLength; + } + + public void buildCodes() { + int[] nextCode = new int[maxLength]; + int code = 0; + codes = new short[freqs.length]; + + if (DeflaterConstants.DEBUGGING) + System.err.println("buildCodes: "+freqs.length); + for (int bits = 0; bits < maxLength; bits++) + { + nextCode[bits] = code; + code += bl_counts[bits] << (15 - bits); + if (DeflaterConstants.DEBUGGING) + System.err.println("bits: "+(bits+1)+" count: "+bl_counts[bits] + +" nextCode: "+Integer.toHexString(code)); + } + if (DeflaterConstants.DEBUGGING && code != 65536) + throw new RuntimeException("Inconsistent bl_counts!"); + + for (int i=0; i < numCodes; i++) + { + int bits = length[i]; + if (bits > 0) + { + if (DeflaterConstants.DEBUGGING) + System.err.println("codes["+i+"] = rev(" + +Integer.toHexString(nextCode[bits-1])+")," + +bits); + codes[i] = bitReverse(nextCode[bits-1]); + nextCode[bits-1] += 1 << (16 - bits); + } + } + } + + private void buildLength(int childs[]) + { + this.length = new byte [freqs.length]; + int numNodes = childs.length / 2; + int numLeafs = (numNodes + 1) / 2; + int overflow = 0; + + for (int i = 0; i < maxLength; i++) + bl_counts[i] = 0; + + /* First calculate optimal bit lengths */ + int lengths[] = new int[numNodes]; + lengths[numNodes-1] = 0; + for (int i = numNodes - 1; i >= 0; i--) + { + if (childs[2*i+1] != -1) + { + int bitLength = lengths[i] + 1; + if (bitLength > maxLength) + { + bitLength = maxLength; + overflow++; + } + lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength; + } + else + { + /* A leaf node */ + int bitLength = lengths[i]; + bl_counts[bitLength - 1]++; + this.length[childs[2*i]] = (byte) lengths[i]; + } + } + + if (DeflaterConstants.DEBUGGING) + { + System.err.println("Tree "+freqs.length+" lengths:"); + for (int i=0; i < numLeafs; i++) + System.err.println("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] + + " len: "+length[childs[2*i]]); + } + + if (overflow == 0) + return; + + int incrBitLen = maxLength - 1; + do + { + /* Find the first bit length which could increase: */ + while (bl_counts[--incrBitLen] == 0) + ; + + /* Move this node one down and remove a corresponding + * amount of overflow nodes. + */ + do + { + bl_counts[incrBitLen]--; + bl_counts[++incrBitLen]++; + overflow -= 1 << (maxLength - 1 - incrBitLen); + } + while (overflow > 0 && incrBitLen < maxLength - 1); + } + while (overflow > 0); + + /* We may have overshot above. Move some nodes from maxLength to + * maxLength-1 in that case. + */ + bl_counts[maxLength-1] += overflow; + bl_counts[maxLength-2] -= overflow; + + /* Now recompute all bit lengths, scanning in increasing + * frequency. It is simpler to reconstruct all lengths instead of + * fixing only the wrong ones. This idea is taken from 'ar' + * written by Haruhiko Okumura. + * + * The nodes were inserted with decreasing frequency into the childs + * array. + */ + int nodePtr = 2 * numLeafs; + for (int bits = maxLength; bits != 0; bits--) + { + int n = bl_counts[bits-1]; + while (n > 0) + { + int childPtr = 2*childs[nodePtr++]; + if (childs[childPtr + 1] == -1) + { + /* We found another leaf */ + length[childs[childPtr]] = (byte) bits; + n--; + } + } + } + if (DeflaterConstants.DEBUGGING) + { + System.err.println("*** After overflow elimination. ***"); + for (int i=0; i < numLeafs; i++) + System.err.println("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] + + " len: "+length[childs[2*i]]); + } + } + + void buildTree() + { + int numSymbols = freqs.length; + + /* heap is a priority queue, sorted by frequency, least frequent + * nodes first. The heap is a binary tree, with the property, that + * the parent node is smaller than both child nodes. This assures + * that the smallest node is the first parent. + * + * The binary tree is encoded in an array: 0 is root node and + * the nodes 2*n+1, 2*n+2 are the child nodes of node n. + */ + int[] heap = new int[numSymbols]; + int heapLen = 0; + int maxCode = 0; + for (int n = 0; n < numSymbols; n++) + { + int freq = freqs[n]; + if (freq != 0) + { + /* Insert n into heap */ + int pos = heapLen++; + int ppos; + while (pos > 0 && + freqs[heap[ppos = (pos - 1) / 2]] > freq) { + heap[pos] = heap[ppos]; + pos = ppos; + } + heap[pos] = n; + maxCode = n; + } + } + + /* We could encode a single literal with 0 bits but then we + * don't see the literals. Therefore we force at least two + * literals to avoid this case. We don't care about order in + * this case, both literals get a 1 bit code. + */ + while (heapLen < 2) + { + int node = maxCode < 2 ? ++maxCode : 0; + heap[heapLen++] = node; + } + + numCodes = Math.max(maxCode + 1, minNumCodes); + + int numLeafs = heapLen; + int[] childs = new int[4*heapLen - 2]; + int[] values = new int[2*heapLen - 1]; + int numNodes = numLeafs; + for (int i = 0; i < heapLen; i++) + { + int node = heap[i]; + childs[2*i] = node; + childs[2*i+1] = -1; + values[i] = freqs[node] << 8; + heap[i] = i; + } + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do + { + int first = heap[0]; + int last = heap[--heapLen]; + + /* Propagate the hole to the leafs of the heap */ + int ppos = 0; + int path = 1; + while (path < heapLen) + { + if (path + 1 < heapLen + && values[heap[path]] > values[heap[path+1]]) + path++; + + heap[ppos] = heap[path]; + ppos = path; + path = path * 2 + 1; + } + + /* Now propagate the last element down along path. Normally + * it shouldn't go too deep. + */ + int lastVal = values[last]; + while ((path = ppos) > 0 + && values[heap[ppos = (path - 1)/2]] > lastVal) + heap[path] = heap[ppos]; + heap[path] = last; + + + int second = heap[0]; + + /* Create a new node father of first and second */ + last = numNodes++; + childs[2*last] = first; + childs[2*last+1] = second; + int mindepth = Math.min(values[first] & 0xff, values[second] & 0xff); + values[last] = lastVal = values[first] + values[second] - mindepth + 1; + + /* Again, propagate the hole to the leafs */ + ppos = 0; + path = 1; + while (path < heapLen) + { + if (path + 1 < heapLen + && values[heap[path]] > values[heap[path+1]]) + path++; + + heap[ppos] = heap[path]; + ppos = path; + path = ppos * 2 + 1; + } + + /* Now propagate the new element down along path */ + while ((path = ppos) > 0 + && values[heap[ppos = (path - 1)/2]] > lastVal) + heap[path] = heap[ppos]; + heap[path] = last; + } + while (heapLen > 1); + + if (heap[0] != childs.length / 2 - 1) + throw new RuntimeException("Weird!"); + + buildLength(childs); + } + + int getEncodedLength() + { + int len = 0; + for (int i = 0; i < freqs.length; i++) + len += freqs[i] * length[i]; + return len; + } + + void calcBLFreq(Tree blTree) { + int max_count; /* max repeat count */ + int min_count; /* min repeat count */ + int count; /* repeat count of the current code */ + int curlen = -1; /* length of current code */ + + int i = 0; + while (i < numCodes) + { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else + { + max_count = 6; + min_count = 3; + if (curlen != nextlen) + { + blTree.freqs[nextlen]++; + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) + { + i++; + if (++count >= max_count) + break; + } + + if (count < min_count) + blTree.freqs[curlen] += count; + else if (curlen != 0) + blTree.freqs[REP_3_6]++; + else if (count <= 10) + blTree.freqs[REP_3_10]++; + else + blTree.freqs[REP_11_138]++; + } + } + + void writeTree(Tree blTree) + { + int max_count; /* max repeat count */ + int min_count; /* min repeat count */ + int count; /* repeat count of the current code */ + int curlen = -1; /* length of current code */ + + int i = 0; + while (i < numCodes) + { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else + { + max_count = 6; + min_count = 3; + if (curlen != nextlen) + { + blTree.writeSymbol(nextlen); + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) + { + i++; + if (++count >= max_count) + break; + } + + if (count < min_count) + { + while (count-- > 0) + blTree.writeSymbol(curlen); + } + else if (curlen != 0) + { + blTree.writeSymbol(REP_3_6); + pending.writeBits(count - 3, 2); + } + else if (count <= 10) + { + blTree.writeSymbol(REP_3_10); + pending.writeBits(count - 3, 3); + } + else + { + blTree.writeSymbol(REP_11_138); + pending.writeBits(count - 11, 7); + } + } + } + } + + + + DeflaterPending pending; + private Tree literalTree, distTree, blTree; + + private short d_buf[]; + private byte l_buf[]; + private int last_lit; + private int extra_bits; + + private static short staticLCodes[]; + private static byte staticLLength[]; + private static short staticDCodes[]; + private static byte staticDLength[]; + + /** + * Reverse the bits of a 16 bit value. + */ + static short bitReverse(int value) { + return (short) (bit4Reverse.charAt(value & 0xf) << 12 + | bit4Reverse.charAt((value >> 4) & 0xf) << 8 + | bit4Reverse.charAt((value >> 8) & 0xf) << 4 + | bit4Reverse.charAt(value >> 12)); + } + + static { + /* See RFC 1951 3.2.6 */ + /* Literal codes */ + staticLCodes = new short[LITERAL_NUM]; + staticLLength = new byte[LITERAL_NUM]; + int i = 0; + while (i < 144) { + staticLCodes[i] = bitReverse((0x030 + i) << 8); + staticLLength[i++] = 8; + } + while (i < 256) { + staticLCodes[i] = bitReverse((0x190 - 144 + i) << 7); + staticLLength[i++] = 9; + } + while (i < 280) { + staticLCodes[i] = bitReverse((0x000 - 256 + i) << 9); + staticLLength[i++] = 7; + } + while (i < LITERAL_NUM) { + staticLCodes[i] = bitReverse((0x0c0 - 280 + i) << 8); + staticLLength[i++] = 8; + } + + /* Distant codes */ + staticDCodes = new short[DIST_NUM]; + staticDLength = new byte[DIST_NUM]; + for (i = 0; i < DIST_NUM; i++) { + staticDCodes[i] = bitReverse(i << 11); + staticDLength[i] = 5; + } + } + + public DeflaterHuffman(DeflaterPending pending) + { + this.pending = pending; + + literalTree = new Tree(LITERAL_NUM, 257, 15); + distTree = new Tree(DIST_NUM, 1, 15); + blTree = new Tree(BITLEN_NUM, 4, 7); + + d_buf = new short[BUFSIZE]; + l_buf = new byte [BUFSIZE]; + } + + public final void reset() { + last_lit = 0; + extra_bits = 0; + literalTree.reset(); + distTree.reset(); + blTree.reset(); + } + + private int l_code(int len) { + if (len == 255) + return 285; + + int code = 257; + while (len >= 8) + { + code += 4; + len >>= 1; + } + return code + len; + } + + private int d_code(int distance) { + int code = 0; + while (distance >= 4) + { + code += 2; + distance >>= 1; + } + return code + distance; + } + + public void sendAllTrees(int blTreeCodes) { + blTree.buildCodes(); + literalTree.buildCodes(); + distTree.buildCodes(); + pending.writeBits(literalTree.numCodes - 257, 5); + pending.writeBits(distTree.numCodes - 1, 5); + pending.writeBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) + pending.writeBits(blTree.length[BL_ORDER[rank]], 3); + literalTree.writeTree(blTree); + distTree.writeTree(blTree); + if (DeflaterConstants.DEBUGGING) + blTree.checkEmpty(); + } + + public void compressBlock() { + for (int i = 0; i < last_lit; i++) + { + int litlen = l_buf[i] & 0xff; + int dist = d_buf[i]; + if (dist-- != 0) + { + if (DeflaterConstants.DEBUGGING) + System.err.print("["+(dist+1)+","+(litlen+3)+"]: "); + + int lc = l_code(litlen); + literalTree.writeSymbol(lc); + + int bits = (lc - 261) / 4; + if (bits > 0 && bits <= 5) + pending.writeBits(litlen & ((1 << bits) - 1), bits); + + int dc = d_code(dist); + distTree.writeSymbol(dc); + + bits = dc / 2 - 1; + if (bits > 0) + pending.writeBits(dist & ((1 << bits) - 1), bits); + } + else + { + if (DeflaterConstants.DEBUGGING) + { + if (litlen > 32 && litlen < 127) + System.err.print("("+(char)litlen+"): "); + else + System.err.print("{"+litlen+"}: "); + } + literalTree.writeSymbol(litlen); + } + } + if (DeflaterConstants.DEBUGGING) + System.err.print("EOF: "); + literalTree.writeSymbol(EOF_SYMBOL); + if (DeflaterConstants.DEBUGGING) + { + literalTree.checkEmpty(); + distTree.checkEmpty(); + } + } + + public void flushStoredBlock(byte[] stored, + int stored_offset, int stored_len, + boolean lastBlock) { + if (DeflaterConstants.DEBUGGING) + System.err.println("Flushing stored block "+ stored_len); + pending.writeBits((DeflaterConstants.STORED_BLOCK << 1) + + (lastBlock ? 1 : 0), 3); + pending.alignToByte(); + pending.writeShort(stored_len); + pending.writeShort(~stored_len); + pending.writeBlock(stored, stored_offset, stored_len); + reset(); + } + + public void flushBlock(byte[] stored, int stored_offset, int stored_len, + boolean lastBlock) { + literalTree.freqs[EOF_SYMBOL]++; + + /* Build trees */ + literalTree.buildTree(); + distTree.buildTree(); + + /* Calculate bitlen frequency */ + literalTree.calcBLFreq(blTree); + distTree.calcBLFreq(blTree); + + /* Build bitlen tree */ + blTree.buildTree(); + + int blTreeCodes = 4; + for (int i = 18; i > blTreeCodes; i--) + { + if (blTree.length[BL_ORDER[i]] > 0) + blTreeCodes = i+1; + } + int opt_len = 14 + blTreeCodes * 3 + blTree.getEncodedLength() + + literalTree.getEncodedLength() + distTree.getEncodedLength() + + extra_bits; + + int static_len = extra_bits; + for (int i = 0; i < LITERAL_NUM; i++) + static_len += literalTree.freqs[i] * staticLLength[i]; + for (int i = 0; i < DIST_NUM; i++) + static_len += distTree.freqs[i] * staticDLength[i]; + if (opt_len >= static_len) + { + /* Force static trees */ + opt_len = static_len; + } + + if (stored_offset >= 0 && stored_len+4 < opt_len >> 3) + { + /* Store Block */ + if (DeflaterConstants.DEBUGGING) + System.err.println("Storing, since " + stored_len + " < " + opt_len + + " <= " + static_len); + flushStoredBlock(stored, stored_offset, stored_len, lastBlock); + } + else if (opt_len == static_len) + { + /* Encode with static tree */ + pending.writeBits((DeflaterConstants.STATIC_TREES << 1) + + (lastBlock ? 1 : 0), 3); + literalTree.setStaticCodes(staticLCodes, staticLLength); + distTree.setStaticCodes(staticDCodes, staticDLength); + compressBlock(); + reset(); + } + else + { + /* Encode with dynamic tree */ + pending.writeBits((DeflaterConstants.DYN_TREES << 1) + + (lastBlock ? 1 : 0), 3); + sendAllTrees(blTreeCodes); + compressBlock(); + reset(); + } + } + + public final boolean isFull() + { + return last_lit == BUFSIZE; + } + + public final boolean tallyLit(int lit) + { + if (DeflaterConstants.DEBUGGING) + { + if (lit > 32 && lit < 127) + System.err.println("("+(char)lit+")"); + else + System.err.println("{"+lit+"}"); + } + d_buf[last_lit] = 0; + l_buf[last_lit++] = (byte) lit; + literalTree.freqs[lit]++; + return last_lit == BUFSIZE; + } + + public final boolean tallyDist(int dist, int len) + { + if (DeflaterConstants.DEBUGGING) + System.err.println("["+dist+","+len+"]"); + + d_buf[last_lit] = (short) dist; + l_buf[last_lit++] = (byte) (len - 3); + + int lc = l_code(len-3); + literalTree.freqs[lc]++; + if (lc >= 265 && lc < 285) + extra_bits += (lc - 261) / 4; + + int dc = d_code(dist-1); + distTree.freqs[dc]++; + if (dc >= 4) + extra_bits += dc / 2 - 1; + return last_lit == BUFSIZE; + } +} diff --git a/libjava/classpath/java/util/zip/DeflaterOutputStream.java b/libjava/classpath/java/util/zip/DeflaterOutputStream.java new file mode 100644 index 000000000..6fd1c5cfb --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterOutputStream.java @@ -0,0 +1,198 @@ +/* DeflaterOutputStream.java - Output filter for compressing. + Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +/** + * This is a special FilterOutputStream deflating the bytes that are + * written through it. It uses the Deflater for deflating. + * + * A special thing to be noted is that flush() doesn't flush + * everything in Sun's JDK, but it does so in jazzlib. This is because + * Sun's Deflater doesn't have a way to flush() everything, without + * finishing the stream. + * + * @author Tom Tromey, Jochen Hoenicke + * @date Jan 11, 2001 + */ +public class DeflaterOutputStream extends FilterOutputStream +{ + /** + * This buffer is used temporarily to retrieve the bytes from the + * deflater and write them to the underlying output stream. + */ + protected byte[] buf; + + /** + * The deflater which is used to deflate the stream. + */ + protected Deflater def; + + /** + * Deflates everything in the def's input buffers. This will call + * def.deflate() until all bytes from the input buffers + * are processed. + */ + protected void deflate() throws IOException + { + while (! def.needsInput()) + { + int len = def.deflate(buf, 0, buf.length); + + // System.err.println("DOS deflated " + len + " out of " + buf.length); + if (len <= 0) + break; + out.write(buf, 0, len); + } + + if (! def.needsInput()) + throw new InternalError("Can't deflate all input?"); + } + + /** + * Creates a new DeflaterOutputStream with a default Deflater and + * default buffer size. + * @param out the output stream where deflated output should be written. + */ + public DeflaterOutputStream(OutputStream out) + { + this(out, new Deflater(), 4096); + } + + /** + * Creates a new DeflaterOutputStream with the given Deflater and + * default buffer size. + * @param out the output stream where deflated output should be written. + * @param defl the underlying deflater. + */ + public DeflaterOutputStream(OutputStream out, Deflater defl) + { + this(out, defl, 4096); + } + + /** + * Creates a new DeflaterOutputStream with the given Deflater and + * buffer size. + * @param out the output stream where deflated output should be written. + * @param defl the underlying deflater. + * @param bufsize the buffer size. + * @exception IllegalArgumentException if bufsize isn't positive. + */ + public DeflaterOutputStream(OutputStream out, Deflater defl, int bufsize) + { + super(out); + if (bufsize <= 0) + throw new IllegalArgumentException("bufsize <= 0"); + buf = new byte[bufsize]; + def = defl; + } + + /** + * Flushes the stream by calling flush() on the deflater and then + * on the underlying stream. This ensures that all bytes are + * flushed. This function doesn't work in Sun's JDK, but only in + * jazzlib. + */ + public void flush() throws IOException + { + def.flush(); + deflate(); + out.flush(); + } + + /** + * Finishes the stream by calling finish() on the deflater. This + * was the only way to ensure that all bytes are flushed in Sun's + * JDK. + */ + public void finish() throws IOException + { + def.finish(); + while (! def.finished()) + { + int len = def.deflate(buf, 0, buf.length); + if (len <= 0) + break; + out.write(buf, 0, len); + } + if (! def.finished()) + throw new InternalError("Can't deflate all input?"); + out.flush(); + } + + /** + * Calls finish() and closes the stream. + */ + public void close() throws IOException + { + finish(); + out.close(); + } + + /** + * Writes a single byte to the compressed output stream. + * @param bval the byte value. + */ + public void write(int bval) throws IOException + { + byte[] b = new byte[1]; + b[0] = (byte) bval; + write(b, 0, 1); + } + + /** + * Writes a len bytes from an array to the compressed stream. + * @param buf the byte array. + * @param off the offset into the byte array where to start. + * @param len the number of bytes to write. + */ + public void write(byte[] buf, int off, int len) throws IOException + { + def.setInput(buf, off, len); + deflate(); + } +} diff --git a/libjava/classpath/java/util/zip/DeflaterPending.java b/libjava/classpath/java/util/zip/DeflaterPending.java new file mode 100644 index 000000000..fabc2264e --- /dev/null +++ b/libjava/classpath/java/util/zip/DeflaterPending.java @@ -0,0 +1,53 @@ +/* java.util.zip.DeflaterPending + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This class stores the pending output of the Deflater. + * + * @author Jochen Hoenicke + * @date Jan 5, 2000 + */ + +class DeflaterPending extends PendingBuffer +{ + public DeflaterPending() + { + super(DeflaterConstants.PENDING_BUF_SIZE); + } +} diff --git a/libjava/classpath/java/util/zip/GZIPInputStream.java b/libjava/classpath/java/util/zip/GZIPInputStream.java new file mode 100644 index 000000000..ed99ee92c --- /dev/null +++ b/libjava/classpath/java/util/zip/GZIPInputStream.java @@ -0,0 +1,355 @@ +/* GZIPInputStream.java - Input filter for reading gzip file + Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * This filter stream is used to decompress a "GZIP" format stream. + * The "GZIP" format is described in RFC 1952. + * + * @author John Leuner + * @author Tom Tromey + * @since JDK 1.1 + */ +public class GZIPInputStream + extends InflaterInputStream +{ + /** + * The magic number found at the start of a GZIP stream. + */ + public static final int GZIP_MAGIC = 0x8b1f; + + /** + * The mask for bit 0 of the flag byte. + */ + static final int FTEXT = 0x1; + + /** + * The mask for bit 1 of the flag byte. + */ + static final int FHCRC = 0x2; + + /** + * The mask for bit 2 of the flag byte. + */ + static final int FEXTRA = 0x4; + + /** + * The mask for bit 3 of the flag byte. + */ + static final int FNAME = 0x8; + + /** + * The mask for bit 4 of the flag byte. + */ + static final int FCOMMENT = 0x10; + + /** + * The CRC-32 checksum value for uncompressed data. + */ + protected CRC32 crc; + + /** + * Indicates whether or not the end of the stream has been reached. + */ + protected boolean eos; + + /** + * Indicates whether or not the GZIP header has been read in. + */ + private boolean readGZIPHeader; + + /** + * Creates a GZIPInputStream with the default buffer size. + * + * @param in The stream to read compressed data from + * (in GZIP format). + * + * @throws IOException if an error occurs during an I/O operation. + */ + public GZIPInputStream(InputStream in) + throws IOException + { + this(in, 4096); + } + + /** + * Creates a GZIPInputStream with the specified buffer size. + * + * @param in The stream to read compressed data from + * (in GZIP format). + * @param size The size of the buffer to use. + * + * @throws IOException if an error occurs during an I/O operation. + * @throws IllegalArgumentException if size + * is less than or equal to 0. + */ + public GZIPInputStream(InputStream in, int size) + throws IOException + { + super(in, new Inflater(true), size); + crc = new CRC32(); + readHeader(); + } + + /** + * Closes the input stream. + * + * @throws IOException if an error occurs during an I/O operation. + */ + public void close() + throws IOException + { + // Nothing to do here. + super.close(); + } + + /** + * Reads in GZIP-compressed data and stores it in uncompressed form + * into an array of bytes. The method will block until either + * enough input data becomes available or the compressed stream + * reaches its end. + * + * @param buf the buffer into which the uncompressed data will + * be stored. + * @param offset the offset indicating where in buf + * the uncompressed data should be placed. + * @param len the number of uncompressed bytes to be read. + */ + public int read(byte[] buf, int offset, int len) throws IOException + { + // We first have to slurp in the GZIP header, then we feed all the + // rest of the data to the superclass. + // + // As we do that we continually update the CRC32. Once the data is + // finished, we check the CRC32. + // + // This means we don't need our own buffer, as everything is done + // in the superclass. + if (!readGZIPHeader) + readHeader(); + + if (eos) + return -1; + + // System.err.println("GZIPIS.read(byte[], off, len ... " + offset + " and len " + len); + + /* We don't have to read the header, + * so we just grab data from the superclass. + */ + int numRead = super.read(buf, offset, len); + if (numRead > 0) + crc.update(buf, offset, numRead); + + if (inf.finished()) + readFooter(); + return numRead; + } + + + /** + * Reads in the GZIP header. + */ + private void readHeader() throws IOException + { + /* 1. Check the two magic bytes */ + CRC32 headCRC = new CRC32(); + int magic = in.read(); + if (magic < 0) + { + eos = true; + return; + } + int magic2 = in.read(); + if ((magic + (magic2 << 8)) != GZIP_MAGIC) + throw new IOException("Error in GZIP header, bad magic code"); + headCRC.update(magic); + headCRC.update(magic2); + + /* 2. Check the compression type (must be 8) */ + int CM = in.read(); + if (CM != Deflater.DEFLATED) + throw new IOException("Error in GZIP header, data not in deflate format"); + headCRC.update(CM); + + /* 3. Check the flags */ + int flags = in.read(); + if (flags < 0) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(flags); + + /* This flag byte is divided into individual bits as follows: + + bit 0 FTEXT + bit 1 FHCRC + bit 2 FEXTRA + bit 3 FNAME + bit 4 FCOMMENT + bit 5 reserved + bit 6 reserved + bit 7 reserved + */ + + /* 3.1 Check the reserved bits are zero */ + if ((flags & 0xd0) != 0) + throw new IOException("Reserved flag bits in GZIP header != 0"); + + /* 4.-6. Skip the modification time, extra flags, and OS type */ + for (int i=0; i< 6; i++) + { + int readByte = in.read(); + if (readByte < 0) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(readByte); + } + + /* 7. Read extra field */ + if ((flags & FEXTRA) != 0) + { + /* Skip subfield id */ + for (int i=0; i< 2; i++) + { + int readByte = in.read(); + if (readByte < 0) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(readByte); + } + if (in.read() < 0 || in.read() < 0) + throw new EOFException("Early EOF in GZIP header"); + + int len1, len2, extraLen; + len1 = in.read(); + len2 = in.read(); + if ((len1 < 0) || (len2 < 0)) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(len1); + headCRC.update(len2); + + extraLen = (len1 << 8) | len2; + for (int i = 0; i < extraLen;i++) + { + int readByte = in.read(); + if (readByte < 0) + throw new EOFException("Early EOF in GZIP header"); + headCRC.update(readByte); + } + } + + /* 8. Read file name */ + if ((flags & FNAME) != 0) + { + int readByte; + while ( (readByte = in.read()) > 0) + headCRC.update(readByte); + if (readByte < 0) + throw new EOFException("Early EOF in GZIP file name"); + headCRC.update(readByte); + } + + /* 9. Read comment */ + if ((flags & FCOMMENT) != 0) + { + int readByte; + while ( (readByte = in.read()) > 0) + headCRC.update(readByte); + + if (readByte < 0) + throw new EOFException("Early EOF in GZIP comment"); + headCRC.update(readByte); + } + + /* 10. Read header CRC */ + if ((flags & FHCRC) != 0) + { + int tempByte; + int crcval = in.read(); + if (crcval < 0) + throw new EOFException("Early EOF in GZIP header"); + + tempByte = in.read(); + if (tempByte < 0) + throw new EOFException("Early EOF in GZIP header"); + + crcval = (crcval << 8) | tempByte; + if (crcval != ((int) headCRC.getValue() & 0xffff)) + throw new IOException("Header CRC value mismatch"); + } + + readGZIPHeader = true; + //System.err.println("Read GZIP header"); + } + + private void readFooter() throws IOException + { + byte[] footer = new byte[8]; + int avail = inf.getRemaining(); + if (avail > 8) + avail = 8; + System.arraycopy(buf, len - inf.getRemaining(), footer, 0, avail); + int needed = 8 - avail; + while (needed > 0) + { + int count = in.read(footer, 8-needed, needed); + if (count <= 0) + throw new EOFException("Early EOF in GZIP footer"); + needed -= count; //Jewel Jan 16 + } + + int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8) + | ((footer[2] & 0xff) << 16) | (footer[3] << 24); + if (crcval != (int) crc.getValue()) + throw new IOException("GZIP crc sum mismatch, theirs \"" + + Integer.toHexString(crcval) + + "\" and ours \"" + + Integer.toHexString( (int) crc.getValue())); + + int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8) + | ((footer[6] & 0xff) << 16) | (footer[7] << 24); + if (total != inf.getTotalOut()) + throw new IOException("Number of bytes mismatch"); + + /* FIXME" XXX Should we support multiple members. + * Difficult, since there may be some bytes still in buf + */ + eos = true; + } +} diff --git a/libjava/classpath/java/util/zip/GZIPOutputStream.java b/libjava/classpath/java/util/zip/GZIPOutputStream.java new file mode 100644 index 000000000..0080ab645 --- /dev/null +++ b/libjava/classpath/java/util/zip/GZIPOutputStream.java @@ -0,0 +1,151 @@ +/* GZIPOutputStream.java - Create a file in gzip format + Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * This filter stream is used to compress a stream into a "GZIP" stream. + * The "GZIP" format is described in RFC 1952. + * + * @author John Leuner + * @author Tom Tromey + * @since JDK 1.1 + */ + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +public class GZIPOutputStream extends DeflaterOutputStream +{ + /** + * CRC-32 value for uncompressed data + */ + protected CRC32 crc; + + /** + * Creates a GZIPOutputStream with the default buffer size + * + * @param out The stream to read data (to be compressed) from + * + */ + public GZIPOutputStream(OutputStream out) throws IOException + { + this(out, 4096); + } + + /** + * Creates a GZIPOutputStream with the specified buffer size + * + * @param out The stream to read compressed data from + * @param size Size of the buffer to use + */ + public GZIPOutputStream(OutputStream out, int size) throws IOException + { + super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size); + crc = new CRC32(); + int mod_time = (int) (System.currentTimeMillis() / 1000L); + byte[] gzipHeader = + { + /* The two magic bytes */ + (byte) GZIPInputStream.GZIP_MAGIC, + (byte) (GZIPInputStream.GZIP_MAGIC >> 8), + + /* The compression type */ + (byte) Deflater.DEFLATED, + + /* The flags (not set) */ + 0, + + /* The modification time */ + (byte) mod_time, (byte) (mod_time >> 8), + (byte) (mod_time >> 16), (byte) (mod_time >> 24), + + /* The extra flags */ + 0, + + /* The OS type (unknown) */ + (byte) 255 + }; + + out.write(gzipHeader); + // System.err.println("wrote GZIP header (" + gzipHeader.length + " bytes )"); + } + + public synchronized void write(byte[] buf, int off, int len) + throws IOException + { + super.write(buf, off, len); + crc.update(buf, off, len); + } + + /** + * Writes remaining compressed output data to the output stream + * and closes it. + */ + public void close() throws IOException + { + finish(); + out.close(); + } + + public void finish() throws IOException + { + super.finish(); + + int totalin = def.getTotalIn(); + int crcval = (int) (crc.getValue() & 0xffffffff); + + // System.err.println("CRC val is " + Integer.toHexString( crcval ) + " and length " + Integer.toHexString(totalin)); + + byte[] gzipFooter = + { + (byte) crcval, (byte) (crcval >> 8), + (byte) (crcval >> 16), (byte) (crcval >> 24), + + (byte) totalin, (byte) (totalin >> 8), + (byte) (totalin >> 16), (byte) (totalin >> 24) + }; + + out.write(gzipFooter); + // System.err.println("wrote GZIP trailer (" + gzipFooter.length + " bytes )"); + } +} diff --git a/libjava/classpath/java/util/zip/Inflater.java b/libjava/classpath/java/util/zip/Inflater.java new file mode 100644 index 000000000..0f094d667 --- /dev/null +++ b/libjava/classpath/java/util/zip/Inflater.java @@ -0,0 +1,726 @@ +/* Inflater.java - Decompress a data stream + Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ + +/** + * Inflater is used to decompress data that has been compressed according + * to the "deflate" standard described in rfc1950. + * + * The usage is as following. First you have to set some input with + * setInput(), then inflate() it. If inflate doesn't + * inflate any bytes there may be three reasons: + *

          + *
        • needsInput() returns true because the input buffer is empty. + * You have to provide more input with setInput(). + * NOTE: needsInput() also returns true when, the stream is finished. + *
        • + *
        • needsDictionary() returns true, you have to provide a preset + * dictionary with setDictionary().
        • + *
        • finished() returns true, the inflater has finished.
        • + *
        + * Once the first output byte is produced, a dictionary will not be + * needed at a later stage. + * + * @author John Leuner, Jochen Hoenicke + * @author Tom Tromey + * @date May 17, 1999 + * @since JDK 1.1 + */ +public class Inflater +{ + /* Copy lengths for literal codes 257..285 */ + private static final int CPLENS[] = + { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 + }; + + /* Extra bits for literal codes 257..285 */ + private static final int CPLEXT[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + + /* Copy offsets for distance codes 0..29 */ + private static final int CPDIST[] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + /* Extra bits for distance codes */ + private static final int CPDEXT[] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13 + }; + + /* This are the state in which the inflater can be. */ + private static final int DECODE_HEADER = 0; + private static final int DECODE_DICT = 1; + private static final int DECODE_BLOCKS = 2; + private static final int DECODE_STORED_LEN1 = 3; + private static final int DECODE_STORED_LEN2 = 4; + private static final int DECODE_STORED = 5; + private static final int DECODE_DYN_HEADER = 6; + private static final int DECODE_HUFFMAN = 7; + private static final int DECODE_HUFFMAN_LENBITS = 8; + private static final int DECODE_HUFFMAN_DIST = 9; + private static final int DECODE_HUFFMAN_DISTBITS = 10; + private static final int DECODE_CHKSUM = 11; + private static final int FINISHED = 12; + + /** This variable contains the current state. */ + private int mode; + + /** + * The adler checksum of the dictionary or of the decompressed + * stream, as it is written in the header resp. footer of the + * compressed stream.
        + * + * Only valid if mode is DECODE_DICT or DECODE_CHKSUM. + */ + private int readAdler; + /** + * The number of bits needed to complete the current state. This + * is valid, if mode is DECODE_DICT, DECODE_CHKSUM, + * DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. + */ + private int neededBits; + private int repLength, repDist; + private int uncomprLen; + /** + * True, if the last block flag was set in the last block of the + * inflated stream. This means that the stream ends after the + * current block. + */ + private boolean isLastBlock; + + /** + * The total number of inflated bytes. + */ + private long totalOut; + /** + * The total number of bytes set with setInput(). This is not the + * value returned by getTotalIn(), since this also includes the + * unprocessed input. + */ + private long totalIn; + /** + * This variable stores the nowrap flag that was given to the constructor. + * True means, that the inflated stream doesn't contain a header nor the + * checksum in the footer. + */ + private boolean nowrap; + + private StreamManipulator input; + private OutputWindow outputWindow; + private InflaterDynHeader dynHeader; + private InflaterHuffmanTree litlenTree, distTree; + private Adler32 adler; + + /** + * Creates a new inflater. + */ + public Inflater () + { + this (false); + } + + /** + * Creates a new inflater. + * @param nowrap true if no header and checksum field appears in the + * stream. This is used for GZIPed input. For compatibility with + * Sun JDK you should provide one byte of input more than needed in + * this case. + */ + public Inflater (boolean nowrap) + { + this.nowrap = nowrap; + this.adler = new Adler32(); + input = new StreamManipulator(); + outputWindow = new OutputWindow(); + mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; + } + + /** + * Finalizes this object. + */ + protected void finalize () + { + /* Exists only for compatibility */ + } + + /** + * Frees all objects allocated by the inflater. There's no reason + * to call this, since you can just rely on garbage collection (even + * for the Sun implementation). Exists only for compatibility + * with Sun's JDK, where the compressor allocates native memory. + * If you call any method (even reset) afterwards the behaviour is + * undefined. + */ + public void end () + { + outputWindow = null; + input = null; + dynHeader = null; + litlenTree = null; + distTree = null; + adler = null; + } + + /** + * Returns true, if the inflater has finished. This means, that no + * input is needed and no output can be produced. + */ + public boolean finished() + { + return mode == FINISHED && outputWindow.getAvailable() == 0; + } + + /** + * Gets the adler checksum. This is either the checksum of all + * uncompressed bytes returned by inflate(), or if needsDictionary() + * returns true (and thus no output was yet produced) this is the + * adler checksum of the expected dictionary. + * @returns the adler checksum. + */ + public int getAdler() + { + return needsDictionary() ? readAdler : (int) adler.getValue(); + } + + /** + * Gets the number of unprocessed input. Useful, if the end of the + * stream is reached and you want to further process the bytes after + * the deflate stream. + * @return the number of bytes of the input which were not processed. + */ + public int getRemaining() + { + return input.getAvailableBytes(); + } + + /** + * Gets the total number of processed compressed input bytes. + * @return the total number of bytes of processed input bytes. + */ + public int getTotalIn() + { + return (int) (totalIn - getRemaining()); + } + + /** + * Gets the total number of processed compressed input bytes. + * @return the total number of bytes of processed input bytes. + * @since 1.5 + */ + public long getBytesRead() + { + return totalIn - getRemaining(); + } + + /** + * Gets the total number of output bytes returned by inflate(). + * @return the total number of output bytes. + */ + public int getTotalOut() + { + return (int) totalOut; + } + + /** + * Gets the total number of output bytes returned by inflate(). + * @return the total number of output bytes. + * @since 1.5 + */ + public long getBytesWritten() + { + return totalOut; + } + + /** + * Inflates the compressed stream to the output buffer. If this + * returns 0, you should check, whether needsDictionary(), + * needsInput() or finished() returns true, to determine why no + * further output is produced. + * @param buf the output buffer. + * @return the number of bytes written to the buffer, 0 if no further + * output can be produced. + * @exception DataFormatException if deflated stream is invalid. + * @exception IllegalArgumentException if buf has length 0. + */ + public int inflate (byte[] buf) throws DataFormatException + { + return inflate (buf, 0, buf.length); + } + + /** + * Inflates the compressed stream to the output buffer. If this + * returns 0, you should check, whether needsDictionary(), + * needsInput() or finished() returns true, to determine why no + * further output is produced. + * @param buf the output buffer. + * @param off the offset into buffer where the output should start. + * @param len the maximum length of the output. + * @return the number of bytes written to the buffer, 0 if no further + * output can be produced. + * @exception DataFormatException if deflated stream is invalid. + * @exception IndexOutOfBoundsException if the off and/or len are wrong. + */ + public int inflate (byte[] buf, int off, int len) throws DataFormatException + { + /* Check for correct buff, off, len triple */ + if (0 > off || off > off + len || off + len > buf.length) + throw new ArrayIndexOutOfBoundsException(); + int count = 0; + for (;;) + { + if (outputWindow.getAvailable() == 0) + { + if (!decode()) + break; + } + else if (len > 0) + { + int more = outputWindow.copyOutput(buf, off, len); + adler.update(buf, off, more); + off += more; + count += more; + totalOut += more; + len -= more; + } + else + break; + } + return count; + } + + /** + * Returns true, if a preset dictionary is needed to inflate the input. + */ + public boolean needsDictionary () + { + return mode == DECODE_DICT && neededBits == 0; + } + + /** + * Returns true, if the input buffer is empty. + * You should then call setInput().
        + * + * NOTE: This method also returns true when the stream is finished. + */ + public boolean needsInput () + { + return input.needsInput (); + } + + /** + * Resets the inflater so that a new stream can be decompressed. All + * pending input and output will be discarded. + */ + public void reset () + { + mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; + totalIn = totalOut = 0; + input.reset(); + outputWindow.reset(); + dynHeader = null; + litlenTree = null; + distTree = null; + isLastBlock = false; + adler.reset(); + } + + /** + * Sets the preset dictionary. This should only be called, if + * needsDictionary() returns true and it should set the same + * dictionary, that was used for deflating. The getAdler() + * function returns the checksum of the dictionary needed. + * @param buffer the dictionary. + * @exception IllegalStateException if no dictionary is needed. + * @exception IllegalArgumentException if the dictionary checksum is + * wrong. + */ + public void setDictionary (byte[] buffer) + { + setDictionary(buffer, 0, buffer.length); + } + + /** + * Sets the preset dictionary. This should only be called, if + * needsDictionary() returns true and it should set the same + * dictionary, that was used for deflating. The getAdler() + * function returns the checksum of the dictionary needed. + * @param buffer the dictionary. + * @param off the offset into buffer where the dictionary starts. + * @param len the length of the dictionary. + * @exception IllegalStateException if no dictionary is needed. + * @exception IllegalArgumentException if the dictionary checksum is + * wrong. + * @exception IndexOutOfBoundsException if the off and/or len are wrong. + */ + public void setDictionary (byte[] buffer, int off, int len) + { + if (!needsDictionary()) + throw new IllegalStateException(); + + adler.update(buffer, off, len); + if ((int) adler.getValue() != readAdler) + throw new IllegalArgumentException("Wrong adler checksum"); + adler.reset(); + outputWindow.copyDict(buffer, off, len); + mode = DECODE_BLOCKS; + } + + /** + * Sets the input. This should only be called, if needsInput() + * returns true. + * @param buf the input. + * @exception IllegalStateException if no input is needed. + */ + public void setInput (byte[] buf) + { + setInput (buf, 0, buf.length); + } + + /** + * Sets the input. This should only be called, if needsInput() + * returns true. + * @param buf the input. + * @param off the offset into buffer where the input starts. + * @param len the length of the input. + * @exception IllegalStateException if no input is needed. + * @exception IndexOutOfBoundsException if the off and/or len are wrong. + */ + public void setInput (byte[] buf, int off, int len) + { + input.setInput (buf, off, len); + totalIn += len; + } + + /** + * Decodes the deflate header. + * @return false if more input is needed. + * @exception DataFormatException if header is invalid. + */ + private boolean decodeHeader () throws DataFormatException + { + int header = input.peekBits(16); + if (header < 0) + return false; + input.dropBits(16); + + /* The header is written in "wrong" byte order */ + header = ((header << 8) | (header >> 8)) & 0xffff; + if (header % 31 != 0) + throw new DataFormatException("Header checksum illegal"); + + if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) + throw new DataFormatException("Compression Method unknown"); + + /* Maximum size of the backwards window in bits. + * We currently ignore this, but we could use it to make the + * inflater window more space efficient. On the other hand the + * full window (15 bits) is needed most times, anyway. + int max_wbits = ((header & 0x7000) >> 12) + 8; + */ + + if ((header & 0x0020) == 0) // Dictionary flag? + { + mode = DECODE_BLOCKS; + } + else + { + mode = DECODE_DICT; + neededBits = 32; + } + return true; + } + + /** + * Decodes the dictionary checksum after the deflate header. + * @return false if more input is needed. + */ + private boolean decodeDict () + { + while (neededBits > 0) + { + int dictByte = input.peekBits(8); + if (dictByte < 0) + return false; + input.dropBits(8); + readAdler = (readAdler << 8) | dictByte; + neededBits -= 8; + } + return false; + } + + /** + * Decodes the huffman encoded symbols in the input stream. + * @return false if more input is needed, true if output window is + * full or the current block ends. + * @exception DataFormatException if deflated stream is invalid. + */ + private boolean decodeHuffman () throws DataFormatException + { + int free = outputWindow.getFreeSpace(); + while (free >= 258) + { + int symbol; + switch (mode) + { + case DECODE_HUFFMAN: + /* This is the inner loop so it is optimized a bit */ + while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0) + { + outputWindow.write(symbol); + if (--free < 258) + return true; + } + if (symbol < 257) + { + if (symbol < 0) + return false; + else + { + /* symbol == 256: end of block */ + distTree = null; + litlenTree = null; + mode = DECODE_BLOCKS; + return true; + } + } + + try + { + repLength = CPLENS[symbol - 257]; + neededBits = CPLEXT[symbol - 257]; + } + catch (ArrayIndexOutOfBoundsException ex) + { + throw new DataFormatException("Illegal rep length code"); + } + /* fall through */ + case DECODE_HUFFMAN_LENBITS: + if (neededBits > 0) + { + mode = DECODE_HUFFMAN_LENBITS; + int i = input.peekBits(neededBits); + if (i < 0) + return false; + input.dropBits(neededBits); + repLength += i; + } + mode = DECODE_HUFFMAN_DIST; + /* fall through */ + case DECODE_HUFFMAN_DIST: + symbol = distTree.getSymbol(input); + if (symbol < 0) + return false; + try + { + repDist = CPDIST[symbol]; + neededBits = CPDEXT[symbol]; + } + catch (ArrayIndexOutOfBoundsException ex) + { + throw new DataFormatException("Illegal rep dist code"); + } + /* fall through */ + case DECODE_HUFFMAN_DISTBITS: + if (neededBits > 0) + { + mode = DECODE_HUFFMAN_DISTBITS; + int i = input.peekBits(neededBits); + if (i < 0) + return false; + input.dropBits(neededBits); + repDist += i; + } + outputWindow.repeat(repLength, repDist); + free -= repLength; + mode = DECODE_HUFFMAN; + break; + default: + throw new IllegalStateException(); + } + } + return true; + } + + /** + * Decodes the adler checksum after the deflate stream. + * @return false if more input is needed. + * @exception DataFormatException if checksum doesn't match. + */ + private boolean decodeChksum () throws DataFormatException + { + while (neededBits > 0) + { + int chkByte = input.peekBits(8); + if (chkByte < 0) + return false; + input.dropBits(8); + readAdler = (readAdler << 8) | chkByte; + neededBits -= 8; + } + if ((int) adler.getValue() != readAdler) + throw new DataFormatException("Adler chksum doesn't match: " + +Integer.toHexString((int)adler.getValue()) + +" vs. "+Integer.toHexString(readAdler)); + mode = FINISHED; + return false; + } + + /** + * Decodes the deflated stream. + * @return false if more input is needed, or if finished. + * @exception DataFormatException if deflated stream is invalid. + */ + private boolean decode () throws DataFormatException + { + switch (mode) + { + case DECODE_HEADER: + return decodeHeader(); + case DECODE_DICT: + return decodeDict(); + case DECODE_CHKSUM: + return decodeChksum(); + + case DECODE_BLOCKS: + if (isLastBlock) + { + if (nowrap) + { + mode = FINISHED; + return false; + } + else + { + input.skipToByteBoundary(); + neededBits = 32; + mode = DECODE_CHKSUM; + return true; + } + } + + int type = input.peekBits(3); + if (type < 0) + return false; + input.dropBits(3); + + if ((type & 1) != 0) + isLastBlock = true; + switch (type >> 1) + { + case DeflaterConstants.STORED_BLOCK: + input.skipToByteBoundary(); + mode = DECODE_STORED_LEN1; + break; + case DeflaterConstants.STATIC_TREES: + litlenTree = InflaterHuffmanTree.defLitLenTree; + distTree = InflaterHuffmanTree.defDistTree; + mode = DECODE_HUFFMAN; + break; + case DeflaterConstants.DYN_TREES: + dynHeader = new InflaterDynHeader(); + mode = DECODE_DYN_HEADER; + break; + default: + throw new DataFormatException("Unknown block type "+type); + } + return true; + + case DECODE_STORED_LEN1: + { + if ((uncomprLen = input.peekBits(16)) < 0) + return false; + input.dropBits(16); + mode = DECODE_STORED_LEN2; + } + /* fall through */ + case DECODE_STORED_LEN2: + { + int nlen = input.peekBits(16); + if (nlen < 0) + return false; + input.dropBits(16); + if (nlen != (uncomprLen ^ 0xffff)) + throw new DataFormatException("broken uncompressed block"); + mode = DECODE_STORED; + } + /* fall through */ + case DECODE_STORED: + { + int more = outputWindow.copyStored(input, uncomprLen); + uncomprLen -= more; + if (uncomprLen == 0) + { + mode = DECODE_BLOCKS; + return true; + } + return !input.needsInput(); + } + + case DECODE_DYN_HEADER: + if (!dynHeader.decode(input)) + return false; + litlenTree = dynHeader.buildLitLenTree(); + distTree = dynHeader.buildDistTree(); + mode = DECODE_HUFFMAN; + /* fall through */ + case DECODE_HUFFMAN: + case DECODE_HUFFMAN_LENBITS: + case DECODE_HUFFMAN_DIST: + case DECODE_HUFFMAN_DISTBITS: + return decodeHuffman(); + case FINISHED: + return false; + default: + throw new IllegalStateException(); + } + } +} diff --git a/libjava/classpath/java/util/zip/InflaterDynHeader.java b/libjava/classpath/java/util/zip/InflaterDynHeader.java new file mode 100644 index 000000000..64e08d6bb --- /dev/null +++ b/libjava/classpath/java/util/zip/InflaterDynHeader.java @@ -0,0 +1,203 @@ +/* java.util.zip.InflaterDynHeader + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +class InflaterDynHeader +{ + private static final int LNUM = 0; + private static final int DNUM = 1; + private static final int BLNUM = 2; + private static final int BLLENS = 3; + private static final int LENS = 4; + private static final int REPS = 5; + + private static final int repMin[] = { 3, 3, 11 }; + private static final int repBits[] = { 2, 3, 7 }; + + + private byte[] blLens; + private byte[] litdistLens; + + private InflaterHuffmanTree blTree; + + private int mode; + private int lnum, dnum, blnum, num; + private int repSymbol; + private byte lastLen; + private int ptr; + + private static final int[] BL_ORDER = + { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + public InflaterDynHeader() + { + } + + public boolean decode(StreamManipulator input) throws DataFormatException + { + decode_loop: + for (;;) + { + switch (mode) + { + case LNUM: + lnum = input.peekBits(5); + if (lnum < 0) + return false; + lnum += 257; + input.dropBits(5); +// System.err.println("LNUM: "+lnum); + mode = DNUM; + /* fall through */ + case DNUM: + dnum = input.peekBits(5); + if (dnum < 0) + return false; + dnum++; + input.dropBits(5); +// System.err.println("DNUM: "+dnum); + num = lnum+dnum; + litdistLens = new byte[num]; + mode = BLNUM; + /* fall through */ + case BLNUM: + blnum = input.peekBits(4); + if (blnum < 0) + return false; + blnum += 4; + input.dropBits(4); + blLens = new byte[19]; + ptr = 0; +// System.err.println("BLNUM: "+blnum); + mode = BLLENS; + /* fall through */ + case BLLENS: + while (ptr < blnum) + { + int len = input.peekBits(3); + if (len < 0) + return false; + input.dropBits(3); +// System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len); + blLens[BL_ORDER[ptr]] = (byte) len; + ptr++; + } + blTree = new InflaterHuffmanTree(blLens); + blLens = null; + ptr = 0; + mode = LENS; + /* fall through */ + case LENS: + { + int symbol; + while (((symbol = blTree.getSymbol(input)) & ~15) == 0) + { + /* Normal case: symbol in [0..15] */ + +// System.err.println("litdistLens["+ptr+"]: "+symbol); + litdistLens[ptr++] = lastLen = (byte) symbol; + + if (ptr == num) + { + /* Finished */ + return true; + } + } + + /* need more input ? */ + if (symbol < 0) + return false; + + /* otherwise repeat code */ + if (symbol >= 17) + { + /* repeat zero */ +// System.err.println("repeating zero"); + lastLen = 0; + } + else + { + if (ptr == 0) + throw new DataFormatException(); + } + repSymbol = symbol-16; + mode = REPS; + } + /* fall through */ + + case REPS: + { + int bits = repBits[repSymbol]; + int count = input.peekBits(bits); + if (count < 0) + return false; + input.dropBits(bits); + count += repMin[repSymbol]; +// System.err.println("litdistLens repeated: "+count); + + if (ptr + count > num) + throw new DataFormatException(); + while (count-- > 0) + litdistLens[ptr++] = lastLen; + + if (ptr == num) + { + /* Finished */ + return true; + } + } + mode = LENS; + continue decode_loop; + } + } + } + + public InflaterHuffmanTree buildLitLenTree() throws DataFormatException + { + byte[] litlenLens = new byte[lnum]; + System.arraycopy(litdistLens, 0, litlenLens, 0, lnum); + return new InflaterHuffmanTree(litlenLens); + } + + public InflaterHuffmanTree buildDistTree() throws DataFormatException + { + byte[] distLens = new byte[dnum]; + System.arraycopy(litdistLens, lnum, distLens, 0, dnum); + return new InflaterHuffmanTree(distLens); + } +} diff --git a/libjava/classpath/java/util/zip/InflaterHuffmanTree.java b/libjava/classpath/java/util/zip/InflaterHuffmanTree.java new file mode 100644 index 000000000..c12c732e0 --- /dev/null +++ b/libjava/classpath/java/util/zip/InflaterHuffmanTree.java @@ -0,0 +1,217 @@ +/* InflaterHuffmanTree.java -- + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +class InflaterHuffmanTree +{ + private static final int MAX_BITLEN = 15; + + private short[] tree; + + static InflaterHuffmanTree defLitLenTree, defDistTree; + + static + { + try + { + byte[] codeLengths = new byte[288]; + int i = 0; + while (i < 144) + codeLengths[i++] = 8; + while (i < 256) + codeLengths[i++] = 9; + while (i < 280) + codeLengths[i++] = 7; + while (i < 288) + codeLengths[i++] = 8; + defLitLenTree = new InflaterHuffmanTree(codeLengths); + + codeLengths = new byte[32]; + i = 0; + while (i < 32) + codeLengths[i++] = 5; + defDistTree = new InflaterHuffmanTree(codeLengths); + } + catch (DataFormatException ex) + { + throw new InternalError + ("InflaterHuffmanTree: static tree length illegal"); + } + } + + /** + * Constructs a Huffman tree from the array of code lengths. + * + * @param codeLengths the array of code lengths + */ + InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException + { + buildTree(codeLengths); + } + + private void buildTree(byte[] codeLengths) throws DataFormatException + { + int[] blCount = new int[MAX_BITLEN+1]; + int[] nextCode = new int[MAX_BITLEN+1]; + for (int i = 0; i < codeLengths.length; i++) + { + int bits = codeLengths[i]; + if (bits > 0) + blCount[bits]++; + } + + int code = 0; + int treeSize = 512; + for (int bits = 1; bits <= MAX_BITLEN; bits++) + { + nextCode[bits] = code; + code += blCount[bits] << (16 - bits); + if (bits >= 10) + { + /* We need an extra table for bit lengths >= 10. */ + int start = nextCode[bits] & 0x1ff80; + int end = code & 0x1ff80; + treeSize += (end - start) >> (16 - bits); + } + } + if (code != 65536) + throw new DataFormatException("Code lengths don't add up properly."); + + /* Now create and fill the extra tables from longest to shortest + * bit len. This way the sub trees will be aligned. + */ + tree = new short[treeSize]; + int treePtr = 512; + for (int bits = MAX_BITLEN; bits >= 10; bits--) + { + int end = code & 0x1ff80; + code -= blCount[bits] << (16 - bits); + int start = code & 0x1ff80; + for (int i = start; i < end; i += 1 << 7) + { + tree[DeflaterHuffman.bitReverse(i)] + = (short) ((-treePtr << 4) | bits); + treePtr += 1 << (bits-9); + } + } + + for (int i = 0; i < codeLengths.length; i++) + { + int bits = codeLengths[i]; + if (bits == 0) + continue; + code = nextCode[bits]; + int revcode = DeflaterHuffman.bitReverse(code); + if (bits <= 9) + { + do + { + tree[revcode] = (short) ((i << 4) | bits); + revcode += 1 << bits; + } + while (revcode < 512); + } + else + { + int subTree = tree[revcode & 511]; + int treeLen = 1 << (subTree & 15); + subTree = -(subTree >> 4); + do + { + tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits); + revcode += 1 << bits; + } + while (revcode < treeLen); + } + nextCode[bits] = code + (1 << (16 - bits)); + } + } + + /** + * Reads the next symbol from input. The symbol is encoded using the + * huffman tree. + * @param input the input source. + * @return the next symbol, or -1 if not enough input is available. + */ + int getSymbol(StreamManipulator input) throws DataFormatException + { + int lookahead, symbol; + if ((lookahead = input.peekBits(9)) >= 0) + { + if ((symbol = tree[lookahead]) >= 0) + { + input.dropBits(symbol & 15); + return symbol >> 4; + } + int subtree = -(symbol >> 4); + int bitlen = symbol & 15; + if ((lookahead = input.peekBits(bitlen)) >= 0) + { + symbol = tree[subtree | (lookahead >> 9)]; + input.dropBits(symbol & 15); + return symbol >> 4; + } + else + { + int bits = input.getAvailableBits(); + lookahead = input.peekBits(bits); + symbol = tree[subtree | (lookahead >> 9)]; + if ((symbol & 15) <= bits) + { + input.dropBits(symbol & 15); + return symbol >> 4; + } + else + return -1; + } + } + else + { + int bits = input.getAvailableBits(); + lookahead = input.peekBits(bits); + symbol = tree[lookahead]; + if (symbol >= 0 && (symbol & 15) <= bits) + { + input.dropBits(symbol & 15); + return symbol >> 4; + } + else + return -1; + } + } +} diff --git a/libjava/classpath/java/util/zip/InflaterInputStream.java b/libjava/classpath/java/util/zip/InflaterInputStream.java new file mode 100644 index 000000000..1c5e9f2dd --- /dev/null +++ b/libjava/classpath/java/util/zip/InflaterInputStream.java @@ -0,0 +1,265 @@ +/* InflaterInputStream.java - Input stream filter for decompressing + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * This filter stream is used to decompress data compressed in the "deflate" + * format. The "deflate" format is described in RFC 1951. + * + * This stream may form the basis for other decompression filters, such + * as the GZIPInputStream. + * + * @author John Leuner + * @author Tom Tromey + * @since 1.1 + */ +public class InflaterInputStream extends FilterInputStream +{ + /** + * Decompressor for this filter + */ + protected Inflater inf; + + /** + * Byte array used as a buffer + */ + protected byte[] buf; + + /** + * Size of buffer + */ + protected int len; + + // We just use this if we are decoding one byte at a time with the + // read() call. + private byte[] onebytebuffer = new byte[1]; + + /** + * Create an InflaterInputStream with the default decompresseor + * and a default buffer size. + * + * @param in the InputStream to read bytes from + */ + public InflaterInputStream(InputStream in) + { + this(in, new Inflater(), 4096); + } + + /** + * Create an InflaterInputStream with the specified decompresseor + * and a default buffer size. + * + * @param in the InputStream to read bytes from + * @param inf the decompressor used to decompress data read from in + */ + public InflaterInputStream(InputStream in, Inflater inf) + { + this(in, inf, 4096); + } + + /** + * Create an InflaterInputStream with the specified decompresseor + * and a specified buffer size. + * + * @param in the InputStream to read bytes from + * @param inf the decompressor used to decompress data read from in + * @param size size of the buffer to use + */ + public InflaterInputStream(InputStream in, Inflater inf, int size) + { + super(in); + + if (in == null) + throw new NullPointerException("in may not be null"); + if (inf == null) + throw new NullPointerException("inf may not be null"); + if (size < 0) + throw new IllegalArgumentException("size may not be negative"); + + this.inf = inf; + this.buf = new byte [size]; + } + + /** + * Returns 0 once the end of the stream (EOF) has been reached. + * Otherwise returns 1. + */ + public int available() throws IOException + { + // According to the JDK 1.2 docs, this should only ever return 0 + // or 1 and should not be relied upon by Java programs. + if (inf == null) + throw new IOException("stream closed"); + return inf.finished() ? 0 : 1; + } + + /** + * Closes the input stream + */ + public synchronized void close() throws IOException + { + if (in != null) + in.close(); + in = null; + } + + /** + * Fills the buffer with more data to decompress. + */ + protected void fill() throws IOException + { + if (in == null) + throw new ZipException ("InflaterInputStream is closed"); + + len = in.read(buf, 0, buf.length); + + if (len < 0) + throw new ZipException("Deflated stream ends early."); + + inf.setInput(buf, 0, len); + } + + /** + * Reads one byte of decompressed data. + * + * The byte is in the lower 8 bits of the int. + */ + public int read() throws IOException + { + int nread = read(onebytebuffer, 0, 1); + if (nread > 0) + return onebytebuffer[0] & 0xff; + return -1; + } + + /** + * Decompresses data into the byte array + * + * @param b the array to read and decompress data into + * @param off the offset indicating where the data should be placed + * @param len the number of bytes to decompress + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (inf == null) + throw new IOException("stream closed"); + if (len == 0) + return 0; + if (inf.finished()) + return -1; + + int count = 0; + while (count == 0) + { + if (inf.needsInput()) + fill(); + + try + { + count = inf.inflate(b, off, len); + if (count == 0) + { + if (this.len == -1) + { + // Couldn't get any more data to feed to the Inflater + return -1; + } + if (inf.needsDictionary()) + throw new ZipException("Inflater needs Dictionary"); + } + } + catch (DataFormatException dfe) + { + throw new ZipException(dfe.getMessage()); + } + } + return count; + } + + /** + * Skip specified number of bytes of uncompressed data + * + * @param n number of bytes to skip + */ + public long skip(long n) throws IOException + { + if (inf == null) + throw new IOException("stream closed"); + if (n < 0) + throw new IllegalArgumentException(); + + if (n == 0) + return 0; + + int buflen = (int) Math.min(n, 2048); + byte[] tmpbuf = new byte[buflen]; + + long skipped = 0L; + while (n > 0L) + { + int numread = read(tmpbuf, 0, buflen); + if (numread <= 0) + break; + n -= numread; + skipped += numread; + buflen = (int) Math.min(n, 2048); + } + + return skipped; + } + + public boolean markSupported() + { + return false; + } + + public void mark(int readLimit) + { + } + + public void reset() throws IOException + { + throw new IOException("reset not supported"); + } +} diff --git a/libjava/classpath/java/util/zip/OutputWindow.java b/libjava/classpath/java/util/zip/OutputWindow.java new file mode 100644 index 000000000..59dadb5f3 --- /dev/null +++ b/libjava/classpath/java/util/zip/OutputWindow.java @@ -0,0 +1,175 @@ +/* OutputWindow.java -- + Copyright (C) 2001, 2004 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * Contains the output from the Inflation process. + * + * We need to have a window so that we can refer backwards into the output stream + * to repeat stuff. + * + * @author John Leuner + * @since 1.1 + */ +class OutputWindow +{ + private static final int WINDOW_SIZE = 1 << 15; + private static final int WINDOW_MASK = WINDOW_SIZE - 1; + + private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes + private int window_end = 0; + private int window_filled = 0; + + public void write(int abyte) + { + if (window_filled++ == WINDOW_SIZE) + throw new IllegalStateException("Window full"); + window[window_end++] = (byte) abyte; + window_end &= WINDOW_MASK; + } + + private void slowRepeat(int rep_start, int len, int dist) + { + while (len-- > 0) + { + window[window_end++] = window[rep_start++]; + window_end &= WINDOW_MASK; + rep_start &= WINDOW_MASK; + } + } + + public void repeat(int len, int dist) + { + if ((window_filled += len) > WINDOW_SIZE) + throw new IllegalStateException("Window full"); + + int rep_start = (window_end - dist) & WINDOW_MASK; + int border = WINDOW_SIZE - len; + if (rep_start <= border && window_end < border) + { + if (len <= dist) + { + System.arraycopy(window, rep_start, window, window_end, len); + window_end += len; + } + else + { + /* We have to copy manually, since the repeat pattern overlaps. + */ + while (len-- > 0) + window[window_end++] = window[rep_start++]; + } + } + else + slowRepeat(rep_start, len, dist); + } + + public int copyStored(StreamManipulator input, int len) + { + len = Math.min(Math.min(len, WINDOW_SIZE - window_filled), + input.getAvailableBytes()); + int copied; + + int tailLen = WINDOW_SIZE - window_end; + if (len > tailLen) + { + copied = input.copyBytes(window, window_end, tailLen); + if (copied == tailLen) + copied += input.copyBytes(window, 0, len - tailLen); + } + else + copied = input.copyBytes(window, window_end, len); + + window_end = (window_end + copied) & WINDOW_MASK; + window_filled += copied; + return copied; + } + + public void copyDict(byte[] dict, int offset, int len) + { + if (window_filled > 0) + throw new IllegalStateException(); + + if (len > WINDOW_SIZE) + { + offset += len - WINDOW_SIZE; + len = WINDOW_SIZE; + } + System.arraycopy(dict, offset, window, 0, len); + window_end = len & WINDOW_MASK; + } + + public int getFreeSpace() + { + return WINDOW_SIZE - window_filled; + } + + public int getAvailable() + { + return window_filled; + } + + public int copyOutput(byte[] output, int offset, int len) + { + int copy_end = window_end; + if (len > window_filled) + len = window_filled; + else + copy_end = (window_end - window_filled + len) & WINDOW_MASK; + + int copied = len; + int tailLen = len - copy_end; + + if (tailLen > 0) + { + System.arraycopy(window, WINDOW_SIZE - tailLen, + output, offset, tailLen); + offset += tailLen; + len = copy_end; + } + System.arraycopy(window, copy_end - len, output, offset, len); + window_filled -= copied; + if (window_filled < 0) + throw new IllegalStateException(); + return copied; + } + + public void reset() { + window_filled = window_end = 0; + } +} diff --git a/libjava/classpath/java/util/zip/PendingBuffer.java b/libjava/classpath/java/util/zip/PendingBuffer.java new file mode 100644 index 000000000..50f561f49 --- /dev/null +++ b/libjava/classpath/java/util/zip/PendingBuffer.java @@ -0,0 +1,199 @@ +/* java.util.zip.PendingBuffer + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This class is general purpose class for writing data to a buffer. + * + * It allows you to write bits as well as bytes + * + * Based on DeflaterPending.java + * + * @author Jochen Hoenicke + * @date Jan 5, 2000 + */ + +class PendingBuffer +{ + protected byte[] buf; + int start; + int end; + + int bits; + int bitCount; + + public PendingBuffer() + { + this( 4096 ); + } + + public PendingBuffer(int bufsize) + { + buf = new byte[bufsize]; + } + + public final void reset() { + start = end = bitCount = 0; + } + + public final void writeByte(int b) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + buf[end++] = (byte) b; + } + + public final void writeShort(int s) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + buf[end++] = (byte) s; + buf[end++] = (byte) (s >> 8); + } + + public final void writeInt(int s) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + buf[end++] = (byte) s; + buf[end++] = (byte) (s >> 8); + buf[end++] = (byte) (s >> 16); + buf[end++] = (byte) (s >> 24); + } + + public final void writeBlock(byte[] block, int offset, int len) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + System.arraycopy(block, offset, buf, end, len); + end += len; + } + + public final int getBitCount() { + return bitCount; + } + + public final void alignToByte() { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + if (bitCount > 0) + { + buf[end++] = (byte) bits; + if (bitCount > 8) + buf[end++] = (byte) (bits >>> 8); + } + bits = 0; + bitCount = 0; + } + + public final void writeBits(int b, int count) + { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + if (DeflaterConstants.DEBUGGING) + System.err.println("writeBits("+Integer.toHexString(b)+","+count+")"); + bits |= b << bitCount; + bitCount += count; + if (bitCount >= 16) { + buf[end++] = (byte) bits; + buf[end++] = (byte) (bits >>> 8); + bits >>>= 16; + bitCount -= 16; + } + } + + public final void writeShortMSB(int s) { + if (DeflaterConstants.DEBUGGING && start != 0) + throw new IllegalStateException(); + buf[end++] = (byte) (s >> 8); + buf[end++] = (byte) s; + } + + public final boolean isFlushed() { + return end == 0; + } + + /** + * Flushes the pending buffer into the given output array. If the + * output array is to small, only a partial flush is done. + * + * @param output the output array; + * @param offset the offset into output array; + * @param length the maximum number of bytes to store; + * @exception IndexOutOfBoundsException if offset or length are + * invalid. + */ + public final int flush(byte[] output, int offset, int length) { + if (bitCount >= 8) + { + buf[end++] = (byte) bits; + bits >>>= 8; + bitCount -= 8; + } + if (length > end - start) + { + length = end - start; + System.arraycopy(buf, start, output, offset, length); + start = 0; + end = 0; + } + else + { + System.arraycopy(buf, start, output, offset, length); + start += length; + } + return length; + } + + /** + * Flushes the pending buffer and returns that data in a new array + * + * @return the output stream + */ + + public final byte[] toByteArray() + { + byte[] ret = new byte[ end - start ]; + System.arraycopy(buf, start, ret, 0, ret.length); + start = 0; + end = 0; + return ret; + } + + +} diff --git a/libjava/classpath/java/util/zip/StreamManipulator.java b/libjava/classpath/java/util/zip/StreamManipulator.java new file mode 100644 index 000000000..105d807e4 --- /dev/null +++ b/libjava/classpath/java/util/zip/StreamManipulator.java @@ -0,0 +1,215 @@ +/* java.util.zip.StreamManipulator + Copyright (C) 2001 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +/** + * This class allows us to retrieve a specified amount of bits from + * the input buffer, as well as copy big byte blocks. + * + * It uses an int buffer to store up to 31 bits for direct + * manipulation. This guarantees that we can get at least 16 bits, + * but we only need at most 15, so this is all safe. + * + * There are some optimizations in this class, for example, you must + * never peek more then 8 bits more than needed, and you must first + * peek bits before you may drop them. This is not a general purpose + * class but optimized for the behaviour of the Inflater. + * + * @author John Leuner, Jochen Hoenicke + */ + +class StreamManipulator +{ + private byte[] window; + private int window_start = 0; + private int window_end = 0; + + private int buffer = 0; + private int bits_in_buffer = 0; + + /** + * Get the next n bits but don't increase input pointer. n must be + * less or equal 16 and if you if this call succeeds, you must drop + * at least n-8 bits in the next call. + * + * @return the value of the bits, or -1 if not enough bits available. */ + public final int peekBits(int n) + { + if (bits_in_buffer < n) + { + if (window_start == window_end) + return -1; + buffer |= (window[window_start++] & 0xff + | (window[window_start++] & 0xff) << 8) << bits_in_buffer; + bits_in_buffer += 16; + } + return buffer & ((1 << n) - 1); + } + + /* Drops the next n bits from the input. You should have called peekBits + * with a bigger or equal n before, to make sure that enough bits are in + * the bit buffer. + */ + public final void dropBits(int n) + { + buffer >>>= n; + bits_in_buffer -= n; + } + + /** + * Gets the next n bits and increases input pointer. This is equivalent + * to peekBits followed by dropBits, except for correct error handling. + * @return the value of the bits, or -1 if not enough bits available. + */ + public final int getBits(int n) + { + int bits = peekBits(n); + if (bits >= 0) + dropBits(n); + return bits; + } + /** + * Gets the number of bits available in the bit buffer. This must be + * only called when a previous peekBits() returned -1. + * @return the number of bits available. + */ + public final int getAvailableBits() + { + return bits_in_buffer; + } + + /** + * Gets the number of bytes available. + * @return the number of bytes available. + */ + public final int getAvailableBytes() + { + return window_end - window_start + (bits_in_buffer >> 3); + } + + /** + * Skips to the next byte boundary. + */ + public void skipToByteBoundary() + { + buffer >>= (bits_in_buffer & 7); + bits_in_buffer &= ~7; + } + + public final boolean needsInput() { + return window_start == window_end; + } + + + /* Copies length bytes from input buffer to output buffer starting + * at output[offset]. You have to make sure, that the buffer is + * byte aligned. If not enough bytes are available, copies fewer + * bytes. + * @param length the length to copy, 0 is allowed. + * @return the number of bytes copied, 0 if no byte is available. + */ + public int copyBytes(byte[] output, int offset, int length) + { + if (length < 0) + throw new IllegalArgumentException("length negative"); + if ((bits_in_buffer & 7) != 0) + /* bits_in_buffer may only be 0 or 8 */ + throw new IllegalStateException("Bit buffer is not aligned!"); + + int count = 0; + while (bits_in_buffer > 0 && length > 0) + { + output[offset++] = (byte) buffer; + buffer >>>= 8; + bits_in_buffer -= 8; + length--; + count++; + } + if (length == 0) + return count; + + int avail = window_end - window_start; + if (length > avail) + length = avail; + System.arraycopy(window, window_start, output, offset, length); + window_start += length; + + if (((window_start - window_end) & 1) != 0) + { + /* We always want an even number of bytes in input, see peekBits */ + buffer = (window[window_start++] & 0xff); + bits_in_buffer = 8; + } + return count + length; + } + + public StreamManipulator() + { + } + + public void reset() + { + window_start = window_end = buffer = bits_in_buffer = 0; + } + + public void setInput(byte[] buf, int off, int len) + { + if (window_start < window_end) + throw new IllegalStateException + ("Old input was not completely processed"); + + int end = off + len; + + /* We want to throw an ArrayIndexOutOfBoundsException early. The + * check is very tricky: it also handles integer wrap around. + */ + if (0 > off || off > end || end > buf.length) + throw new ArrayIndexOutOfBoundsException(); + + if ((len & 1) != 0) + { + /* We always want an even number of bytes in input, see peekBits */ + buffer |= (buf[off++] & 0xff) << bits_in_buffer; + bits_in_buffer += 8; + } + + window = buf; + window_start = off; + window_end = end; + } +} diff --git a/libjava/classpath/java/util/zip/ZipConstants.java b/libjava/classpath/java/util/zip/ZipConstants.java new file mode 100644 index 000000000..69f589a31 --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipConstants.java @@ -0,0 +1,93 @@ +/* java.util.zip.ZipConstants + Copyright (C) 2001, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +interface ZipConstants +{ + /* The local file header */ + int LOCHDR = 30; + long LOCSIG = 'P'|('K'<<8)|(3<<16)|(4<<24); + + int LOCVER = 4; + int LOCFLG = 6; + int LOCHOW = 8; + int LOCTIM = 10; + int LOCCRC = 14; + int LOCSIZ = 18; + int LOCLEN = 22; + int LOCNAM = 26; + int LOCEXT = 28; + + /* The Data descriptor */ + long EXTSIG = 'P'|('K'<<8)|(7<<16)|(8<<24); + int EXTHDR = 16; + + int EXTCRC = 4; + int EXTSIZ = 8; + int EXTLEN = 12; + + /* The central directory file header */ + long CENSIG = 'P'|('K'<<8)|(1<<16)|(2<<24); + int CENHDR = 46; + + int CENVEM = 4; + int CENVER = 6; + int CENFLG = 8; + int CENHOW = 10; + int CENTIM = 12; + int CENCRC = 16; + int CENSIZ = 20; + int CENLEN = 24; + int CENNAM = 28; + int CENEXT = 30; + int CENCOM = 32; + int CENDSK = 34; + int CENATT = 36; + int CENATX = 38; + int CENOFF = 42; + + /* The entries in the end of central directory */ + long ENDSIG = 'P'|('K'<<8)|(5<<16)|(6<<24); + int ENDHDR = 22; + + int ENDSUB = 8; + int ENDTOT = 10; + int ENDSIZ = 12; + int ENDOFF = 16; + int ENDCOM = 20; +} diff --git a/libjava/classpath/java/util/zip/ZipEntry.java b/libjava/classpath/java/util/zip/ZipEntry.java new file mode 100644 index 000000000..73afc893b --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipEntry.java @@ -0,0 +1,457 @@ +/* ZipEntry.java -- + Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.util.Calendar; + +/** + * This class represents a member of a zip archive. ZipFile and + * ZipInputStream will give you instances of this class as information + * about the members in an archive. On the other hand ZipOutputStream + * needs an instance of this class to create a new member. + * + * @author Jochen Hoenicke + */ +public class ZipEntry implements ZipConstants, Cloneable +{ + private static final byte KNOWN_SIZE = 1; + private static final byte KNOWN_CSIZE = 2; + private static final byte KNOWN_CRC = 4; + private static final byte KNOWN_TIME = 8; + private static final byte KNOWN_DOSTIME = 16; + private static final byte KNOWN_EXTRA = 32; + + /** Immutable name of the entry */ + private final String name; + /** Uncompressed size */ + private int size; + /** Compressed size */ + private long compressedSize = -1; + /** CRC of uncompressed data */ + private int crc; + /** Comment or null if none */ + private String comment = null; + /** The compression method. Either DEFLATED or STORED, by default -1. */ + private byte method = -1; + /** Flags specifying what we know about this entry */ + private byte known = 0; + /** + * The 32bit DOS encoded format for the time of this entry. Only valid if + * KNOWN_DOSTIME is set in known. + */ + private int dostime; + /** + * The 64bit Java encoded millisecond time since the beginning of the epoch. + * Only valid if KNOWN_TIME is set in known. + */ + private long time; + /** Extra data */ + private byte[] extra = null; + + int flags; /* used by ZipOutputStream */ + int offset; /* used by ZipFile and ZipOutputStream */ + + /** + * Compression method. This method doesn't compress at all. + */ + public static final int STORED = 0; + /** + * Compression method. This method uses the Deflater. + */ + public static final int DEFLATED = 8; + + /** + * Creates a zip entry with the given name. + * @param name the name. May include directory components separated + * by '/'. + * + * @exception NullPointerException when name is null. + * @exception IllegalArgumentException when name is bigger then 65535 chars. + */ + public ZipEntry(String name) + { + int length = name.length(); + if (length > 65535) + throw new IllegalArgumentException("name length is " + length); + this.name = name; + } + + /** + * Creates a copy of the given zip entry. + * @param e the entry to copy. + */ + public ZipEntry(ZipEntry e) + { + this(e, e.name); + } + + ZipEntry(ZipEntry e, String name) + { + this.name = name; + known = e.known; + size = e.size; + compressedSize = e.compressedSize; + crc = e.crc; + dostime = e.dostime; + time = e.time; + method = e.method; + extra = e.extra; + comment = e.comment; + } + + final void setDOSTime(int dostime) + { + this.dostime = dostime; + known |= KNOWN_DOSTIME; + known &= ~KNOWN_TIME; + } + + final int getDOSTime() + { + if ((known & KNOWN_DOSTIME) != 0) + return dostime; + else if ((known & KNOWN_TIME) != 0) + { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25 + | (cal.get(Calendar.MONTH) + 1) << 21 + | (cal.get(Calendar.DAY_OF_MONTH)) << 16 + | (cal.get(Calendar.HOUR_OF_DAY)) << 11 + | (cal.get(Calendar.MINUTE)) << 5 + | (cal.get(Calendar.SECOND)) >> 1; + known |= KNOWN_DOSTIME; + return dostime; + } + else + return 0; + } + + /** + * Creates a copy of this zip entry. + */ + public Object clone() + { + // JCL defines this as being the same as the copy constructor above, + // except that value of the "extra" field is also copied. Take care + // that in the case of a subclass we use clone() rather than the copy + // constructor. + ZipEntry clone; + if (this.getClass() == ZipEntry.class) + clone = new ZipEntry(this); + else + { + try + { + clone = (ZipEntry) super.clone(); + } + catch (CloneNotSupportedException e) + { + throw new InternalError(); + } + } + if (extra != null) + { + clone.extra = new byte[extra.length]; + System.arraycopy(extra, 0, clone.extra, 0, extra.length); + } + return clone; + } + + /** + * Returns the entry name. The path components in the entry are + * always separated by slashes ('/'). + */ + public String getName() + { + return name; + } + + /** + * Sets the time of last modification of the entry. + * @time the time of last modification of the entry. + */ + public void setTime(long time) + { + this.time = time; + this.known |= KNOWN_TIME; + this.known &= ~KNOWN_DOSTIME; + } + + /** + * Gets the time of last modification of the entry. + * @return the time of last modification of the entry, or -1 if unknown. + */ + public long getTime() + { + // The extra bytes might contain the time (posix/unix extension) + parseExtra(); + + if ((known & KNOWN_TIME) != 0) + return time; + else if ((known & KNOWN_DOSTIME) != 0) + { + int sec = 2 * (dostime & 0x1f); + int min = (dostime >> 5) & 0x3f; + int hrs = (dostime >> 11) & 0x1f; + int day = (dostime >> 16) & 0x1f; + int mon = ((dostime >> 21) & 0xf) - 1; + int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */ + + try + { + Calendar cal = Calendar.getInstance(); + cal.set(year, mon, day, hrs, min, sec); + time = cal.getTimeInMillis(); + known |= KNOWN_TIME; + return time; + } + catch (RuntimeException ex) + { + /* Ignore illegal time stamp */ + known &= ~KNOWN_TIME; + return -1; + } + } + else + return -1; + } + + /** + * Sets the size of the uncompressed data. + * @exception IllegalArgumentException if size is not in 0..0xffffffffL + */ + public void setSize(long size) + { + if ((size & 0xffffffff00000000L) != 0) + throw new IllegalArgumentException(); + this.size = (int) size; + this.known |= KNOWN_SIZE; + } + + /** + * Gets the size of the uncompressed data. + * @return the size or -1 if unknown. + */ + public long getSize() + { + return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L; + } + + /** + * Sets the size of the compressed data. + */ + public void setCompressedSize(long csize) + { + this.compressedSize = csize; + } + + /** + * Gets the size of the compressed data. + * @return the size or -1 if unknown. + */ + public long getCompressedSize() + { + return compressedSize; + } + + /** + * Sets the crc of the uncompressed data. + * @exception IllegalArgumentException if crc is not in 0..0xffffffffL + */ + public void setCrc(long crc) + { + if ((crc & 0xffffffff00000000L) != 0) + throw new IllegalArgumentException(); + this.crc = (int) crc; + this.known |= KNOWN_CRC; + } + + /** + * Gets the crc of the uncompressed data. + * @return the crc or -1 if unknown. + */ + public long getCrc() + { + return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L; + } + + /** + * Sets the compression method. Only DEFLATED and STORED are + * supported. + * @exception IllegalArgumentException if method is not supported. + * @see ZipOutputStream#DEFLATED + * @see ZipOutputStream#STORED + */ + public void setMethod(int method) + { + if (method != ZipOutputStream.STORED + && method != ZipOutputStream.DEFLATED) + throw new IllegalArgumentException(); + this.method = (byte) method; + } + + /** + * Gets the compression method. + * @return the compression method or -1 if unknown. + */ + public int getMethod() + { + return method; + } + + /** + * Sets the extra data. + * @exception IllegalArgumentException if extra is longer than 0xffff bytes. + */ + public void setExtra(byte[] extra) + { + if (extra == null) + { + this.extra = null; + return; + } + if (extra.length > 0xffff) + throw new IllegalArgumentException(); + this.extra = extra; + } + + private void parseExtra() + { + // Already parsed? + if ((known & KNOWN_EXTRA) != 0) + return; + + if (extra == null) + { + known |= KNOWN_EXTRA; + return; + } + + try + { + int pos = 0; + while (pos < extra.length) + { + int sig = (extra[pos++] & 0xff) + | (extra[pos++] & 0xff) << 8; + int len = (extra[pos++] & 0xff) + | (extra[pos++] & 0xff) << 8; + if (sig == 0x5455) + { + /* extended time stamp */ + int flags = extra[pos]; + if ((flags & 1) != 0) + { + long time = ((extra[pos+1] & 0xff) + | (extra[pos+2] & 0xff) << 8 + | (extra[pos+3] & 0xff) << 16 + | (extra[pos+4] & 0xff) << 24); + setTime(time*1000); + } + } + pos += len; + } + } + catch (ArrayIndexOutOfBoundsException ex) + { + /* be lenient */ + } + + known |= KNOWN_EXTRA; + return; + } + + /** + * Gets the extra data. + * @return the extra data or null if not set. + */ + public byte[] getExtra() + { + return extra; + } + + /** + * Sets the entry comment. + * @exception IllegalArgumentException if comment is longer than 0xffff. + */ + public void setComment(String comment) + { + if (comment != null && comment.length() > 0xffff) + throw new IllegalArgumentException(); + this.comment = comment; + } + + /** + * Gets the comment. + * @return the comment or null if not set. + */ + public String getComment() + { + return comment; + } + + /** + * Gets true, if the entry is a directory. This is solely + * determined by the name, a trailing slash '/' marks a directory. + */ + public boolean isDirectory() + { + int nlen = name.length(); + return nlen > 0 && name.charAt(nlen - 1) == '/'; + } + + /** + * Gets the string representation of this ZipEntry. This is just + * the name as returned by getName(). + */ + public String toString() + { + return name; + } + + /** + * Gets the hashCode of this ZipEntry. This is just the hashCode + * of the name. Note that the equals method isn't changed, though. + */ + public int hashCode() + { + return name.hashCode(); + } +} diff --git a/libjava/classpath/java/util/zip/ZipException.java b/libjava/classpath/java/util/zip/ZipException.java new file mode 100644 index 000000000..c5bfc1e7c --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipException.java @@ -0,0 +1,72 @@ +/* ZipException.java - exception representing a zip related error + Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.util.zip; + +import java.io.IOException; + +/** + * Thrown during the creation or input of a zip file. + * + * @author Jochen Hoenicke + * @author Per Bothner + * @status updated to 1.4 + */ +public class ZipException extends IOException +{ + /** + * Compatible with JDK 1.0+. + */ + private static final long serialVersionUID = 8000196834066748623L; + + /** + * Create an exception without a message. + */ + public ZipException() + { + } + + /** + * Create an exception with a message. + * + * @param msg the message + */ + public ZipException (String msg) + { + super(msg); + } +} diff --git a/libjava/classpath/java/util/zip/ZipFile.java b/libjava/classpath/java/util/zip/ZipFile.java new file mode 100644 index 000000000..3963bcb1e --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipFile.java @@ -0,0 +1,782 @@ +/* ZipFile.java -- + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import gnu.java.util.EmptyEnumeration; + +import java.io.EOFException; +import java.io.File; +import java.io.FileNotFoundException; +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; + +/** + * This class represents a Zip archive. You can ask for the contained + * entries, or get an input stream for a file entry. The entry is + * automatically decompressed. + * + * This class is thread safe: You can open input streams for arbitrary + * entries in different threads. + * + * @author Jochen Hoenicke + * @author Artur Biesiadowski + */ +public class ZipFile implements ZipConstants +{ + + /** + * Mode flag to open a zip file for reading. + */ + public static final int OPEN_READ = 0x1; + + /** + * Mode flag to delete a zip file after reading. + */ + public static final int OPEN_DELETE = 0x4; + + /** + * This field isn't defined in the JDK's ZipConstants, but should be. + */ + static final int ENDNRD = 4; + + // Name of this zip file. + private final String name; + + // File from which zip entries are read. + private final RandomAccessFile raf; + + // The entries of this zip file when initialized and not yet closed. + private LinkedHashMap entries; + + private boolean closed = false; + + + /** + * Helper function to open RandomAccessFile and throw the proper + * ZipException in case opening the file fails. + * + * @param name the file name, or null if file is provided + * + * @param file the file, or null if name is provided + * + * @return the newly open RandomAccessFile, never null + */ + private RandomAccessFile openFile(String name, + File file) + throws ZipException, IOException + { + try + { + return + (name != null) + ? new RandomAccessFile(name, "r") + : new RandomAccessFile(file, "r"); + } + catch (FileNotFoundException f) + { + ZipException ze = new ZipException(f.getMessage()); + ze.initCause(f); + throw ze; + } + } + + + /** + * Opens a Zip file with the given name for reading. + * @exception IOException if a i/o error occured. + * @exception ZipException if the file doesn't contain a valid zip + * archive. + */ + public ZipFile(String name) throws ZipException, IOException + { + this.raf = openFile(name,null); + this.name = name; + checkZipFile(); + } + + /** + * Opens a Zip file reading the given File. + * @exception IOException if a i/o error occured. + * @exception ZipException if the file doesn't contain a valid zip + * archive. + */ + public ZipFile(File file) throws ZipException, IOException + { + this.raf = openFile(null,file); + this.name = file.getPath(); + checkZipFile(); + } + + /** + * Opens a Zip file reading the given File in the given mode. + * + * If the OPEN_DELETE mode is specified, the zip file will be deleted at + * some time moment after it is opened. It will be deleted before the zip + * file is closed or the Virtual Machine exits. + * + * The contents of the zip file will be accessible until it is closed. + * + * @since JDK1.3 + * @param mode Must be one of OPEN_READ or OPEN_READ | OPEN_DELETE + * + * @exception IOException if a i/o error occured. + * @exception ZipException if the file doesn't contain a valid zip + * archive. + */ + public ZipFile(File file, int mode) throws ZipException, IOException + { + if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE)) + throw new IllegalArgumentException("invalid mode"); + if ((mode & OPEN_DELETE) != 0) + file.deleteOnExit(); + this.raf = openFile(null,file); + this.name = file.getPath(); + checkZipFile(); + } + + private void checkZipFile() throws ZipException + { + boolean valid = false; + + try + { + byte[] buf = new byte[4]; + raf.readFully(buf); + int sig = buf[0] & 0xFF + | ((buf[1] & 0xFF) << 8) + | ((buf[2] & 0xFF) << 16) + | ((buf[3] & 0xFF) << 24); + valid = sig == LOCSIG; + } + catch (IOException _) + { + } + + if (!valid) + { + try + { + raf.close(); + } + catch (IOException _) + { + } + throw new ZipException("Not a valid zip file"); + } + } + + /** + * Checks if file is closed and throws an exception. + */ + private void checkClosed() + { + if (closed) + throw new IllegalStateException("ZipFile has closed: " + name); + } + + /** + * Read the central directory of a zip file and fill the entries + * array. This is called exactly once when first needed. It is called + * while holding the lock on raf. + * + * @exception IOException if a i/o error occured. + * @exception ZipException if the central directory is malformed + */ + private void readEntries() throws ZipException, IOException + { + /* Search for the End Of Central Directory. When a zip comment is + * present the directory may start earlier. + * Note that a comment has a maximum length of 64K, so that is the + * maximum we search backwards. + */ + PartialInputStream inp = new PartialInputStream(raf, 4096); + long pos = raf.length() - ENDHDR; + long top = Math.max(0, pos - 65536); + do + { + if (pos < top) + throw new ZipException + ("central directory not found, probably not a zip file: " + name); + inp.seek(pos--); + } + while (inp.readLeInt() != ENDSIG); + + if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD) + throw new EOFException(name); + int count = inp.readLeShort(); + if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ) + throw new EOFException(name); + int centralOffset = inp.readLeInt(); + + entries = new LinkedHashMap (count+count/2); + inp.seek(centralOffset); + + for (int i = 0; i < count; i++) + { + if (inp.readLeInt() != CENSIG) + throw new ZipException("Wrong Central Directory signature: " + name); + + inp.skip(6); + int method = inp.readLeShort(); + int dostime = inp.readLeInt(); + int crc = inp.readLeInt(); + int csize = inp.readLeInt(); + int size = inp.readLeInt(); + int nameLen = inp.readLeShort(); + int extraLen = inp.readLeShort(); + int commentLen = inp.readLeShort(); + inp.skip(8); + int offset = inp.readLeInt(); + String name = inp.readString(nameLen); + + ZipEntry entry = new ZipEntry(name); + entry.setMethod(method); + entry.setCrc(crc & 0xffffffffL); + entry.setSize(size & 0xffffffffL); + entry.setCompressedSize(csize & 0xffffffffL); + entry.setDOSTime(dostime); + if (extraLen > 0) + { + byte[] extra = new byte[extraLen]; + inp.readFully(extra); + entry.setExtra(extra); + } + if (commentLen > 0) + { + entry.setComment(inp.readString(commentLen)); + } + entry.offset = offset; + entries.put(name, entry); + } + } + + /** + * Closes the ZipFile. This also closes all input streams given by + * this class. After this is called, no further method should be + * called. + * + * @exception IOException if a i/o error occured. + */ + public void close() throws IOException + { + RandomAccessFile raf = this.raf; + if (raf == null) + return; + + synchronized (raf) + { + closed = true; + entries = null; + raf.close(); + } + } + + /** + * Calls the close() method when this ZipFile has not yet + * been explicitly closed. + */ + protected void finalize() throws IOException + { + if (!closed && raf != null) close(); + } + + /** + * Returns an enumeration of all Zip entries in this Zip file. + * + * @exception IllegalStateException when the ZipFile has already been closed + */ + public Enumeration entries() + { + checkClosed(); + + try + { + return new ZipEntryEnumeration(getEntries().values().iterator()); + } + catch (IOException ioe) + { + return new EmptyEnumeration(); + } + } + + /** + * Checks that the ZipFile is still open and reads entries when necessary. + * + * @exception IllegalStateException when the ZipFile has already been closed. + * @exception IOException when the entries could not be read. + */ + private LinkedHashMap getEntries() throws IOException + { + synchronized(raf) + { + checkClosed(); + + if (entries == null) + readEntries(); + + return entries; + } + } + + /** + * Searches for a zip entry in this archive with the given name. + * + * @param name the name. May contain directory components separated by + * slashes ('/'). + * @return the zip entry, or null if no entry with that name exists. + * + * @exception IllegalStateException when the ZipFile has already been closed + */ + public ZipEntry getEntry(String name) + { + checkClosed(); + + try + { + LinkedHashMap entries = getEntries(); + ZipEntry entry = entries.get(name); + // If we didn't find it, maybe it's a directory. + if (entry == null && !name.endsWith("/")) + entry = entries.get(name + '/'); + return entry != null ? new ZipEntry(entry, name) : null; + } + catch (IOException ioe) + { + return null; + } + } + + /** + * Creates an input stream reading the given zip entry as + * uncompressed data. Normally zip entry should be an entry + * returned by getEntry() or entries(). + * + * This implementation returns null if the requested entry does not + * exist. This decision is not obviously correct, however, it does + * appear to mirror Sun's implementation, and it is consistant with + * their javadoc. On the other hand, the old JCL book, 2nd Edition, + * claims that this should return a "non-null ZIP entry". We have + * chosen for now ignore the old book, as modern versions of Ant (an + * important application) depend on this behaviour. See discussion + * in this thread: + * http://gcc.gnu.org/ml/java-patches/2004-q2/msg00602.html + * + * @param entry the entry to create an InputStream for. + * @return the input stream, or null if the requested entry does not exist. + * + * @exception IllegalStateException when the ZipFile has already been closed + * @exception IOException if a i/o error occured. + * @exception ZipException if the Zip archive is malformed. + */ + public InputStream getInputStream(ZipEntry entry) throws IOException + { + checkClosed(); + + LinkedHashMap entries = getEntries(); + String name = entry.getName(); + ZipEntry zipEntry = entries.get(name); + if (zipEntry == null) + return null; + + PartialInputStream inp = new PartialInputStream(raf, 1024); + inp.seek(zipEntry.offset); + + if (inp.readLeInt() != LOCSIG) + throw new ZipException("Wrong Local header signature: " + name); + + inp.skip(4); + + if (zipEntry.getMethod() != inp.readLeShort()) + throw new ZipException("Compression method mismatch: " + name); + + inp.skip(16); + + int nameLen = inp.readLeShort(); + int extraLen = inp.readLeShort(); + inp.skip(nameLen + extraLen); + + inp.setLength(zipEntry.getCompressedSize()); + + int method = zipEntry.getMethod(); + switch (method) + { + case ZipOutputStream.STORED: + return inp; + case ZipOutputStream.DEFLATED: + inp.addDummyByte(); + final Inflater inf = new Inflater(true); + final int sz = (int) entry.getSize(); + return new InflaterInputStream(inp, inf) + { + public int available() throws IOException + { + if (sz == -1) + return super.available(); + if (super.available() != 0) + return sz - inf.getTotalOut(); + return 0; + } + }; + default: + throw new ZipException("Unknown compression method " + method); + } + } + + /** + * Returns the (path) name of this zip file. + */ + public String getName() + { + return name; + } + + /** + * Returns the number of entries in this zip file. + * + * @exception IllegalStateException when the ZipFile has already been closed + */ + public int size() + { + checkClosed(); + + try + { + return getEntries().size(); + } + catch (IOException ioe) + { + return 0; + } + } + + private static class ZipEntryEnumeration implements Enumeration + { + private final Iterator elements; + + public ZipEntryEnumeration(Iterator elements) + { + this.elements = elements; + } + + public boolean hasMoreElements() + { + return elements.hasNext(); + } + + public ZipEntry nextElement() + { + /* We return a clone, just to be safe that the user doesn't + * change the entry. + */ + return (ZipEntry) (elements.next().clone()); + } + } + + 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; + private int pos; + private long end; + // We may need to supply an extra dummy byte to our reader. + // See Inflater. We use a count here to simplify the logic + // elsewhere in this class. Note that we ignore the dummy + // byte in methods where we know it is not needed. + private int dummyByteCount; + + public PartialInputStream(RandomAccessFile raf, int bufferSize) + throws IOException + { + this.raf = raf; + buffer = new byte[bufferSize]; + bufferOffset = -buffer.length; + pos = buffer.length; + end = raf.length(); + } + + void setLength(long length) + { + end = bufferOffset + pos + length; + } + + private void fillBuffer() throws IOException + { + synchronized (raf) + { + long len = end - bufferOffset; + if (len == 0 && dummyByteCount > 0) + { + buffer[0] = 0; + dummyByteCount = 0; + } + else + { + raf.seek(bufferOffset); + raf.readFully(buffer, 0, (int) Math.min(buffer.length, len)); + } + } + } + + public int available() + { + long amount = end - (bufferOffset + pos); + if (amount > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + return (int) amount; + } + + public int read() throws IOException + { + if (bufferOffset + pos >= end + dummyByteCount) + return -1; + if (pos == buffer.length) + { + bufferOffset += buffer.length; + pos = 0; + fillBuffer(); + } + + return buffer[pos++] & 0xFF; + } + + public int read(byte[] b, int off, int len) throws IOException + { + if (len > end + dummyByteCount - (bufferOffset + pos)) + { + len = (int) (end + dummyByteCount - (bufferOffset + pos)); + if (len == 0) + return -1; + } + + int totalBytesRead = Math.min(buffer.length - pos, len); + System.arraycopy(buffer, pos, b, off, totalBytesRead); + pos += totalBytesRead; + off += totalBytesRead; + len -= totalBytesRead; + + while (len > 0) + { + bufferOffset += buffer.length; + pos = 0; + fillBuffer(); + int remain = Math.min(buffer.length, len); + System.arraycopy(buffer, pos, b, off, remain); + pos += remain; + off += remain; + len -= remain; + totalBytesRead += remain; + } + + return totalBytesRead; + } + + public long skip(long amount) throws IOException + { + if (amount < 0) + return 0; + if (amount > end - (bufferOffset + pos)) + amount = end - (bufferOffset + pos); + seek(bufferOffset + pos + amount); + return amount; + } + + void seek(long newpos) throws IOException + { + long offset = newpos - bufferOffset; + if (offset >= 0 && offset <= buffer.length) + { + pos = (int) offset; + } + else + { + bufferOffset = newpos; + pos = 0; + fillBuffer(); + } + } + + void readFully(byte[] buf) throws IOException + { + if (read(buf, 0, buf.length) != buf.length) + throw new EOFException(); + } + + void readFully(byte[] buf, int off, int len) throws IOException + { + if (read(buf, off, len) != len) + throw new EOFException(); + } + + int readLeShort() throws IOException + { + 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 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 + { + if (length > end - (bufferOffset + pos)) + throw new EOFException(); + + String result = null; + try + { + if (buffer.length - pos >= length) + { + result = decodeChars(buffer, pos, length); + pos += length; + } + else + { + byte[] b = new byte[length]; + readFully(b); + result = decodeChars(b, 0, length); + } + } + catch (UnsupportedEncodingException uee) + { + throw new AssertionError(uee); + } + return result; + } + + public void addDummyByte() + { + dummyByteCount = 1; + } + } +} diff --git a/libjava/classpath/java/util/zip/ZipInputStream.java b/libjava/classpath/java/util/zip/ZipInputStream.java new file mode 100644 index 000000000..3eae026da --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipInputStream.java @@ -0,0 +1,381 @@ +/* ZipInputStream.java -- + Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +/** + * This is a FilterInputStream that reads the files in an zip archive + * one after another. It has a special method to get the zip entry of + * the next file. The zip entry contains information about the file name + * size, compressed size, CRC, etc. + * + * It includes support for STORED and DEFLATED entries. + * + * @author Jochen Hoenicke + */ +public class ZipInputStream extends InflaterInputStream implements ZipConstants +{ + private CRC32 crc = new CRC32(); + private ZipEntry entry = null; + + private int csize; + private int size; + private int method; + private int flags; + private int avail; + private boolean entryAtEOF; + + /** + * Creates a new Zip input stream, reading a zip archive. + */ + public ZipInputStream(InputStream in) + { + super(in, new Inflater(true)); + } + + private void fillBuf() throws IOException + { + avail = len = in.read(buf, 0, buf.length); + } + + private int readBuf(byte[] out, int offset, int length) throws IOException + { + if (avail <= 0) + { + fillBuf(); + if (avail <= 0) + return -1; + } + if (length > avail) + length = avail; + System.arraycopy(buf, len - avail, out, offset, length); + avail -= length; + return length; + } + + private void readFully(byte[] out) throws IOException + { + int off = 0; + int len = out.length; + while (len > 0) + { + int count = readBuf(out, off, len); + if (count == -1) + throw new EOFException(); + off += count; + len -= count; + } + } + + private int readLeByte() throws IOException + { + if (avail <= 0) + { + fillBuf(); + if (avail <= 0) + throw new ZipException("EOF in header"); + } + return buf[len - avail--] & 0xff; + } + + /** + * Read an unsigned short in little endian byte order. + */ + private int readLeShort() throws IOException + { + return readLeByte() | (readLeByte() << 8); + } + + /** + * Read an int in little endian byte order. + */ + private int readLeInt() throws IOException + { + return readLeShort() | (readLeShort() << 16); + } + + /** + * Open the next entry from the zip archive, and return its description. + * If the previous entry wasn't closed, this method will close it. + */ + public ZipEntry getNextEntry() throws IOException + { + if (crc == null) + throw new IOException("Stream closed."); + if (entry != null) + closeEntry(); + + int header = readLeInt(); + if (header == CENSIG) + { + /* Central Header reached. */ + close(); + return null; + } + if (header != LOCSIG) + throw new ZipException("Wrong Local header signature: " + + Integer.toHexString(header)); + /* skip version */ + readLeShort(); + flags = readLeShort(); + method = readLeShort(); + int dostime = readLeInt(); + int crc = readLeInt(); + csize = readLeInt(); + size = readLeInt(); + int nameLen = readLeShort(); + int extraLen = readLeShort(); + + if (method == ZipOutputStream.STORED && csize != size) + throw new ZipException("Stored, but compressed != uncompressed"); + + + byte[] buffer = new byte[nameLen]; + readFully(buffer); + String name; + try + { + name = new String(buffer, "UTF-8"); + } + catch (UnsupportedEncodingException uee) + { + throw new AssertionError(uee); + } + + entry = createZipEntry(name); + entryAtEOF = false; + entry.setMethod(method); + if ((flags & 8) == 0) + { + entry.setCrc(crc & 0xffffffffL); + entry.setSize(size & 0xffffffffL); + entry.setCompressedSize(csize & 0xffffffffL); + } + entry.setDOSTime(dostime); + if (extraLen > 0) + { + byte[] extra = new byte[extraLen]; + readFully(extra); + entry.setExtra(extra); + } + + if (method == ZipOutputStream.DEFLATED && avail > 0) + { + System.arraycopy(buf, len - avail, buf, 0, avail); + len = avail; + avail = 0; + inf.setInput(buf, 0, len); + } + return entry; + } + + private void readDataDescr() throws IOException + { + if (readLeInt() != EXTSIG) + throw new ZipException("Data descriptor signature not found"); + entry.setCrc(readLeInt() & 0xffffffffL); + csize = readLeInt(); + size = readLeInt(); + entry.setSize(size & 0xffffffffL); + entry.setCompressedSize(csize & 0xffffffffL); + } + + /** + * Closes the current zip entry and moves to the next one. + */ + public void closeEntry() throws IOException + { + if (crc == null) + throw new IOException("Stream closed."); + if (entry == null) + return; + + if (method == ZipOutputStream.DEFLATED) + { + if ((flags & 8) != 0) + { + /* We don't know how much we must skip, read until end. */ + byte[] tmp = new byte[2048]; + while (read(tmp) > 0) + ; + + /* read will close this entry */ + return; + } + csize -= inf.getTotalIn(); + avail = inf.getRemaining(); + } + + if (avail > csize && csize >= 0) + avail -= csize; + else + { + csize -= avail; + avail = 0; + while (csize != 0) + { + long skipped = in.skip(csize & 0xffffffffL); + if (skipped <= 0) + throw new ZipException("zip archive ends early."); + csize -= skipped; + } + } + + size = 0; + crc.reset(); + if (method == ZipOutputStream.DEFLATED) + inf.reset(); + entry = null; + entryAtEOF = true; + } + + public int available() throws IOException + { + return entryAtEOF ? 0 : 1; + } + + /** + * Reads a byte from the current zip entry. + * @return the byte or -1 on EOF. + * @exception IOException if a i/o error occured. + * @exception ZipException if the deflated stream is corrupted. + */ + public int read() throws IOException + { + byte[] b = new byte[1]; + if (read(b, 0, 1) <= 0) + return -1; + return b[0] & 0xff; + } + + /** + * Reads a block of bytes from the current zip entry. + * @return the number of bytes read (may be smaller, even before + * EOF), or -1 on EOF. + * @exception IOException if a i/o error occured. + * @exception ZipException if the deflated stream is corrupted. + */ + public int read(byte[] b, int off, int len) throws IOException + { + if (len == 0) + return 0; + if (crc == null) + throw new IOException("Stream closed."); + if (entry == null) + return -1; + boolean finished = false; + switch (method) + { + case ZipOutputStream.DEFLATED: + len = super.read(b, off, len); + if (len < 0) + { + if (!inf.finished()) + throw new ZipException("Inflater not finished!?"); + avail = inf.getRemaining(); + if ((flags & 8) != 0) + readDataDescr(); + + if (inf.getTotalIn() != csize + || inf.getTotalOut() != size) + throw new ZipException("size mismatch: "+csize+";"+size+" <-> "+inf.getTotalIn()+";"+inf.getTotalOut()); + inf.reset(); + finished = true; + } + break; + + case ZipOutputStream.STORED: + + if (len > csize && csize >= 0) + len = csize; + + len = readBuf(b, off, len); + if (len > 0) + { + csize -= len; + size -= len; + } + + if (csize == 0) + finished = true; + else if (len < 0) + throw new ZipException("EOF in stored block"); + break; + } + + if (len > 0) + crc.update(b, off, len); + + if (finished) + { + if ((crc.getValue() & 0xffffffffL) != entry.getCrc()) + throw new ZipException("CRC mismatch"); + crc.reset(); + entry = null; + entryAtEOF = true; + } + return len; + } + + /** + * Closes the zip file. + * @exception IOException if a i/o error occured. + */ + public void close() throws IOException + { + super.close(); + crc = null; + entry = null; + entryAtEOF = true; + } + + /** + * Creates a new zip entry for the given name. This is equivalent + * to new ZipEntry(name). + * @param name the name of the zip entry. + */ + protected ZipEntry createZipEntry(String name) + { + return new ZipEntry(name); + } +} diff --git a/libjava/classpath/java/util/zip/ZipOutputStream.java b/libjava/classpath/java/util/zip/ZipOutputStream.java new file mode 100644 index 000000000..bc1c3e99c --- /dev/null +++ b/libjava/classpath/java/util/zip/ZipOutputStream.java @@ -0,0 +1,440 @@ +/* ZipOutputStream.java -- + Copyright (C) 2001, 2004, 2005, 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util.zip; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Enumeration; +import java.util.Vector; + +/** + * This is a FilterOutputStream that writes the files into a zip + * archive one after another. It has a special method to start a new + * zip entry. The zip entries contains information about the file name + * size, compressed size, CRC, etc. + * + * It includes support for STORED and DEFLATED entries. + * + * This class is not thread safe. + * + * @author Jochen Hoenicke + */ +public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants +{ + private Vector entries = new Vector(); + private CRC32 crc = new CRC32(); + private ZipEntry curEntry = null; + + private int curMethod; + private int size; + private int offset = 0; + + private byte[] zipComment = new byte[0]; + private int defaultMethod = DEFLATED; + + /** + * Our Zip version is hard coded to 1.0 resp. 2.0 + */ + private static final int ZIP_STORED_VERSION = 10; + private static final int ZIP_DEFLATED_VERSION = 20; + + /** + * Compression method. This method doesn't compress at all. + */ + public static final int STORED = 0; + + /** + * Compression method. This method uses the Deflater. + */ + public static final int DEFLATED = 8; + + /** + * Creates a new Zip output stream, writing a zip archive. + * @param out the output stream to which the zip archive is written. + */ + public ZipOutputStream(OutputStream out) + { + super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); + } + + /** + * Set the zip file comment. + * @param comment the comment. + * @exception IllegalArgumentException if encoding of comment is + * longer than 0xffff bytes. + */ + public void setComment(String comment) + { + byte[] commentBytes; + try + { + commentBytes = comment.getBytes("UTF-8"); + } + catch (UnsupportedEncodingException uee) + { + throw new AssertionError(uee); + } + if (commentBytes.length > 0xffff) + throw new IllegalArgumentException("Comment too long."); + zipComment = commentBytes; + } + + /** + * Sets default compression method. If the Zip entry specifies + * another method its method takes precedence. + * @param method the method. + * @exception IllegalArgumentException if method is not supported. + * @see #STORED + * @see #DEFLATED + */ + public void setMethod(int method) + { + if (method != STORED && method != DEFLATED) + throw new IllegalArgumentException("Method not supported."); + defaultMethod = method; + } + + /** + * Sets default compression level. The new level will be activated + * immediately. + * @exception IllegalArgumentException if level is not supported. + * @see Deflater + */ + public void setLevel(int level) + { + def.setLevel(level); + } + + /** + * Write an unsigned short in little endian byte order. + */ + private void writeLeShort(int value) throws IOException + { + out.write(value & 0xff); + out.write((value >> 8) & 0xff); + } + + /** + * Write an int in little endian byte order. + */ + private void writeLeInt(int value) throws IOException + { + writeLeShort(value); + writeLeShort(value >> 16); + } + + /** + * Write a long value as an int. Some of the zip constants + * are declared as longs even though they fit perfectly well + * into integers. + */ + private void writeLeInt(long value) throws IOException + { + writeLeInt((int) value); + } + + /** + * Starts a new Zip entry. It automatically closes the previous + * entry if present. If the compression method is stored, the entry + * must have a valid size and crc, otherwise all elements (except + * name) are optional, but must be correct if present. If the time + * is not set in the entry, the current time is used. + * @param entry the entry. + * @exception IOException if an I/O error occured. + * @exception ZipException if stream was finished. + */ + public void putNextEntry(ZipEntry entry) throws IOException + { + if (entries == null) + throw new ZipException("ZipOutputStream was finished"); + + int method = entry.getMethod(); + int flags = 0; + if (method == -1) + method = defaultMethod; + + if (method == STORED) + { + if (entry.getCompressedSize() >= 0) + { + if (entry.getSize() < 0) + entry.setSize(entry.getCompressedSize()); + else if (entry.getSize() != entry.getCompressedSize()) + throw new ZipException + ("Method STORED, but compressed size != size"); + } + else + entry.setCompressedSize(entry.getSize()); + + if (entry.getSize() < 0) + throw new ZipException("Method STORED, but size not set"); + if (entry.getCrc() < 0) + throw new ZipException("Method STORED, but crc not set"); + } + else if (method == DEFLATED) + { + if (entry.getCompressedSize() < 0 + || entry.getSize() < 0 || entry.getCrc() < 0) + flags |= 8; + } + + if (curEntry != null) + closeEntry(); + + if (entry.getTime() < 0) + entry.setTime(System.currentTimeMillis()); + + entry.flags = flags; + entry.offset = offset; + entry.setMethod(method); + curMethod = method; + /* Write the local file header */ + writeLeInt(LOCSIG); + writeLeShort(method == STORED + ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); + writeLeShort(flags); + writeLeShort(method); + writeLeInt(entry.getDOSTime()); + if ((flags & 8) == 0) + { + writeLeInt((int)entry.getCrc()); + writeLeInt((int)entry.getCompressedSize()); + writeLeInt((int)entry.getSize()); + } + else + { + writeLeInt(0); + writeLeInt(0); + writeLeInt(0); + } + byte[] name; + try + { + name = entry.getName().getBytes("UTF-8"); + } + catch (UnsupportedEncodingException uee) + { + throw new AssertionError(uee); + } + if (name.length > 0xffff) + throw new ZipException("Name too long."); + byte[] extra = entry.getExtra(); + if (extra == null) + extra = new byte[0]; + writeLeShort(name.length); + writeLeShort(extra.length); + out.write(name); + out.write(extra); + + offset += LOCHDR + name.length + extra.length; + + /* Activate the entry. */ + + curEntry = entry; + crc.reset(); + if (method == DEFLATED) + def.reset(); + size = 0; + } + + /** + * Closes the current entry. + * @exception IOException if an I/O error occured. + * @exception ZipException if no entry is active. + */ + public void closeEntry() throws IOException + { + if (curEntry == null) + throw new ZipException("No open entry"); + + /* First finish the deflater, if appropriate */ + if (curMethod == DEFLATED) + super.finish(); + + int csize = curMethod == DEFLATED ? def.getTotalOut() : size; + + if (curEntry.getSize() < 0) + curEntry.setSize(size); + else if (curEntry.getSize() != size) + throw new ZipException("size was "+size + +", but I expected "+curEntry.getSize()); + + if (curEntry.getCompressedSize() < 0) + curEntry.setCompressedSize(csize); + else if (curEntry.getCompressedSize() != csize) + throw new ZipException("compressed size was "+csize + +", but I expected "+curEntry.getSize()); + + if (curEntry.getCrc() < 0) + curEntry.setCrc(crc.getValue()); + else if (curEntry.getCrc() != crc.getValue()) + throw new ZipException("crc was " + Long.toHexString(crc.getValue()) + + ", but I expected " + + Long.toHexString(curEntry.getCrc())); + + offset += csize; + + /* Now write the data descriptor entry if needed. */ + if (curMethod == DEFLATED && (curEntry.flags & 8) != 0) + { + writeLeInt(EXTSIG); + writeLeInt((int)curEntry.getCrc()); + writeLeInt((int)curEntry.getCompressedSize()); + writeLeInt((int)curEntry.getSize()); + offset += EXTHDR; + } + + entries.addElement(curEntry); + curEntry = null; + } + + /** + * Writes the given buffer to the current entry. + * @exception IOException if an I/O error occured. + * @exception ZipException if no entry is active. + */ + public void write(byte[] b, int off, int len) throws IOException + { + if (curEntry == null) + throw new ZipException("No open entry."); + + switch (curMethod) + { + case DEFLATED: + super.write(b, off, len); + break; + + case STORED: + out.write(b, off, len); + break; + } + + crc.update(b, off, len); + size += len; + } + + /** + * Finishes the stream. This will write the central directory at the + * end of the zip file and flush the stream. + * @exception IOException if an I/O error occured. + */ + public void finish() throws IOException + { + if (entries == null) + return; + if (curEntry != null) + closeEntry(); + + int numEntries = 0; + int sizeEntries = 0; + + Enumeration e = entries.elements(); + while (e.hasMoreElements()) + { + ZipEntry entry = (ZipEntry) e.nextElement(); + + int method = entry.getMethod(); + writeLeInt(CENSIG); + writeLeShort(method == STORED + ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); + writeLeShort(method == STORED + ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION); + writeLeShort(entry.flags); + writeLeShort(method); + writeLeInt(entry.getDOSTime()); + writeLeInt((int)entry.getCrc()); + writeLeInt((int)entry.getCompressedSize()); + writeLeInt((int)entry.getSize()); + + byte[] name; + try + { + name = entry.getName().getBytes("UTF-8"); + } + catch (UnsupportedEncodingException uee) + { + throw new AssertionError(uee); + } + if (name.length > 0xffff) + throw new ZipException("Name too long."); + byte[] extra = entry.getExtra(); + if (extra == null) + extra = new byte[0]; + String str = entry.getComment(); + byte[] comment; + try + { + comment = str != null ? str.getBytes("UTF-8") : new byte[0]; + } + catch (UnsupportedEncodingException uee) + { + throw new AssertionError(uee); + } + if (comment.length > 0xffff) + throw new ZipException("Comment too long."); + + writeLeShort(name.length); + writeLeShort(extra.length); + writeLeShort(comment.length); + writeLeShort(0); /* disk number */ + writeLeShort(0); /* internal file attr */ + writeLeInt(0); /* external file attr */ + writeLeInt(entry.offset); + + out.write(name); + out.write(extra); + out.write(comment); + numEntries++; + sizeEntries += CENHDR + name.length + extra.length + comment.length; + } + + writeLeInt(ENDSIG); + writeLeShort(0); /* disk number */ + writeLeShort(0); /* disk with start of central dir */ + writeLeShort(numEntries); + writeLeShort(numEntries); + writeLeInt(sizeEntries); + writeLeInt(offset); + writeLeShort(zipComment.length); + out.write(zipComment); + out.flush(); + entries = null; + } +} diff --git a/libjava/classpath/java/util/zip/package.html b/libjava/classpath/java/util/zip/package.html new file mode 100644 index 000000000..5f3ac4917 --- /dev/null +++ b/libjava/classpath/java/util/zip/package.html @@ -0,0 +1,47 @@ + + + + +GNU Classpath - java.util.zip + + +

        Utility classes to manipulate zip and gzip archives as files or streams, +includes checksum and compression support.

        + + + -- cgit v1.2.3

    + * Returns the hyperbolic cosine of the given value. For a value, + * x, the hyperbolic cosine is (ex + + * e-x)/2 + * with e being Euler's number. The returned + * result is within 2.5 ulps of the exact result. + *